From 77363cc6dba3277df932f4ee0f91eac06741d7b6 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 4 Jun 2019 23:00:11 +0200 Subject: [PATCH 001/305] Show hash on sendrawtransaction (#796) * Show hash on sendrawtransaction * Update RpcServer.cs * txid * relayed * Revert to hash * txid -> hash * Update RpcServer.cs * Fix test --- neo.UnitTests/UT_Block.cs | 9 ++------- neo.UnitTests/UT_Transaction.cs | 2 +- neo/Network/P2P/Payloads/Transaction.cs | 2 +- neo/Network/RPC/RpcServer.cs | 12 ++++++++---- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/neo.UnitTests/UT_Block.cs b/neo.UnitTests/UT_Block.cs index c0c1163d5e..f7acf254be 100644 --- a/neo.UnitTests/UT_Block.cs +++ b/neo.UnitTests/UT_Block.cs @@ -240,12 +240,7 @@ public void RebuildMerkleRoot_Updates() public void ToJson() { UInt256 val256 = UInt256.Zero; - UInt256 merkRoot; - UInt160 val160; - uint timestampVal, 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 var merkRoot, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 1); JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); @@ -264,7 +259,7 @@ public void ToJson() jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; - txObj[0]["txid"].AsString().Should().Be("0x7647acf1ef8a841b87f2369398a0e9f0ccde0ed9e835d980657103da6da59580"); + txObj[0]["hash"].AsString().Should().Be("0x7647acf1ef8a841b87f2369398a0e9f0ccde0ed9e835d980657103da6da59580"); txObj[0]["size"].AsNumber().Should().Be(50); txObj[0]["version"].AsNumber().Should().Be(0); ((JArray)txObj[0]["attributes"]).Count.Should().Be(0); diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 2600867986..b192a2479d 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -82,7 +82,7 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["txid"].AsString().Should().Be("0x38274692538dfecaae36f8fd518d92bae25607d491c40a8f927cc06bd97ab2c8"); + jObj["hash"].AsString().Should().Be("0x38274692538dfecaae36f8fd518d92bae25607d491c40a8f927cc06bd97ab2c8"); jObj["size"].AsNumber().Should().Be(81); jObj["version"].AsNumber().Should().Be(0); ((JArray)jObj["attributes"]).Count.Should().Be(0); diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 52a6fc867a..b21f02ccbb 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -176,7 +176,7 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) public JObject ToJson() { JObject json = new JObject(); - json["txid"] = Hash.ToString(); + json["hash"] = Hash.ToString(); json["size"] = Size; json["version"] = Version; json["nonce"] = Nonce; diff --git a/neo/Network/RPC/RpcServer.cs b/neo/Network/RPC/RpcServer.cs index 6b5e7f549d..6bfdd15618 100644 --- a/neo/Network/RPC/RpcServer.cs +++ b/neo/Network/RPC/RpcServer.cs @@ -90,12 +90,16 @@ private JObject GetInvokeResult(byte[] script) return json; } - private static JObject GetRelayResult(RelayResultReason reason) + private static JObject GetRelayResult(RelayResultReason reason, UInt256 hash) { switch (reason) { case RelayResultReason.Succeed: - return true; + { + var ret = new JObject(); + ret["hash"] = hash.ToString(); + return ret; + } case RelayResultReason.AlreadyExists: throw new RpcException(-501, "Block or transaction already exists and cannot be sent repeatedly."); case RelayResultReason.OutOfMemory: @@ -606,13 +610,13 @@ private JObject ListPlugins() private JObject SendRawTransaction(Transaction tx) { RelayResultReason reason = system.Blockchain.Ask(tx).Result; - return GetRelayResult(reason); + return GetRelayResult(reason, tx.Hash); } private JObject SubmitBlock(Block block) { RelayResultReason reason = system.Blockchain.Ask(block).Result; - return GetRelayResult(reason); + return GetRelayResult(reason, block.Hash); } private JObject ValidateAddress(string address) From 94070491ee57bf11e18f27baeb473cbbe7e1c1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Mon, 10 Jun 2019 06:15:31 -0300 Subject: [PATCH 002/305] Drop invalid messages and set low priority (3.x) (#805) --- neo/Network/P2P/ProtocolHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/Network/P2P/ProtocolHandler.cs b/neo/Network/P2P/ProtocolHandler.cs index 099b2d6dca..487efda6dd 100644 --- a/neo/Network/P2P/ProtocolHandler.cs +++ b/neo/Network/P2P/ProtocolHandler.cs @@ -309,7 +309,7 @@ public ProtocolHandlerMailbox(Settings settings, Config config) protected override bool IsHighPriority(object message) { - if (!(message is Message msg)) return true; + if (!(message is Message msg)) return false; switch (msg.Command) { case MessageCommand.Consensus: @@ -327,7 +327,7 @@ protected override bool IsHighPriority(object message) protected override bool ShallDrop(object message, IEnumerable queue) { - if (!(message is Message msg)) return false; + if (!(message is Message msg)) return true; switch (msg.Command) { case MessageCommand.GetAddr: From 7dc1b56b4228d31e5b494239e79a90633023b4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Tue, 11 Jun 2019 17:04:02 -0300 Subject: [PATCH 003/305] Test travis with most recent dotnet (#815) --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 760992a634..516dd5855c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ dist: bionic osx_image: xcode9.1 mono: none -dotnet: 2.1.502 +dotnet: 2.2.300 before_install: - cd neo.UnitTests From 29bdd9d8a24abb6ce0164c74371609160e1e60ed Mon Sep 17 00:00:00 2001 From: Alvaro Diaz Date: Tue, 11 Jun 2019 22:19:41 +0200 Subject: [PATCH 004/305] Create SECURITY.md (#811) --- SECURITY.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..24894d5e9a --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +The purpose of NEO vulnerability bounty program is to be proactive about blockchain security by providing a channel for security researchers to report potential security vulnerabilities identified related to our underlying infrastructure. + +First, it is recommended to read the following page https://neo.org/dev/bounty, if you find a security vulnerability in NEO, please report it as indicated on that page. + +Please withhold public disclosure until the security team has addressed the vulnerability and it has been solved. + +We appreciate your efforts to responsibly disclose your findings, and we will make every effort to acknowledge your contributions. + +The security team will acknowledge your email within 5 business days. You will receive a more detailed response within 10 business days. + +When in doubt, please do send us a report. From fa8b85fd321e70ec5862cd91045676d075c7ea40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Tue, 11 Jun 2019 17:54:26 -0300 Subject: [PATCH 005/305] Declaring timeout variables for connection (#807) * Declaring timeout variables for connection * Improving comments --- neo/Network/P2P/Connection.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/neo/Network/P2P/Connection.cs b/neo/Network/P2P/Connection.cs index a6ae95f8f5..b7a007ad62 100644 --- a/neo/Network/P2P/Connection.cs +++ b/neo/Network/P2P/Connection.cs @@ -19,12 +19,20 @@ internal class Ack : Tcp.Event { public static Ack Instance = new Ack(); } private readonly IActorRef tcp; private readonly WebSocket ws; private bool disconnected = false; + /// + /// connection initial timeout (in seconds) before any package has been accepted + /// + private double connectionTimeoutLimitStart = 10; + /// + /// connection timeout (in seconds) after every `OnReceived(ByteString data)` event + /// + private double connectionTimeoutLimit = 60; protected Connection(object connection, IPEndPoint remote, IPEndPoint local) { this.Remote = remote; this.Local = local; - this.timer = Context.System.Scheduler.ScheduleTellOnceCancelable(TimeSpan.FromSeconds(10), Self, Timer.Instance, ActorRefs.NoSender); + this.timer = Context.System.Scheduler.ScheduleTellOnceCancelable(TimeSpan.FromSeconds(connectionTimeoutLimitStart), Self, Timer.Instance, ActorRefs.NoSender); switch (connection) { case IActorRef tcp: @@ -100,7 +108,7 @@ protected override void OnReceive(object message) private void OnReceived(ByteString data) { timer.CancelIfNotNull(); - timer = Context.System.Scheduler.ScheduleTellOnceCancelable(TimeSpan.FromMinutes(1), Self, Timer.Instance, ActorRefs.NoSender); + timer = Context.System.Scheduler.ScheduleTellOnceCancelable(TimeSpan.FromSeconds(connectionTimeoutLimit), Self, Timer.Instance, ActorRefs.NoSender); try { OnData(data); From b6b4c89c790a4bed3c63f7c494577d1a53ad6adc Mon Sep 17 00:00:00 2001 From: Shine Li Date: Thu, 13 Jun 2019 01:00:26 +0800 Subject: [PATCH 006/305] change load method (#816) --- neo/Plugins/Plugin.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo/Plugins/Plugin.cs b/neo/Plugins/Plugin.cs index 59699e7562..862ff6ae9a 100644 --- a/neo/Plugins/Plugin.cs +++ b/neo/Plugins/Plugin.cs @@ -97,7 +97,8 @@ internal static void LoadPlugins(NeoSystem system) if (!Directory.Exists(pluginsPath)) return; foreach (string filename in Directory.EnumerateFiles(pluginsPath, "*.dll", SearchOption.TopDirectoryOnly)) { - Assembly assembly = Assembly.LoadFile(filename); + var file = File.ReadAllBytes(filename); + Assembly assembly = Assembly.Load(file); foreach (Type type in assembly.ExportedTypes) { if (!type.IsSubclassOf(typeof(Plugin))) continue; From e9633788c0f13279f3e99fd4bcbbbdc84c93520a Mon Sep 17 00:00:00 2001 From: Igor Machado Coelho Date: Wed, 12 Jun 2019 14:58:04 -0300 Subject: [PATCH 007/305] Tests for ProtocolHandler Mailboxes (Neo 3.x) (#809) * tests for mailboxes on 3x * Fixing UT to MessageCommand * Fixing string * Explicit Byte * MessageCommand with random byte * space * testing all cases * Update UT_ConsensusServiceMailbox.cs * Update UT_RemoteNodeMailbox.cs * Update UT_TaskManagerMailbox.cs --- neo.UnitTests/UT_ConsensusServiceMailbox.cs | 37 ++++ neo.UnitTests/UT_ProtocolHandlerMailbox.cs | 194 ++++++++++++++++++++ neo.UnitTests/UT_RemoteNodeMailbox.cs | 36 ++++ neo.UnitTests/UT_TaskManagerMailbox.cs | 36 ++++ neo/Consensus/ConsensusService.cs | 2 +- neo/IO/Actors/PriorityMailbox.cs | 4 +- neo/Ledger/Blockchain.cs | 2 +- neo/Network/P2P/ProtocolHandler.cs | 4 +- neo/Network/P2P/RemoteNode.cs | 2 +- neo/Network/P2P/TaskManager.cs | 2 +- 10 files changed, 311 insertions(+), 8 deletions(-) create mode 100644 neo.UnitTests/UT_ConsensusServiceMailbox.cs create mode 100644 neo.UnitTests/UT_ProtocolHandlerMailbox.cs create mode 100644 neo.UnitTests/UT_RemoteNodeMailbox.cs create mode 100644 neo.UnitTests/UT_TaskManagerMailbox.cs diff --git a/neo.UnitTests/UT_ConsensusServiceMailbox.cs b/neo.UnitTests/UT_ConsensusServiceMailbox.cs new file mode 100644 index 0000000000..de0d02cf2e --- /dev/null +++ b/neo.UnitTests/UT_ConsensusServiceMailbox.cs @@ -0,0 +1,37 @@ +using Akka.TestKit; +using Akka.TestKit.Xunit2; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using Moq; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Network.P2P; +using Akka.Configuration; +using Neo.Consensus; + +namespace Neo.UnitTests +{ + [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); + } + } +} diff --git a/neo.UnitTests/UT_ProtocolHandlerMailbox.cs b/neo.UnitTests/UT_ProtocolHandlerMailbox.cs new file mode 100644 index 0000000000..4645e3cfdc --- /dev/null +++ b/neo.UnitTests/UT_ProtocolHandlerMailbox.cs @@ -0,0 +1,194 @@ +using Akka.TestKit; +using Akka.TestKit.Xunit2; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using Moq; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Network.P2P; +using Akka.Configuration; +using Neo.IO; +using System.Linq; +using System.Collections.Generic; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_ProtocolHandlerMailbox : TestKit + { + private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism + + ProtocolHandlerMailbox 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 ProtocolHandlerMailbox(akkaSettings, config); + } + + [TestMethod] + public void ProtocolHandlerMailbox_Test_IsHighPriority() + { + ISerializable s = null; + + //handshaking + uut.IsHighPriority(Message.Create(MessageCommand.Version, s)).Should().Be(true); + uut.IsHighPriority(Message.Create(MessageCommand.Verack, s)).Should().Be(true); + + //connectivity + uut.IsHighPriority(Message.Create(MessageCommand.GetAddr, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Addr, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Ping, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Pong, s)).Should().Be(false); + + //synchronization + uut.IsHighPriority(Message.Create(MessageCommand.GetHeaders, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Headers, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.GetBlocks, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Mempool, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Inv, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.GetData, s)).Should().Be(false); + 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.Reject, s)).Should().Be(false); + + //SPV protocol + uut.IsHighPriority(Message.Create(MessageCommand.FilterLoad, s)).Should().Be(true); + uut.IsHighPriority(Message.Create(MessageCommand.FilterAdd, s)).Should().Be(true); + uut.IsHighPriority(Message.Create(MessageCommand.FilterClear, s)).Should().Be(true); + uut.IsHighPriority(Message.Create(MessageCommand.MerkleBlock, s)).Should().Be(false); + + //others + uut.IsHighPriority(Message.Create(MessageCommand.Alert, s)).Should().Be(true); + + // any random object (non Message) should not have priority + object obj = null; + uut.IsHighPriority(obj).Should().Be(false); + } + + + [TestMethod] + public void ProtocolHandlerMailbox_Test_ShallDrop() + { + // using this for messages + ISerializable s = null; + Message msg = null; // multiple uses + // empty queue + IEnumerable emptyQueue = Enumerable.Empty(); + + // any random object (non Message) should be dropped + object obj = null; + uut.ShallDrop(obj, emptyQueue).Should().Be(true); + + //handshaking + // Version (no drop) + msg = Message.Create(MessageCommand.Version, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // Verack (no drop) + msg = Message.Create(MessageCommand.Verack, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + + //connectivity + // GetAddr (drop) + msg = Message.Create(MessageCommand.GetAddr, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(true); + // Addr (no drop) + msg = Message.Create(MessageCommand.Addr, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // Ping (no drop) + msg = Message.Create(MessageCommand.Ping, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // Pong (no drop) + msg = Message.Create(MessageCommand.Pong, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + + //synchronization + // GetHeaders (drop) + msg = Message.Create(MessageCommand.GetHeaders, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(true); + // Headers (no drop) + msg = Message.Create(MessageCommand.Headers, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // GetBlocks (drop) + msg = Message.Create(MessageCommand.GetBlocks, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(true); + // Mempool (drop) + msg = Message.Create(MessageCommand.Mempool, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(true); + // Inv (no drop) + msg = Message.Create(MessageCommand.Inv, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // GetData (drop) + msg = Message.Create(MessageCommand.GetData, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(true); + // NotFound (no drop) + msg = Message.Create(MessageCommand.NotFound, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // Transaction (no drop) + msg = Message.Create(MessageCommand.Transaction, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // Block (no drop) + msg = Message.Create(MessageCommand.Block, s); + 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); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // Reject (no drop) + msg = Message.Create(MessageCommand.Reject, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + + //SPV protocol + // FilterLoad (no drop) + msg = Message.Create(MessageCommand.FilterLoad, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // FilterAdd (no drop) + msg = Message.Create(MessageCommand.FilterAdd, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // FilterClear (no drop) + msg = Message.Create(MessageCommand.FilterClear, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + // MerkleBlock (no drop) + msg = Message.Create(MessageCommand.MerkleBlock, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + + //others + // Alert (no drop) + msg = Message.Create(MessageCommand.Alert, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + } + } +} diff --git a/neo.UnitTests/UT_RemoteNodeMailbox.cs b/neo.UnitTests/UT_RemoteNodeMailbox.cs new file mode 100644 index 0000000000..95b732e18a --- /dev/null +++ b/neo.UnitTests/UT_RemoteNodeMailbox.cs @@ -0,0 +1,36 @@ +using Akka.TestKit; +using Akka.TestKit.Xunit2; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using Moq; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Network.P2P; +using Akka.Configuration; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_RemoteNodeMailbox : TestKit + { + private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism + + RemoteNodeMailbox 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 RemoteNodeMailbox(akkaSettings, config); + } + } +} diff --git a/neo.UnitTests/UT_TaskManagerMailbox.cs b/neo.UnitTests/UT_TaskManagerMailbox.cs new file mode 100644 index 0000000000..4d6dd32a01 --- /dev/null +++ b/neo.UnitTests/UT_TaskManagerMailbox.cs @@ -0,0 +1,36 @@ +using Akka.TestKit; +using Akka.TestKit.Xunit2; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using Moq; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Network.P2P; +using Akka.Configuration; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_TaskManagerMailbox : TestKit + { + private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism + + TaskManagerMailbox 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 TaskManagerMailbox(akkaSettings, config); + } + } +} diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 3517b822af..2f538f2b6e 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -626,7 +626,7 @@ public ConsensusServiceMailbox(Akka.Actor.Settings settings, Config config) { } - protected override bool IsHighPriority(object message) + internal protected override bool IsHighPriority(object message) { switch (message) { diff --git a/neo/IO/Actors/PriorityMailbox.cs b/neo/IO/Actors/PriorityMailbox.cs index fa345302ea..c6c8a8fbe2 100644 --- a/neo/IO/Actors/PriorityMailbox.cs +++ b/neo/IO/Actors/PriorityMailbox.cs @@ -18,7 +18,7 @@ public override IMessageQueue Create(IActorRef owner, ActorSystem system) return new PriorityMessageQueue(ShallDrop, IsHighPriority); } - protected virtual bool IsHighPriority(object message) => false; - protected virtual bool ShallDrop(object message, IEnumerable queue) => false; + internal protected virtual bool IsHighPriority(object message) => false; + internal protected virtual bool ShallDrop(object message, IEnumerable queue) => false; } } diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index d00bde3c39..4f887b4817 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -528,7 +528,7 @@ public BlockchainMailbox(Akka.Actor.Settings settings, Config config) { } - protected override bool IsHighPriority(object message) + internal protected override bool IsHighPriority(object message) { switch (message) { diff --git a/neo/Network/P2P/ProtocolHandler.cs b/neo/Network/P2P/ProtocolHandler.cs index 487efda6dd..7f9821709b 100644 --- a/neo/Network/P2P/ProtocolHandler.cs +++ b/neo/Network/P2P/ProtocolHandler.cs @@ -307,7 +307,7 @@ public ProtocolHandlerMailbox(Settings settings, Config config) { } - protected override bool IsHighPriority(object message) + internal protected override bool IsHighPriority(object message) { if (!(message is Message msg)) return false; switch (msg.Command) @@ -325,7 +325,7 @@ protected override bool IsHighPriority(object message) } } - protected override bool ShallDrop(object message, IEnumerable queue) + internal protected override bool ShallDrop(object message, IEnumerable queue) { if (!(message is Message msg)) return true; switch (msg.Command) diff --git a/neo/Network/P2P/RemoteNode.cs b/neo/Network/P2P/RemoteNode.cs index 0157e67ebe..7700c896b2 100644 --- a/neo/Network/P2P/RemoteNode.cs +++ b/neo/Network/P2P/RemoteNode.cs @@ -256,7 +256,7 @@ internal class RemoteNodeMailbox : PriorityMailbox { public RemoteNodeMailbox(Settings settings, Config config) : base(settings, config) { } - protected override bool IsHighPriority(object message) + internal protected override bool IsHighPriority(object message) { switch (message) { diff --git a/neo/Network/P2P/TaskManager.cs b/neo/Network/P2P/TaskManager.cs index f7322b448e..9e028f7967 100644 --- a/neo/Network/P2P/TaskManager.cs +++ b/neo/Network/P2P/TaskManager.cs @@ -249,7 +249,7 @@ public TaskManagerMailbox(Akka.Actor.Settings settings, Config config) { } - protected override bool IsHighPriority(object message) + internal protected override bool IsHighPriority(object message) { switch (message) { From 4f7428caa45ac7840b4736e514ece342231acf4c Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 12 Jun 2019 22:03:22 +0200 Subject: [PATCH 008/305] Const (#818) --- neo/Network/P2P/Connection.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/neo/Network/P2P/Connection.cs b/neo/Network/P2P/Connection.cs index b7a007ad62..d741f1d0ee 100644 --- a/neo/Network/P2P/Connection.cs +++ b/neo/Network/P2P/Connection.cs @@ -12,22 +12,22 @@ public abstract class Connection : UntypedActor internal class Timer { public static Timer Instance = new Timer(); } internal class Ack : Tcp.Event { public static Ack Instance = new Ack(); } - public IPEndPoint Remote { get; } - public IPEndPoint Local { get; } - - private ICancelable timer; - private readonly IActorRef tcp; - private readonly WebSocket ws; - private bool disconnected = false; /// /// connection initial timeout (in seconds) before any package has been accepted /// - private double connectionTimeoutLimitStart = 10; + private const int connectionTimeoutLimitStart = 10; /// /// connection timeout (in seconds) after every `OnReceived(ByteString data)` event /// - private double connectionTimeoutLimit = 60; + private const int connectionTimeoutLimit = 60; + public IPEndPoint Remote { get; } + public IPEndPoint Local { get; } + + private ICancelable timer; + private readonly IActorRef tcp; + private readonly WebSocket ws; + private bool disconnected = false; protected Connection(object connection, IPEndPoint remote, IPEndPoint local) { this.Remote = remote; From f179678a644064fdc4e001b0201a60636dcf7f87 Mon Sep 17 00:00:00 2001 From: Igor Machado Coelho Date: Wed, 12 Jun 2019 23:44:02 -0300 Subject: [PATCH 009/305] Tested all akka mailboxes (Neo 3) (#823) * tested all akka mailboxes * better names --- neo.UnitTests/UT_ConsensusServiceMailbox.cs | 14 ++++++++++++++ neo.UnitTests/UT_RemoteNodeMailbox.cs | 16 ++++++++++++++++ neo.UnitTests/UT_TaskManagerMailbox.cs | 21 +++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/neo.UnitTests/UT_ConsensusServiceMailbox.cs b/neo.UnitTests/UT_ConsensusServiceMailbox.cs index de0d02cf2e..4000b99f61 100644 --- a/neo.UnitTests/UT_ConsensusServiceMailbox.cs +++ b/neo.UnitTests/UT_ConsensusServiceMailbox.cs @@ -33,5 +33,19 @@ public void TestSetup() var akkaSettings = new Akka.Actor.Settings(system, config); uut = new ConsensusServiceMailbox(akkaSettings, config); } + + [TestMethod] + public void ConsensusServiceMailbox_Test_IsHighPriority() + { + // high priority + uut.IsHighPriority(new ConsensusPayload()).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/neo.UnitTests/UT_RemoteNodeMailbox.cs b/neo.UnitTests/UT_RemoteNodeMailbox.cs index 95b732e18a..9b35bf44e0 100644 --- a/neo.UnitTests/UT_RemoteNodeMailbox.cs +++ b/neo.UnitTests/UT_RemoteNodeMailbox.cs @@ -8,6 +8,9 @@ using Neo.Network.P2P.Payloads; using Neo.Network.P2P; using Akka.Configuration; +using System.Net; +using Akka.Actor; +using Akka.IO; namespace Neo.UnitTests { @@ -32,5 +35,18 @@ public void TestSetup() var akkaSettings = new Akka.Actor.Settings(system, config); uut = new RemoteNodeMailbox(akkaSettings, config); } + + [TestMethod] + public void RemoteNode_Test_IsHighPriority() + { + // high priority commands + uut.IsHighPriority(new Tcp.ConnectionClosed()).Should().Be(true); + uut.IsHighPriority(new Connection.Timer()).Should().Be(true); + uut.IsHighPriority(new Connection.Ack()).Should().Be(true); + + // any random object should not have priority + object obj = null; + uut.IsHighPriority(obj).Should().Be(false); + } } } diff --git a/neo.UnitTests/UT_TaskManagerMailbox.cs b/neo.UnitTests/UT_TaskManagerMailbox.cs index 4d6dd32a01..17244d80d9 100644 --- a/neo.UnitTests/UT_TaskManagerMailbox.cs +++ b/neo.UnitTests/UT_TaskManagerMailbox.cs @@ -32,5 +32,26 @@ public void TestSetup() var akkaSettings = new Akka.Actor.Settings(system, config); uut = new TaskManagerMailbox(akkaSettings, config); } + + [TestMethod] + public void TaskManager_Test_IsHighPriority() + { + // high priority + uut.IsHighPriority(new TaskManager.Register()).Should().Be(true); + uut.IsHighPriority(new TaskManager.RestartTasks()).Should().Be(true); + + // low priority + // -> NewTasks: generic InvPayload + uut.IsHighPriority(new TaskManager.NewTasks{ Payload = new InvPayload() }).Should().Be(false); + + // 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); + + // any random object should not have priority + object obj = null; + uut.IsHighPriority(obj).Should().Be(false); + } } } From 776088d83157029f71e30274c2ca183d0c92467e Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 13 Jun 2019 10:41:32 +0200 Subject: [PATCH 010/305] GetInvocationCounter (#813) * GetInvocationCounter * UT as sample * Fix travis * Update neo/SmartContract/InteropService.cs * Update neo/SmartContract/ApplicationEngine.cs Co-Authored-By: Erik Zhang * Update neo/SmartContract/InteropService.cs Co-Authored-By: Erik Zhang * We need Writable dictionary * internal --- neo.UnitTests/UT_Syscalls.cs | 64 ++++++++++++++++++++++++++ neo/SmartContract/ApplicationEngine.cs | 1 + neo/SmartContract/InteropService.cs | 21 +++++++++ 3 files changed, 86 insertions(+) create mode 100644 neo.UnitTests/UT_Syscalls.cs diff --git a/neo.UnitTests/UT_Syscalls.cs b/neo.UnitTests/UT_Syscalls.cs new file mode 100644 index 0000000000..82c857b6d9 --- /dev/null +++ b/neo.UnitTests/UT_Syscalls.cs @@ -0,0 +1,64 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Ledger; +using Neo.SmartContract; +using Neo.VM; +using System.Linq; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_Syscalls + { + [TestMethod] + public void System_Runtime_GetInvocationCounter() + { + var snapshot = TestBlockchain.GetStore().GetSnapshot(); + var contracts = (TestDataCache)snapshot.Contracts; + + // Call System.Runtime.GetInvocationCounter syscall + + var script = new ScriptBuilder(); + script.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); + + // Init A,B,C contracts + // First two drops is for drop method and arguments + + var contractA = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script.ToArray()).ToArray() }; + var contractB = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; + var contractC = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; + + contracts.DeleteWhere((a, b) => true); + contracts.Add(contractA.ScriptHash, contractA); + contracts.Add(contractB.ScriptHash, contractB); + contracts.Add(contractC.ScriptHash, contractC); + + // Call A,B,B,C + + script = new ScriptBuilder(); + script.EmitSysCall(InteropService.System_Contract_Call, contractA.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.System_Contract_Call, contractC.ScriptHash.ToArray(), "dummyMain", 0); + + // Execute + + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(engine.Execute(), VMState.HALT); + + // Check the results + + CollectionAssert.AreEqual + ( + engine.ResultStack.Select(u => (int)((VM.Types.Integer)u).GetBigInteger()).ToArray(), + new int[] + { + 1, /* A */ + 1, /* B */ + 2, /* B */ + 1 /* C */ + } + ); + } + } +} \ No newline at end of file diff --git a/neo/SmartContract/ApplicationEngine.cs b/neo/SmartContract/ApplicationEngine.cs index 5562ac0ef2..ced43d379e 100644 --- a/neo/SmartContract/ApplicationEngine.cs +++ b/neo/SmartContract/ApplicationEngine.cs @@ -27,6 +27,7 @@ public partial class ApplicationEngine : ExecutionEngine public UInt160 CallingScriptHash => hashes.Count > 1 ? hashes.Peek(1) : null; public UInt160 EntryScriptHash => hashes.Count > 0 ? hashes.Peek(hashes.Count - 1) : null; public IReadOnlyList Notifications => notifications; + internal Dictionary InvocationCounter { get; } = new Dictionary(); public ApplicationEngine(TriggerType trigger, IVerifiable container, Snapshot snapshot, long gas, bool testMode = false) { diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index b110fe7796..413f9e8f9c 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -39,6 +39,7 @@ public static partial class InteropService public static readonly uint System_Runtime_GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250); public static readonly uint System_Runtime_Serialize = Register("System.Runtime.Serialize", Runtime_Serialize, 0_00100000); public static readonly uint System_Runtime_Deserialize = Register("System.Runtime.Deserialize", Runtime_Deserialize, 0_00500000); + public static readonly uint System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400); public static readonly uint System_Crypto_Verify = Register("System.Crypto.Verify", Crypto_Verify, 0_01000000); public static readonly uint System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400); public static readonly uint System_Blockchain_GetHeader = Register("System.Blockchain.GetHeader", Blockchain_GetHeader, 0_00007000); @@ -212,6 +213,17 @@ private static bool Runtime_Serialize(ApplicationEngine engine) return true; } + private static bool Runtime_GetInvocationCounter(ApplicationEngine engine) + { + if (!engine.InvocationCounter.TryGetValue(engine.CurrentScriptHash, out var counter)) + { + return false; + } + + engine.CurrentContext.EvaluationStack.Push(counter); + return true; + } + private static bool Runtime_Deserialize(ApplicationEngine engine) { StackItem item; @@ -499,6 +511,15 @@ private static bool Contract_Call(ApplicationEngine engine) if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method.GetString())) return false; + if (engine.InvocationCounter.TryGetValue(contract.ScriptHash, out var counter)) + { + engine.InvocationCounter[contract.ScriptHash] = counter + 1; + } + else + { + engine.InvocationCounter[contract.ScriptHash] = 1; + } + ExecutionContext context_new = engine.LoadScript(contract.Script, 1); context_new.EvaluationStack.Push(args); context_new.EvaluationStack.Push(method); From 74681c46f481490ac2594c04a2a6de1a5fb5fc38 Mon Sep 17 00:00:00 2001 From: Shargon Date: Sat, 15 Jun 2019 10:50:01 +0200 Subject: [PATCH 011/305] Remove empty keys (#825) * Close https://github.com/neo-project/neo/issues/824 * optimize * remove redundancy * simplified * allow const empty array * Update InteropService.cs * Update InteropService.cs --- neo/SmartContract/InteropService.cs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 413f9e8f9c..ac75c421dc 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -546,15 +546,26 @@ private static bool PutEx(ApplicationEngine engine, StorageContext context, byte if (value.Length > MaxStorageValueSize) return false; if (context.IsReadOnly) return false; if (!CheckStorageContext(engine, context)) return false; + StorageKey skey = new StorageKey { ScriptHash = context.ScriptHash, Key = key }; - StorageItem item = engine.Snapshot.Storages.GetAndChange(skey, () => new StorageItem()); - if (item.IsConstant) return false; - item.Value = value; - item.IsConstant = flags.HasFlag(StorageFlags.Constant); + + if (engine.Snapshot.Storages.TryGet(skey)?.IsConstant == true) return false; + + if (value.Length == 0 && !flags.HasFlag(StorageFlags.Constant)) + { + // If put 'value' is empty (and non-const), we remove it (implicit `Storage.Delete`) + engine.Snapshot.Storages.Delete(skey); + } + else + { + StorageItem item = engine.Snapshot.Storages.GetAndChange(skey, () => new StorageItem()); + item.Value = value; + item.IsConstant = flags.HasFlag(StorageFlags.Constant); + } return true; } From 50bd3ce4bd20af8e7abc52cab38cb231929b5c5b Mon Sep 17 00:00:00 2001 From: Igor Machado Coelho Date: Mon, 17 Jun 2019 05:59:51 -0300 Subject: [PATCH 012/305] Improved README (#836) --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index edf06084c8..c1832add4f 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,11 @@ From 2017 until the middle of 2020 (estimated), NEO 2.x will still be supported A link to the essential C# reference implementation can be seen below: -* [neo-core](https://github.com/neo-project/neo/tree/master-2.x) -* [neo-vm](https://github.com/neo-project/neo-vm/tree/master-2.x) -* [neo-cli](https://github.com/neo-project/neo-cli/tree/master-2.x) -* [neo-plugins](https://github.com/neo-project/neo-plugins/tree/master-2.x) -* [neo-compiler](https://github.com/neo-project/neo-compiler/tree/master-2.x) +* [neo](https://github.com/neo-project/neo/tree/master-2.x): core implementation (branch `master-2.x`) +* [neo-vm](https://github.com/neo-project/neo-vm/tree/master-2.x): virtual machine (branch `master-2.x`) +* [neo-cli](https://github.com/neo-project/neo-cli/tree/master-2.x): command-line interface (branch `master-2.x`) +* [neo-plugins](https://github.com/neo-project/neo-plugins/tree/master-2.x): plugins (branch `master-2.x`) +* [neo-devpack-dotnet](https://github.com/neo-project/neo-devpack-dotnet/tree/master-2.x): NeoContract compiler and development pack for .NET (branch `master-2.x`) NEO 1.x was known as Antshares and the roots of the code can be found historically in this current repository. From 92f657273835dec57b3e400c0fe3593ee28b0787 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 19 Jun 2019 07:45:12 +0200 Subject: [PATCH 013/305] Allow to parse uppercase 0X (#842) --- neo.UnitTests/UT_UIntBenchmarks.cs | 12 ++++++++++++ neo/UInt160.cs | 4 ++-- neo/UInt256.cs | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/neo.UnitTests/UT_UIntBenchmarks.cs b/neo.UnitTests/UT_UIntBenchmarks.cs index 1dea95f035..882b5647ae 100644 --- a/neo.UnitTests/UT_UIntBenchmarks.cs +++ b/neo.UnitTests/UT_UIntBenchmarks.cs @@ -47,6 +47,18 @@ public void TestSetup() } } + [TestMethod] + public void Test_UInt160_Parse() + { + string uint160strbig = "0x0001020304050607080900010203040506070809"; + UInt160 num1 = UInt160.Parse(uint160strbig); + num1.ToString().Should().Be("0x0001020304050607080900010203040506070809"); + + string uint160strbig2 = "0X0001020304050607080900010203040506070809"; + UInt160 num2 = UInt160.Parse(uint160strbig2); + num2.ToString().Should().Be("0x0001020304050607080900010203040506070809"); + } + private byte[] RandomBytes(int count) { byte[] randomBytes = new byte[count]; diff --git a/neo/UInt160.cs b/neo/UInt160.cs index ef8e2b1a96..fd11b7fb9b 100644 --- a/neo/UInt160.cs +++ b/neo/UInt160.cs @@ -77,7 +77,7 @@ public static new UInt160 Parse(string value) { if (value == null) throw new ArgumentNullException(); - if (value.StartsWith("0x")) + if (value.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) value = value.Substring(2); if (value.Length != 40) throw new FormatException(); @@ -95,7 +95,7 @@ public static bool TryParse(string s, out UInt160 result) result = null; return false; } - if (s.StartsWith("0x")) + if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) s = s.Substring(2); if (s.Length != 40) { diff --git a/neo/UInt256.cs b/neo/UInt256.cs index 511c6cf34e..de3c9ecd47 100644 --- a/neo/UInt256.cs +++ b/neo/UInt256.cs @@ -78,7 +78,7 @@ public static new UInt256 Parse(string s) { if (s == null) throw new ArgumentNullException(); - if (s.StartsWith("0x")) + if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) s = s.Substring(2); if (s.Length != 64) throw new FormatException(); @@ -96,7 +96,7 @@ public static bool TryParse(string s, out UInt256 result) result = null; return false; } - if (s.StartsWith("0x")) + if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) s = s.Substring(2); if (s.Length != 64) { From bfd3b1d5b0f662166d8b40244be56d7d50eab2bb Mon Sep 17 00:00:00 2001 From: Harry Pierson Date: Wed, 19 Jun 2019 19:44:55 -0700 Subject: [PATCH 014/305] Allow for programmatic configuration of protocol settings (#845) --- neo.UnitTests/UT_ProtocolSettings.cs | 96 ++++++++++++++++++++++++++++ neo/ProtocolSettings.cs | 28 ++++++-- 2 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 neo.UnitTests/UT_ProtocolSettings.cs diff --git a/neo.UnitTests/UT_ProtocolSettings.cs b/neo.UnitTests/UT_ProtocolSettings.cs new file mode 100644 index 0000000000..92cabae19b --- /dev/null +++ b/neo.UnitTests/UT_ProtocolSettings.cs @@ -0,0 +1,96 @@ +using FluentAssertions; +using Microsoft.Extensions.Configuration; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_ProtocolSettings + { + // since ProtocolSettings.Default is designed to be writable only once, use reflection to + // reset the underlying _default field to null before and after running tests in this class. + static void ResetProtocolSettings() + { + var defaultField = typeof(ProtocolSettings) + .GetField("_default", BindingFlags.Static | BindingFlags.NonPublic); + defaultField.SetValue(null, null); + } + + [TestInitialize] + public void Initialize() + { + ResetProtocolSettings(); + } + + [TestCleanup] + public void Cleanup() + { + ResetProtocolSettings(); + } + + [TestMethod] + public void Default_Magic_should_be_mainnet_Magic_value() + { + var mainNetMagic = 0x4F454Eu; + ProtocolSettings.Default.Magic.Should().Be(mainNetMagic); + } + + [TestMethod] + public void Can_initialize_ProtocolSettings() + { + var expectedMagic = 12345u; + + var dict = new Dictionary() + { + { "ProtocolConfiguration:Magic", $"{expectedMagic}" } + }; + + var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build(); + ProtocolSettings.Initialize(config).Should().BeTrue(); + ProtocolSettings.Default.Magic.Should().Be(expectedMagic); + } + + [TestMethod] + public void Cant_initialize_ProtocolSettings_after_default_settings_used() + { + var mainNetMagic = 0x4F454Eu; + ProtocolSettings.Default.Magic.Should().Be(mainNetMagic); + + var updatedMagic = 54321u; + var dict = new Dictionary() + { + { "ProtocolConfiguration:Magic", $"{updatedMagic}" } + }; + + var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build(); + ProtocolSettings.Initialize(config).Should().BeFalse(); + ProtocolSettings.Default.Magic.Should().Be(mainNetMagic); + } + + [TestMethod] + public void Cant_initialize_ProtocolSettings_twice() + { + var expectedMagic = 12345u; + var dict = new Dictionary() + { + { "ProtocolConfiguration:Magic", $"{expectedMagic}" } + }; + + var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build(); + ProtocolSettings.Initialize(config).Should().BeTrue(); + + var updatedMagic = 54321u; + dict = new Dictionary() + { + { "ProtocolConfiguration:Magic", $"{updatedMagic}" } + }; + config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build(); + ProtocolSettings.Initialize(config).Should().BeFalse(); + ProtocolSettings.Default.Magic.Should().Be(expectedMagic); + } + } +} diff --git a/neo/ProtocolSettings.cs b/neo/ProtocolSettings.cs index e50559b070..0be8639034 100644 --- a/neo/ProtocolSettings.cs +++ b/neo/ProtocolSettings.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Configuration; using System.Linq; +using System.Threading; namespace Neo { @@ -11,12 +12,31 @@ public class ProtocolSettings public string[] SeedList { get; } public uint SecondsPerBlock { get; } - public static ProtocolSettings Default { get; } + static ProtocolSettings _default; - static ProtocolSettings() + static bool UpdateDefault(IConfiguration configuration) { - IConfigurationSection section = new ConfigurationBuilder().AddJsonFile("protocol.json", true).Build().GetSection("ProtocolConfiguration"); - Default = new ProtocolSettings(section); + var settings = new ProtocolSettings(configuration.GetSection("ProtocolConfiguration")); + return null == Interlocked.CompareExchange(ref _default, settings, null); + } + + public static bool Initialize(IConfiguration configuration) + { + return UpdateDefault(configuration); + } + + public static ProtocolSettings Default + { + get + { + if (_default == null) + { + var configuration = new ConfigurationBuilder().AddJsonFile("protocol.json", true).Build(); + UpdateDefault(configuration); + } + + return _default; + } } private ProtocolSettings(IConfigurationSection section) From b3acca0a359c9c1d27bc45f397bed3e81b203b6b Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 20 Jun 2019 09:28:19 +0200 Subject: [PATCH 015/305] Clean unused code (#843) * Clean code * Restore asset descriptor * Clean --- neo/Network/P2P/TaskSession.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/neo/Network/P2P/TaskSession.cs b/neo/Network/P2P/TaskSession.cs index 6d732150f9..5617e0efa1 100644 --- a/neo/Network/P2P/TaskSession.cs +++ b/neo/Network/P2P/TaskSession.cs @@ -15,7 +15,6 @@ internal class TaskSession public readonly HashSet AvailableTasks = new HashSet(); public bool HasTask => Tasks.Count > 0; - public bool HeaderTask => Tasks.ContainsKey(UInt256.Zero); public uint StartHeight { get; } public TaskSession(IActorRef node, VersionPayload version) From 4296769d2b88730bb1c8cab3eb58c98e33777c6c Mon Sep 17 00:00:00 2001 From: Igor Machado Coelho Date: Fri, 21 Jun 2019 10:16:45 -0300 Subject: [PATCH 016/305] Json NEP6Wallet with basic tests (#847) * Json NEP6Wallet with basic tests * Scrypt is null, why * testing ScryptParameters * ScryptParameters not null, then null, why * string is correct, still null * shouldnt be null * correctly using out for readonly * reading from json * format * Scrypt to lowercase --- neo.UnitTests/UT_NEP6Wallet.cs | 41 +++++++++++++++++++++++ neo.UnitTests/UT_ScryptParameters.cs | 50 ++++++++++++++++++++++++++++ neo/Wallets/NEP6/NEP6Wallet.cs | 23 +++++++++---- 3 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 neo.UnitTests/UT_NEP6Wallet.cs create mode 100644 neo.UnitTests/UT_ScryptParameters.cs diff --git a/neo.UnitTests/UT_NEP6Wallet.cs b/neo.UnitTests/UT_NEP6Wallet.cs new file mode 100644 index 0000000000..187e487030 --- /dev/null +++ b/neo.UnitTests/UT_NEP6Wallet.cs @@ -0,0 +1,41 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.Wallets.NEP6; +using System; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_NEP6Wallet + { + NEP6Wallet uut; + + [TestInitialize] + public void TestSetup() + { + JObject wallet = new JObject(); + wallet["name"] = "name"; + wallet["version"] = new System.Version().ToString(); + wallet["scrypt"] = ScryptParameters.Default.ToJson(); + // test minimally scryptparameters parsing here + ScryptParameters.FromJson(wallet["scrypt"]).Should().NotBeNull(); + ScryptParameters.FromJson(wallet["scrypt"]).N.Should().Be(ScryptParameters.Default.N); + wallet["accounts"] = new JArray(); + //accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson(p, this)).ToDictionary(p => p.ScriptHash); + wallet["extra"] = new JObject(); + // check string json + wallet.ToString().Should().Be("{\"name\":\"name\",\"version\":\"0.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":{}}"); + uut = new NEP6Wallet(wallet); + } + + [TestMethod] + public void Test_NEP6Wallet_Json() + { + uut.Name.Should().Be("name"); + uut.Version.Should().Be(new Version()); + uut.Scrypt.Should().NotBeNull(); + uut.Scrypt.N.Should().Be(ScryptParameters.Default.N); + } + } +} diff --git a/neo.UnitTests/UT_ScryptParameters.cs b/neo.UnitTests/UT_ScryptParameters.cs new file mode 100644 index 0000000000..e0021f0eb0 --- /dev/null +++ b/neo.UnitTests/UT_ScryptParameters.cs @@ -0,0 +1,50 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.Wallets.NEP6; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_ScryptParameters + { + ScryptParameters uut; + + [TestInitialize] + public void TestSetup() + { + uut = ScryptParameters.Default; + } + + [TestMethod] + public void Test_Default_ScryptParameters() + { + uut.N.Should().Be(16384); + uut.R.Should().Be(8); + uut.P.Should().Be(8); + } + + [TestMethod] + public void Test_ScryptParameters_Default_ToJson() + { + JObject json = ScryptParameters.Default.ToJson(); + json["n"].AsNumber().Should().Be(ScryptParameters.Default.N); + json["r"].AsNumber().Should().Be(ScryptParameters.Default.R); + json["p"].AsNumber().Should().Be(ScryptParameters.Default.P); + } + + [TestMethod] + public void Test_Default_ScryptParameters_FromJson() + { + JObject json = new JObject(); + json["n"] = 16384; + json["r"] = 8; + json["p"] = 8; + + ScryptParameters uut2 = ScryptParameters.FromJson(json); + uut2.N.Should().Be(ScryptParameters.Default.N); + uut2.R.Should().Be(ScryptParameters.Default.R); + uut2.P.Should().Be(ScryptParameters.Default.P); + } + } +} diff --git a/neo/Wallets/NEP6/NEP6Wallet.cs b/neo/Wallets/NEP6/NEP6Wallet.cs index 4e7c8039a8..9fe133d9ce 100644 --- a/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/neo/Wallets/NEP6/NEP6Wallet.cs @@ -16,10 +16,10 @@ public class NEP6Wallet : Wallet private string password; private string name; private Version version; - public readonly ScryptParameters Scrypt; private readonly Dictionary accounts; private readonly JObject extra; + public readonly ScryptParameters Scrypt; public override string Name => name; public override Version Version => version; @@ -33,11 +33,7 @@ public NEP6Wallet(string path, string name = null) { wallet = JObject.Parse(reader); } - this.name = wallet["name"]?.AsString(); - this.version = Version.Parse(wallet["version"].AsString()); - this.Scrypt = ScryptParameters.FromJson(wallet["scrypt"]); - this.accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson(p, this)).ToDictionary(p => p.ScriptHash); - this.extra = wallet["extra"]; + LoadFromJson(wallet, out Scrypt, out accounts, out extra); } else { @@ -49,6 +45,21 @@ public NEP6Wallet(string path, string name = null) } } + public NEP6Wallet(JObject wallet) + { + this.path = ""; + LoadFromJson(wallet, out Scrypt, out accounts, out extra); + } + + private void LoadFromJson(JObject wallet, out ScryptParameters scrypt, out Dictionary accounts, out JObject extra) + { + this.name = wallet["name"]?.AsString(); + this.version = Version.Parse(wallet["version"].AsString()); + scrypt = ScryptParameters.FromJson(wallet["scrypt"]); + accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson(p, this)).ToDictionary(p => p.ScriptHash); + extra = wallet["extra"]; + } + private void AddAccount(NEP6Account account, bool is_import) { lock (accounts) From 2d9eb2c01dea23019e7c476fb5815e84b9c9bcf0 Mon Sep 17 00:00:00 2001 From: Shine Li Date: Sat, 22 Jun 2019 23:51:46 +0800 Subject: [PATCH 017/305] Feature load environment config (#851) --- neo/Helper.cs | 18 +++++++++++++++++- neo/ProtocolSettings.cs | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/neo/Helper.cs b/neo/Helper.cs index 90c68b030c..8c562740bd 100644 --- a/neo/Helper.cs +++ b/neo/Helper.cs @@ -1,4 +1,6 @@ -using System; +using Microsoft.Extensions.Configuration; +using Neo.Plugins; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -271,5 +273,19 @@ internal static BigInteger WeightedAverage(this IEnumerable source, Func + /// Load configuration with different Environment Variable + /// + /// Configuration + /// IConfigurationRoot + public static IConfigurationRoot LoadConfig(string config) + { + var env = Environment.GetEnvironmentVariable("NEO_NETWORK"); + var configFile = string.IsNullOrWhiteSpace(env) ? $"{config}.json" : $"{config}.{env}.json"; + return new ConfigurationBuilder() + .AddJsonFile(configFile, true) + .Build(); + } } } diff --git a/neo/ProtocolSettings.cs b/neo/ProtocolSettings.cs index 0be8639034..ba4c192e8b 100644 --- a/neo/ProtocolSettings.cs +++ b/neo/ProtocolSettings.cs @@ -31,7 +31,7 @@ public static ProtocolSettings Default { if (_default == null) { - var configuration = new ConfigurationBuilder().AddJsonFile("protocol.json", true).Build(); + var configuration = Helper.LoadConfig("protocol"); UpdateDefault(configuration); } From 55d164274802d735f0ae1ab4c7723e86c5e9bc8e Mon Sep 17 00:00:00 2001 From: Shargon Date: Sun, 23 Jun 2019 02:22:21 +0200 Subject: [PATCH 018/305] Charge for size and witnesses (#791) * Fee= VerificationCost+ApplicationCost * NetworkFee for verification * Clean * Clean * Update * Remove priority from Policy and MemoryPool * Fix CalculateFee * Allow to cancel transactions with more Gas * Revert change We need to test this first * Add Cosigners * some fixes * Update Transaction.cs * format * fix overflow * fix `Wallet.MakeTransaction()` * fix `Transaction.CalculateFees()` * remove contract get from verification * Revert "remove contract get from verification" This reverts commit 6d0dad25ce09a1e25bf47ef2944bb213cbea6f42. * Fix fee calculation * fix * fix tests * Update MemoryPool.cs * fix tests * UT - Good calculation! * Clean * Revert Nep5Token.cs * Revert conditional * Improve readability * Revert "Improve readability" This reverts commit cd61b983e3a61aef43621e3e38a507381051ebaa. * Possible fix for unit test * Error verbosity * Add using for ApplicationEngine * Fix unit test * more readable merkleroot test * Sample for multisig contract * Sign ut * Signed! * Same format for single signature unit test * Prevent create unwanted objects * format * Remove `snapshot` from `MakeTransaction()` * Rename * format * using json based NEP6Wallet import * at least using read serializable array * revert last commit --- .../Nep5NativeContractExtensions.cs | 13 +- neo.UnitTests/TestUtils.cs | 16 +- neo.UnitTests/TestVerifiable.cs | 12 +- neo.UnitTests/UT_Block.cs | 66 ++--- neo.UnitTests/UT_Consensus.cs | 2 +- neo.UnitTests/UT_Header.cs | 8 +- neo.UnitTests/UT_MemoryPool.cs | 207 ++++---------- neo.UnitTests/UT_Policy.cs | 80 +----- neo.UnitTests/UT_PoolItem.cs | 11 +- neo.UnitTests/UT_Syscalls.cs | 4 +- neo.UnitTests/UT_Transaction.cs | 213 ++++++++++++++- neo/Consensus/ConsensusContext.cs | 4 +- neo/Cryptography/Helper.cs | 3 +- neo/Ledger/Blockchain.cs | 15 +- neo/Ledger/MemoryPool.cs | 131 +++------ neo/Network/P2P/Payloads/BlockBase.cs | 30 ++- neo/Network/P2P/Payloads/ConsensusPayload.cs | 26 +- neo/Network/P2P/Payloads/IVerifiable.cs | 4 +- neo/Network/P2P/Payloads/Transaction.cs | 125 ++++----- .../P2P/Payloads/TransactionAttributeUsage.cs | 1 + .../ContractParametersContext.cs | 178 +++++++----- neo/SmartContract/Helper.cs | 48 ++-- neo/SmartContract/InteropService.NEO.cs | 24 +- neo/SmartContract/InteropService.cs | 3 +- neo/SmartContract/Native/NativeContract.cs | 2 +- neo/SmartContract/Native/PolicyContract.cs | 52 ---- neo/SmartContract/Native/Tokens/GasToken.cs | 6 +- neo/SmartContract/WitnessWrapper.cs | 27 ++ neo/Wallets/Wallet.cs | 255 ++++++++++++------ 29 files changed, 821 insertions(+), 745 deletions(-) create mode 100644 neo/SmartContract/WitnessWrapper.cs diff --git a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs index 5f80114dc9..a87fd7c3e1 100644 --- a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs +++ b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs @@ -15,9 +15,9 @@ public static class Nep5NativeContractExtensions { internal class ManualWitness : IVerifiable { - private readonly UInt160 _hashForVerify; + private readonly UInt160[] _hashForVerify; - public Witness Witness + public Witness[] Witnesses { get => throw new NotImplementedException(); set => throw new NotImplementedException(); @@ -25,19 +25,16 @@ public Witness Witness public int Size => 0; - public ManualWitness(UInt160 hashForVerify) + public ManualWitness(params UInt160[] hashForVerify) { - _hashForVerify = hashForVerify; + _hashForVerify = hashForVerify ?? new UInt160[0]; } public void Deserialize(BinaryReader reader) { } public void DeserializeUnsigned(BinaryReader reader) { } - public UInt160 GetScriptHashForVerification(Persistence.Snapshot snapshot) - { - return _hashForVerify; - } + public UInt160[] GetScriptHashesForVerifying(Persistence.Snapshot snapshot) => _hashForVerify; public void Serialize(BinaryWriter writer) { } diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index 8660328bf7..9bf06acbf7 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -28,11 +28,11 @@ public static Transaction GetTransaction() Script = new byte[1], Sender = UInt160.Zero, Attributes = new TransactionAttribute[0], - Witness = new Witness + Witnesses = new Witness[]{ new Witness { InvocationScript = new byte[0], VerificationScript = new byte[0] - } + } } }; } @@ -61,7 +61,8 @@ public static void SetupBlockWithValues(Block block, UInt256 val256, out UInt256 private static void setupBlockBaseWithValues(BlockBase bb, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal) { bb.PrevHash = val256; - merkRootVal = new UInt256(new byte[] { 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197 }); + merkRootVal = UInt256.Parse("0xd841af3d6bd7adb4bca24306725f9aec363edb10de3cafc5f8cca948d7b0290f"); + bb.MerkleRoot = merkRootVal; timestampVal = new DateTime(1968, 06, 01, 0, 0, 0, DateTimeKind.Utc).ToTimestamp(); bb.Timestamp = timestampVal; @@ -86,10 +87,13 @@ public static Transaction CreateRandomHashTransaction() Script = randomBytes, Sender = UInt160.Zero, Attributes = new TransactionAttribute[0], - Witness = new Witness + Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new byte[0] + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } } }; } diff --git a/neo.UnitTests/TestVerifiable.cs b/neo.UnitTests/TestVerifiable.cs index 78d3d314c0..dfe21c2d0f 100644 --- a/neo.UnitTests/TestVerifiable.cs +++ b/neo.UnitTests/TestVerifiable.cs @@ -7,9 +7,13 @@ namespace Neo.UnitTests { public class TestVerifiable : IVerifiable { - private string testStr = "testStr"; + private readonly string testStr = "testStr"; - public Witness Witness { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public Witness[] Witnesses + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } public int Size => throw new NotImplementedException(); @@ -23,7 +27,7 @@ public void DeserializeUnsigned(BinaryReader reader) throw new NotImplementedException(); } - public UInt160 GetScriptHashForVerification(Snapshot snapshot) + public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) { throw new NotImplementedException(); } @@ -35,7 +39,7 @@ public void Serialize(BinaryWriter writer) public void SerializeUnsigned(BinaryWriter writer) { - writer.Write((string) testStr); + writer.Write((string)testStr); } } } \ No newline at end of file diff --git a/neo.UnitTests/UT_Block.cs b/neo.UnitTests/UT_Block.cs index f7acf254be..886f5099f0 100644 --- a/neo.UnitTests/UT_Block.cs +++ b/neo.UnitTests/UT_Block.cs @@ -28,12 +28,7 @@ public void Transactions_Get() public void Header_Get() { UInt256 val256 = UInt256.Zero; - UInt256 merkRootVal; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 0); + TestUtils.SetupBlockWithValues(uut, val256, out var merkRootVal, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 0); uut.Header.Should().NotBeNull(); uut.Header.PrevHash.Should().Be(val256); @@ -47,46 +42,31 @@ public void Header_Get() public void Size_Get() { UInt256 val256 = UInt256.Zero; - UInt256 merkRootVal; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 0); - // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 3 + TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 0); + // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 4 // block 9 + 1 - uut.Size.Should().Be(109); + uut.Size.Should().Be(110); } [TestMethod] public void Size_Get_1_Transaction() { UInt256 val256 = UInt256.Zero; - UInt256 merkRootVal; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 0); + TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 0); uut.Transactions = new[] { TestUtils.GetTransaction() }; - uut.Size.Should().Be(159); + uut.Size.Should().Be(161); } [TestMethod] public void Size_Get_3_Transaction() { UInt256 val256 = UInt256.Zero; - UInt256 merkRootVal; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 0); + TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 0); uut.Transactions = new[] { @@ -95,19 +75,14 @@ public void Size_Get_3_Transaction() TestUtils.GetTransaction() }; - uut.Size.Should().Be(259); + uut.Size.Should().Be(263); } [TestMethod] public void Serialize() { UInt256 val256 = UInt256.Zero; - UInt256 merkRootVal; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(uut, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 1); + TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 1); byte[] data; using (MemoryStream stream = new MemoryStream()) @@ -119,7 +94,7 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 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, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 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, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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 }; + byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 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, 1, 0, 1, 0, 0 }; data.Length.Should().Be(requiredData.Length); for (int i = 0; i < data.Length; i++) @@ -132,16 +107,11 @@ public void Serialize() public void Deserialize() { UInt256 val256 = UInt256.Zero; - UInt256 merkRoot; - UInt160 val160; - uint timestampVal, indexVal; - Witness scriptVal; - Transaction[] transactionsVal; - TestUtils.SetupBlockWithValues(new Block(), val256, out merkRoot, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 1); + TestUtils.SetupBlockWithValues(new Block(), val256, out var merkRoot, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 1); uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - byte[] data = new byte[] { 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, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 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, 1, 81, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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 }; + byte[] data = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 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, 1, 0, 0 }; int index = 0; using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) { @@ -244,23 +214,23 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0xe0b482b6e4c176c9af520dd7caa6d80a9aeaeb80d016e788c702b05f5ac5ba4b"); - jObj["size"].AsNumber().Should().Be(159); + jObj["hash"].AsString().Should().Be("0x1d8642796276c8ce3c5c03b8984a1b593d99b49a63d830bb06f800b8c953be77"); + jObj["size"].AsNumber().Should().Be(161); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - jObj["merkleroot"].AsString().Should().Be("0xc59eab3a287f74405b80325bc26c66ac7ef18d94c434a18d60950d3f098280f2"); + jObj["merkleroot"].AsString().Should().Be("0xd841af3d6bd7adb4bca24306725f9aec363edb10de3cafc5f8cca948d7b0290f"); jObj["time"].AsNumber().Should().Be(4244941696); jObj["index"].AsNumber().Should().Be(0); jObj["nextconsensus"].AsString().Should().Be("AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM"); - JObject scObj = jObj["witness"]; + JObject scObj = ((JArray)jObj["witnesses"])[0]; scObj["invocation"].AsString().Should().Be(""); scObj["verification"].AsString().Should().Be("51"); jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; - txObj[0]["hash"].AsString().Should().Be("0x7647acf1ef8a841b87f2369398a0e9f0ccde0ed9e835d980657103da6da59580"); - txObj[0]["size"].AsNumber().Should().Be(50); + txObj[0]["hash"].AsString().Should().Be("0x64ed4e0d79407c60bde534feb44fbbd19bd065282d27ecd3a1a7a647f66affa6"); + txObj[0]["size"].AsNumber().Should().Be(51); txObj[0]["version"].AsNumber().Should().Be(0); ((JArray)txObj[0]["attributes"]).Count.Should().Be(0); txObj[0]["net_fee"].AsString().Should().Be("0"); diff --git a/neo.UnitTests/UT_Consensus.cs b/neo.UnitTests/UT_Consensus.cs index 4ea314238b..d410bb5142 100644 --- a/neo.UnitTests/UT_Consensus.cs +++ b/neo.UnitTests/UT_Consensus.cs @@ -75,7 +75,7 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() // Creating proposed block Header header = new Header(); TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal); - header.Size.Should().Be(100); + header.Size.Should().Be(101); Console.WriteLine($"header {header} hash {header.Hash} timstamp {timestampVal}"); diff --git a/neo.UnitTests/UT_Header.cs b/neo.UnitTests/UT_Header.cs index e3df13e14d..82744a7f8b 100644 --- a/neo.UnitTests/UT_Header.cs +++ b/neo.UnitTests/UT_Header.cs @@ -22,9 +22,9 @@ public void Size_Get() { UInt256 val256 = UInt256.Zero; TestUtils.SetupHeaderWithValues(uut, val256, out _, out _, out _, out _, out _); - // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 3 + // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 4 // header 1 - uut.Size.Should().Be(100); + uut.Size.Should().Be(101); } [TestMethod] @@ -35,7 +35,7 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - byte[] data = new byte[] { 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, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 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, 1, 81, 0 }; + byte[] data = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 0 }; int index = 0; using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) { @@ -106,7 +106,7 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 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, 242, 128, 130, 9, 63, 13, 149, 96, 141, 161, 52, 196, 148, 141, 241, 126, 172, 102, 108, 194, 91, 50, 128, 91, 64, 116, 127, 40, 58, 171, 158, 197, 128, 171, 4, 253, 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, 1, 81, 0 }; + byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 0 }; data.Length.Should().Be(requiredData.Length); for (int i = 0; i < data.Length; i++) diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/UT_MemoryPool.cs index a35c85fcc7..90cd43d5d5 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/UT_MemoryPool.cs @@ -53,173 +53,87 @@ private Transaction CreateTransactionWithFee(long fee) mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = fee; mock.Object.Attributes = new TransactionAttribute[0]; - mock.Object.Witness = new Witness + mock.Object.Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new byte[0] + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } }; return mock.Object; } - private Transaction CreateHighPriorityTransaction() + private Transaction CreateTransaction() { return CreateTransactionWithFee(LongRandom(100000, 100000000, TestUtils.TestRandom)); } - private Transaction CreateLowPriorityTransaction() - { - long rNetFee = LongRandom(0, 10000, TestUtils.TestRandom); - // [0,0.001] GAS a fee lower than the threshold of 0.001 GAS (not enough to be a high priority TX) - return CreateTransactionWithFee(rNetFee); - } - - private bool IsLowPriority(Transaction tx) - { - return tx.FeePerByte < 1000; - } - - private void AddTransactions(int count, bool isHighPriority = false) + private void AddTransactions(int count) { for (int i = 0; i < count; i++) { - var txToAdd = isHighPriority ? CreateHighPriorityTransaction() : CreateLowPriorityTransaction(); + var txToAdd = CreateTransaction(); Console.WriteLine($"created tx: {txToAdd.Hash}"); _unit.TryAdd(txToAdd.Hash, txToAdd); } } - private void AddLowPriorityTransactions(int count) => AddTransactions(count); - public void AddHighPriorityTransactions(int count) => AddTransactions(count, true); [TestMethod] - public void LowPriorityCapacityTest() + public void CapacityTest() { // Add over the capacity items, verify that the verified count increases each time - AddLowPriorityTransactions(50); - _unit.VerifiedCount.ShouldBeEquivalentTo(50); - AddLowPriorityTransactions(51); - Console.WriteLine($"VerifiedCount: {_unit.VerifiedCount} LowPrioCount {_unit.SortedLowPrioTxCount} HighPrioCount {_unit.SortedHighPrioTxCount}"); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(100); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0); + AddTransactions(101); - _unit.VerifiedCount.ShouldBeEquivalentTo(100); - _unit.UnVerifiedCount.ShouldBeEquivalentTo(0); - _unit.Count.ShouldBeEquivalentTo(100); - } - - [TestMethod] - public void HighPriorityCapacityTest() - { - // Add over the capacity items, verify that the verified count increases each time - AddHighPriorityTransactions(101); - - Console.WriteLine($"VerifiedCount: {_unit.VerifiedCount} LowPrioCount {_unit.SortedLowPrioTxCount} HighPrioCount {_unit.SortedHighPrioTxCount}"); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(100); + Console.WriteLine($"VerifiedCount: {_unit.VerifiedCount} Count {_unit.SortedTxCount}"); + _unit.SortedTxCount.ShouldBeEquivalentTo(100); _unit.VerifiedCount.ShouldBeEquivalentTo(100); _unit.UnVerifiedCount.ShouldBeEquivalentTo(0); _unit.Count.ShouldBeEquivalentTo(100); } - [TestMethod] - public void HighPriorityPushesOutLowPriority() - { - // Add over the capacity items, verify that the verified count increases each time - AddLowPriorityTransactions(70); - AddHighPriorityTransactions(40); - - Console.WriteLine($"VerifiedCount: {_unit.VerifiedCount} LowPrioCount {_unit.SortedLowPrioTxCount} HighPrioCount {_unit.SortedHighPrioTxCount}"); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(60); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(40); - _unit.Count.ShouldBeEquivalentTo(100); - } - - [TestMethod] - public void LowPriorityDoesNotPushOutHighPrority() - { - AddHighPriorityTransactions(70); - AddLowPriorityTransactions(40); - - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(30); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(70); - _unit.Count.ShouldBeEquivalentTo(100); - } - [TestMethod] public void BlockPersistMovesTxToUnverifiedAndReverification() { - AddHighPriorityTransactions(70); - AddLowPriorityTransactions(30); + AddTransactions(70); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(70); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(30); + _unit.SortedTxCount.ShouldBeEquivalentTo(70); var block = new Block { Transactions = _unit.GetSortedVerifiedTransactions().Take(10) - .Concat(_unit.GetSortedVerifiedTransactions().Where(x => IsLowPriority(x)).Take(5)).ToArray() + .Concat(_unit.GetSortedVerifiedTransactions().Take(5)).ToArray() }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(60); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(25); + _unit.SortedTxCount.ShouldBeEquivalentTo(0); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(60); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(9); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(1); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(51); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(24); + _unit.SortedTxCount.ShouldBeEquivalentTo(10); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(50); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(18); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(2); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(42); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(23); + _unit.SortedTxCount.ShouldBeEquivalentTo(20); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(40); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(27); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(3); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(33); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(22); + _unit.SortedTxCount.ShouldBeEquivalentTo(30); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(30); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(36); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(4); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(24); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(21); + _unit.SortedTxCount.ShouldBeEquivalentTo(40); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(20); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(45); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(5); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(15); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(20); + _unit.SortedTxCount.ShouldBeEquivalentTo(50); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(10); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(54); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(6); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(6); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(19); - - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(60); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(10); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(15); - - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(60); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(20); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(5); - - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(60); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(25); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(0); + _unit.SortedTxCount.ShouldBeEquivalentTo(60); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); } private void verifyTransactionsSortedDescending(IEnumerable transactions) @@ -248,8 +162,7 @@ private void verifyTransactionsSortedDescending(IEnumerable transac [TestMethod] public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() { - AddLowPriorityTransactions(50); - AddHighPriorityTransactions(50); + AddTransactions(100); var sortedVerifiedTxs = _unit.GetSortedVerifiedTransactions().ToList(); // verify all 100 transactions are returned in sorted order @@ -260,37 +173,31 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() var block = new Block { Transactions = new Transaction[0] }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(50); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(50); + _unit.SortedTxCount.ShouldBeEquivalentTo(0); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(100); // We can verify the order they are re-verified by reverifying 2 at a time while (_unit.UnVerifiedCount > 0) { - _unit.GetVerifiedAndUnverifiedTransactions(out IEnumerable sortedVerifiedTransactions, - out IEnumerable sortedUnverifiedTransactions); + _unit.GetVerifiedAndUnverifiedTransactions(out var sortedVerifiedTransactions, out var sortedUnverifiedTransactions); sortedVerifiedTransactions.Count().ShouldBeEquivalentTo(0); var sortedUnverifiedArray = sortedUnverifiedTransactions.ToArray(); verifyTransactionsSortedDescending(sortedUnverifiedArray); - var maxHighPriorityTransaction = sortedUnverifiedArray.First(); - var maxLowPriorityTransaction = sortedUnverifiedArray.First(tx => IsLowPriority(tx)); + var maxTransaction = sortedUnverifiedArray.First(); + var minTransaction = sortedUnverifiedArray.Last(); // reverify 1 high priority and 1 low priority transaction - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(2, Blockchain.Singleton.GetSnapshot()); + _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(1, Blockchain.Singleton.GetSnapshot()); var verifiedTxs = _unit.GetSortedVerifiedTransactions().ToArray(); - verifiedTxs.Length.ShouldBeEquivalentTo(2); - verifiedTxs[0].ShouldBeEquivalentTo(maxHighPriorityTransaction); - verifiedTxs[1].ShouldBeEquivalentTo(maxLowPriorityTransaction); - var blockWith2Tx = new Block { Transactions = new[] { maxHighPriorityTransaction, maxLowPriorityTransaction } }; + verifiedTxs.Length.ShouldBeEquivalentTo(1); + verifiedTxs[0].ShouldBeEquivalentTo(maxTransaction); + var blockWith2Tx = new Block { Transactions = new[] { maxTransaction, minTransaction } }; // verify and remove the 2 transactions from the verified pool _unit.UpdatePoolForBlockPersisted(blockWith2Tx, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0); + _unit.SortedTxCount.ShouldBeEquivalentTo(0); } - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(0); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); } void VerifyCapacityThresholdForAttemptingToAddATransaction() @@ -306,11 +213,11 @@ void VerifyCapacityThresholdForAttemptingToAddATransaction() [TestMethod] public void VerifyCanTransactionFitInPoolWorksAsIntended() { - AddLowPriorityTransactions(100); + AddTransactions(100); VerifyCapacityThresholdForAttemptingToAddATransaction(); - AddHighPriorityTransactions(50); + AddTransactions(50); VerifyCapacityThresholdForAttemptingToAddATransaction(); - AddHighPriorityTransactions(50); + AddTransactions(50); VerifyCapacityThresholdForAttemptingToAddATransaction(); } @@ -321,31 +228,27 @@ public void CapacityTestWithUnverifiedHighProirtyTransactions() // low priority transactions // Fill pool with high priority transactions - AddHighPriorityTransactions(99); + AddTransactions(99); // move all to unverified var block = new Block { Transactions = new Transaction[0] }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); - _unit.CanTransactionFitInPool(CreateLowPriorityTransaction()).ShouldBeEquivalentTo(true); - AddHighPriorityTransactions(1); - _unit.CanTransactionFitInPool(CreateLowPriorityTransaction()).ShouldBeEquivalentTo(false); + _unit.CanTransactionFitInPool(CreateTransaction()).ShouldBeEquivalentTo(true); + AddTransactions(1); + _unit.CanTransactionFitInPool(CreateTransactionWithFee(0)).ShouldBeEquivalentTo(false); } [TestMethod] public void TestInvalidateAll() { - AddHighPriorityTransactions(30); - AddLowPriorityTransactions(60); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(30); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(60); + AddTransactions(30); + + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); + _unit.SortedTxCount.ShouldBeEquivalentTo(30); _unit.InvalidateAllTransactions(); - _unit.UnverifiedSortedHighPrioTxCount.ShouldBeEquivalentTo(30); - _unit.UnverifiedSortedLowPrioTxCount.ShouldBeEquivalentTo(60); - _unit.SortedHighPrioTxCount.ShouldBeEquivalentTo(0); - _unit.SortedLowPrioTxCount.ShouldBeEquivalentTo(0); + _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(30); + _unit.SortedTxCount.ShouldBeEquivalentTo(0); } } } diff --git a/neo.UnitTests/UT_Policy.cs b/neo.UnitTests/UT_Policy.cs index da93eca368..09a09b20ba 100644 --- a/neo.UnitTests/UT_Policy.cs +++ b/neo.UnitTests/UT_Policy.cs @@ -32,20 +32,12 @@ public void Check_Initialize() NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); - (keyCount + 5).Should().Be(snapshot.Storages.GetChangeSet().Count()); + (keyCount + 3).Should().Be(snapshot.Storages.GetChangeSet().Count()); var ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); ret.Should().BeOfType(); ret.GetBigInteger().Should().Be(512); - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionsPerBlock"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(20); - - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionSize"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(256); - ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); ret.GetBigInteger().Should().Be(1000); @@ -90,76 +82,6 @@ public void Check_SetMaxTransactionsPerBlock() ret.GetBigInteger().Should().Be(1); } - [TestMethod] - public void Check_SetMaxLowPriorityTransactionsPerBlock() - { - var snapshot = Store.GetSnapshot().Clone(); - - // Fake blockchain - - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - - NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); - - // Without signature - - var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), - "setMaxLowPriorityTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionsPerBlock"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(20); - - // With signature - - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), - "setMaxLowPriorityTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionsPerBlock"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(1); - } - - [TestMethod] - public void Check_SetMaxLowPriorityTransactionSize() - { - var snapshot = Store.GetSnapshot().Clone(); - - // Fake blockchain - - snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - - NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); - - // Without signature - - var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), - "setMaxLowPriorityTransactionSize", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionSize"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(256); - - // With signature - - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), - "setMaxLowPriorityTransactionSize", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxLowPriorityTransactionSize"); - ret.Should().BeOfType(); - ret.GetBigInteger().Should().Be(1); - } - [TestMethod] public void Check_SetFeePerByte() { diff --git a/neo.UnitTests/UT_PoolItem.cs b/neo.UnitTests/UT_PoolItem.cs index 272dcf4084..cedf7f3fa9 100644 --- a/neo.UnitTests/UT_PoolItem.cs +++ b/neo.UnitTests/UT_PoolItem.cs @@ -122,17 +122,20 @@ public static Transaction GenerateTx(long networkFee, int size, byte[] overrideS Sender = UInt160.Zero, NetworkFee = networkFee, Attributes = new TransactionAttribute[0], - Witness = new Witness + Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new byte[0] + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } } }; int diff = size - tx.Size; if (diff < 0) throw new ArgumentException(); if (diff > 0) - tx.Witness.VerificationScript = new byte[diff]; + tx.Witnesses[0].VerificationScript = new byte[diff]; return tx; } } diff --git a/neo.UnitTests/UT_Syscalls.cs b/neo.UnitTests/UT_Syscalls.cs index 82c857b6d9..c6642c4e88 100644 --- a/neo.UnitTests/UT_Syscalls.cs +++ b/neo.UnitTests/UT_Syscalls.cs @@ -27,7 +27,9 @@ public void System_Runtime_GetInvocationCounter() var contractB = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; var contractC = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; - contracts.DeleteWhere((a, b) => true); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractA.ScriptHash.ToArray())); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractB.ScriptHash.ToArray())); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractC.ScriptHash.ToArray())); contracts.Add(contractA.ScriptHash, contractA); contracts.Add(contractB.ScriptHash, contractB); contracts.Add(contractC.ScriptHash, contractC); diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index b192a2479d..18aafb31bb 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -1,8 +1,17 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; using Neo.IO; using Neo.IO.Json; +using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.SmartContract.Native.Tokens; +using Neo.VM; +using Neo.Wallets; +using Neo.Wallets.NEP6; namespace Neo.UnitTests { @@ -10,11 +19,13 @@ namespace Neo.UnitTests public class UT_Transaction { Transaction uut; + Store store; [TestInitialize] public void TestSetup() { uut = new Transaction(); + store = TestBlockchain.GetStore(); } [TestMethod] @@ -38,15 +49,15 @@ public void Script_Set() [TestMethod] public void Gas_Get() { - uut.Gas.Should().Be(0); + uut.SystemFee.Should().Be(0); } [TestMethod] public void Gas_Set() { long val = 4200000000; - uut.Gas = val; - uut.Gas.Should().Be(val); + uut.SystemFee = val; + uut.SystemFee.Should().Be(val); } [TestMethod] @@ -55,16 +66,187 @@ public void Size_Get() uut.Script = TestUtils.GetByteArray(32, 0x42); uut.Sender = UInt160.Zero; uut.Attributes = new TransactionAttribute[0]; - uut.Witness = new Witness + uut.Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new byte[0] + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } }; uut.Version.Should().Be(0); uut.Script.Length.Should().Be(32); uut.Script.GetVarSize().Should().Be(33); - uut.Size.Should().Be(81); + uut.Size.Should().Be(82); + } + + private NEP6Wallet GenerateTestWallet() + { + JObject wallet = new JObject(); + wallet["name"] = "noname"; + wallet["version"] = new System.Version().ToString(); + wallet["scrypt"] = ScryptParameters.Default.ToJson(); + wallet["accounts"] = new JArray(); + wallet["extra"] = null; + wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"0.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":null}"); + return new NEP6Wallet(wallet); + } + + [TestMethod] + public void FeeIsMultiSigContract() + { + var store = TestBlockchain.GetStore(); + var walletA = GenerateTestWallet(); + var walletB = GenerateTestWallet(); + var snapshot = store.GetSnapshot(); + + using (var unlockA = walletA.Unlock("123")) + using (var unlockB = walletB.Unlock("123")) + { + var a = walletA.CreateAccount(); + var b = walletB.CreateAccount(); + + var multiSignContract = Contract.CreateMultiSigContract(2, + new ECPoint[] + { + a.GetKey().PublicKey, + b.GetKey().PublicKey + }); + + var acc = walletA.CreateAccount(multiSignContract, a.GetKey()); + acc = walletB.CreateAccount(multiSignContract, b.GetKey()); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + // Make transaction + + var tx = walletA.MakeTransaction(new TransferOutput[] + { + new TransferOutput() + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = acc.ScriptHash, + Value = new BigDecimal(1,8) + } + }, acc.ScriptHash); + + Assert.IsNotNull(tx); + + // Sign + + var data = new ContractParametersContext(tx); + Assert.IsTrue(walletA.Sign(data)); + Assert.IsTrue(walletB.Sign(data)); + Assert.IsTrue(data.Completed); + + tx.Witnesses = data.GetWitnesses(); + + // Fast check + + Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + + // Check + + long verificationGas = 0; + foreach (var witness in tx.Witnesses) + { + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + { + engine.LoadScript(witness.VerificationScript); + engine.LoadScript(witness.InvocationScript); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + verificationGas += engine.GasConsumed; + } + } + + var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); + Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); + } + } + + [TestMethod] + public void FeeIsSignatureContract() + { + var wallet = GenerateTestWallet(); + var snapshot = store.GetSnapshot(); + + using (var unlock = wallet.Unlock("123")) + { + var acc = wallet.CreateAccount(); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + // Make transaction + + var tx = wallet.MakeTransaction(new TransferOutput[] + { + new TransferOutput() + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = acc.ScriptHash, + Value = new BigDecimal(1,8) + } + }, acc.ScriptHash); + + Assert.IsNotNull(tx); + + // Sign + + var data = new ContractParametersContext(tx); + Assert.IsTrue(wallet.Sign(data)); + tx.Witnesses = data.GetWitnesses(); + + // Fast check + + Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + + // Check + + long verificationGas = 0; + foreach (var witness in tx.Witnesses) + { + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + { + engine.LoadScript(witness.VerificationScript); + engine.LoadScript(witness.InvocationScript); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + verificationGas += engine.GasConsumed; + } + } + + var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); + Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); + } } [TestMethod] @@ -72,23 +254,26 @@ public void ToJson() { uut.Script = TestUtils.GetByteArray(32, 0x42); uut.Sender = UInt160.Zero; - uut.Gas = 4200000000; + uut.SystemFee = 4200000000; uut.Attributes = new TransactionAttribute[0]; - uut.Witness = new Witness + uut.Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new byte[0] + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } }; JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0x38274692538dfecaae36f8fd518d92bae25607d491c40a8f927cc06bd97ab2c8"); - jObj["size"].AsNumber().Should().Be(81); + jObj["hash"].AsString().Should().Be("0xee00d595ccd48a650f62adaccbb9c979e2dc7ef66fb5b1413f0f74d563a2d9c6"); + jObj["size"].AsNumber().Should().Be(82); jObj["version"].AsNumber().Should().Be(0); ((JArray)jObj["attributes"]).Count.Should().Be(0); jObj["net_fee"].AsString().Should().Be("0"); jObj["script"].AsString().Should().Be("4220202020202020202020202020202020202020202020202020202020202020"); - jObj["gas"].AsNumber().Should().Be(42); + jObj["sys_fee"].AsNumber().Should().Be(42); } } } diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 3affe821f5..5fa53ce567 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -85,7 +85,7 @@ public Block CreateBlock() sc.AddSignature(contract, Validators[i], CommitPayloads[i].GetDeserializedMessage().Signature); j++; } - Block.Witness = sc.GetWitness(); + Block.Witness = sc.GetWitnesses()[0]; Block.Transactions = TransactionHashes.Select(p => Transactions[p]).ToArray(); return Block; } @@ -202,7 +202,7 @@ private void SignPayload(ConsensusPayload payload) { return; } - payload.Witness = sc.GetWitness(); + payload.Witness = sc.GetWitnesses()[0]; } public ConsensusPayload MakePrepareRequest() diff --git a/neo/Cryptography/Helper.cs b/neo/Cryptography/Helper.cs index f46aad0121..48410049f1 100644 --- a/neo/Cryptography/Helper.cs +++ b/neo/Cryptography/Helper.cs @@ -117,7 +117,8 @@ public static byte[] Sha256(this byte[] value, int offset, int count) internal static bool Test(this BloomFilter filter, Transaction tx) { if (filter.Check(tx.Hash.ToArray())) return true; - if (filter.Check(tx.Sender.ToArray())) return true; + if (tx.Witnesses.Any(p => filter.Check(p.ScriptHash.ToArray()))) + return true; return false; } diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 4f887b4817..059560573b 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -152,12 +152,15 @@ private static Transaction DeployNativeContracts() Version = 0, Script = script, Sender = (new[] { (byte)OpCode.PUSHT }).ToScriptHash(), - Gas = 0, + SystemFee = 0, Attributes = new TransactionAttribute[0], - Witness = new Witness + Witnesses = new[] { - InvocationScript = new byte[0], - VerificationScript = new[] { (byte)OpCode.PUSHT } + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new[] { (byte)OpCode.PUSHT } + } } }; } @@ -280,7 +283,7 @@ private RelayResultReason OnNewBlock(Block block) block_cache_unverified.Remove(blockToPersist.Index); Persist(blockToPersist); - if (blocksPersisted++ < blocksToPersistList.Count - (2 + Math.Max(0,(15 - SecondsPerBlock)))) continue; + if (blocksPersisted++ < blocksToPersistList.Count - (2 + Math.Max(0, (15 - SecondsPerBlock)))) 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 @@ -433,7 +436,7 @@ private void Persist(Block block) BlockIndex = block.Index, Transaction = tx }); - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, snapshot.Clone(), tx.Gas)) + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, snapshot.Clone(), tx.SystemFee)) { engine.LoadScript(tx.Script); if (!engine.Execute().HasFlag(VMState.FAULT)) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index c3748ef7ec..f4fee9bdf2 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -1,15 +1,15 @@ -using Neo.Network.P2P.Payloads; +using Akka.Util.Internal; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.Plugins; +using Neo.SmartContract.Native; using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; -using Akka.Util.Internal; -using Neo.Network.P2P; -using Neo.Persistence; -using Neo.Plugins; -using Neo.SmartContract.Native; namespace Neo.Ledger { @@ -20,12 +20,10 @@ public class MemoryPool : IReadOnlyCollection private const int BlocksTillRebroadcastHighPriorityPoolTx = 10; private int RebroadcastMultiplierThreshold => Capacity / 10; - private static readonly double MaxSecondsToReverifyHighPrioTx = (double) Blockchain.SecondsPerBlock / 3; - private static readonly double MaxSecondsToReverifyLowPrioTx = (double) Blockchain.SecondsPerBlock / 5; + private static readonly double MaxSecondsToReverifyTx = (double)Blockchain.SecondsPerBlock / 3; // These two are not expected to be hit, they are just safegaurds. - private static readonly double MaxSecondsToReverifyHighPrioTxPerIdle = (double) Blockchain.SecondsPerBlock / 15; - private static readonly double MaxSecondsToReverifyLowPrioTxPerIdle = (double) Blockchain.SecondsPerBlock / 30; + private static readonly double MaxSecondsToReverifyTxPerIdle = (double)Blockchain.SecondsPerBlock / 15; private readonly NeoSystem _system; @@ -44,34 +42,25 @@ public class MemoryPool : IReadOnlyCollection /// private readonly Dictionary _unsortedTransactions = new Dictionary(); /// - /// Stores the verified high priority sorted transactins currently in the pool. - /// - private readonly SortedSet _sortedHighPrioTransactions = new SortedSet(); - /// - /// Stores the verified low priority sorted transactions currently in the pool. + /// Stores the verified sorted transactins currently in the pool. /// - private readonly SortedSet _sortedLowPrioTransactions = new SortedSet(); + private readonly SortedSet _sortedTransactions = new SortedSet(); /// /// Store the unverified transactions currently in the pool. /// /// Transactions in this data structure were valid in some prior block, but may no longer be valid. /// The top ones that could make it into the next block get verified and moved into the verified data structures - /// (_unsortedTransactions, _sortedLowPrioTransactions, and _sortedHighPrioTransactions) after each block. + /// (_unsortedTransactions, and _sortedTransactions) after each block. /// private readonly Dictionary _unverifiedTransactions = new Dictionary(); - private readonly SortedSet _unverifiedSortedHighPriorityTransactions = new SortedSet(); - private readonly SortedSet _unverifiedSortedLowPriorityTransactions = new SortedSet(); + private readonly SortedSet _unverifiedSortedTransactions = new SortedSet(); // Internal methods to aid in unit testing - internal int SortedHighPrioTxCount => _sortedHighPrioTransactions.Count; - internal int SortedLowPrioTxCount => _sortedLowPrioTransactions.Count; - internal int UnverifiedSortedHighPrioTxCount => _unverifiedSortedHighPriorityTransactions.Count; - internal int UnverifiedSortedLowPrioTxCount => _unverifiedSortedLowPriorityTransactions.Count; + internal int SortedTxCount => _sortedTransactions.Count; + internal int UnverifiedSortedTxCount => _unverifiedSortedTransactions.Count; private int _maxTxPerBlock; - private int _maxLowPriorityTxPerBlock; - private long _feePerByte; /// /// Total maximum capacity of transactions the pool can hold. @@ -113,8 +102,6 @@ public MemoryPool(NeoSystem system, int capacity) internal void LoadPolicy(Snapshot snapshot) { _maxTxPerBlock = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); - _maxLowPriorityTxPerBlock = (int)NativeContract.Policy.GetMaxLowPriorityTransactionsPerBlock(snapshot); - _feePerByte = NativeContract.Policy.GetFeePerByte(snapshot); } /// @@ -129,8 +116,7 @@ public bool ContainsKey(UInt256 hash) _txRwLock.EnterReadLock(); try { - return _unsortedTransactions.ContainsKey(hash) - || _unverifiedTransactions.ContainsKey(hash); + return _unsortedTransactions.ContainsKey(hash) || _unverifiedTransactions.ContainsKey(hash); } finally { @@ -192,10 +178,8 @@ public IEnumerable GetVerifiedTransactions() _txRwLock.EnterReadLock(); try { - verifiedTransactions = _sortedHighPrioTransactions.Reverse().Select(p => p.Tx) - .Concat(_sortedLowPrioTransactions.Reverse().Select(p => p.Tx)).ToArray(); - unverifiedTransactions = _unverifiedSortedHighPriorityTransactions.Reverse().Select(p => p.Tx) - .Concat(_unverifiedSortedLowPriorityTransactions.Reverse().Select(p => p.Tx)).ToArray(); + verifiedTransactions = _sortedTransactions.Reverse().Select(p => p.Tx).ToArray(); + unverifiedTransactions = _unverifiedSortedTransactions.Reverse().Select(p => p.Tx).ToArray(); } finally { @@ -208,9 +192,7 @@ public IEnumerable GetSortedVerifiedTransactions() _txRwLock.EnterReadLock(); try { - return _sortedHighPrioTransactions.Reverse().Select(p => p.Tx) - .Concat(_sortedLowPrioTransactions.Reverse().Select(p => p.Tx)) - .ToArray(); + return _sortedTransactions.Reverse().Select(p => p.Tx).ToArray(); } finally { @@ -239,25 +221,16 @@ public IEnumerable GetSortedVerifiedTransactions() private PoolItem GetLowestFeeTransaction(out Dictionary unsortedTxPool, out SortedSet sortedPool) { - var minItem = GetLowestFeeTransaction(_sortedLowPrioTransactions, _unverifiedSortedLowPriorityTransactions, - out sortedPool); - - if (minItem != null) - { - unsortedTxPool = Object.ReferenceEquals(sortedPool, _unverifiedSortedLowPriorityTransactions) - ? _unverifiedTransactions : _unsortedTransactions; - return minItem; - } + sortedPool = null; try { - return GetLowestFeeTransaction(_sortedHighPrioTransactions, _unverifiedSortedHighPriorityTransactions, - out sortedPool); + return GetLowestFeeTransaction(_sortedTransactions, _unverifiedSortedTransactions, out sortedPool); } finally { - unsortedTxPool = Object.ReferenceEquals(sortedPool, _unverifiedSortedHighPriorityTransactions) - ? _unverifiedTransactions : _unsortedTransactions; + unsortedTxPool = Object.ReferenceEquals(sortedPool, _unverifiedSortedTransactions) + ? _unverifiedTransactions : _unsortedTransactions; } } @@ -269,12 +242,6 @@ internal bool CanTransactionFitInPool(Transaction tx) return GetLowestFeeTransaction(out _, out _).CompareTo(tx) <= 0; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool IsLowPriority(Transaction tx) - { - return tx.FeePerByte < _feePerByte; - } - /// /// Adds an already verified transaction to the memory pool. /// @@ -295,9 +262,8 @@ internal bool TryAdd(UInt256 hash, Transaction tx) try { _unsortedTransactions.Add(hash, poolItem); + _sortedTransactions.Add(poolItem); - SortedSet pool = IsLowPriority(tx) ? _sortedLowPrioTransactions : _sortedHighPrioTransactions; - pool.Add(poolItem); if (Count > Capacity) removedTransactions = RemoveOverCapacity(); } @@ -338,9 +304,8 @@ private bool TryRemoveVerified(UInt256 hash, out PoolItem item) return false; _unsortedTransactions.Remove(hash); - SortedSet pool = IsLowPriority(item.Tx) - ? _sortedLowPrioTransactions : _sortedHighPrioTransactions; - pool.Remove(item); + _sortedTransactions.Remove(item); + return true; } @@ -351,31 +316,22 @@ internal bool TryRemoveUnVerified(UInt256 hash, out PoolItem item) return false; _unverifiedTransactions.Remove(hash); - SortedSet pool = IsLowPriority(item.Tx) - ? _unverifiedSortedLowPriorityTransactions : _unverifiedSortedHighPriorityTransactions; - pool.Remove(item); + _unverifiedSortedTransactions.Remove(item); return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void InvalidateVerifiedTransactions() { - foreach (PoolItem item in _sortedHighPrioTransactions) + foreach (PoolItem item in _sortedTransactions) { if (_unverifiedTransactions.TryAdd(item.Tx.Hash, item)) - _unverifiedSortedHighPriorityTransactions.Add(item); - } - - foreach (PoolItem item in _sortedLowPrioTransactions) - { - if (_unverifiedTransactions.TryAdd(item.Tx.Hash, item)) - _unverifiedSortedLowPriorityTransactions.Add(item); + _unverifiedSortedTransactions.Add(item); } // Clear the verified transactions now, since they all must be reverified. _unsortedTransactions.Clear(); - _sortedHighPrioTransactions.Clear(); - _sortedLowPrioTransactions.Clear(); + _sortedTransactions.Clear(); } // Note: this must only be called from a single thread (the Blockchain actor) @@ -405,11 +361,8 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) return; LoadPolicy(snapshot); - - ReverifyTransactions(_sortedHighPrioTransactions, _unverifiedSortedHighPriorityTransactions, - _maxTxPerBlock, MaxSecondsToReverifyHighPrioTx, snapshot); - ReverifyTransactions(_sortedLowPrioTransactions, _unverifiedSortedLowPriorityTransactions, - _maxLowPriorityTxPerBlock, MaxSecondsToReverifyLowPrioTx, snapshot); + ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, + _maxTxPerBlock, MaxSecondsToReverifyTx, snapshot); } internal void InvalidateAllTransactions() @@ -446,7 +399,7 @@ internal void InvalidateAllTransactions() _txRwLock.EnterWriteLock(); try { - int blocksTillRebroadcast = Object.ReferenceEquals(unverifiedSortedTxPool, _sortedHighPrioTransactions) + int blocksTillRebroadcast = Object.ReferenceEquals(unverifiedSortedTxPool, _sortedTransactions) ? BlocksTillRebroadcastHighPriorityPoolTx : BlocksTillRebroadcastLowPriorityPoolTx; if (Count > RebroadcastMultiplierThreshold) @@ -504,23 +457,11 @@ internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, Snapsho if (Blockchain.Singleton.Height < Blockchain.Singleton.HeaderHeight) return false; - if (_unverifiedSortedHighPriorityTransactions.Count > 0) - { - // Always leave at least 1 tx for low priority tx - int verifyCount = _sortedHighPrioTransactions.Count > _maxTxPerBlock || maxToVerify == 1 - ? 1 : maxToVerify - 1; - maxToVerify -= ReverifyTransactions(_sortedHighPrioTransactions, _unverifiedSortedHighPriorityTransactions, - verifyCount, MaxSecondsToReverifyHighPrioTxPerIdle, snapshot); - - if (maxToVerify == 0) maxToVerify++; - } - - if (_unverifiedSortedLowPriorityTransactions.Count > 0) + if (_unverifiedSortedTransactions.Count > 0) { - int verifyCount = _sortedLowPrioTransactions.Count > _maxLowPriorityTxPerBlock - ? 1 : maxToVerify; - ReverifyTransactions(_sortedLowPrioTransactions, _unverifiedSortedLowPriorityTransactions, - verifyCount, MaxSecondsToReverifyLowPrioTxPerIdle, snapshot); + int verifyCount = _sortedTransactions.Count > _maxTxPerBlock ? 1 : maxToVerify; + ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, + verifyCount, MaxSecondsToReverifyTxPerIdle, snapshot); } return _unverifiedTransactions.Count > 0; diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 769b457e9e..9f56ca0d97 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -17,7 +17,7 @@ public abstract class BlockBase : IVerifiable public uint Timestamp; public uint Index; public UInt160 NextConsensus; - public Witness Witness { get; set; } + public Witness Witness; private UInt256 _hash = null; public UInt256 Hash @@ -32,11 +32,25 @@ public UInt256 Hash } } - public virtual int Size => sizeof(uint) + PrevHash.Size + MerkleRoot.Size + sizeof(uint) + sizeof(uint) + NextConsensus.Size + Witness.Size; + public virtual int Size => sizeof(uint) + PrevHash.Size + MerkleRoot.Size + sizeof(uint) + sizeof(uint) + NextConsensus.Size + 1 + Witness.Size; + + Witness[] IVerifiable.Witnesses + { + get + { + return new[] { Witness }; + } + set + { + if (value.Length != 1) throw new ArgumentException(); + Witness = value[0]; + } + } public virtual void Deserialize(BinaryReader reader) { ((IVerifiable)this).DeserializeUnsigned(reader); + if (reader.ReadByte() != 1) throw new FormatException(); Witness = reader.ReadSerializable(); } @@ -50,18 +64,18 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) NextConsensus = reader.ReadSerializable(); } - UInt160 IVerifiable.GetScriptHashForVerification(Snapshot snapshot) + UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) { - if (PrevHash == UInt256.Zero) return Witness.ScriptHash; + if (PrevHash == UInt256.Zero) return new[] { Witness.ScriptHash }; Header prev_header = snapshot.GetHeader(PrevHash); if (prev_header == null) throw new InvalidOperationException(); - return prev_header.NextConsensus; + return new[] { prev_header.NextConsensus }; } public virtual void Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write(Witness); + writer.Write((byte)1); writer.Write(Witness); } void IVerifiable.SerializeUnsigned(BinaryWriter writer) @@ -85,7 +99,7 @@ public virtual JObject ToJson() json["time"] = Timestamp; json["index"] = Index; json["nextconsensus"] = NextConsensus.ToAddress(); - json["witness"] = Witness.ToJson(); + json["witnesses"] = new JArray(Witness.ToJson()); return json; } @@ -95,7 +109,7 @@ public virtual bool Verify(Snapshot snapshot) if (prev_header == null) return false; if (prev_header.Index + 1 != Index) return false; if (prev_header.Timestamp >= Timestamp) return false; - if (!this.VerifyWitness(snapshot, 1_00000000)) return false; + if (!this.VerifyWitnesses(snapshot, 1_00000000)) return false; return true; } } diff --git a/neo/Network/P2P/Payloads/ConsensusPayload.cs b/neo/Network/P2P/Payloads/ConsensusPayload.cs index cdbf459418..7a20e7c46b 100644 --- a/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ b/neo/Network/P2P/Payloads/ConsensusPayload.cs @@ -17,7 +17,7 @@ public class ConsensusPayload : IInventory public uint BlockIndex; public ushort ValidatorIndex; public byte[] Data; - public Witness Witness { get; set; } + public Witness Witness; private ConsensusMessage _deserializedMessage = null; public ConsensusMessage ConsensusMessage @@ -60,7 +60,20 @@ public UInt256 Hash sizeof(ushort) + //ValidatorIndex sizeof(uint) + //Timestamp Data.GetVarSize() + //Data - Witness.Size; //Witness + 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 { @@ -70,6 +83,7 @@ public UInt256 Hash void ISerializable.Deserialize(BinaryReader reader) { ((IVerifiable)this).DeserializeUnsigned(reader); + if (reader.ReadByte() != 1) throw new FormatException(); Witness = reader.ReadSerializable(); } @@ -82,18 +96,18 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) Data = reader.ReadVarBytes(); } - UInt160 IVerifiable.GetScriptHashForVerification(Snapshot snapshot) + UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) { ECPoint[] validators = NativeContract.NEO.GetNextBlockValidators(snapshot); if (validators.Length <= ValidatorIndex) throw new InvalidOperationException(); - return Contract.CreateSignatureRedeemScript(validators[ValidatorIndex]).ToScriptHash(); + return new[] { Contract.CreateSignatureRedeemScript(validators[ValidatorIndex]).ToScriptHash() }; } void ISerializable.Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write(Witness); + writer.Write((byte)1); writer.Write(Witness); } void IVerifiable.SerializeUnsigned(BinaryWriter writer) @@ -109,7 +123,7 @@ public bool Verify(Snapshot snapshot) { if (BlockIndex <= snapshot.Height) return false; - return this.VerifyWitness(snapshot, 0_02000000); + return this.VerifyWitnesses(snapshot, 0_02000000); } } } diff --git a/neo/Network/P2P/Payloads/IVerifiable.cs b/neo/Network/P2P/Payloads/IVerifiable.cs index 87a7dcb701..50651ad9ff 100644 --- a/neo/Network/P2P/Payloads/IVerifiable.cs +++ b/neo/Network/P2P/Payloads/IVerifiable.cs @@ -6,11 +6,11 @@ namespace Neo.Network.P2P.Payloads { public interface IVerifiable : ISerializable { - Witness Witness { get; set; } + Witness[] Witnesses { get; set; } void DeserializeUnsigned(BinaryReader reader); - UInt160 GetScriptHashForVerification(Snapshot snapshot); + UInt160[] GetScriptHashesForVerifying(Snapshot snapshot); void SerializeUnsigned(BinaryWriter writer); } diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index b21f02ccbb..152ac12b59 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -1,11 +1,9 @@ using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; -using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; -using Neo.VM; using Neo.Wallets; using System; using System.Collections.Generic; @@ -23,17 +21,22 @@ public class Transaction : IEquatable, IInventory /// Maximum number of attributes that can be contained within a transaction /// private const int MaxTransactionAttributes = 16; - private const long VerificationGasLimited = 0_10000000; public byte Version; public uint Nonce; - public byte[] Script; public UInt160 Sender; - public long Gas; + /// + /// Distributed to NEO holders. + /// + public long SystemFee; + /// + /// Distributed to consensus nodes. + /// public long NetworkFee; public uint ValidUntilBlock; public TransactionAttribute[] Attributes; - public Witness Witness { get; set; } + public byte[] Script; + public Witness[] Witnesses { get; set; } /// /// The NetworkFee for the transaction divided by its Size. @@ -56,63 +59,23 @@ public UInt256 Hash InventoryType IInventory.InventoryType => InventoryType.TX; - public int Size => - sizeof(byte) + //Version - sizeof(uint) + //Nonce - Script.GetVarSize() + //Script - Sender.Size + //Sender - sizeof(long) + //Gas - sizeof(long) + //NetworkFee - sizeof(uint) + //ValidUntilBlock - Attributes.GetVarSize() + //Attributes - Witness.Size; //Witnesses + public const int HeaderSize = + sizeof(byte) + //Version + sizeof(uint) + //Nonce + 20 + //Sender + sizeof(long) + //Gas + sizeof(long) + //NetworkFee + sizeof(uint); //ValidUntilBlock - public void CalculateFees() - { - if (Sender is null) Sender = UInt160.Zero; - if (Attributes is null) Attributes = new TransactionAttribute[0]; - if (Witness is null) Witness = new Witness - { - InvocationScript = new byte[65], - VerificationScript = new byte[39] - }; - _hash = null; - long consumed; - using (ApplicationEngine engine = ApplicationEngine.Run(Script, this)) - { - if (engine.State.HasFlag(VMState.FAULT)) - throw new InvalidOperationException(); - consumed = engine.GasConsumed; - } - _hash = null; - long d = (long)NativeContract.GAS.Factor; - Gas = consumed - ApplicationEngine.GasFree; - if (Gas <= 0) - { - Gas = 0; - } - else - { - long remainder = Gas % d; - if (remainder == 0) return; - if (remainder > 0) - Gas += d - remainder; - else - Gas -= remainder; - } - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) - { - long feeperbyte = NativeContract.Policy.GetFeePerByte(snapshot); - long fee = feeperbyte * Size; - if (fee > NetworkFee) - NetworkFee = fee; - } - } + public int Size => HeaderSize + + Attributes.GetVarSize() + //Attributes + Script.GetVarSize() + //Script + Witnesses.GetVarSize(); //Witnesses void ISerializable.Deserialize(BinaryReader reader) { DeserializeUnsigned(reader); - Witness = reader.ReadSerializable(); + Witnesses = reader.ReadSerializableArray(); } public void DeserializeUnsigned(BinaryReader reader) @@ -120,17 +83,19 @@ public void DeserializeUnsigned(BinaryReader reader) Version = reader.ReadByte(); if (Version > 0) throw new FormatException(); Nonce = reader.ReadUInt32(); - Script = reader.ReadVarBytes(ushort.MaxValue); - if (Script.Length == 0) throw new FormatException(); Sender = reader.ReadSerializable(); - Gas = reader.ReadInt64(); - if (Gas < 0) throw new FormatException(); - if (Gas % NativeContract.GAS.Factor != 0) throw new FormatException(); + SystemFee = reader.ReadInt64(); + if (SystemFee < 0) throw new FormatException(); + if (SystemFee % NativeContract.GAS.Factor != 0) throw new FormatException(); NetworkFee = reader.ReadInt64(); if (NetworkFee < 0) throw new FormatException(); - if (Gas + NetworkFee < Gas) throw new FormatException(); + if (SystemFee + NetworkFee < SystemFee) throw new FormatException(); ValidUntilBlock = reader.ReadUInt32(); Attributes = reader.ReadSerializableArray(MaxTransactionAttributes); + var cosigners = Attributes.Where(p => p.Usage == TransactionAttributeUsage.Cosigner).Select(p => new UInt160(p.Data)).ToArray(); + if (cosigners.Distinct().Count() != cosigners.Length) throw new FormatException(); + Script = reader.ReadVarBytes(ushort.MaxValue); + if (Script.Length == 0) throw new FormatException(); } public bool Equals(Transaction other) @@ -150,27 +115,29 @@ public override int GetHashCode() return Hash.GetHashCode(); } - public UInt160 GetScriptHashForVerification(Snapshot snapshot) + public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) { - return Sender; + var hashes = new HashSet { Sender }; + hashes.UnionWith(Attributes.Where(p => p.Usage == TransactionAttributeUsage.Cosigner).Select(p => new UInt160(p.Data))); + return hashes.OrderBy(p => p).ToArray(); } void ISerializable.Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write(Witness); + writer.Write(Witnesses); } void IVerifiable.SerializeUnsigned(BinaryWriter writer) { writer.Write(Version); writer.Write(Nonce); - writer.WriteVarBytes(Script); writer.Write(Sender); - writer.Write(Gas); + writer.Write(SystemFee); writer.Write(NetworkFee); writer.Write(ValidUntilBlock); writer.Write(Attributes); + writer.WriteVarBytes(Script); } public JObject ToJson() @@ -180,13 +147,13 @@ public JObject ToJson() json["size"] = Size; json["version"] = Version; json["nonce"] = Nonce; - json["script"] = Script.ToHexString(); json["sender"] = Sender.ToAddress(); - json["gas"] = new BigDecimal(Gas, (byte)NativeContract.GAS.Decimals).ToString(); - json["net_fee"] = new BigDecimal(NetworkFee, (byte)NativeContract.GAS.Decimals).ToString(); + json["sys_fee"] = new BigDecimal(SystemFee, NativeContract.GAS.Decimals).ToString(); + json["net_fee"] = new BigDecimal(NetworkFee, NativeContract.GAS.Decimals).ToString(); json["valid_until_block"] = ValidUntilBlock; json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); - json["witness"] = Witness.ToJson(); + json["script"] = Script.ToHexString(); + json["witnesses"] = Witnesses.Select(p => p.ToJson()).ToArray(); return json; } @@ -201,16 +168,16 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) return false; int size = Size; if (size > MaxTransactionSize) return false; - if (size > NativeContract.Policy.GetMaxLowPriorityTransactionSize(snapshot) && NetworkFee / size < NativeContract.Policy.GetFeePerByte(snapshot)) - return false; - if (NativeContract.Policy.GetBlockedAccounts(snapshot).Contains(Sender)) + long net_fee = NetworkFee - size * NativeContract.Policy.GetFeePerByte(snapshot); + if (net_fee < 0) return false; + if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(GetScriptHashesForVerifying(snapshot)).Count() > 0) return false; BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); - BigInteger fee = Gas + NetworkFee; + BigInteger fee = SystemFee + NetworkFee; if (balance < fee) return false; - fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Sum(p => p.Gas + p.NetworkFee); + fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Select(p => (BigInteger)(p.SystemFee + p.NetworkFee)).Sum(); if (balance < fee) return false; - return this.VerifyWitness(snapshot, VerificationGasLimited); + return this.VerifyWitnesses(snapshot, net_fee); } } } diff --git a/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs b/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs index 89c1a1cec2..d878348c28 100644 --- a/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs +++ b/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs @@ -2,6 +2,7 @@ { public enum TransactionAttributeUsage : byte { + Cosigner = 0x20, Url = 0x81 } } diff --git a/neo/SmartContract/ContractParametersContext.cs b/neo/SmartContract/ContractParametersContext.cs index 8af153119d..8fda2c8450 100644 --- a/neo/SmartContract/ContractParametersContext.cs +++ b/neo/SmartContract/ContractParametersContext.cs @@ -15,65 +15,101 @@ namespace Neo.SmartContract { public class ContractParametersContext { + private class ContextItem + { + public byte[] Script; + public ContractParameter[] Parameters; + public Dictionary Signatures; + + private ContextItem() { } + + public ContextItem(Contract contract) + { + this.Script = contract.Script; + this.Parameters = contract.ParameterList.Select(p => new ContractParameter { Type = p }).ToArray(); + } + + public static ContextItem FromJson(JObject json) + { + return new ContextItem + { + Script = json["script"]?.AsString().HexToBytes(), + Parameters = ((JArray)json["parameters"]).Select(p => ContractParameter.FromJson(p)).ToArray(), + Signatures = json["signatures"]?.Properties.Select(p => new + { + PublicKey = ECPoint.Parse(p.Key, ECCurve.Secp256r1), + Signature = p.Value.AsString().HexToBytes() + }).ToDictionary(p => p.PublicKey, p => p.Signature) + }; + } + + public JObject ToJson() + { + JObject json = new JObject(); + if (Script != null) + json["script"] = Script.ToHexString(); + json["parameters"] = new JArray(Parameters.Select(p => p.ToJson())); + if (Signatures != null) + { + json["signatures"] = new JObject(); + foreach (var signature in Signatures) + json["signatures"][signature.Key.ToString()] = signature.Value.ToHexString(); + } + return json; + } + } + public readonly IVerifiable Verifiable; - private byte[] Script; - private ContractParameter[] Parameters; - private Dictionary Signatures; + private readonly Dictionary ContextItems; public bool Completed { get { - if (Parameters is null) return false; - return Parameters.All(p => p.Value != null); + if (ContextItems.Count < ScriptHashes.Count) + return false; + return ContextItems.Values.All(p => p != null && p.Parameters.All(q => q.Value != null)); } } - private UInt160 _ScriptHash = null; - public UInt160 ScriptHash + private UInt160[] _ScriptHashes = null; + public IReadOnlyList ScriptHashes { get { - if (_ScriptHash == null) + if (_ScriptHashes == null) using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { - _ScriptHash = Verifiable.GetScriptHashForVerification(snapshot); + _ScriptHashes = Verifiable.GetScriptHashesForVerifying(snapshot); } - return _ScriptHash; + return _ScriptHashes; } } public ContractParametersContext(IVerifiable verifiable) { this.Verifiable = verifiable; + this.ContextItems = new Dictionary(); } public bool Add(Contract contract, int index, object parameter) { - if (!ScriptHash.Equals(contract.ScriptHash)) return false; - if (Parameters is null) - { - Script = contract.Script; - Parameters = contract.ParameterList.Select(p => new ContractParameter { Type = p }).ToArray(); - } - Parameters[index].Value = parameter; + ContextItem item = CreateItem(contract); + if (item == null) return false; + item.Parameters[index].Value = parameter; return true; } public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) { - if (contract.Script.IsMultiSigContract()) + if (contract.Script.IsMultiSigContract(out _, out _)) { - if (!ScriptHash.Equals(contract.ScriptHash)) return false; - if (Parameters is null) - { - Script = contract.Script; - Parameters = contract.ParameterList.Select(p => new ContractParameter { Type = p }).ToArray(); - } - if (Parameters.All(p => p.Value != null)) return false; - if (Signatures == null) - Signatures = new Dictionary(); - else if (Signatures.ContainsKey(pubkey)) + ContextItem item = CreateItem(contract); + if (item == null) return false; + if (item.Parameters.All(p => p.Value != null)) return false; + if (item.Signatures == null) + item.Signatures = new Dictionary(); + else if (item.Signatures.ContainsKey(pubkey)) return false; List points = new List(); { @@ -94,15 +130,15 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) } } if (!points.Contains(pubkey)) return false; - Signatures.Add(pubkey, signature); - if (Signatures.Count == contract.ParameterList.Length) + item.Signatures.Add(pubkey, signature); + if (item.Signatures.Count == contract.ParameterList.Length) { Dictionary dic = points.Select((p, i) => new { PublicKey = p, Index = i }).ToDictionary(p => p.PublicKey, p => p.Index); - byte[][] sigs = Signatures.Select(p => new + byte[][] sigs = item.Signatures.Select(p => new { Signature = p.Value, Index = dic[p.Key] @@ -110,7 +146,7 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) for (int i = 0; i < sigs.Length; i++) if (!Add(contract, i, sigs[i])) throw new InvalidOperationException(); - Signatures = null; + item.Signatures = null; } return true; } @@ -134,52 +170,69 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) } } + private ContextItem CreateItem(Contract contract) + { + if (ContextItems.TryGetValue(contract.ScriptHash, out ContextItem item)) + return item; + if (!ScriptHashes.Contains(contract.ScriptHash)) + return null; + item = new ContextItem(contract); + ContextItems.Add(contract.ScriptHash, item); + return item; + } + public static ContractParametersContext FromJson(JObject json) { - IVerifiable verifiable = typeof(ContractParametersContext).GetTypeInfo().Assembly.CreateInstance(json["type"].AsString()) as IVerifiable; - if (verifiable == null) throw new FormatException(); + var type = typeof(ContractParametersContext).GetTypeInfo().Assembly.GetType(json["type"].AsString()); + if (!typeof(IVerifiable).IsAssignableFrom(type)) throw new FormatException(); + + var verifiable = (IVerifiable)Activator.CreateInstance(type); using (MemoryStream ms = new MemoryStream(json["hex"].AsString().HexToBytes(), false)) using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) { verifiable.DeserializeUnsigned(reader); } - return new ContractParametersContext(verifiable) + ContractParametersContext context = new ContractParametersContext(verifiable); + foreach (var property in json["items"].Properties) { - Script = json["script"]?.AsString().HexToBytes(), - Parameters = ((JArray)json["parameters"])?.Select(p => ContractParameter.FromJson(p)).ToArray(), - Signatures = json["signatures"]?.Properties.Select(p => new - { - PublicKey = ECPoint.Parse(p.Key, ECCurve.Secp256r1), - Signature = p.Value.AsString().HexToBytes() - }).ToDictionary(p => p.PublicKey, p => p.Signature) - }; + context.ContextItems.Add(UInt160.Parse(property.Key), ContextItem.FromJson(property.Value)); + } + return context; } - public ContractParameter GetParameter(int index) + public ContractParameter GetParameter(UInt160 scriptHash, int index) { - return GetParameters()?[index]; + return GetParameters(scriptHash)?[index]; } - public IReadOnlyList GetParameters() + public IReadOnlyList GetParameters(UInt160 scriptHash) { - return Parameters; + if (!ContextItems.TryGetValue(scriptHash, out ContextItem item)) + return null; + return item.Parameters; } - public Witness GetWitness() + public Witness[] GetWitnesses() { if (!Completed) throw new InvalidOperationException(); - using (ScriptBuilder sb = new ScriptBuilder()) + Witness[] witnesses = new Witness[ScriptHashes.Count]; + for (int i = 0; i < ScriptHashes.Count; i++) { - foreach (ContractParameter parameter in Parameters.Reverse()) + ContextItem item = ContextItems[ScriptHashes[i]]; + using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitPush(parameter); + foreach (ContractParameter parameter in item.Parameters.Reverse()) + { + sb.EmitPush(parameter); + } + witnesses[i] = new Witness + { + InvocationScript = sb.ToArray(), + VerificationScript = item.Script ?? new byte[0] + }; } - return new Witness - { - InvocationScript = sb.ToArray(), - VerificationScript = Script ?? new byte[0] - }; } + return witnesses; } public static ContractParametersContext Parse(string value) @@ -198,16 +251,9 @@ public JObject ToJson() writer.Flush(); json["hex"] = ms.ToArray().ToHexString(); } - if (Script != null) - json["script"] = Script.ToHexString(); - if (Parameters != null) - json["parameters"] = new JArray(Parameters.Select(p => p.ToJson())); - if (Signatures != null) - { - json["signatures"] = new JObject(); - foreach (var signature in Signatures) - json["signatures"][signature.Key.ToString()] = signature.Value.ToHexString(); - } + json["items"] = new JObject(); + foreach (var item in ContextItems) + json["items"][item.Key.ToString()] = item.Value.ToJson(); return json; } diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index ddd16de13e..f40dad212e 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -108,9 +108,9 @@ private static StackItem DeserializeStackItem(BinaryReader reader, uint maxArray return stack_temp.Peek(); } - public static bool IsMultiSigContract(this byte[] script) + public static bool IsMultiSigContract(this byte[] script, out int m, out int n) { - int m, n = 0; + m = 0; n = 0; int i = 0; if (script.Length < 41) return false; if (script[i] > (byte)OpCode.PUSH16) return false; @@ -170,7 +170,7 @@ public static bool IsSignatureContract(this byte[] script) public static bool IsStandardContract(this byte[] script) { - return script.IsSignatureContract() || script.IsMultiSigContract(); + return script.IsSignatureContract() || script.IsMultiSigContract(out _, out _); } public static byte[] Serialize(this StackItem item) @@ -246,33 +246,39 @@ public static UInt160 ToScriptHash(this byte[] script) return new UInt160(Crypto.Default.Hash160(script)); } - internal static bool VerifyWitness(this IVerifiable verifiable, Snapshot snapshot, long gas) + internal static bool VerifyWitnesses(this IVerifiable verifiable, Snapshot snapshot, long gas) { - UInt160 hash; + if (gas < 0) return false; + + UInt160[] hashes; try { - hash = verifiable.GetScriptHashForVerification(snapshot); + hashes = verifiable.GetScriptHashesForVerifying(snapshot); } catch (InvalidOperationException) { return false; } - byte[] verification = verifiable.Witness.VerificationScript; - if (verification.Length == 0) - { - verification = snapshot.Contracts.TryGet(hash)?.Script; - if (verification is null) return false; - } - else + if (hashes.Length != verifiable.Witnesses.Length) return false; + for (int i = 0; i < hashes.Length; i++) { - if (hash != verifiable.Witness.ScriptHash) return false; - } - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, gas)) - { - engine.LoadScript(verification); - engine.LoadScript(verifiable.Witness.InvocationScript); - if (engine.Execute().HasFlag(VMState.FAULT)) return false; - if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return false; + byte[] verification = verifiable.Witnesses[i].VerificationScript; + if (verification.Length == 0) + { + verification = snapshot.Contracts.TryGet(hashes[i])?.Script; + if (verification is null) return false; + } + else + { + if (hashes[i] != verifiable.Witnesses[i].ScriptHash) return false; + } + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, gas)) + { + engine.LoadScript(verification); + engine.LoadScript(verifiable.Witnesses[i].InvocationScript); + if (engine.Execute().HasFlag(VMState.FAULT)) return false; + if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return false; + } } return true; } diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index d1e1d50aa3..a496497962 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -25,7 +25,8 @@ static partial class InteropService public static readonly uint Neo_Header_GetMerkleRoot = Register("Neo.Header.GetMerkleRoot", Header_GetMerkleRoot, 0_00000400); public static readonly uint Neo_Header_GetNextConsensus = Register("Neo.Header.GetNextConsensus", Header_GetNextConsensus, 0_00000400); public static readonly uint Neo_Transaction_GetScript = Register("Neo.Transaction.GetScript", Transaction_GetScript, 0_00000400); - public static readonly uint Neo_Transaction_GetWitnessScript = Register("Neo.Transaction.GetWitnessScript", Transaction_GetWitnessScript, 0_00000400); + public static readonly uint Neo_Transaction_GetWitnesses = Register("Neo.Transaction.GetWitnesses", Transaction_GetWitnesses, 0_00010000); + public static readonly uint Neo_Witness_GetVerificationScript = Register("Neo.Witness.GetVerificationScript", Witness_GetVerificationScript, 0_00000400); public static readonly uint Neo_Account_IsStandard = Register("Neo.Account.IsStandard", Account_IsStandard, 0_00030000); public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice); public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice); @@ -206,16 +207,27 @@ private static bool Transaction_GetScript(ApplicationEngine engine) return false; } - private static bool Transaction_GetWitnessScript(ApplicationEngine engine) + private static bool Transaction_GetWitnesses(ApplicationEngine engine) { if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) { Transaction tx = _interface.GetInterface(); if (tx == null) return false; - byte[] script = tx.Witness.VerificationScript; - if (script.Length == 0) - script = engine.Snapshot.Contracts[tx.Sender].Script; - engine.CurrentContext.EvaluationStack.Push(script); + if (tx.Witnesses.Length > engine.MaxArraySize) + return false; + engine.CurrentContext.EvaluationStack.Push(WitnessWrapper.Create(tx, engine.Snapshot).Select(p => StackItem.FromInterface(p)).ToArray()); + return true; + } + return false; + } + + private static bool Witness_GetVerificationScript(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + WitnessWrapper witness = _interface.GetInterface(); + if (witness == null) return false; + engine.CurrentContext.EvaluationStack.Push(witness.VerificationScript); return true; } return false; diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index ac75c421dc..2574bd3753 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -147,7 +147,8 @@ private static bool Runtime_GetTrigger(ApplicationEngine engine) internal static bool CheckWitness(ApplicationEngine engine, UInt160 hash) { - return hash.Equals(engine.ScriptContainer.GetScriptHashForVerification(engine.Snapshot)); + var _hashes_for_verifying = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); + return _hashes_for_verifying.Contains(hash); } private static bool CheckWitness(ApplicationEngine engine, ECPoint pubkey) diff --git a/neo/SmartContract/Native/NativeContract.cs b/neo/SmartContract/Native/NativeContract.cs index 22c54ac127..41a700ee9c 100644 --- a/neo/SmartContract/Native/NativeContract.cs +++ b/neo/SmartContract/Native/NativeContract.cs @@ -79,7 +79,7 @@ protected StorageKey CreateStorageKey(byte prefix, byte[] key = null) return storageKey; } - protected StorageKey CreateStorageKey(byte prefix, ISerializable key) + internal protected StorageKey CreateStorageKey(byte prefix, ISerializable key) { return CreateStorageKey(prefix, key.ToArray()); } diff --git a/neo/SmartContract/Native/PolicyContract.cs b/neo/SmartContract/Native/PolicyContract.cs index e5f8dbeefe..03781a5cb7 100644 --- a/neo/SmartContract/Native/PolicyContract.cs +++ b/neo/SmartContract/Native/PolicyContract.cs @@ -18,8 +18,6 @@ public sealed class PolicyContract : NativeContract public override string ServiceName => "Neo.Native.Policy"; private const byte Prefix_MaxTransactionsPerBlock = 23; - private const byte Prefix_MaxLowPriorityTransactionsPerBlock = 34; - private const byte Prefix_MaxLowPriorityTransactionSize = 29; private const byte Prefix_FeePerByte = 10; private const byte Prefix_BlockedAccounts = 15; @@ -42,14 +40,6 @@ internal override bool Initialize(ApplicationEngine engine) { Value = BitConverter.GetBytes(512u) }); - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MaxLowPriorityTransactionsPerBlock), new StorageItem - { - Value = BitConverter.GetBytes(20u) - }); - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MaxLowPriorityTransactionSize), new StorageItem - { - Value = BitConverter.GetBytes(256u) - }); engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem { Value = BitConverter.GetBytes(1000L) @@ -72,28 +62,6 @@ public uint GetMaxTransactionsPerBlock(Snapshot snapshot) return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxTransactionsPerBlock)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] - private StackItem GetMaxLowPriorityTransactionsPerBlock(ApplicationEngine engine, VMArray args) - { - return GetMaxLowPriorityTransactionsPerBlock(engine.Snapshot); - } - - public uint GetMaxLowPriorityTransactionsPerBlock(Snapshot snapshot) - { - return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxLowPriorityTransactionsPerBlock)].Value, 0); - } - - [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] - private StackItem GetMaxLowPriorityTransactionSize(ApplicationEngine engine, VMArray args) - { - return GetMaxLowPriorityTransactionSize(engine.Snapshot); - } - - public uint GetMaxLowPriorityTransactionSize(Snapshot snapshot) - { - return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxLowPriorityTransactionSize)].Value, 0); - } - [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] private StackItem GetFeePerByte(ApplicationEngine engine, VMArray args) { @@ -126,26 +94,6 @@ private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray a return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" }, AllowedTriggers = TriggerType.Application)] - private StackItem SetMaxLowPriorityTransactionsPerBlock(ApplicationEngine engine, VMArray args) - { - if (!CheckValidators(engine)) return false; - uint value = (uint)args[0].GetBigInteger(); - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxLowPriorityTransactionsPerBlock)); - storage.Value = BitConverter.GetBytes(value); - return true; - } - - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" }, AllowedTriggers = TriggerType.Application)] - private StackItem SetMaxLowPriorityTransactionSize(ApplicationEngine engine, VMArray args) - { - if (!CheckValidators(engine)) return false; - uint value = (uint)args[0].GetBigInteger(); - StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxLowPriorityTransactionSize)); - storage.Value = BitConverter.GetBytes(value); - return true; - } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" }, AllowedTriggers = TriggerType.Application)] private StackItem SetFeePerByte(ApplicationEngine engine, VMArray args) { diff --git a/neo/SmartContract/Native/Tokens/GasToken.cs b/neo/SmartContract/Native/Tokens/GasToken.cs index 0bcc303fe7..ca74a41855 100644 --- a/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/neo/SmartContract/Native/Tokens/GasToken.cs @@ -29,11 +29,11 @@ protected override bool OnPersist(ApplicationEngine engine) { if (!base.OnPersist(engine)) return false; foreach (Transaction tx in engine.Snapshot.PersistingBlock.Transactions) - Burn(engine, tx.Sender, tx.Gas + tx.NetworkFee); + Burn(engine, tx.Sender, tx.SystemFee + tx.NetworkFee); ECPoint[] validators = NEO.GetNextBlockValidators(engine.Snapshot); UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.Snapshot.PersistingBlock.ConsensusData.PrimaryIndex]).ToScriptHash(); Mint(engine, primary, engine.Snapshot.PersistingBlock.Transactions.Sum(p => p.NetworkFee)); - BigInteger sys_fee = GetSysFeeAmount(engine.Snapshot, engine.Snapshot.PersistingBlock.Index - 1) + engine.Snapshot.PersistingBlock.Transactions.Sum(p => p.Gas); + BigInteger sys_fee = GetSysFeeAmount(engine.Snapshot, engine.Snapshot.PersistingBlock.Index - 1) + engine.Snapshot.PersistingBlock.Transactions.Sum(p => p.SystemFee); StorageKey key = CreateStorageKey(Prefix_SystemFeeAmount, BitConverter.GetBytes(engine.Snapshot.PersistingBlock.Index)); engine.Snapshot.Storages.Add(key, new StorageItem { @@ -52,7 +52,7 @@ private StackItem GetSysFeeAmount(ApplicationEngine engine, VMArray args) public BigInteger GetSysFeeAmount(Snapshot snapshot, uint index) { - if (index == 0) return Blockchain.GenesisBlock.Transactions.Sum(p => p.Gas); + if (index == 0) return Blockchain.GenesisBlock.Transactions.Sum(p => p.SystemFee); StorageKey key = CreateStorageKey(Prefix_SystemFeeAmount, BitConverter.GetBytes(index)); StorageItem storage = snapshot.Storages.TryGet(key); if (storage is null) return BigInteger.Zero; diff --git a/neo/SmartContract/WitnessWrapper.cs b/neo/SmartContract/WitnessWrapper.cs new file mode 100644 index 0000000000..f67690cad5 --- /dev/null +++ b/neo/SmartContract/WitnessWrapper.cs @@ -0,0 +1,27 @@ +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using System.Linq; + +namespace Neo.SmartContract +{ + internal class WitnessWrapper + { + public byte[] VerificationScript; + + public static WitnessWrapper[] Create(IVerifiable verifiable, Snapshot snapshot) + { + WitnessWrapper[] wrappers = verifiable.Witnesses.Select(p => new WitnessWrapper + { + VerificationScript = p.VerificationScript + }).ToArray(); + if (wrappers.Any(p => p.VerificationScript.Length == 0)) + { + UInt160[] hashes = verifiable.GetScriptHashesForVerifying(snapshot); + for (int i = 0; i < wrappers.Length; i++) + if (wrappers[i].VerificationScript.Length == 0) + wrappers[i].VerificationScript = snapshot.Contracts[hashes[i]].Script; + } + return wrappers; + } + } +} diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index a7578651d0..6358a02995 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -1,4 +1,5 @@ using Neo.Cryptography; +using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -18,8 +19,6 @@ namespace Neo.Wallets { public abstract class Wallet { - private static readonly Random rand = new Random(); - public abstract string Name { get; } public abstract Version Version { get; } @@ -49,27 +48,62 @@ public WalletAccount CreateAccount(Contract contract, byte[] privateKey) return CreateAccount(contract, new KeyPair(privateKey)); } - public void FillTransaction(Transaction tx, UInt160 sender = null) + private List<(UInt160 Account, BigInteger Value)> FindPayingAccounts(List<(UInt160 Account, BigInteger Value)> orderedAccounts, BigInteger amount) { - if (tx.Nonce == 0) - tx.Nonce = (uint)rand.Next(); - if (tx.ValidUntilBlock == 0) - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) - tx.ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement; - tx.CalculateFees(); - UInt160[] accounts = sender is null ? GetAccounts().Where(p => !p.Lock && !p.WatchOnly).Select(p => p.ScriptHash).ToArray() : new[] { sender }; - BigInteger fee = tx.Gas + tx.NetworkFee; - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) - foreach (UInt160 account in accounts) + var result = new List<(UInt160 Account, BigInteger Value)>(); + BigInteger sum_balance = orderedAccounts.Select(p => p.Value).Sum(); + if (sum_balance == amount) + { + result.AddRange(orderedAccounts); + orderedAccounts.Clear(); + } + else + { + for (int i = 0; i < orderedAccounts.Count; i++) { - BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, account); - if (balance >= fee) + if (orderedAccounts[i].Value < amount) + continue; + if (orderedAccounts[i].Value == amount) { - tx.Sender = account; - return; + result.Add(orderedAccounts[i]); + orderedAccounts.RemoveAt(i); } + else + { + result.Add((orderedAccounts[i].Account, amount)); + orderedAccounts[i] = (orderedAccounts[i].Account, orderedAccounts[i].Value - amount); + } + break; } - throw new InvalidOperationException(); + if (result.Count == 0) + { + int i = orderedAccounts.Count - 1; + while (orderedAccounts[i].Value <= amount) + { + result.Add(orderedAccounts[i]); + amount -= orderedAccounts[i].Value; + orderedAccounts.RemoveAt(i); + i--; + } + for (i = 0; i < orderedAccounts.Count; i++) + { + if (orderedAccounts[i].Value < amount) + continue; + if (orderedAccounts[i].Value == amount) + { + result.Add(orderedAccounts[i]); + orderedAccounts.RemoveAt(i); + } + else + { + result.Add((orderedAccounts[i].Account, amount)); + orderedAccounts[i] = (orderedAccounts[i].Account, orderedAccounts[i].Value - amount); + } + break; + } + } + } + return result; } public WalletAccount GetAccount(ECPoint pubkey) @@ -120,7 +154,7 @@ public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, int N byte[] encryptedkey = new byte[32]; Buffer.BlockCopy(data, 7, encryptedkey, 0, 32); byte[] prikey = XOR(encryptedkey.AES256Decrypt(derivedhalf2), derivedhalf1); - Cryptography.ECC.ECPoint pubkey = Cryptography.ECC.ECCurve.Secp256r1.G * prikey; + ECPoint pubkey = Cryptography.ECC.ECCurve.Secp256r1.G * prikey; UInt160 script_hash = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); string address = script_hash.ToAddress(); if (!Encoding.ASCII.GetBytes(address).Sha256().Sha256().Take(4).SequenceEqual(addresshash)) @@ -168,10 +202,8 @@ public virtual WalletAccount Import(string nep2, string passphrase) return account; } - public Transaction MakeTransaction(IEnumerable attributes, TransferOutput[] outputs, UInt160 from = null) + public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null) { - uint nonce = (uint)rand.Next(); - var totalPay = outputs.GroupBy(p => p.AssetId, (k, g) => (k, g.Select(p => p.Value.Value).Sum())).ToArray(); UInt160[] accounts; if (from is null) { @@ -179,80 +211,153 @@ public Transaction MakeTransaction(IEnumerable attributes, } else { - if (!Contains(from)) return null; + if (!Contains(from)) + throw new ArgumentException($"The address {from.ToString()} was not found in the wallet"); accounts = new[] { from }; } - TransactionAttribute[] attr = attributes?.ToArray() ?? new TransactionAttribute[0]; using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { - uint validUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement; - foreach (UInt160 account in accounts) + HashSet cosigners = new HashSet(); + byte[] script; + List<(UInt160 Account, BigInteger Value)> balances_gas = null; + using (ScriptBuilder sb = new ScriptBuilder()) { - Transaction tx = MakeTransaction(snapshot, 0, nonce, totalPay, outputs, account, validUntilBlock, attr); - if (tx != null) return tx; + 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()) + { + sb2.EmitAppCall(assetId, "balanceOf", account); + using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, testMode: true)) + { + if (engine.State.HasFlag(VMState.FAULT)) + throw new InvalidOperationException($"Execution for {assetId.ToString()}.balanceOf('{account.ToString()}' fault"); + BigInteger value = engine.ResultStack.Pop().GetBigInteger(); + 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) + { + balances = balances.OrderBy(p => p.Value).ToList(); + var balances_used = FindPayingAccounts(balances, output.Value.Value); + cosigners.UnionWith(balances_used.Select(p => p.Account)); + foreach (var (account, value) in balances_used) + { + sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); + sb.Emit(OpCode.THROWIFNOT); + } + } + if (assetId.Equals(NativeContract.GAS.Hash)) + balances_gas = balances; + } + 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(); + TransactionAttribute[] attributes = cosigners.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Cosigner, Data = p.ToArray() }).ToArray(); + return MakeTransaction(snapshot, attributes, script, balances_gas); } - return null; } - private Transaction MakeTransaction(Snapshot snapshot, byte version, uint nonce, (UInt160, BigInteger)[] totalPay, TransferOutput[] outputs, UInt160 sender, uint validUntilBlock, TransactionAttribute[] attributes) + public Transaction MakeTransaction(TransactionAttribute[] attributes, byte[] script, UInt160 sender = null) { - BigInteger balance_gas = BigInteger.Zero; - foreach (var (assetId, amount) in totalPay) - using (ScriptBuilder sb = new ScriptBuilder()) - { - sb.EmitAppCall(assetId, "balanceOf", sender); - ApplicationEngine engine = ApplicationEngine.Run(sb.ToArray()); - if (engine.State.HasFlag(VMState.FAULT)) return null; - BigInteger balance = engine.ResultStack.Peek().GetBigInteger(); - if (balance < amount) return null; - if (assetId.Equals(NativeContract.GAS.Hash)) - { - balance_gas = balance - amount; - if (balance_gas.Sign <= 0) return null; - } - } - byte[] script; - using (ScriptBuilder sb = new ScriptBuilder()) + UInt160[] accounts; + if (sender is null) { - foreach (var output in outputs) - { - sb.EmitAppCall(output.AssetId, "transfer", sender, output.ScriptHash, output.Value.Value); - sb.Emit(OpCode.THROWIFNOT); - } - script = sb.ToArray(); + accounts = GetAccounts().Where(p => !p.Lock && !p.WatchOnly).Select(p => p.ScriptHash).ToArray(); } - Transaction tx = new Transaction + else { - Version = version, - Nonce = nonce, - Script = script, - Sender = sender, - ValidUntilBlock = validUntilBlock, - Attributes = attributes - }; - try + if (!Contains(sender)) + throw new ArgumentException($"The address {sender.ToString()} was not found in the wallet"); + accounts = new[] { sender }; + } + using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { - tx.CalculateFees(); + var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); + return MakeTransaction(snapshot, attributes, script, balances_gas); } - catch (InvalidOperationException) + } + + private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] attributes, byte[] script, List<(UInt160 Account, BigInteger Value)> balances_gas) + { + Random rand = new Random(); + foreach (var (account, value) in balances_gas) { - return null; + Transaction tx = new Transaction + { + Version = 0, + Nonce = (uint)rand.Next(), + Script = script, + Sender = account, + ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement, + Attributes = attributes + }; + using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, tx, testMode: true)) + { + if (engine.State.HasFlag(VMState.FAULT)) + throw new InvalidOperationException($"Failed execution for '{script.ToHexString()}'"); + tx.SystemFee = Math.Max(engine.GasConsumed - ApplicationEngine.GasFree, 0); + if (tx.SystemFee > 0) + { + long d = (long)NativeContract.GAS.Factor; + long remainder = tx.SystemFee % d; + if (remainder > 0) + tx.SystemFee += d - remainder; + else if (remainder < 0) + tx.SystemFee -= remainder; + } + } + UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); + int size = Transaction.HeaderSize + attributes.GetVarSize() + script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); + foreach (UInt160 hash in hashes) + { + script = GetAccount(hash)?.Contract?.Script ?? snapshot.Contracts.TryGet(hash)?.Script; + if (script is null) continue; + if (script.IsSignatureContract()) + { + size += 66 + script.GetVarSize(); + tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); + } + else if (script.IsMultiSigContract(out int m, out int n)) + { + int size_inv = 65 * m; + size += IO.Helper.GetVarSize(size_inv) + size_inv + script.GetVarSize(); + tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] * m; + using (ScriptBuilder sb = new ScriptBuilder()) + tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; + tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] * n; + using (ScriptBuilder sb = new ScriptBuilder()) + tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; + tx.NetworkFee += InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null) * n; + } + else + { + //We can support more contract types in the future. + } + } + tx.NetworkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); + if (value >= tx.SystemFee + tx.NetworkFee) return tx; } - BigInteger fee = tx.Gas + tx.NetworkFee; - if (balance_gas == BigInteger.Zero) - balance_gas = NativeContract.GAS.BalanceOf(snapshot, sender); - if (balance_gas < fee) return null; - return tx; + throw new InvalidOperationException("Insufficient GAS"); } public bool Sign(ContractParametersContext context) { - WalletAccount account = GetAccount(context.ScriptHash); - if (account?.HasKey != true) return false; - KeyPair key = account.GetKey(); - byte[] signature = context.Verifiable.Sign(key); - return context.AddSignature(account.Contract, key.PublicKey, signature); + bool fSuccess = false; + foreach (UInt160 scriptHash in context.ScriptHashes) + { + WalletAccount account = GetAccount(scriptHash); + if (account?.HasKey != true) continue; + KeyPair key = account.GetKey(); + byte[] signature = context.Verifiable.Sign(key); + fSuccess |= context.AddSignature(account.Contract, key.PublicKey, signature); + } + return fSuccess; } public abstract bool VerifyPassword(string password); From a2bc77dfacbae7f17776b082d314b3b3ba0c247d Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 24 Jun 2019 15:54:29 +0200 Subject: [PATCH 019/305] Remove log from tx hash (#854) --- neo.UnitTests/UT_MemoryPool.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/UT_MemoryPool.cs index 90cd43d5d5..1beabc1d65 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/UT_MemoryPool.cs @@ -74,9 +74,10 @@ private void AddTransactions(int count) for (int i = 0; i < count; i++) { var txToAdd = CreateTransaction(); - Console.WriteLine($"created tx: {txToAdd.Hash}"); _unit.TryAdd(txToAdd.Hash, txToAdd); } + + Console.WriteLine($"created {count} tx"); } From fd1ab91aee5420ec802d7bd70774a4ff856422bd Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 26 Jun 2019 11:25:27 +0200 Subject: [PATCH 020/305] Store transaction result (#864) * Store transaction result * optimize --- neo/Ledger/Blockchain.cs | 10 +++++++--- neo/Ledger/TransactionState.cs | 11 ++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 059560573b..181421cc3f 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -431,15 +431,19 @@ private void Persist(Block block) snapshot.Blocks.Add(block.Hash, block.Trim()); foreach (Transaction tx in block.Transactions) { - snapshot.Transactions.Add(tx.Hash, new TransactionState + var state = new TransactionState { BlockIndex = block.Index, Transaction = tx - }); + }; + + snapshot.Transactions.Add(tx.Hash, state); + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, snapshot.Clone(), tx.SystemFee)) { engine.LoadScript(tx.Script); - if (!engine.Execute().HasFlag(VMState.FAULT)) + state.VMState = engine.Execute(); + if (state.VMState == VMState.HALT) { engine.Snapshot.Commit(); } diff --git a/neo/Ledger/TransactionState.cs b/neo/Ledger/TransactionState.cs index 3b3e445b61..c493bd631e 100644 --- a/neo/Ledger/TransactionState.cs +++ b/neo/Ledger/TransactionState.cs @@ -1,5 +1,6 @@ using Neo.IO; using Neo.Network.P2P.Payloads; +using Neo.VM; using System.IO; namespace Neo.Ledger @@ -7,15 +8,20 @@ namespace Neo.Ledger public class TransactionState : ICloneable, ISerializable { public uint BlockIndex; + public VMState VMState; public Transaction Transaction; - int ISerializable.Size => sizeof(uint) + Transaction.Size; + int ISerializable.Size => + sizeof(uint) + // BlockIndex + sizeof(VMState) + // VMState + Transaction.Size; // Transaction TransactionState ICloneable.Clone() { return new TransactionState { BlockIndex = BlockIndex, + VMState = VMState, Transaction = Transaction }; } @@ -23,18 +29,21 @@ TransactionState ICloneable.Clone() 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); } } From b79f2a9595f24135fdeb1d54b87fd7f20a441670 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 26 Jun 2019 13:02:48 +0200 Subject: [PATCH 021/305] Clean memory (#868) * Clean memory * Other * Other * Update Wallet.cs --- neo/Cryptography/Base58.cs | 1 + neo/Cryptography/Helper.cs | 8 ++++++-- neo/Wallets/Wallet.cs | 8 +++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/neo/Cryptography/Base58.cs b/neo/Cryptography/Base58.cs index bb5dd42df9..fd5bf4d432 100644 --- a/neo/Cryptography/Base58.cs +++ b/neo/Cryptography/Base58.cs @@ -29,6 +29,7 @@ public static byte[] Decode(string input) } byte[] tmp = new byte[bytes.Length - (stripSignByte ? 1 : 0) + leadingZeros]; Array.Copy(bytes, stripSignByte ? 1 : 0, tmp, leadingZeros, tmp.Length - leadingZeros); + Array.Clear(bytes, 0, bytes.Length); return tmp; } diff --git a/neo/Cryptography/Helper.cs b/neo/Cryptography/Helper.cs index 48410049f1..ff1bb8d3d7 100644 --- a/neo/Cryptography/Helper.cs +++ b/neo/Cryptography/Helper.cs @@ -79,7 +79,9 @@ public static byte[] Base58CheckDecode(this string input) byte[] checksum = buffer.Sha256(0, buffer.Length - 4).Sha256(); if (!buffer.Skip(buffer.Length - 4).SequenceEqual(checksum.Take(4))) throw new FormatException(); - return buffer.Take(buffer.Length - 4).ToArray(); + var ret = buffer.Take(buffer.Length - 4).ToArray(); + Array.Clear(buffer, 0, buffer.Length); + return ret; } public static string Base58CheckEncode(this byte[] data) @@ -88,7 +90,9 @@ public static string Base58CheckEncode(this byte[] data) byte[] buffer = new byte[data.Length + 4]; Buffer.BlockCopy(data, 0, buffer, 0, data.Length); Buffer.BlockCopy(checksum, 0, buffer, data.Length, 4); - return Base58.Encode(buffer); + var ret = Base58.Encode(buffer); + Array.Clear(buffer, 0, buffer.Length); + return ret; } public static byte[] RIPEMD160(this IEnumerable value) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index 6358a02995..fc490dbea7 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -148,12 +148,18 @@ public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, int N throw new FormatException(); byte[] addresshash = new byte[4]; Buffer.BlockCopy(data, 3, addresshash, 0, 4); - byte[] derivedkey = SCrypt.DeriveKey(Encoding.UTF8.GetBytes(passphrase), addresshash, N, r, p, 64); + byte[] datapassphrase = Encoding.UTF8.GetBytes(passphrase); + byte[] derivedkey = SCrypt.DeriveKey(datapassphrase, addresshash, N, r, p, 64); + Array.Clear(datapassphrase, 0, datapassphrase.Length); byte[] derivedhalf1 = derivedkey.Take(32).ToArray(); byte[] derivedhalf2 = derivedkey.Skip(32).ToArray(); + Array.Clear(derivedkey, 0, derivedkey.Length); byte[] encryptedkey = new byte[32]; Buffer.BlockCopy(data, 7, encryptedkey, 0, 32); + Array.Clear(data, 0, data.Length); byte[] prikey = XOR(encryptedkey.AES256Decrypt(derivedhalf2), derivedhalf1); + Array.Clear(derivedhalf1, 0, derivedhalf1.Length); + Array.Clear(derivedhalf2, 0, derivedhalf2.Length); ECPoint pubkey = Cryptography.ECC.ECCurve.Secp256r1.G * prikey; UInt160 script_hash = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); string address = script_hash.ToAddress(); From 5a54eae7fa9311d24d380a75c70a62fdb69f29d2 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 26 Jun 2019 16:33:52 +0200 Subject: [PATCH 022/305] Deserialize limit (#870) * Deserialize limit * Delete optional parameters * Use `ExecutionEngine.MaxSizeForBigInteger` --- neo.UnitTests/UT_NeoToken.cs | 2 +- neo/SmartContract/Helper.cs | 10 +++++----- neo/SmartContract/InteropService.cs | 2 +- neo/SmartContract/Native/Tokens/Nep5AccountState.cs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/neo.UnitTests/UT_NeoToken.cs b/neo.UnitTests/UT_NeoToken.cs index 02f63c086e..8ef2314c23 100644 --- a/neo.UnitTests/UT_NeoToken.cs +++ b/neo.UnitTests/UT_NeoToken.cs @@ -344,7 +344,7 @@ internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint[] votes) { - var st = (VM.Types.Struct)trackable.Item.Value.DeserializeStackItem(3); + var st = (VM.Types.Struct)trackable.Item.Value.DeserializeStackItem(3, 32); st.Count.Should().Be(3); st.Select(u => u.GetType()).ToArray().Should().BeEquivalentTo(new Type[] { typeof(VM.Types.Integer), typeof(VM.Types.Integer), typeof(VM.Types.ByteArray) }); // Balance diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index f40dad212e..259e997b09 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -17,16 +17,16 @@ namespace Neo.SmartContract { public static class Helper { - public static StackItem DeserializeStackItem(this byte[] data, uint maxArraySize) + public static StackItem DeserializeStackItem(this byte[] data, uint maxArraySize, uint maxItemSize) { using (MemoryStream ms = new MemoryStream(data, false)) using (BinaryReader reader = new BinaryReader(ms)) { - return DeserializeStackItem(reader, maxArraySize); + return DeserializeStackItem(reader, maxArraySize, maxItemSize); } } - private static StackItem DeserializeStackItem(BinaryReader reader, uint maxArraySize) + private static StackItem DeserializeStackItem(BinaryReader reader, uint maxArraySize, uint maxItemSize) { Stack deserialized = new Stack(); int undeserialized = 1; @@ -36,13 +36,13 @@ private static StackItem DeserializeStackItem(BinaryReader reader, uint maxArray switch (type) { case StackItemType.ByteArray: - deserialized.Push(new ByteArray(reader.ReadVarBytes())); + deserialized.Push(new ByteArray(reader.ReadVarBytes((int)maxItemSize))); break; case StackItemType.Boolean: deserialized.Push(new VMBoolean(reader.ReadBoolean())); break; case StackItemType.Integer: - deserialized.Push(new Integer(new BigInteger(reader.ReadVarBytes()))); + deserialized.Push(new Integer(new BigInteger(reader.ReadVarBytes(ExecutionEngine.MaxSizeForBigInteger)))); break; case StackItemType.Array: case StackItemType.Struct: diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 2574bd3753..77a17369fa 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -230,7 +230,7 @@ private static bool Runtime_Deserialize(ApplicationEngine engine) StackItem item; try { - item = engine.CurrentContext.EvaluationStack.Pop().GetByteArray().DeserializeStackItem(engine.MaxArraySize); + item = engine.CurrentContext.EvaluationStack.Pop().GetByteArray().DeserializeStackItem(engine.MaxArraySize, engine.MaxItemSize); } catch (FormatException) { diff --git a/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/neo/SmartContract/Native/Tokens/Nep5AccountState.cs index 249569ebfb..2cc19f4619 100644 --- a/neo/SmartContract/Native/Tokens/Nep5AccountState.cs +++ b/neo/SmartContract/Native/Tokens/Nep5AccountState.cs @@ -19,7 +19,7 @@ public Nep5AccountState(byte[] data) public void FromByteArray(byte[] data) { - FromStruct((Struct)data.DeserializeStackItem(16)); + FromStruct((Struct)data.DeserializeStackItem(16, 32)); } protected virtual void FromStruct(Struct @struct) From 41ae0e333cac003129f7e61a5262edfbc261d8cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Wed, 26 Jun 2019 12:35:43 -0300 Subject: [PATCH 023/305] Fix memory leak on TaskManager due to continuous growth of knownHashes (3.0) (#871) --- neo/IO/Caching/FIFOSet.cs | 21 ++++++++++++++++++++- neo/Network/P2P/TaskManager.cs | 9 ++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/neo/IO/Caching/FIFOSet.cs b/neo/IO/Caching/FIFOSet.cs index ae02039825..6dd52dbccf 100644 --- a/neo/IO/Caching/FIFOSet.cs +++ b/neo/IO/Caching/FIFOSet.cs @@ -1,9 +1,12 @@ using System; +using System.Collections; +using System.Collections.Generic; using System.Collections.Specialized; +using System.Linq; namespace Neo.IO.Caching { - internal class FIFOSet where T : IEquatable + internal class FIFOSet : IEnumerable where T : IEquatable { private readonly int maxCapacity; private readonly int removeCount; @@ -37,5 +40,21 @@ public bool Add(T item) dictionary.Add(item, null); return true; } + + public void ExceptWith(IEnumerable hashes) + { + foreach (var hash in hashes) + { + dictionary.Remove(hash); + } + } + + public IEnumerator GetEnumerator() + { + var entries = dictionary.Values.Cast().ToArray(); + foreach (var entry in entries) yield return entry; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/neo/Network/P2P/TaskManager.cs b/neo/Network/P2P/TaskManager.cs index 9e028f7967..56f4e12765 100644 --- a/neo/Network/P2P/TaskManager.cs +++ b/neo/Network/P2P/TaskManager.cs @@ -1,6 +1,7 @@ using Akka.Actor; using Akka.Configuration; using Neo.IO.Actors; +using Neo.IO.Caching; using Neo.Ledger; using Neo.Network.P2P.Payloads; using System; @@ -24,7 +25,13 @@ private class Timer { } private readonly NeoSystem system; private const int MaxConncurrentTasks = 3; - private readonly HashSet knownHashes = new HashSet(); + + /// + /// The limit `Blockchain.Singleton.MemPool.Capacity * 2` was the same value used in ProtocolHandler + /// + private static readonly int MaxCachedHashes = Blockchain.Singleton.MemPool.Capacity * 2; + private readonly FIFOSet knownHashes = new FIFOSet(MaxCachedHashes); + private readonly Dictionary globalTasks = new Dictionary(); private readonly Dictionary sessions = new Dictionary(); private readonly ICancelable timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimerInterval, TimerInterval, Context.Self, new Timer(), ActorRefs.NoSender); From 4a7d95ce8a9fa6e74a926ca76106fe5209d32815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Thu, 27 Jun 2019 14:38:48 -0300 Subject: [PATCH 024/305] Adjusting TaskManager FIFOSet init (#876) Adjusting TaskManager FifoSet with the style of ProtocolHandler constructor --- neo/Network/P2P/TaskManager.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/neo/Network/P2P/TaskManager.cs b/neo/Network/P2P/TaskManager.cs index 56f4e12765..cd96547df8 100644 --- a/neo/Network/P2P/TaskManager.cs +++ b/neo/Network/P2P/TaskManager.cs @@ -25,13 +25,7 @@ private class Timer { } private readonly NeoSystem system; private const int MaxConncurrentTasks = 3; - - /// - /// The limit `Blockchain.Singleton.MemPool.Capacity * 2` was the same value used in ProtocolHandler - /// - private static readonly int MaxCachedHashes = Blockchain.Singleton.MemPool.Capacity * 2; - private readonly FIFOSet knownHashes = new FIFOSet(MaxCachedHashes); - + private readonly FIFOSet knownHashes; private readonly Dictionary globalTasks = new Dictionary(); private readonly Dictionary sessions = new Dictionary(); private readonly ICancelable timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimerInterval, TimerInterval, Context.Self, new Timer(), ActorRefs.NoSender); @@ -42,6 +36,7 @@ private class Timer { } public TaskManager(NeoSystem system) { this.system = system; + this.knownHashes = new FIFOSet(Blockchain.Singleton.MemPool.Capacity * 2); } private void OnHeaderTaskCompleted() From 228181e85a14179ecaf4b1fc6e93116812a92425 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 28 Jun 2019 13:34:03 +0200 Subject: [PATCH 025/305] Version badge in readme (#875) * Version badge in readme * Update README.md * Alts --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c1832add4f..6273e7350c 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,18 @@

- - Coverage Status + + Current Coverage Status - + Current TravisCI build status. - + License + + + Current neo version. -

NEO 3.0 (under development): A distributed network for the Smart Economy From 6c5e2cf0543a4bbace18e8a506558486660ef0ea Mon Sep 17 00:00:00 2001 From: Igor Machado Coelho Date: Sat, 29 Jun 2019 17:54:50 -0300 Subject: [PATCH 026/305] dotnet format (#879) --- neo.UnitTests/UT_ConsensusServiceMailbox.cs | 6 +-- neo.UnitTests/UT_ProtocolHandlerMailbox.cs | 46 ++++++++++----------- neo.UnitTests/UT_RemoteNodeMailbox.cs | 2 +- neo.UnitTests/UT_StorageItem.cs | 4 +- neo.UnitTests/UT_TaskManagerMailbox.cs | 8 ++-- neo/Consensus/ConsensusService.cs | 4 +- neo/Consensus/RecoveryRequest.cs | 2 +- neo/UIntBase.cs | 2 +- 8 files changed, 37 insertions(+), 37 deletions(-) diff --git a/neo.UnitTests/UT_ConsensusServiceMailbox.cs b/neo.UnitTests/UT_ConsensusServiceMailbox.cs index 4000b99f61..fded2ab54e 100644 --- a/neo.UnitTests/UT_ConsensusServiceMailbox.cs +++ b/neo.UnitTests/UT_ConsensusServiceMailbox.cs @@ -13,7 +13,7 @@ namespace Neo.UnitTests { [TestClass] - public class UT_ConsensusServiceMailbox : TestKit + public class UT_ConsensusServiceMailbox : TestKit { private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism @@ -42,10 +42,10 @@ public void ConsensusServiceMailbox_Test_IsHighPriority() 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); + uut.IsHighPriority(obj).Should().Be(false); } } } diff --git a/neo.UnitTests/UT_ProtocolHandlerMailbox.cs b/neo.UnitTests/UT_ProtocolHandlerMailbox.cs index 4645e3cfdc..edd5a4191f 100644 --- a/neo.UnitTests/UT_ProtocolHandlerMailbox.cs +++ b/neo.UnitTests/UT_ProtocolHandlerMailbox.cs @@ -15,7 +15,7 @@ namespace Neo.UnitTests { [TestClass] - public class UT_ProtocolHandlerMailbox : TestKit + public class UT_ProtocolHandlerMailbox : TestKit { private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism @@ -96,99 +96,99 @@ public void ProtocolHandlerMailbox_Test_ShallDrop() // Version (no drop) msg = Message.Create(MessageCommand.Version, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Verack (no drop) msg = Message.Create(MessageCommand.Verack, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); //connectivity // GetAddr (drop) msg = Message.Create(MessageCommand.GetAddr, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(true); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); // Addr (no drop) msg = Message.Create(MessageCommand.Addr, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Ping (no drop) msg = Message.Create(MessageCommand.Ping, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Pong (no drop) msg = Message.Create(MessageCommand.Pong, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); //synchronization // GetHeaders (drop) msg = Message.Create(MessageCommand.GetHeaders, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(true); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); // Headers (no drop) msg = Message.Create(MessageCommand.Headers, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // GetBlocks (drop) msg = Message.Create(MessageCommand.GetBlocks, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(true); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); // Mempool (drop) msg = Message.Create(MessageCommand.Mempool, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(true); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); // Inv (no drop) msg = Message.Create(MessageCommand.Inv, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // GetData (drop) msg = Message.Create(MessageCommand.GetData, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(true); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); // NotFound (no drop) msg = Message.Create(MessageCommand.NotFound, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Transaction (no drop) msg = Message.Create(MessageCommand.Transaction, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Block (no drop) msg = Message.Create(MessageCommand.Block, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Consensus (no drop) msg = Message.Create(MessageCommand.Consensus, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // Reject (no drop) msg = Message.Create(MessageCommand.Reject, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); //SPV protocol // FilterLoad (no drop) msg = Message.Create(MessageCommand.FilterLoad, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // FilterAdd (no drop) msg = Message.Create(MessageCommand.FilterAdd, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // FilterClear (no drop) msg = Message.Create(MessageCommand.FilterClear, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); // MerkleBlock (no drop) msg = Message.Create(MessageCommand.MerkleBlock, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); //others // Alert (no drop) msg = Message.Create(MessageCommand.Alert, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[]{ msg }).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); } } } diff --git a/neo.UnitTests/UT_RemoteNodeMailbox.cs b/neo.UnitTests/UT_RemoteNodeMailbox.cs index 9b35bf44e0..3e9e8a7c3b 100644 --- a/neo.UnitTests/UT_RemoteNodeMailbox.cs +++ b/neo.UnitTests/UT_RemoteNodeMailbox.cs @@ -15,7 +15,7 @@ namespace Neo.UnitTests { [TestClass] - public class UT_RemoteNodeMailbox : TestKit + public class UT_RemoteNodeMailbox : TestKit { private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism diff --git a/neo.UnitTests/UT_StorageItem.cs b/neo.UnitTests/UT_StorageItem.cs index 602a46ed11..c7372eb455 100644 --- a/neo.UnitTests/UT_StorageItem.cs +++ b/neo.UnitTests/UT_StorageItem.cs @@ -27,7 +27,7 @@ public void Value_Get() [TestMethod] public void Value_Set() { - byte[] val = new byte[] { 0x42, 0x32}; + byte[] val = new byte[] { 0x42, 0x32 }; uut.Value = val; uut.Value.Length.Should().Be(2); uut.Value[0].Should().Be(val[0]); @@ -56,7 +56,7 @@ public void Clone() StorageItem newSi = ((ICloneable)uut).Clone(); newSi.Value.Length.Should().Be(10); newSi.Value[0].Should().Be(0x42); - for (int i=1; i<10; i++) + for (int i = 1; i < 10; i++) { newSi.Value[i].Should().Be(0x20); } diff --git a/neo.UnitTests/UT_TaskManagerMailbox.cs b/neo.UnitTests/UT_TaskManagerMailbox.cs index 17244d80d9..2ba4ff2d85 100644 --- a/neo.UnitTests/UT_TaskManagerMailbox.cs +++ b/neo.UnitTests/UT_TaskManagerMailbox.cs @@ -12,7 +12,7 @@ namespace Neo.UnitTests { [TestClass] - public class UT_TaskManagerMailbox : TestKit + public class UT_TaskManagerMailbox : TestKit { private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism @@ -42,12 +42,12 @@ public void TaskManager_Test_IsHighPriority() // low priority // -> NewTasks: generic InvPayload - uut.IsHighPriority(new TaskManager.NewTasks{ Payload = new InvPayload() }).Should().Be(false); + uut.IsHighPriority(new TaskManager.NewTasks { Payload = new InvPayload() }).Should().Be(false); // 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.Block } }).Should().Be(true); + uut.IsHighPriority(new TaskManager.NewTasks { Payload = new InvPayload { Type = InventoryType.Consensus } }).Should().Be(true); // any random object should not have priority object obj = null; diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 2f538f2b6e..459a9e055b 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -232,7 +232,7 @@ private void OnCommitReceived(ConsensusPayload payload, Commit commit) // this function increases existing timer (never decreases) with a value proportional to `maxDelayInBlockTimes`*`Blockchain.SecondsPerBlock` private void ExtendTimerByFactor(int maxDelayInBlockTimes) { - TimeSpan nextDelay = expected_delay - (TimeProvider.Current.UtcNow - clock_started) + TimeSpan.FromMilliseconds(maxDelayInBlockTimes*Blockchain.SecondsPerBlock * 1000.0 / context.M); + TimeSpan nextDelay = expected_delay - (TimeProvider.Current.UtcNow - clock_started) + TimeSpan.FromMilliseconds(maxDelayInBlockTimes * Blockchain.SecondsPerBlock * 1000.0 / context.M); if (!context.WatchOnly && !context.ViewChanging && !context.CommitSent && (nextDelay > TimeSpan.Zero)) ChangeTimer(nextDelay); } @@ -263,7 +263,7 @@ private void OnConsensusPayload(ConsensusPayload payload) { return; } - context.LastSeenMessage[payload.ValidatorIndex] = (int) payload.BlockIndex; + context.LastSeenMessage[payload.ValidatorIndex] = (int)payload.BlockIndex; foreach (IP2PPlugin plugin in Plugin.P2PPlugins) if (!plugin.OnConsensusMessage(payload)) return; diff --git a/neo/Consensus/RecoveryRequest.cs b/neo/Consensus/RecoveryRequest.cs index 3152264894..971b49a324 100644 --- a/neo/Consensus/RecoveryRequest.cs +++ b/neo/Consensus/RecoveryRequest.cs @@ -13,7 +13,7 @@ public class RecoveryRequest : ConsensusMessage public override int Size => base.Size + sizeof(uint); //Timestamp - + public RecoveryRequest() : base(ConsensusMessageType.RecoveryRequest) { } public override void Deserialize(BinaryReader reader) diff --git a/neo/UIntBase.cs b/neo/UIntBase.cs index 4ab43c83f9..3808001022 100644 --- a/neo/UIntBase.cs +++ b/neo/UIntBase.cs @@ -5,7 +5,7 @@ namespace Neo { - + /// /// Base class for little-endian unsigned integers. Two classes inherit from this: UInt160 and UInt256. /// Only basic comparison/serialization are proposed for these classes. For arithmetic purposes, use BigInteger class. From 366233aaa993a06dd500e7feff3a56a844eaaf9c Mon Sep 17 00:00:00 2001 From: Ricardo Prado <38396062+lock9@users.noreply.github.com> Date: Wed, 3 Jul 2019 05:09:02 -0300 Subject: [PATCH 027/305] UT for Version/Verack messages (#853) * UT for Version/Verack messages * Indentation * Indentation * Update ProtocolHandler.cs * Fixing ClassInit; Removing shutdown; Initializing testblockchain during class setup (only once) * Running formatter * Eriks suggestion * fix typo * removed ProtocolFactory * remove TestProtocolFactory * simplify * CreateActor * Revert "CreateActor" This reverts commit 3ad56f7293b1bf63778dac3e7a55307c776b6ff0. * removed parameter from RemoteNode * fix * removed unused parameters * format --- neo.UnitTests/UT_Culture.cs | 31 ++++++++--- neo.UnitTests/UT_ProtocolHandler.cs | 50 +++++++++++++++++ neo.UnitTests/UT_RemoteNode.cs | 85 +++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 neo.UnitTests/UT_ProtocolHandler.cs create mode 100644 neo.UnitTests/UT_RemoteNode.cs diff --git a/neo.UnitTests/UT_Culture.cs b/neo.UnitTests/UT_Culture.cs index 28dc09aef4..2ddfee5381 100644 --- a/neo.UnitTests/UT_Culture.cs +++ b/neo.UnitTests/UT_Culture.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; @@ -36,7 +37,8 @@ select new var cultures = new string[] { "en-US", "zh-CN", "de-DE", "ko-KR", "ja-JP" }; var originalUICulture = CultureInfo.CurrentCulture; - var emtpyObjArray = new object[] { }; + var emptyObjArray = new object[] { }; + var testContext = new object[] { new UnitTestContext() }; // run all the tests, varying the culture each time. try @@ -47,26 +49,26 @@ select new foreach (var c in testClasses) { - var instance = c.Constructor.Invoke(emtpyObjArray); + var instance = c.Constructor.Invoke(emptyObjArray); if (c.ClassInit != null) { - c.ClassInit.Invoke(instance, emtpyObjArray); + c.ClassInit.Invoke(instance, testContext); } foreach (var m in c.TestMethods) { if (c.TestInit != null) { - c.TestInit.Invoke(instance, emtpyObjArray); + c.TestInit.Invoke(instance, emptyObjArray); } - m.Invoke(instance, emtpyObjArray); + m.Invoke(instance, emptyObjArray); if (c.TestCleanup != null) { - c.TestCleanup.Invoke(instance, emtpyObjArray); + c.TestCleanup.Invoke(instance, emptyObjArray); } } if (c.ClassCleanup != null) { - c.ClassCleanup.Invoke(instance, emtpyObjArray); + c.ClassCleanup.Invoke(instance, emptyObjArray); } } } @@ -79,6 +81,21 @@ select new } } + public class UnitTestContext : TestContext + { + public override IDictionary Properties => throw new NotImplementedException(); + + public override void WriteLine(string message) + { + Console.WriteLine(message); + } + + public override void WriteLine(string format, params object[] args) + { + Console.WriteLine(format, args); + } + } + public class NotReRunnableAttribute : Attribute { diff --git a/neo.UnitTests/UT_ProtocolHandler.cs b/neo.UnitTests/UT_ProtocolHandler.cs new file mode 100644 index 0000000000..63bfe90e44 --- /dev/null +++ b/neo.UnitTests/UT_ProtocolHandler.cs @@ -0,0 +1,50 @@ +using Akka.TestKit.Xunit2; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.P2P; +using Neo.Network.P2P.Capabilities; +using Neo.Network.P2P.Payloads; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_ProtocolHandler : TestKit + { + private NeoSystem testBlockchain; + + [TestCleanup] + public void Cleanup() + { + Shutdown(); + } + + [TestInitialize] + public void TestSetup() + { + testBlockchain = TestBlockchain.InitializeMockNeoSystem(); + } + + [TestMethod] + public void ProtocolHandler_Test_SendVersion_TellParent() + { + var senderProbe = CreateTestProbe(); + var parent = CreateTestProbe(); + var protocolActor = ActorOfAsTestActorRef(() => new ProtocolHandler(testBlockchain), parent); + + var payload = new VersionPayload() + { + UserAgent = "".PadLeft(1024, '0'), + Nonce = 1, + Magic = 2, + Timestamp = 5, + Version = 6, + Capabilities = new NodeCapability[] + { + new ServerCapability(NodeCapabilityType.TcpServer, 25) + } + }; + + senderProbe.Send(protocolActor, Message.Create(MessageCommand.Version, payload)); + parent.ExpectMsg(); + } + } +} diff --git a/neo.UnitTests/UT_RemoteNode.cs b/neo.UnitTests/UT_RemoteNode.cs new file mode 100644 index 0000000000..6b19461748 --- /dev/null +++ b/neo.UnitTests/UT_RemoteNode.cs @@ -0,0 +1,85 @@ +using Akka.IO; +using Akka.TestKit.Xunit2; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.P2P; +using Neo.Network.P2P.Capabilities; +using Neo.Network.P2P.Payloads; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_RemoteNode : TestKit + { + private static NeoSystem testBlockchain; + + public UT_RemoteNode() + : base($"remote-node-mailbox {{ mailbox-type: \"{typeof(RemoteNodeMailbox).AssemblyQualifiedName}\" }}" + + $"protocol-handler-mailbox {{ mailbox-type: \"{typeof(ProtocolHandlerMailbox).AssemblyQualifiedName}\" }}") + { + } + + [ClassInitialize] + public static void TestSetup(TestContext ctx) + { + testBlockchain = TestBlockchain.InitializeMockNeoSystem(); + } + + [TestMethod] + public void RemoteNode_Test_Abort_DifferentMagic() + { + var connectionTestProbe = CreateTestProbe(); + var remoteNodeActor = ActorOfAsTestActorRef(() => new RemoteNode(testBlockchain, connectionTestProbe, null, null)); + + connectionTestProbe.ExpectMsg(); + + var payload = new VersionPayload() + { + UserAgent = "".PadLeft(1024, '0'), + Nonce = 1, + Magic = 2, + Timestamp = 5, + Version = 6, + Capabilities = new NodeCapability[] + { + new ServerCapability(NodeCapabilityType.TcpServer, 25) + } + }; + + var testProbe = CreateTestProbe(); + testProbe.Send(remoteNodeActor, payload); + + connectionTestProbe.ExpectMsg(); + } + + [TestMethod] + public void RemoteNode_Test_Accept_IfSameMagic() + { + var connectionTestProbe = CreateTestProbe(); + var remoteNodeActor = ActorOfAsTestActorRef(() => new RemoteNode(testBlockchain, connectionTestProbe, null, null)); + + connectionTestProbe.ExpectMsg(); + + var payload = new VersionPayload() + { + UserAgent = "Unit Test".PadLeft(1024, '0'), + Nonce = 1, + Magic = ProtocolSettings.Default.Magic, + Timestamp = 5, + Version = 6, + Capabilities = new NodeCapability[] + { + new ServerCapability(NodeCapabilityType.TcpServer, 25) + } + }; + + var testProbe = CreateTestProbe(); + testProbe.Send(remoteNodeActor, payload); + + var verackMessage = connectionTestProbe.ExpectMsg(); + + //Verack + verackMessage.Data.Count.Should().Be(3); + } + } +} From aa61ae5dc1bf33cb483b75160ef52da737d32e37 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 3 Jul 2019 12:36:08 +0200 Subject: [PATCH 028/305] Fix AssetDescriptor (#882) * Fix * Better with fixed gas * Remove testnet * Fix Better with fixed gas Remove testnet * UT Asset Description * Reduce limit --- neo.UnitTests/UT_AssetDescription.cs | 38 ++++++++++++++++++++++++++++ neo/Wallets/AssetDescriptor.cs | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 neo.UnitTests/UT_AssetDescription.cs diff --git a/neo.UnitTests/UT_AssetDescription.cs b/neo.UnitTests/UT_AssetDescription.cs new file mode 100644 index 0000000000..175814b91f --- /dev/null +++ b/neo.UnitTests/UT_AssetDescription.cs @@ -0,0 +1,38 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Persistence; +using Neo.SmartContract.Native; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_AssetDescription + { + private Store Store; + + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + Store = TestBlockchain.GetStore(); + } + + [TestMethod] + public void Check_GAS() + { + var descriptor = new Wallets.AssetDescriptor(NativeContract.GAS.Hash); + descriptor.AssetId.Should().Be(NativeContract.GAS.Hash); + descriptor.AssetName.Should().Be("GAS"); + descriptor.Decimals.Should().Be(8); + } + + [TestMethod] + public void Check_NEO() + { + var descriptor = new Wallets.AssetDescriptor(NativeContract.NEO.Hash); + descriptor.AssetId.Should().Be(NativeContract.NEO.Hash); + descriptor.AssetName.Should().Be("NEO"); + descriptor.Decimals.Should().Be(0); + } + } +} \ No newline at end of file diff --git a/neo/Wallets/AssetDescriptor.cs b/neo/Wallets/AssetDescriptor.cs index 25e40dc04e..49144834bb 100644 --- a/neo/Wallets/AssetDescriptor.cs +++ b/neo/Wallets/AssetDescriptor.cs @@ -19,7 +19,7 @@ public AssetDescriptor(UInt160 asset_id) sb.EmitAppCall(asset_id, "name"); script = sb.ToArray(); } - ApplicationEngine engine = ApplicationEngine.Run(script); + ApplicationEngine engine = ApplicationEngine.Run(script, extraGAS: 3_000_000); if (engine.State.HasFlag(VMState.FAULT)) throw new ArgumentException(); this.AssetId = asset_id; this.AssetName = engine.ResultStack.Pop().GetString(); From e4a6c3f1d8b356caa18e7f232015296edc20b944 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 6 Jul 2019 02:50:52 +0800 Subject: [PATCH 029/305] Limit SYSCALLs in Verification (#856) * Limit SYSCALLs in Verification * Allow deployed contract as verification script * Simplify re-verification * Clear mempool when policy is changed * put _policyChanged in local scope * Reverify transactions when policy changes --- neo.UnitTests/UT_MemoryPool.cs | 1 + neo/Ledger/MemoryPool.cs | 23 +++- neo/Network/P2P/Payloads/Transaction.cs | 30 +++-- neo/SmartContract/InteropDescriptor.cs | 40 ++++++ neo/SmartContract/InteropService.NEO.cs | 58 ++++----- neo/SmartContract/InteropService.cs | 122 ++++++++---------- .../Native/ContractMethodAttribute.cs | 1 - .../Native/ContractMethodMetadata.cs | 1 - neo/SmartContract/Native/NativeContract.cs | 7 +- neo/SmartContract/Native/PolicyContract.cs | 8 +- neo/SmartContract/Native/Tokens/NeoToken.cs | 4 +- neo/SmartContract/Native/Tokens/Nep5Token.cs | 2 +- 12 files changed, 171 insertions(+), 126 deletions(-) create mode 100644 neo/SmartContract/InteropDescriptor.cs diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/UT_MemoryPool.cs index 1beabc1d65..ee79d96842 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/UT_MemoryPool.cs @@ -48,6 +48,7 @@ private Transaction CreateTransactionWithFee(long fee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); + mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny>())).Returns(true); mock.Setup(p => p.Verify(It.IsAny(), It.IsAny>())).Returns(true); mock.Object.Script = randomBytes; mock.Object.Sender = UInt160.Zero; diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index f4fee9bdf2..94f7ec9e0f 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -1,3 +1,4 @@ +using Akka.Actor; using Akka.Util.Internal; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; @@ -61,6 +62,7 @@ public class MemoryPool : IReadOnlyCollection internal int UnverifiedSortedTxCount => _unverifiedSortedTransactions.Count; private int _maxTxPerBlock; + private long _feePerByte; /// /// Total maximum capacity of transactions the pool can hold. @@ -99,9 +101,13 @@ public MemoryPool(NeoSystem system, int capacity) Capacity = capacity; } - internal void LoadPolicy(Snapshot snapshot) + internal bool LoadPolicy(Snapshot snapshot) { _maxTxPerBlock = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); + long newFeePerByte = NativeContract.Policy.GetFeePerByte(snapshot); + bool policyChanged = newFeePerByte > _feePerByte; + _feePerByte = newFeePerByte; + return policyChanged; } /// @@ -337,6 +343,8 @@ internal void InvalidateVerifiedTransactions() // Note: this must only be called from a single thread (the Blockchain actor) internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) { + bool policyChanged = LoadPolicy(snapshot); + _txRwLock.EnterWriteLock(); try { @@ -349,6 +357,14 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) // Add all the previously verified transactions back to the unverified transactions InvalidateVerifiedTransactions(); + + if (policyChanged) + { + foreach (PoolItem item in _unverifiedSortedTransactions.Reverse()) + _system.Blockchain.Tell(item.Tx, ActorRefs.NoSender); + _unverifiedTransactions.Clear(); + _unverifiedSortedTransactions.Clear(); + } } finally { @@ -357,10 +373,9 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) // 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) + if (block.Index > 0 && block.Index < Blockchain.Singleton.HeaderHeight || policyChanged) return; - LoadPolicy(snapshot); ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, _maxTxPerBlock, MaxSecondsToReverifyTx, snapshot); } @@ -388,7 +403,7 @@ internal void InvalidateAllTransactions() // Since unverifiedSortedTxPool is ordered in an ascending manner, we take from the end. foreach (PoolItem item in unverifiedSortedTxPool.Reverse().Take(count)) { - if (item.Tx.Verify(snapshot, _unsortedTransactions.Select(p => p.Value.Tx))) + if (item.Tx.Reverify(snapshot, _unsortedTransactions.Select(p => p.Value.Tx))) reverifiedItems.Add(item); else // Transaction no longer valid -- it will be removed from unverifiedTxPool. invalidItems.Add(item); diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 152ac12b59..923fe6447f 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -122,6 +122,26 @@ public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) return hashes.OrderBy(p => p).ToArray(); } + public virtual bool Reverify(Snapshot snapshot, IEnumerable mempool) + { + if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) + return false; + if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(GetScriptHashesForVerifying(snapshot)).Count() > 0) + return false; + BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); + BigInteger fee = SystemFee + NetworkFee; + if (balance < fee) return false; + fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Select(p => (BigInteger)(p.SystemFee + p.NetworkFee)).Sum(); + if (balance < fee) return false; + UInt160[] hashes = GetScriptHashesForVerifying(snapshot); + for (int i = 0; i < hashes.Length; i++) + { + if (Witnesses[i].VerificationScript.Length > 0) continue; + if (snapshot.Contracts.TryGet(hashes[i]) is null) return false; + } + return true; + } + void ISerializable.Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); @@ -164,19 +184,11 @@ bool IInventory.Verify(Snapshot snapshot) public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) { - if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) - return false; + if (!Reverify(snapshot, mempool)) return false; int size = Size; if (size > MaxTransactionSize) return false; long net_fee = NetworkFee - size * NativeContract.Policy.GetFeePerByte(snapshot); if (net_fee < 0) return false; - if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(GetScriptHashesForVerifying(snapshot)).Count() > 0) - return false; - BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); - BigInteger fee = SystemFee + NetworkFee; - if (balance < fee) return false; - fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Select(p => (BigInteger)(p.SystemFee + p.NetworkFee)).Sum(); - if (balance < fee) return false; return this.VerifyWitnesses(snapshot, net_fee); } } diff --git a/neo/SmartContract/InteropDescriptor.cs b/neo/SmartContract/InteropDescriptor.cs new file mode 100644 index 0000000000..c283e474de --- /dev/null +++ b/neo/SmartContract/InteropDescriptor.cs @@ -0,0 +1,40 @@ +using Neo.VM; +using System; + +namespace Neo.SmartContract +{ + internal class InteropDescriptor + { + public string Method { get; } + public uint Hash { get; } + public Func Handler { get; } + public long Price { get; } + public Func, long> PriceCalculator { get; } + public TriggerType AllowedTriggers { get; } + + public InteropDescriptor(string method, Func handler, long price, TriggerType allowedTriggers) + : this(method, handler, allowedTriggers) + { + this.Price = price; + } + + public InteropDescriptor(string method, Func handler, Func, long> priceCalculator, TriggerType allowedTriggers) + : this(method, handler, allowedTriggers) + { + this.PriceCalculator = priceCalculator; + } + + private InteropDescriptor(string method, Func handler, TriggerType allowedTriggers) + { + this.Method = method; + this.Hash = method.ToInteropMethodHash(); + this.Handler = handler; + this.AllowedTriggers = allowedTriggers; + } + + public long GetPrice(RandomAccessStack stack) + { + return PriceCalculator is null ? Price : PriceCalculator(stack); + } + } +} diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index a496497962..5787cebdb4 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -18,37 +18,37 @@ namespace Neo.SmartContract { static partial class InteropService { - public static readonly uint Neo_Native_Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0); - public static readonly uint Neo_Crypto_CheckSig = Register("Neo.Crypto.CheckSig", Crypto_CheckSig, 0_01000000); - public static readonly uint Neo_Crypto_CheckMultiSig = Register("Neo.Crypto.CheckMultiSig", Crypto_CheckMultiSig, GetCheckMultiSigPrice); - public static readonly uint Neo_Header_GetVersion = Register("Neo.Header.GetVersion", Header_GetVersion, 0_00000400); - public static readonly uint Neo_Header_GetMerkleRoot = Register("Neo.Header.GetMerkleRoot", Header_GetMerkleRoot, 0_00000400); - public static readonly uint Neo_Header_GetNextConsensus = Register("Neo.Header.GetNextConsensus", Header_GetNextConsensus, 0_00000400); - public static readonly uint Neo_Transaction_GetScript = Register("Neo.Transaction.GetScript", Transaction_GetScript, 0_00000400); - public static readonly uint Neo_Transaction_GetWitnesses = Register("Neo.Transaction.GetWitnesses", Transaction_GetWitnesses, 0_00010000); - public static readonly uint Neo_Witness_GetVerificationScript = Register("Neo.Witness.GetVerificationScript", Witness_GetVerificationScript, 0_00000400); - public static readonly uint Neo_Account_IsStandard = Register("Neo.Account.IsStandard", Account_IsStandard, 0_00030000); - public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice); - public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice); - public static readonly uint Neo_Contract_GetScript = Register("Neo.Contract.GetScript", Contract_GetScript, 0_00000400); - public static readonly uint Neo_Contract_IsPayable = Register("Neo.Contract.IsPayable", Contract_IsPayable, 0_00000400); - public static readonly uint Neo_Storage_Find = Register("Neo.Storage.Find", Storage_Find, 0_01000000); - public static readonly uint Neo_Enumerator_Create = Register("Neo.Enumerator.Create", Enumerator_Create, 0_00000400); - public static readonly uint Neo_Enumerator_Next = Register("Neo.Enumerator.Next", Enumerator_Next, 0_01000000); - public static readonly uint Neo_Enumerator_Value = Register("Neo.Enumerator.Value", Enumerator_Value, 0_00000400); - public static readonly uint Neo_Enumerator_Concat = Register("Neo.Enumerator.Concat", Enumerator_Concat, 0_00000400); - public static readonly uint Neo_Iterator_Create = Register("Neo.Iterator.Create", Iterator_Create, 0_00000400); - public static readonly uint Neo_Iterator_Key = Register("Neo.Iterator.Key", Iterator_Key, 0_00000400); - public static readonly uint Neo_Iterator_Keys = Register("Neo.Iterator.Keys", Iterator_Keys, 0_00000400); - public static readonly uint Neo_Iterator_Values = Register("Neo.Iterator.Values", Iterator_Values, 0_00000400); - public static readonly uint Neo_Iterator_Concat = Register("Neo.Iterator.Concat", Iterator_Concat, 0_00000400); - public static readonly uint Neo_Json_Serialize = Register("Neo.Json.Serialize", Json_Serialize, 0_00100000); - public static readonly uint Neo_Json_Deserialize = Register("Neo.Json.Deserialize", Json_Deserialize, 0_00500000); + public static readonly uint Neo_Native_Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application); + public static readonly uint Neo_Crypto_CheckSig = Register("Neo.Crypto.CheckSig", Crypto_CheckSig, 0_01000000, TriggerType.All); + public static readonly uint Neo_Crypto_CheckMultiSig = Register("Neo.Crypto.CheckMultiSig", Crypto_CheckMultiSig, GetCheckMultiSigPrice, TriggerType.All); + public static readonly uint Neo_Header_GetVersion = Register("Neo.Header.GetVersion", Header_GetVersion, 0_00000400, TriggerType.Application); + public static readonly uint Neo_Header_GetMerkleRoot = Register("Neo.Header.GetMerkleRoot", Header_GetMerkleRoot, 0_00000400, TriggerType.Application); + public static readonly uint Neo_Header_GetNextConsensus = Register("Neo.Header.GetNextConsensus", Header_GetNextConsensus, 0_00000400, TriggerType.Application); + public static readonly uint Neo_Transaction_GetScript = Register("Neo.Transaction.GetScript", Transaction_GetScript, 0_00000400, TriggerType.All); + public static readonly uint Neo_Transaction_GetWitnesses = Register("Neo.Transaction.GetWitnesses", Transaction_GetWitnesses, 0_00010000, TriggerType.All); + public static readonly uint Neo_Witness_GetVerificationScript = Register("Neo.Witness.GetVerificationScript", Witness_GetVerificationScript, 0_00000400, TriggerType.All); + public static readonly uint Neo_Account_IsStandard = Register("Neo.Account.IsStandard", Account_IsStandard, 0_00030000, TriggerType.All); + public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application); + public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application); + public static readonly uint Neo_Contract_GetScript = Register("Neo.Contract.GetScript", Contract_GetScript, 0_00000400, TriggerType.Application); + public static readonly uint Neo_Contract_IsPayable = Register("Neo.Contract.IsPayable", Contract_IsPayable, 0_00000400, TriggerType.Application); + public static readonly uint Neo_Storage_Find = Register("Neo.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application); + public static readonly uint Neo_Enumerator_Create = Register("Neo.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All); + public static readonly uint Neo_Enumerator_Next = Register("Neo.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All); + public static readonly uint Neo_Enumerator_Value = Register("Neo.Enumerator.Value", Enumerator_Value, 0_00000400, TriggerType.All); + public static readonly uint Neo_Enumerator_Concat = Register("Neo.Enumerator.Concat", Enumerator_Concat, 0_00000400, TriggerType.All); + public static readonly uint Neo_Iterator_Create = Register("Neo.Iterator.Create", Iterator_Create, 0_00000400, TriggerType.All); + public static readonly uint Neo_Iterator_Key = Register("Neo.Iterator.Key", Iterator_Key, 0_00000400, TriggerType.All); + public static readonly uint Neo_Iterator_Keys = Register("Neo.Iterator.Keys", Iterator_Keys, 0_00000400, TriggerType.All); + public static readonly uint Neo_Iterator_Values = Register("Neo.Iterator.Values", Iterator_Values, 0_00000400, TriggerType.All); + public static readonly uint Neo_Iterator_Concat = Register("Neo.Iterator.Concat", Iterator_Concat, 0_00000400, TriggerType.All); + public static readonly uint Neo_Json_Serialize = Register("Neo.Json.Serialize", Json_Serialize, 0_00100000, TriggerType.All); + public static readonly uint Neo_Json_Deserialize = Register("Neo.Json.Deserialize", Json_Deserialize, 0_00500000, TriggerType.All); static InteropService() { foreach (NativeContract contract in NativeContract.Contracts) - Register(contract.ServiceName, contract.Invoke, contract.GetPrice); + Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application); } private static long GetCheckMultiSigPrice(RandomAccessStack stack) @@ -70,7 +70,6 @@ private static long GetDeploymentPrice(RandomAccessStack stack) private static bool Native_Deploy(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) return false; if (engine.Snapshot.PersistingBlock.Index != 0) return false; foreach (NativeContract contract in NativeContract.Contracts) { @@ -244,7 +243,6 @@ private static bool Account_IsStandard(ApplicationEngine engine) private static bool Contract_Create(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) return false; byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); if (script.Length > 1024 * 1024) return false; @@ -269,8 +267,6 @@ private static bool Contract_Create(ApplicationEngine engine) private static bool Contract_Update(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) return false; - byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); if (script.Length > 1024 * 1024) return false; var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString(); diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 77a17369fa..669a937885 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -23,47 +23,45 @@ public static partial class InteropService public const int MaxStorageKeySize = 64; public const int MaxStorageValueSize = ushort.MaxValue; - private static readonly Dictionary> methods = new Dictionary>(); - private static readonly Dictionary prices = new Dictionary(); - private static readonly Dictionary, long>> priceCalculators = new Dictionary, long>>(); - - public static readonly uint System_ExecutionEngine_GetScriptContainer = Register("System.ExecutionEngine.GetScriptContainer", ExecutionEngine_GetScriptContainer, 0_00000250); - public static readonly uint System_ExecutionEngine_GetExecutingScriptHash = Register("System.ExecutionEngine.GetExecutingScriptHash", ExecutionEngine_GetExecutingScriptHash, 0_00000400); - public static readonly uint System_ExecutionEngine_GetCallingScriptHash = Register("System.ExecutionEngine.GetCallingScriptHash", ExecutionEngine_GetCallingScriptHash, 0_00000400); - public static readonly uint System_ExecutionEngine_GetEntryScriptHash = Register("System.ExecutionEngine.GetEntryScriptHash", ExecutionEngine_GetEntryScriptHash, 0_00000400); - public static readonly uint System_Runtime_Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250); - public static readonly uint System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250); - public static readonly uint System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000); - public static readonly uint System_Runtime_Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_00000250); - public static readonly uint System_Runtime_Log = Register("System.Runtime.Log", Runtime_Log, 0_00300000); - public static readonly uint System_Runtime_GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250); - public static readonly uint System_Runtime_Serialize = Register("System.Runtime.Serialize", Runtime_Serialize, 0_00100000); - public static readonly uint System_Runtime_Deserialize = Register("System.Runtime.Deserialize", Runtime_Deserialize, 0_00500000); - public static readonly uint System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400); - public static readonly uint System_Crypto_Verify = Register("System.Crypto.Verify", Crypto_Verify, 0_01000000); - public static readonly uint System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400); - public static readonly uint System_Blockchain_GetHeader = Register("System.Blockchain.GetHeader", Blockchain_GetHeader, 0_00007000); - public static readonly uint System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000); - public static readonly uint System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000); - public static readonly uint System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000); - public static readonly uint System_Blockchain_GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000); - public static readonly uint System_Header_GetIndex = Register("System.Header.GetIndex", Header_GetIndex, 0_00000400); - public static readonly uint System_Header_GetHash = Register("System.Header.GetHash", Header_GetHash, 0_00000400); - public static readonly uint System_Header_GetPrevHash = Register("System.Header.GetPrevHash", Header_GetPrevHash, 0_00000400); - public static readonly uint System_Header_GetTimestamp = Register("System.Header.GetTimestamp", Header_GetTimestamp, 0_00000400); - public static readonly uint System_Block_GetTransactionCount = Register("System.Block.GetTransactionCount", Block_GetTransactionCount, 0_00000400); - public static readonly uint System_Block_GetTransactions = Register("System.Block.GetTransactions", Block_GetTransactions, 0_00010000); - public static readonly uint System_Block_GetTransaction = Register("System.Block.GetTransaction", Block_GetTransaction, 0_00000400); - public static readonly uint System_Transaction_GetHash = Register("System.Transaction.GetHash", Transaction_GetHash, 0_00000400); - public static readonly uint System_Contract_Call = Register("System.Contract.Call", Contract_Call, 0_01000000); - public static readonly uint System_Contract_Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000); - public static readonly uint System_Storage_GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400); - public static readonly uint System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400); - public static readonly uint System_Storage_Get = Register("System.Storage.Get", Storage_Get, 0_01000000); - public static readonly uint System_Storage_Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice); - public static readonly uint System_Storage_PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice); - public static readonly uint System_Storage_Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000); - public static readonly uint System_StorageContext_AsReadOnly = Register("System.StorageContext.AsReadOnly", StorageContext_AsReadOnly, 0_00000400); + private static readonly Dictionary methods = new Dictionary(); + + public static readonly uint System_ExecutionEngine_GetScriptContainer = Register("System.ExecutionEngine.GetScriptContainer", ExecutionEngine_GetScriptContainer, 0_00000250, TriggerType.All); + public static readonly uint System_ExecutionEngine_GetExecutingScriptHash = Register("System.ExecutionEngine.GetExecutingScriptHash", ExecutionEngine_GetExecutingScriptHash, 0_00000400, TriggerType.All); + public static readonly uint System_ExecutionEngine_GetCallingScriptHash = Register("System.ExecutionEngine.GetCallingScriptHash", ExecutionEngine_GetCallingScriptHash, 0_00000400, TriggerType.All); + public static readonly uint System_ExecutionEngine_GetEntryScriptHash = Register("System.ExecutionEngine.GetEntryScriptHash", ExecutionEngine_GetEntryScriptHash, 0_00000400, TriggerType.All); + public static readonly uint System_Runtime_Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250, TriggerType.All); + public static readonly uint System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250, TriggerType.All); + public static readonly uint System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All); + public static readonly uint System_Runtime_Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_00000250, TriggerType.All); + public static readonly uint System_Runtime_Log = Register("System.Runtime.Log", Runtime_Log, 0_00300000, TriggerType.All); + public static readonly uint System_Runtime_GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application); + public static readonly uint System_Runtime_Serialize = Register("System.Runtime.Serialize", Runtime_Serialize, 0_00100000, TriggerType.All); + public static readonly uint System_Runtime_Deserialize = Register("System.Runtime.Deserialize", Runtime_Deserialize, 0_00500000, TriggerType.All); + public static readonly uint System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400, TriggerType.All); + public static readonly uint System_Crypto_Verify = Register("System.Crypto.Verify", Crypto_Verify, 0_01000000, TriggerType.All); + public static readonly uint System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); + public static readonly uint System_Blockchain_GetHeader = Register("System.Blockchain.GetHeader", Blockchain_GetHeader, 0_00007000, TriggerType.Application); + public static readonly uint System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application); + public static readonly uint System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application); + public static readonly uint System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application); + public static readonly uint System_Blockchain_GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application); + public static readonly uint System_Header_GetIndex = Register("System.Header.GetIndex", Header_GetIndex, 0_00000400, TriggerType.Application); + public static readonly uint System_Header_GetHash = Register("System.Header.GetHash", Header_GetHash, 0_00000400, TriggerType.Application); + public static readonly uint System_Header_GetPrevHash = Register("System.Header.GetPrevHash", Header_GetPrevHash, 0_00000400, TriggerType.Application); + public static readonly uint System_Header_GetTimestamp = Register("System.Header.GetTimestamp", Header_GetTimestamp, 0_00000400, TriggerType.Application); + public static readonly uint System_Block_GetTransactionCount = Register("System.Block.GetTransactionCount", Block_GetTransactionCount, 0_00000400, TriggerType.Application); + public static readonly uint System_Block_GetTransactions = Register("System.Block.GetTransactions", Block_GetTransactions, 0_00010000, TriggerType.Application); + public static readonly uint System_Block_GetTransaction = Register("System.Block.GetTransaction", Block_GetTransaction, 0_00000400, TriggerType.Application); + public static readonly uint System_Transaction_GetHash = Register("System.Transaction.GetHash", Transaction_GetHash, 0_00000400, TriggerType.All); + public static readonly uint System_Contract_Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.Application); + public static readonly uint System_Contract_Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application); + public static readonly uint System_Storage_GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application); + public static readonly uint System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400, TriggerType.Application); + public static readonly uint System_Storage_Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application); + public static readonly uint System_Storage_Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice, TriggerType.Application); + public static readonly uint System_Storage_PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application); + public static readonly uint System_Storage_Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application); + public static readonly uint System_StorageContext_AsReadOnly = Register("System.StorageContext.AsReadOnly", StorageContext_AsReadOnly, 0_00000400, TriggerType.Application); private static bool CheckStorageContext(ApplicationEngine engine, StorageContext context) { @@ -75,10 +73,7 @@ private static bool CheckStorageContext(ApplicationEngine engine, StorageContext public static long GetPrice(uint hash, RandomAccessStack stack) { - if (prices.TryGetValue(hash, out long price)) - return price; - else - return priceCalculators[hash](stack); + return methods[hash].GetPrice(stack); } private static long GetStoragePrice(RandomAccessStack stack) @@ -88,25 +83,25 @@ private static long GetStoragePrice(RandomAccessStack stack) internal static bool Invoke(ApplicationEngine engine, uint method) { - if (!methods.TryGetValue(method, out Func func)) + if (!methods.TryGetValue(method, out InteropDescriptor descriptor)) + return false; + if (!descriptor.AllowedTriggers.HasFlag(engine.Trigger)) return false; - return func(engine); + return descriptor.Handler(engine); } - private static uint Register(string method, Func handler, long price) + private static uint Register(string method, Func handler, long price, TriggerType allowedTriggers) { - uint hash = method.ToInteropMethodHash(); - methods.Add(hash, handler); - prices.Add(hash, price); - return hash; + InteropDescriptor descriptor = new InteropDescriptor(method, handler, price, allowedTriggers); + methods.Add(descriptor.Hash, descriptor); + return descriptor.Hash; } - private static uint Register(string method, Func handler, Func, long> priceCalculator) + private static uint Register(string method, Func handler, Func, long> priceCalculator, TriggerType allowedTriggers) { - uint hash = method.ToInteropMethodHash(); - methods.Add(hash, handler); - priceCalculators.Add(hash, priceCalculator); - return hash; + InteropDescriptor descriptor = new InteropDescriptor(method, handler, priceCalculator, allowedTriggers); + methods.Add(descriptor.Hash, descriptor); + return descriptor.Hash; } private static bool ExecutionEngine_GetScriptContainer(ApplicationEngine engine) @@ -185,15 +180,7 @@ private static bool Runtime_Log(ApplicationEngine engine) private static bool Runtime_GetTime(ApplicationEngine engine) { - if (engine.Snapshot.PersistingBlock == null) - { - Header header = engine.Snapshot.GetHeader(engine.Snapshot.CurrentBlockHash); - engine.CurrentContext.EvaluationStack.Push(header.Timestamp + Blockchain.SecondsPerBlock); - } - else - { - engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.PersistingBlock.Timestamp); - } + engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.PersistingBlock.Timestamp); return true; } @@ -529,7 +516,6 @@ private static bool Contract_Call(ApplicationEngine engine) private static bool Contract_Destroy(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) return false; UInt160 hash = engine.CurrentScriptHash; ContractState contract = engine.Snapshot.Contracts.TryGet(hash); if (contract == null) return true; @@ -542,7 +528,6 @@ private static bool Contract_Destroy(ApplicationEngine engine) private static bool PutEx(ApplicationEngine engine, StorageContext context, byte[] key, byte[] value, StorageFlags flags) { - if (engine.Trigger != TriggerType.Application) return false; if (key.Length > MaxStorageKeySize) return false; if (value.Length > MaxStorageValueSize) return false; if (context.IsReadOnly) return false; @@ -593,7 +578,6 @@ private static bool Storage_PutEx(ApplicationEngine engine) private static bool Storage_Delete(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) return false; if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) { StorageContext context = _interface.GetInterface(); diff --git a/neo/SmartContract/Native/ContractMethodAttribute.cs b/neo/SmartContract/Native/ContractMethodAttribute.cs index 145979a6cb..393737d92e 100644 --- a/neo/SmartContract/Native/ContractMethodAttribute.cs +++ b/neo/SmartContract/Native/ContractMethodAttribute.cs @@ -7,7 +7,6 @@ internal class ContractMethodAttribute : Attribute { public string Name { get; set; } public long Price { get; } - public TriggerType AllowedTriggers { get; set; } = TriggerType.All; public ContractParameterType ReturnType { get; } public ContractParameterType[] ParameterTypes { get; set; } = new ContractParameterType[0]; public string[] ParameterNames { get; set; } = new string[0]; diff --git a/neo/SmartContract/Native/ContractMethodMetadata.cs b/neo/SmartContract/Native/ContractMethodMetadata.cs index 1c67b553e2..f63344eec9 100644 --- a/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -8,6 +8,5 @@ internal class ContractMethodMetadata { public Func Delegate; public long Price; - public TriggerType AllowedTriggers; } } diff --git a/neo/SmartContract/Native/NativeContract.cs b/neo/SmartContract/Native/NativeContract.cs index 41a700ee9c..08971dcf6b 100644 --- a/neo/SmartContract/Native/NativeContract.cs +++ b/neo/SmartContract/Native/NativeContract.cs @@ -57,8 +57,7 @@ protected NativeContract() methods.Add(name, new ContractMethodMetadata { Delegate = (Func)method.CreateDelegate(typeof(Func), this), - Price = attribute.Price, - AllowedTriggers = attribute.AllowedTriggers + Price = attribute.Price }); } this.Manifest.Abi.Methods = descriptors.ToArray(); @@ -92,7 +91,6 @@ internal bool Invoke(ApplicationEngine engine) VMArray args = (VMArray)engine.CurrentContext.EvaluationStack.Pop(); if (!methods.TryGetValue(operation, out ContractMethodMetadata method)) return false; - if (!method.AllowedTriggers.HasFlag(engine.Trigger)) return false; StackItem result = method.Delegate(engine, args); engine.CurrentContext.EvaluationStack.Push(result); return true; @@ -110,9 +108,10 @@ internal virtual bool Initialize(ApplicationEngine engine) return true; } - [ContractMethod(0, ContractParameterType.Boolean, AllowedTriggers = TriggerType.System)] + [ContractMethod(0, ContractParameterType.Boolean)] protected StackItem OnPersist(ApplicationEngine engine, VMArray args) { + if (engine.Trigger != TriggerType.System) return false; return OnPersist(engine); } diff --git a/neo/SmartContract/Native/PolicyContract.cs b/neo/SmartContract/Native/PolicyContract.cs index 03781a5cb7..2109e8b65d 100644 --- a/neo/SmartContract/Native/PolicyContract.cs +++ b/neo/SmartContract/Native/PolicyContract.cs @@ -84,7 +84,7 @@ public UInt160[] GetBlockedAccounts(Snapshot snapshot) return snapshot.Storages[CreateStorageKey(Prefix_BlockedAccounts)].Value.AsSerializableArray(); } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray args) { if (!CheckValidators(engine)) return false; @@ -94,7 +94,7 @@ private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray a return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetFeePerByte(ApplicationEngine engine, VMArray args) { if (!CheckValidators(engine)) return false; @@ -104,7 +104,7 @@ private StackItem SetFeePerByte(ApplicationEngine engine, VMArray args) return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] private StackItem BlockAccount(ApplicationEngine engine, VMArray args) { if (!CheckValidators(engine)) return false; @@ -118,7 +118,7 @@ private StackItem BlockAccount(ApplicationEngine engine, VMArray args) return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] private StackItem UnblockAccount(ApplicationEngine engine, VMArray args) { if (!CheckValidators(engine)) return false; diff --git a/neo/SmartContract/Native/Tokens/NeoToken.cs b/neo/SmartContract/Native/Tokens/NeoToken.cs index 5320171059..6de1ac1970 100644 --- a/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -132,7 +132,7 @@ public BigInteger UnclaimedGas(Snapshot snapshot, UInt160 account, uint end) return CalculateBonus(snapshot, state.Balance, state.BalanceHeight, end); } - [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] private StackItem RegisterValidator(ApplicationEngine engine, VMArray args) { ECPoint pubkey = args[0].GetByteArray().AsSerializable(); @@ -150,7 +150,7 @@ private bool RegisterValidator(Snapshot snapshot, ECPoint pubkey) return true; } - [ContractMethod(5_00000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(5_00000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" })] private StackItem Vote(ApplicationEngine engine, VMArray args) { UInt160 account = new UInt160(args[0].GetByteArray()); diff --git a/neo/SmartContract/Native/Tokens/Nep5Token.cs b/neo/SmartContract/Native/Tokens/Nep5Token.cs index 8234bbcac4..6a2e1c78b3 100644 --- a/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -158,7 +158,7 @@ public virtual BigInteger BalanceOf(Snapshot snapshot, UInt160 account) return state.Balance; } - [ContractMethod(0_08000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" }, AllowedTriggers = TriggerType.Application)] + [ContractMethod(0_08000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" })] protected StackItem Transfer(ApplicationEngine engine, VMArray args) { UInt160 from = new UInt160(args[0].GetByteArray()); From 9a6d96b86f384efcff9b1240431cef2e8313fd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Fri, 5 Jul 2019 18:17:18 -0300 Subject: [PATCH 030/305] Adding total number of lines on README (#884) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6273e7350c..7f6402eb06 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ Current neo version. + + total lines. +

NEO 3.0 (under development): A distributed network for the Smart Economy From 6073040ed7f6f982af784ae0f55f3a4f6e03280b Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 8 Jul 2019 09:14:44 +0200 Subject: [PATCH 031/305] DeriveKey Scrypt unit test (#886) * Strengthen PBKDF2 * Update SCrypt.cs * Scrypt DeriveKey ut * Deterministic behaviour --- neo.UnitTests/UT_Scrypt.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 neo.UnitTests/UT_Scrypt.cs diff --git a/neo.UnitTests/UT_Scrypt.cs b/neo.UnitTests/UT_Scrypt.cs new file mode 100644 index 0000000000..2d5100aa53 --- /dev/null +++ b/neo.UnitTests/UT_Scrypt.cs @@ -0,0 +1,19 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using System; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_Scrypt + { + [TestMethod] + public void DeriveKeyTest() + { + int N = 16384, r = 8, p = 8; + + var derivedkey = SCrypt.DeriveKey(new byte[] { 0x01, 0x02, 0x03 }, new byte[] { 0x04, 0x05, 0x06 }, N, r, p, 64).ToHexString(); + Assert.AreEqual("2bb9c7bb9c392f0dd37821b76e42b01944902520f48d00946a51e72c960fba0a3c62a87d835c9df10a8ad66a04cdf02fbb10b9d7396c20959f28d6cb3ddfdffb", derivedkey); + } + } +} From f482362f416629da7312247bdc9f9e4bfea692ee Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 8 Jul 2019 11:31:47 +0200 Subject: [PATCH 032/305] Remove Debug.Assert (#893) * if Debug * if DEBUG --- neo/Cryptography/ECC/ECFieldElement.cs | 4 ---- neo/Cryptography/ECC/ECPoint.cs | 2 -- 2 files changed, 6 deletions(-) diff --git a/neo/Cryptography/ECC/ECFieldElement.cs b/neo/Cryptography/ECC/ECFieldElement.cs index 4ce303dcc8..e33cab4206 100644 --- a/neo/Cryptography/ECC/ECFieldElement.cs +++ b/neo/Cryptography/ECC/ECFieldElement.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Linq; using System.Numerics; @@ -47,8 +46,6 @@ private static BigInteger[] FastLucasSequence(BigInteger p, BigInteger P, BigInt int n = k.GetBitLength(); int s = k.GetLowestSetBit(); - Debug.Assert(k.TestBit(s)); - BigInteger Uh = 1; BigInteger Vl = 2; BigInteger Vh = P; @@ -131,7 +128,6 @@ public ECFieldElement Sqrt() V += curve.Q; } V >>= 1; - Debug.Assert((V * V).Mod(curve.Q) == Value); return new ECFieldElement(V, curve); } } diff --git a/neo/Cryptography/ECC/ECPoint.cs b/neo/Cryptography/ECC/ECPoint.cs index 6c0746adab..18b1cdab1a 100644 --- a/neo/Cryptography/ECC/ECPoint.cs +++ b/neo/Cryptography/ECC/ECPoint.cs @@ -1,6 +1,5 @@ using Neo.IO; using System; -using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; @@ -386,7 +385,6 @@ private static sbyte[] WindowNaf(sbyte width, BigInteger k) { if (x.Y.Equals(y.Y)) return x.Twice(); - Debug.Assert(x.Y.Equals(-y.Y)); return x.Curve.Infinity; } ECFieldElement gamma = (y.Y - x.Y) / (y.X - x.X); From bf67fc1b3090048f62494c3483339d5e510b25a0 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 9 Jul 2019 10:10:42 +0200 Subject: [PATCH 033/305] Ensure the hash length (3x) (#896) * Ensure the hash length * Update UIntBase.cs * Fix ecpoint --- neo/Cryptography/ECC/ECPoint.cs | 18 ++++++++++++++---- neo/UIntBase.cs | 12 +++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/neo/Cryptography/ECC/ECPoint.cs b/neo/Cryptography/ECC/ECPoint.cs index 18b1cdab1a..dbd556eb50 100644 --- a/neo/Cryptography/ECC/ECPoint.cs +++ b/neo/Cryptography/ECC/ECPoint.cs @@ -112,11 +112,21 @@ public static ECPoint DeserializeFrom(BinaryReader reader, ECCurve curve) { case 0x02: case 0x03: - reader.Read(buffer, 1, expectedLength); - return DecodePoint(buffer.Take(1 + expectedLength).ToArray(), curve); + { + if (reader.Read(buffer, 1, expectedLength) != expectedLength) + { + throw new FormatException(); + } + return DecodePoint(buffer.Take(1 + expectedLength).ToArray(), curve); + } case 0x04: - reader.Read(buffer, 1, expectedLength * 2); - return DecodePoint(buffer, curve); + { + if (reader.Read(buffer, 1, expectedLength * 2) != expectedLength * 2) + { + throw new FormatException(); + } + return DecodePoint(buffer, curve); + } default: throw new FormatException("Invalid point encoding " + buffer[0]); } diff --git a/neo/UIntBase.cs b/neo/UIntBase.cs index 3808001022..22431c4cae 100644 --- a/neo/UIntBase.cs +++ b/neo/UIntBase.cs @@ -5,7 +5,6 @@ namespace Neo { - /// /// Base class for little-endian unsigned integers. Two classes inherit from this: UInt160 and UInt256. /// Only basic comparison/serialization are proposed for these classes. For arithmetic purposes, use BigInteger class. @@ -15,7 +14,7 @@ public abstract class UIntBase : IEquatable, ISerializable /// /// Storing unsigned int in a little-endian byte array. /// - private byte[] data_bytes; + private readonly byte[] data_bytes; /// /// Number of bytes of the unsigned int. @@ -44,7 +43,10 @@ protected UIntBase(int bytes, byte[] value) /// void ISerializable.Deserialize(BinaryReader reader) { - reader.Read(data_bytes, 0, data_bytes.Length); + if (reader.Read(data_bytes, 0, data_bytes.Length) != data_bytes.Length) + { + throw new FormatException(); + } } /// @@ -53,7 +55,7 @@ void ISerializable.Deserialize(BinaryReader reader) /// public bool Equals(UIntBase other) { - if (ReferenceEquals(other, null)) + if (other is null) return false; if (ReferenceEquals(this, other)) return true; @@ -68,7 +70,7 @@ public bool Equals(UIntBase other) /// public override bool Equals(object obj) { - if (ReferenceEquals(obj, null)) + if (obj is null) return false; if (!(obj is UIntBase)) return false; From 45838754d556d0a235f9beb22617ef709e97fb4c Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 10 Jul 2019 20:49:03 +0200 Subject: [PATCH 034/305] Fix fifoset (3x) (#905) * Fix fifo set * More tests for FIFO size * Fixing indexes * Testing asset of different sets * Testing again * Update UT_FifoSet.cs * Update UT_FifoSet.cs * Tests for fifo max size * Fixing indentation --- neo.UnitTests/UT_FifoSet.cs | 54 +++++++++++++++++++++++++++++++++++++ neo/IO/Caching/FIFOSet.cs | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 neo.UnitTests/UT_FifoSet.cs diff --git a/neo.UnitTests/UT_FifoSet.cs b/neo.UnitTests/UT_FifoSet.cs new file mode 100644 index 0000000000..ff5ccf9c27 --- /dev/null +++ b/neo.UnitTests/UT_FifoSet.cs @@ -0,0 +1,54 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Caching; +using System.Linq; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_FifoSet + { + [TestMethod] + public void FifoSetTest() + { + var a = UInt256.Zero; + var b = new UInt256(); + var c = new UInt256(new byte[32] { + 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 + }); + + var set = new FIFOSet(3); + + Assert.IsTrue(set.Add(a)); + Assert.IsFalse(set.Add(a)); + Assert.IsFalse(set.Add(b)); + Assert.IsTrue(set.Add(c)); + + CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a, c }); + + var d = new UInt256(new byte[32] { + 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, 0x02 + }); + + // Testing Fifo max size + Assert.IsTrue(set.Add(d)); + CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a, c, d }); + + var e = new UInt256(new byte[32] { + 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, 0x03 + }); + + Assert.IsTrue(set.Add(e)); + Assert.IsFalse(set.Add(e)); + CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { c, d, e }); + } + } +} diff --git a/neo/IO/Caching/FIFOSet.cs b/neo/IO/Caching/FIFOSet.cs index 6dd52dbccf..7769cb5ad5 100644 --- a/neo/IO/Caching/FIFOSet.cs +++ b/neo/IO/Caching/FIFOSet.cs @@ -51,7 +51,7 @@ public void ExceptWith(IEnumerable hashes) public IEnumerator GetEnumerator() { - var entries = dictionary.Values.Cast().ToArray(); + var entries = dictionary.Keys.Cast().ToArray(); foreach (var entry in entries) yield return entry; } From 4928065f7bc39321d19ba7d918751192a270ffcb Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 11 Jul 2019 11:39:02 +0200 Subject: [PATCH 035/305] Speed up multisig ut (#908) * Speedup multisign ut * Start tasks --- neo.UnitTests/UT_Transaction.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 18aafb31bb..fc4b89b4a9 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -12,6 +12,7 @@ using Neo.VM; using Neo.Wallets; using Neo.Wallets.NEP6; +using System.Threading.Tasks; namespace Neo.UnitTests { @@ -104,8 +105,14 @@ public void FeeIsMultiSigContract() using (var unlockA = walletA.Unlock("123")) using (var unlockB = walletB.Unlock("123")) { - var a = walletA.CreateAccount(); - var b = walletB.CreateAccount(); + var ta = new Task(() => walletA.CreateAccount()); + var tb = new Task(() => walletA.CreateAccount()); + ta.Start(); + tb.Start(); + Task.WaitAll(ta, tb); + + var a = ta.Result; + var b = tb.Result; var multiSignContract = Contract.CreateMultiSigContract(2, new ECPoint[] @@ -114,8 +121,13 @@ public void FeeIsMultiSigContract() b.GetKey().PublicKey }); - var acc = walletA.CreateAccount(multiSignContract, a.GetKey()); - acc = walletB.CreateAccount(multiSignContract, b.GetKey()); + ta = new Task(() => walletA.CreateAccount(multiSignContract, a.GetKey())); + tb = new Task(() => walletB.CreateAccount(multiSignContract, b.GetKey())); + ta.Start(); + tb.Start(); + Task.WaitAll(ta, tb); + + var acc = tb.Result; // Fake balance From 1dde609fb1837f45a193bc2ef452bda904badc86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Thu, 11 Jul 2019 10:04:51 -0300 Subject: [PATCH 036/305] Speed up mempool reverify on policy change (#892) * Speed up mempool reverify on policy change * Caching newFeePerByte before loop * Reverting fee per byte check when PolicyChange * optimize --- neo/Ledger/MemoryPool.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 94f7ec9e0f..96e17dc34f 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -361,7 +361,8 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) if (policyChanged) { foreach (PoolItem item in _unverifiedSortedTransactions.Reverse()) - _system.Blockchain.Tell(item.Tx, ActorRefs.NoSender); + if(item.Tx.FeePerByte >= _feePerByte) + _system.Blockchain.Tell(item.Tx, ActorRefs.NoSender); _unverifiedTransactions.Clear(); _unverifiedSortedTransactions.Clear(); } From f1c2879dd1a00c4918a1dc80ece595befc4aba14 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 11 Jul 2019 17:08:56 +0200 Subject: [PATCH 037/305] Update UT_Transaction.cs (#913) --- neo.UnitTests/UT_Transaction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index fc4b89b4a9..5b06a816a5 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -106,7 +106,7 @@ public void FeeIsMultiSigContract() using (var unlockB = walletB.Unlock("123")) { var ta = new Task(() => walletA.CreateAccount()); - var tb = new Task(() => walletA.CreateAccount()); + var tb = new Task(() => walletB.CreateAccount()); ta.Start(); tb.Start(); Task.WaitAll(ta, tb); From 338d0508e19be5036d317a9af3097eb82387a8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Sat, 13 Jul 2019 11:25:09 -0300 Subject: [PATCH 038/305] Codecov badge (#917) * Codecov badge * Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7f6402eb06..77e2a2e566 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,21 @@

- - Current Coverage Status - Current TravisCI build status. - - License - Current neo version. + + Current Coverage Status. + - total lines. + Current total lines. + + License. +

NEO 3.0 (under development): A distributed network for the Smart Economy From 884bd405b6daa191b755ce120bd6a968e6fd204b Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 15 Jul 2019 23:00:55 +0200 Subject: [PATCH 039/305] Super speed multisg unit test (#915) * Speed up * Remove async --- neo.UnitTests/UT_Transaction.cs | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/UT_Transaction.cs index 5b06a816a5..dc41d3a0d8 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/UT_Transaction.cs @@ -12,7 +12,6 @@ using Neo.VM; using Neo.Wallets; using Neo.Wallets.NEP6; -using System.Threading.Tasks; namespace Neo.UnitTests { @@ -87,10 +86,10 @@ private NEP6Wallet GenerateTestWallet() JObject wallet = new JObject(); wallet["name"] = "noname"; wallet["version"] = new System.Version().ToString(); - wallet["scrypt"] = ScryptParameters.Default.ToJson(); + wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = null; - wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"0.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":null}"); + wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"0.0\",\"scrypt\":{\"n\":0,\"r\":0,\"p\":0},\"accounts\":[],\"extra\":null}"); return new NEP6Wallet(wallet); } @@ -105,14 +104,8 @@ public void FeeIsMultiSigContract() using (var unlockA = walletA.Unlock("123")) using (var unlockB = walletB.Unlock("123")) { - var ta = new Task(() => walletA.CreateAccount()); - var tb = new Task(() => walletB.CreateAccount()); - ta.Start(); - tb.Start(); - Task.WaitAll(ta, tb); - - var a = ta.Result; - var b = tb.Result; + var a = walletA.CreateAccount(); + var b = walletB.CreateAccount(); var multiSignContract = Contract.CreateMultiSigContract(2, new ECPoint[] @@ -121,13 +114,8 @@ public void FeeIsMultiSigContract() b.GetKey().PublicKey }); - ta = new Task(() => walletA.CreateAccount(multiSignContract, a.GetKey())); - tb = new Task(() => walletB.CreateAccount(multiSignContract, b.GetKey())); - ta.Start(); - tb.Start(); - Task.WaitAll(ta, tb); - - var acc = tb.Result; + walletA.CreateAccount(multiSignContract, a.GetKey()); + var acc = walletB.CreateAccount(multiSignContract, b.GetKey()); // Fake balance From 81e5faf11eb1d1d243d7a68ecc2428231269cf35 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 16 Jul 2019 13:55:22 +0200 Subject: [PATCH 040/305] Get notifications (#898) * Get notifications * Allow all notifications * Check array size * Update neo/SmartContract/InteropService.cs Co-Authored-By: Erik Zhang * Unit test * optimize * Return hash and state --- neo.UnitTests/UT_InteropService.cs | 193 ++++++++++++++++++++++++++++ neo/SmartContract/InteropService.cs | 16 +++ neo/UInt160.cs | 11 +- neo/UInt256.cs | 11 +- 4 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 neo.UnitTests/UT_InteropService.cs diff --git a/neo.UnitTests/UT_InteropService.cs b/neo.UnitTests/UT_InteropService.cs new file mode 100644 index 0000000000..b5e3626109 --- /dev/null +++ b/neo.UnitTests/UT_InteropService.cs @@ -0,0 +1,193 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; +using Neo.VM; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_InteropService + { + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + } + + [TestMethod] + public void Runtime_GetNotifications_Test() + { + UInt160 scriptHash2; + var snapshot = TestBlockchain.GetStore().GetSnapshot(); + + using (var script = new ScriptBuilder()) + { + // Drop arguments + + script.Emit(VM.OpCode.TOALTSTACK); + script.Emit(VM.OpCode.DROP); + script.Emit(VM.OpCode.FROMALTSTACK); + + // Notify method + + script.EmitSysCall(InteropService.System_Runtime_Notify); + + // Add return + + script.EmitPush(true); + + // Mock contract + + scriptHash2 = script.ToArray().ToScriptHash(); + + snapshot.Contracts.Delete(scriptHash2); + snapshot.Contracts.Add(scriptHash2, new Ledger.ContractState() + { + Script = script.ToArray(), + Manifest = ContractManifest.CreateDefault(scriptHash2), + }); + } + + // Wrong length + + using (var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true)) + using (var script = new ScriptBuilder()) + { + // Retrive + + script.EmitPush(1); + script.EmitSysCall(InteropService.System_Runtime_GetNotifications); + + // Execute + + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(VMState.FAULT, engine.Execute()); + } + + // All test + + using (var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true)) + using (var script = new ScriptBuilder()) + { + // Notification 1 -> 13 + + script.EmitPush(13); + script.EmitSysCall(InteropService.System_Runtime_Notify); + + // Call script + + script.EmitAppCall(scriptHash2, "test"); + + // Drop return + + script.Emit(OpCode.DROP); + + // Receive all notifications + + script.EmitPush(UInt160.Zero.ToArray()); + script.EmitSysCall(InteropService.System_Runtime_GetNotifications); + + // Execute + + engine.LoadScript(script.ToArray()); + var currentScriptHash = engine.EntryScriptHash; + + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.AreEqual(2, engine.Notifications.Count); + + Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(VM.Types.Array)); + + var array = (VM.Types.Array)engine.ResultStack.Pop(); + + // Check syscall result + + AssertNotification(array[1], scriptHash2, "test"); + AssertNotification(array[0], currentScriptHash, 13); + + // Check notifications + + Assert.AreEqual(scriptHash2, engine.Notifications[1].ScriptHash); + Assert.AreEqual("test", engine.Notifications[1].State.GetString()); + + Assert.AreEqual(currentScriptHash, engine.Notifications[0].ScriptHash); + Assert.AreEqual(13, engine.Notifications[0].State.GetBigInteger()); + } + + // Script notifications + + using (var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true)) + using (var script = new ScriptBuilder()) + { + // Notification 1 -> 13 + + script.EmitPush(13); + script.EmitSysCall(InteropService.System_Runtime_Notify); + + // Call script + + script.EmitAppCall(scriptHash2, "test"); + + // Drop return + + script.Emit(OpCode.DROP); + + // Receive all notifications + + script.EmitPush(scriptHash2.ToArray()); + script.EmitSysCall(InteropService.System_Runtime_GetNotifications); + + // Execute + + engine.LoadScript(script.ToArray()); + var currentScriptHash = engine.EntryScriptHash; + + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.AreEqual(2, engine.Notifications.Count); + + Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(VM.Types.Array)); + + var array = (VM.Types.Array)engine.ResultStack.Pop(); + + // Check syscall result + + AssertNotification(array[0], scriptHash2, "test"); + + // Check notifications + + Assert.AreEqual(scriptHash2, engine.Notifications[1].ScriptHash); + Assert.AreEqual("test", engine.Notifications[1].State.GetString()); + + Assert.AreEqual(currentScriptHash, engine.Notifications[0].ScriptHash); + Assert.AreEqual(13, engine.Notifications[0].State.GetBigInteger()); + } + + // Clean storage + + snapshot.Contracts.Delete(scriptHash2); + } + + private void AssertNotification(StackItem stackItem, UInt160 scriptHash, string notification) + { + Assert.IsInstanceOfType(stackItem, typeof(VM.Types.Array)); + + var array = (VM.Types.Array)stackItem; + Assert.AreEqual(2, array.Count); + CollectionAssert.AreEqual(scriptHash.ToArray(), array[0].GetByteArray()); + Assert.AreEqual(notification, array[1].GetString()); + } + + private void AssertNotification(StackItem stackItem, UInt160 scriptHash, int notification) + { + Assert.IsInstanceOfType(stackItem, typeof(VM.Types.Array)); + + var array = (VM.Types.Array)stackItem; + Assert.AreEqual(2, array.Count); + CollectionAssert.AreEqual(scriptHash.ToArray(), array[0].GetByteArray()); + Assert.AreEqual(notification, array[1].GetBigInteger()); + } + } +} \ No newline at end of file diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 669a937885..fbb3a8655d 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -38,6 +38,7 @@ public static partial class InteropService public static readonly uint System_Runtime_Serialize = Register("System.Runtime.Serialize", Runtime_Serialize, 0_00100000, TriggerType.All); public static readonly uint System_Runtime_Deserialize = Register("System.Runtime.Deserialize", Runtime_Deserialize, 0_00500000, TriggerType.All); public static readonly uint System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400, TriggerType.All); + public static readonly uint System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", Runtime_GetNotifications, 0_00010000, TriggerType.All); public static readonly uint System_Crypto_Verify = Register("System.Crypto.Verify", Crypto_Verify, 0_01000000, TriggerType.All); public static readonly uint System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); public static readonly uint System_Blockchain_GetHeader = Register("System.Blockchain.GetHeader", Blockchain_GetHeader, 0_00007000, TriggerType.Application); @@ -201,6 +202,21 @@ private static bool Runtime_Serialize(ApplicationEngine engine) return true; } + private static bool Runtime_GetNotifications(ApplicationEngine engine) + { + var data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + if (data.Length != UInt160.Length) return false; + if (!engine.CheckArraySize(engine.Notifications.Count)) return false; + + var hash = new UInt160(data); + IEnumerable notifications = engine.Notifications; + if (!hash.Equals(UInt160.Zero)) + notifications = notifications.Where(p => p.ScriptHash == hash); + + engine.CurrentContext.EvaluationStack.Push(notifications.Select(u => new VM.Types.Array(new StackItem[] { u.ScriptHash.ToArray(), u.State })).ToArray()); + return true; + } + private static bool Runtime_GetInvocationCounter(ApplicationEngine engine) { if (!engine.InvocationCounter.TryGetValue(engine.CurrentScriptHash, out var counter)) diff --git a/neo/UInt160.cs b/neo/UInt160.cs index fd11b7fb9b..0c6bea4b5a 100644 --- a/neo/UInt160.cs +++ b/neo/UInt160.cs @@ -9,6 +9,7 @@ namespace Neo ///
public class UInt160 : UIntBase, IComparable, IEquatable { + public const int Length = 20; public static readonly UInt160 Zero = new UInt160(); /// @@ -23,7 +24,7 @@ public UInt160() /// The byte[] constructor invokes base class UIntBase constructor for 20 bytes /// public UInt160(byte[] value) - : base(20, value) + : base(Length, value) { } @@ -79,7 +80,7 @@ public static new UInt160 Parse(string value) throw new ArgumentNullException(); if (value.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) value = value.Substring(2); - if (value.Length != 40) + if (value.Length != Length * 2) throw new FormatException(); return new UInt160(value.HexToBytes().Reverse().ToArray()); } @@ -97,13 +98,13 @@ public static bool TryParse(string s, out UInt160 result) } if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) s = s.Substring(2); - if (s.Length != 40) + if (s.Length != Length * 2) { result = null; return false; } - byte[] data = new byte[20]; - for (int i = 0; i < 20; i++) + byte[] data = new byte[Length]; + for (int i = 0; i < Length; i++) if (!byte.TryParse(s.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier, null, out data[i])) { result = null; diff --git a/neo/UInt256.cs b/neo/UInt256.cs index de3c9ecd47..ec79ae21ea 100644 --- a/neo/UInt256.cs +++ b/neo/UInt256.cs @@ -9,6 +9,7 @@ namespace Neo ///
public class UInt256 : UIntBase, IComparable, IEquatable { + public const int Length = 32; public static readonly UInt256 Zero = new UInt256(); @@ -24,7 +25,7 @@ public UInt256() /// The byte[] constructor invokes base class UIntBase constructor for 32 bytes ///
public UInt256(byte[] value) - : base(32, value) + : base(Length, value) { } @@ -80,7 +81,7 @@ public static new UInt256 Parse(string s) throw new ArgumentNullException(); if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) s = s.Substring(2); - if (s.Length != 64) + if (s.Length != Length * 2) throw new FormatException(); return new UInt256(s.HexToBytes().Reverse().ToArray()); } @@ -98,13 +99,13 @@ public static bool TryParse(string s, out UInt256 result) } if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) s = s.Substring(2); - if (s.Length != 64) + if (s.Length != Length * 2) { result = null; return false; } - byte[] data = new byte[32]; - for (int i = 0; i < 32; i++) + byte[] data = new byte[Length]; + for (int i = 0; i < Length; i++) if (!byte.TryParse(s.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier, null, out data[i])) { result = null; From 74146ebb08ceaf2169330a1123d29b72425920f9 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 17 Jul 2019 17:37:54 +0800 Subject: [PATCH 041/305] Gas initialize (#911) --- neo.UnitTests/UT_GasToken.cs | 24 ++++++++++----------- neo.UnitTests/UT_NeoToken.cs | 4 ++-- neo/Ledger/Blockchain.cs | 2 +- neo/SmartContract/Native/Tokens/GasToken.cs | 9 ++++++++ 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/neo.UnitTests/UT_GasToken.cs b/neo.UnitTests/UT_GasToken.cs index cdf73eecbb..72a0c4d7ae 100644 --- a/neo.UnitTests/UT_GasToken.cs +++ b/neo.UnitTests/UT_GasToken.cs @@ -52,12 +52,12 @@ public void Check_BalanceOfTransferAndBurn() NativeContract.NEO.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)); var supply = NativeContract.GAS.TotalSupply(snapshot); - supply.Should().Be(0); + supply.Should().Be(3000000000000000); // Check unclaim var unclaim = UT_NeoToken.Check_UnclaimedGas(snapshot, from); - unclaim.Value.Should().Be(new BigInteger(800000000000)); + unclaim.Value.Should().Be(new BigInteger(600000000000)); unclaim.State.Should().BeTrue(); // Transfer @@ -66,7 +66,7 @@ public void Check_BalanceOfTransferAndBurn() NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(100_000_000); NativeContract.NEO.BalanceOf(snapshot, to).Should().Be(0); - NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(800000000000); + NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(3000600000000000); NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(0); // Check unclaim @@ -76,7 +76,7 @@ public void Check_BalanceOfTransferAndBurn() unclaim.State.Should().BeTrue(); supply = NativeContract.GAS.TotalSupply(snapshot); - supply.Should().Be(800000000000); + supply.Should().Be(3000600000000000); snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 3); // Gas @@ -84,16 +84,16 @@ public void Check_BalanceOfTransferAndBurn() keyCount = snapshot.Storages.GetChangeSet().Count(); - NativeContract.GAS.Transfer(snapshot, from, to, 800000000000, false).Should().BeFalse(); // Not signed - NativeContract.GAS.Transfer(snapshot, from, to, 800000000001, true).Should().BeFalse(); // More than balance - NativeContract.GAS.Transfer(snapshot, from, to, 800000000000, true).Should().BeTrue(); // All balance + NativeContract.GAS.Transfer(snapshot, from, to, 3000600000000000, false).Should().BeFalse(); // Not signed + NativeContract.GAS.Transfer(snapshot, from, to, 3000600000000001, true).Should().BeFalse(); // More than balance + NativeContract.GAS.Transfer(snapshot, from, to, 3000600000000000, true).Should().BeTrue(); // All balance // Balance of - NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(800000000000); + NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000600000000000); NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(0); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount); // All + snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 1); // All // Burn @@ -106,19 +106,19 @@ public void Check_BalanceOfTransferAndBurn() // Burn more than expected Assert.ThrowsException(() => - NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(800000000001))); + NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000600000000001))); // Real burn NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(1)); - NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(799999999999); + NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000599999999999); keyCount.Should().Be(snapshot.Storages.GetChangeSet().Count()); // Burn all - NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(799999999999)); + NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000599999999999)); (keyCount - 1).Should().Be(snapshot.Storages.GetChangeSet().Count()); diff --git a/neo.UnitTests/UT_NeoToken.cs b/neo.UnitTests/UT_NeoToken.cs index 8ef2314c23..3997573c0f 100644 --- a/neo.UnitTests/UT_NeoToken.cs +++ b/neo.UnitTests/UT_NeoToken.cs @@ -91,7 +91,7 @@ public void Check_UnclaimedGas() Blockchain.StandbyValidators).ToScriptHash().ToArray(); var unclaim = Check_UnclaimedGas(snapshot, from); - unclaim.Value.Should().Be(new BigInteger(800000000000)); + unclaim.Value.Should().Be(new BigInteger(600000000000)); unclaim.State.Should().BeTrue(); unclaim = Check_UnclaimedGas(snapshot, new byte[19]); @@ -146,7 +146,7 @@ public void Check_Transfer() // Check unclaim var unclaim = Check_UnclaimedGas(snapshot, from); - unclaim.Value.Should().Be(new BigInteger(800000000000)); + unclaim.Value.Should().Be(new BigInteger(600000000000)); unclaim.State.Should().BeTrue(); // Transfer diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 181421cc3f..5d9ad00ee9 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -30,7 +30,7 @@ public class FillCompleted { } public static readonly uint SecondsPerBlock = ProtocolSettings.Default.SecondsPerBlock; public const uint DecrementInterval = 2000000; public const int MaxValidators = 1024; - public static readonly uint[] GenerationAmount = { 8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + public static readonly uint[] GenerationAmount = { 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; public static readonly TimeSpan TimePerBlock = TimeSpan.FromSeconds(SecondsPerBlock); public static readonly ECPoint[] StandbyValidators = ProtocolSettings.Default.StandbyValidators.OfType().Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); diff --git a/neo/SmartContract/Native/Tokens/GasToken.cs b/neo/SmartContract/Native/Tokens/GasToken.cs index ca74a41855..fd39979670 100644 --- a/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/neo/SmartContract/Native/Tokens/GasToken.cs @@ -25,6 +25,15 @@ internal GasToken() { } + internal override bool Initialize(ApplicationEngine engine) + { + if (!base.Initialize(engine)) return false; + if (TotalSupply(engine.Snapshot) != BigInteger.Zero) return false; + UInt160 account = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, Blockchain.StandbyValidators).ToScriptHash(); + Mint(engine, account, 30_000_000 * Factor); + return true; + } + protected override bool OnPersist(ApplicationEngine engine) { if (!base.OnPersist(engine)) return false; From 2faf037d35e576878827e37341d0327fb717d32b Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 17 Jul 2019 20:21:58 +0200 Subject: [PATCH 042/305] Add ChangeView.Reason (#926) * Change view reason * Move order for compatibility * Moved to other file * Change order * Change agreement --- neo/Consensus/ChangeView.cs | 13 +++++++++++-- neo/Consensus/ChangeViewReason.cs | 11 +++++++++++ neo/Consensus/ConsensusContext.cs | 3 ++- neo/Consensus/ConsensusService.cs | 19 +++++++++++++------ 4 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 neo/Consensus/ChangeViewReason.cs diff --git a/neo/Consensus/ChangeView.cs b/neo/Consensus/ChangeView.cs index 5cc00cc77a..7d5b3e96eb 100644 --- a/neo/Consensus/ChangeView.cs +++ b/neo/Consensus/ChangeView.cs @@ -8,6 +8,7 @@ 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 @@ -15,8 +16,14 @@ public class ChangeView : ConsensusMessage /// public uint Timestamp; - public override int Size => base.Size - + sizeof(uint); //Timestamp + /// + /// Reason + /// + public ChangeViewReason Reason; + + public override int Size => base.Size + + sizeof(uint) + // Timestamp + sizeof(ChangeViewReason); // Reason public ChangeView() : base(ConsensusMessageType.ChangeView) { } @@ -24,12 +31,14 @@ public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); Timestamp = reader.ReadUInt32(); + Reason = (ChangeViewReason)reader.ReadByte(); } public override void Serialize(BinaryWriter writer) { base.Serialize(writer); writer.Write(Timestamp); + writer.Write((byte)Reason); } } } diff --git a/neo/Consensus/ChangeViewReason.cs b/neo/Consensus/ChangeViewReason.cs new file mode 100644 index 0000000000..64a2a6053e --- /dev/null +++ b/neo/Consensus/ChangeViewReason.cs @@ -0,0 +1,11 @@ +namespace Neo.Consensus +{ + public enum ChangeViewReason : byte + { + Timeout = 0x0, + ChangeAgreement = 0x1, + TxNotFound = 0x2, + TxRejectedByPolicy = 0x3, + TxInvalid = 0x4, + } +} \ No newline at end of file diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 5fa53ce567..0d6855a623 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -159,10 +159,11 @@ public bool Load() } } - public ConsensusPayload MakeChangeView() + public ConsensusPayload MakeChangeView(ChangeViewReason reason) { return ChangeViewPayloads[MyIndex] = MakeSignedPayload(new ChangeView { + Reason = reason, Timestamp = TimeProvider.Current.UtcNow.ToTimestamp() }); } diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 459a9e055b..b8e594d642 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -63,13 +63,13 @@ private bool AddTransaction(Transaction tx, bool verify) if (verify && !tx.Verify(context.Snapshot, context.Transactions.Values)) { Log($"Invalid transaction: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning); - RequestChangeView(); + RequestChangeView(ChangeViewReason.TxInvalid); return false; } if (!Plugin.CheckPolicy(tx)) { Log($"reject tx: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning); - RequestChangeView(); + RequestChangeView(ChangeViewReason.TxRejectedByPolicy); return false; } context.Transactions[tx.Hash] = tx; @@ -124,7 +124,7 @@ private void CheckExpectedView(byte viewNumber) // 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() }); + localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeChangeView(ChangeViewReason.ChangeAgreement) }); } InitializeConsensus(viewNumber); } @@ -547,7 +547,14 @@ private void OnTimer(Timer timer) } else { - RequestChangeView(); + var reason = ChangeViewReason.Timeout; + + if (context.Block != null && context.TransactionHashes.Count() > context.Transactions.Count) + { + reason = ChangeViewReason.TxNotFound; + } + + RequestChangeView(reason); } } } @@ -575,7 +582,7 @@ public static Props Props(IActorRef localNode, IActorRef taskManager, Store stor return Akka.Actor.Props.Create(() => new ConsensusService(localNode, taskManager, store, wallet)).WithMailbox("consensus-service-mailbox"); } - private void RequestChangeView() + private void RequestChangeView(ChangeViewReason reason) { if (context.WatchOnly) return; // Request for next view is always one view more than the current context.ViewNumber @@ -591,7 +598,7 @@ private void RequestChangeView() return; } Log($"request change view: height={context.Block.Index} view={context.ViewNumber} nv={expectedView} nc={context.CountCommitted} nf={context.CountFailed}"); - localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeChangeView() }); + localNode.Tell(new LocalNode.SendDirectly { Inventory = context.MakeChangeView(reason) }); CheckExpectedView(expectedView); } From 01a88a2fe98a9e5212957af37f79dee7050db373 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 18 Jul 2019 09:15:05 +0200 Subject: [PATCH 043/305] Optimization (#929) --- neo.UnitTests/UT_StorageKey.cs | 17 +++++++++++++++-- neo/Ledger/StorageKey.cs | 5 ++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/neo.UnitTests/UT_StorageKey.cs b/neo.UnitTests/UT_StorageKey.cs index 965e46ae4e..ab718f0f89 100644 --- a/neo.UnitTests/UT_StorageKey.cs +++ b/neo.UnitTests/UT_StorageKey.cs @@ -1,5 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.Ledger; namespace Neo.UnitTests @@ -21,6 +22,19 @@ public void ScriptHash_Get() uut.ScriptHash.Should().BeNull(); } + [TestMethod] + public void Size() + { + var ut = new StorageKey() { Key = new byte[17], ScriptHash = UInt160.Zero }; + ut.ToArray().Length.Should().Be(((ISerializable)ut).Size); + + ut = new StorageKey() { Key = new byte[0], ScriptHash = UInt160.Zero }; + ut.ToArray().Length.Should().Be(((ISerializable)ut).Size); + + ut = new StorageKey() { Key = new byte[16], ScriptHash = UInt160.Zero }; + ut.ToArray().Length.Should().Be(((ISerializable)ut).Size); + } + [TestMethod] public void ScriptHash_Set() { @@ -113,6 +127,5 @@ public void GetHashCode_Get() uut.Key = TestUtils.GetByteArray(10, 0x42); uut.GetHashCode().Should().Be(806209853); } - } -} +} \ No newline at end of file diff --git a/neo/Ledger/StorageKey.cs b/neo/Ledger/StorageKey.cs index 891fb081ec..f975b7a422 100644 --- a/neo/Ledger/StorageKey.cs +++ b/neo/Ledger/StorageKey.cs @@ -30,9 +30,8 @@ public bool Equals(StorageKey other) public override bool Equals(object obj) { - if (obj is null) return false; - if (!(obj is StorageKey)) return false; - return Equals((StorageKey)obj); + if (!(obj is StorageKey other)) return false; + return Equals(other); } public override int GetHashCode() From fd52018bdcab645c7bd03018daac2a22289e66fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Thu, 18 Jul 2019 05:55:16 -0300 Subject: [PATCH 044/305] Fixing wrong size on ConsensusPayload (#931) --- neo/Network/P2P/Payloads/ConsensusPayload.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/neo/Network/P2P/Payloads/ConsensusPayload.cs b/neo/Network/P2P/Payloads/ConsensusPayload.cs index 7a20e7c46b..a4c6e86ee6 100644 --- a/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ b/neo/Network/P2P/Payloads/ConsensusPayload.cs @@ -58,7 +58,6 @@ public UInt256 Hash PrevHash.Size + //PrevHash sizeof(uint) + //BlockIndex sizeof(ushort) + //ValidatorIndex - sizeof(uint) + //Timestamp Data.GetVarSize() + //Data 1 + Witness.Size; //Witness From deb3e6d9a512d82a0b8b2fc938f8dc52fd25b6ba Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 19 Jul 2019 18:28:26 +0200 Subject: [PATCH 045/305] Temporary fix for #922 (#936) --- neo.UnitTests/UT_Culture.cs | 6 +++--- neo.UnitTests/UT_RemoteNode.cs | 5 +++-- neo.UnitTests/UT_RemoteNodeMailbox.cs | 12 +++--------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/neo.UnitTests/UT_Culture.cs b/neo.UnitTests/UT_Culture.cs index 2ddfee5381..807e67990c 100644 --- a/neo.UnitTests/UT_Culture.cs +++ b/neo.UnitTests/UT_Culture.cs @@ -1,9 +1,9 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Neo.UnitTests { @@ -18,7 +18,7 @@ public void All_Tests_Cultures() { // get all tests in the unit test project assembly var testClasses = (from t in typeof(NotReRunnableAttribute).GetTypeInfo().Assembly.DefinedTypes - where t.GetCustomAttribute() != null + where t.GetCustomAttribute() != null && t.GetCustomAttribute() == null select new { Constructor = t.GetConstructor(new Type[] { }), diff --git a/neo.UnitTests/UT_RemoteNode.cs b/neo.UnitTests/UT_RemoteNode.cs index 6b19461748..60d587ed13 100644 --- a/neo.UnitTests/UT_RemoteNode.cs +++ b/neo.UnitTests/UT_RemoteNode.cs @@ -1,4 +1,4 @@ -using Akka.IO; +using Akka.IO; using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -8,7 +8,8 @@ namespace Neo.UnitTests { - [TestClass] + [TestClass] + [NotReRunnable] public class UT_RemoteNode : TestKit { private static NeoSystem testBlockchain; diff --git a/neo.UnitTests/UT_RemoteNodeMailbox.cs b/neo.UnitTests/UT_RemoteNodeMailbox.cs index 3e9e8a7c3b..ff3e6e17d9 100644 --- a/neo.UnitTests/UT_RemoteNodeMailbox.cs +++ b/neo.UnitTests/UT_RemoteNodeMailbox.cs @@ -1,20 +1,14 @@ -using Akka.TestKit; +using System; +using Akka.IO; using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using Moq; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; using Neo.Network.P2P; -using Akka.Configuration; -using System.Net; -using Akka.Actor; -using Akka.IO; namespace Neo.UnitTests { [TestClass] + [NotReRunnable] public class UT_RemoteNodeMailbox : TestKit { private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism From a3d53d044b299f2ed5f80a5d141cf26a32ebc05c Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 19 Jul 2019 18:40:21 +0200 Subject: [PATCH 046/305] Scrypt from 3s to 16ms (#937) --- neo.UnitTests/UT_Scrypt.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo.UnitTests/UT_Scrypt.cs b/neo.UnitTests/UT_Scrypt.cs index 2d5100aa53..8e2bf1e30a 100644 --- a/neo.UnitTests/UT_Scrypt.cs +++ b/neo.UnitTests/UT_Scrypt.cs @@ -10,10 +10,10 @@ public class UT_Scrypt [TestMethod] public void DeriveKeyTest() { - int N = 16384, r = 8, p = 8; + int N = 32, r = 2, p = 2; var derivedkey = SCrypt.DeriveKey(new byte[] { 0x01, 0x02, 0x03 }, new byte[] { 0x04, 0x05, 0x06 }, N, r, p, 64).ToHexString(); - Assert.AreEqual("2bb9c7bb9c392f0dd37821b76e42b01944902520f48d00946a51e72c960fba0a3c62a87d835c9df10a8ad66a04cdf02fbb10b9d7396c20959f28d6cb3ddfdffb", derivedkey); + Assert.AreEqual("b6274d3a81892c24335ab46a08ec16d040ac00c5943b212099a44b76a9b8102631ab988fa07fb35357cee7b0e3910098c0774c0e97399997676d890b2bf2bb25", derivedkey); } } } From b19ca6d948a5d221f5990b46efb6ab4d755be4d8 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 22 Jul 2019 03:59:52 +0200 Subject: [PATCH 047/305] Remove minner tx hole (#934) * Remove minner tx hole * Fix equals --- neo.UnitTests/UT_Block.cs | 27 ++++++++------------------- neo/Network/P2P/Payloads/Block.cs | 13 +++++++------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/neo.UnitTests/UT_Block.cs b/neo.UnitTests/UT_Block.cs index 886f5099f0..f6b9218070 100644 --- a/neo.UnitTests/UT_Block.cs +++ b/neo.UnitTests/UT_Block.cs @@ -1,5 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.IO.Json; using Neo.Network.P2P.Payloads; using System.IO; @@ -86,21 +87,13 @@ public void Serialize() byte[] data; using (MemoryStream stream = new MemoryStream()) + using (BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, true)) { - using (BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, true)) - { - uut.Serialize(writer); - data = stream.ToArray(); - } + uut.Serialize(writer); + data = stream.ToArray(); } - byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 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, 1, 0, 1, 0, 0 }; - - data.Length.Should().Be(requiredData.Length); - for (int i = 0; i < data.Length; i++) - { - data[i].Should().Be(requiredData[i]); - } + Assert.AreEqual(data.ToHexString(), "0000000000000000000000000000000000000000000000000000000000000000000000000f29b0d748a9ccf8c5af3cde10db3e36ec9a5f720643a2bcb4add76b3daf41d880ab04fd0000000000000000000000000000000000000000000000000100015101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"); } [TestMethod] @@ -111,14 +104,10 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - byte[] data = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 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, 1, 0, 0 }; - int index = 0; - using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) + using (MemoryStream ms = new MemoryStream("0000000000000000000000000000000000000000000000000000000000000000000000000f29b0d748a9ccf8c5af3cde10db3e36ec9a5f720643a2bcb4add76b3daf41d880ab04fd0000000000000000000000000000000000000000000000000100015101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000".HexToBytes(), false)) + using (BinaryReader reader = new BinaryReader(ms)) { - using (BinaryReader reader = new BinaryReader(ms)) - { - uut.Deserialize(reader); - } + uut.Deserialize(reader); } assertStandardBlockTestVals(val256, merkRoot, val160, timestampVal, indexVal, scriptVal, transactionsVal); diff --git a/neo/Network/P2P/Payloads/Block.cs b/neo/Network/P2P/Payloads/Block.cs index 58faf54cb1..b63035e0ed 100644 --- a/neo/Network/P2P/Payloads/Block.cs +++ b/neo/Network/P2P/Payloads/Block.cs @@ -12,7 +12,7 @@ namespace Neo.Network.P2P.Payloads public class Block : BlockBase, IInventory, IEquatable { public const int MaxContentsPerBlock = ushort.MaxValue; - public const int MaxTransactionsPerBlock = MaxContentsPerBlock - 1; + public const int MaxTransactionsPerBlock = MaxContentsPerBlock; public ConsensusData ConsensusData; public Transaction[] Transactions; @@ -41,13 +41,13 @@ public Header Header InventoryType IInventory.InventoryType => InventoryType.Block; public override int Size => base.Size - + IO.Helper.GetVarSize(Transactions.Length + 1) //Count + + IO.Helper.GetVarSize(Transactions.Length) //Count + ConsensusData.Size //ConsensusData + Transactions.Sum(p => p.Size); //Transactions public static UInt256 CalculateMerkleRoot(UInt256 consensusDataHash, params UInt256[] transactionHashes) { - List hashes = new List(transactionHashes.Length + 1) { consensusDataHash }; + List hashes = new List(transactionHashes.Length) { consensusDataHash }; hashes.AddRange(transactionHashes); return MerkleTree.ComputeRoot(hashes); } @@ -58,7 +58,7 @@ public override void Deserialize(BinaryReader reader) int count = (int)reader.ReadVarInt(MaxContentsPerBlock); if (count == 0) throw new FormatException(); ConsensusData = reader.ReadSerializable(); - Transactions = new Transaction[count - 1]; + Transactions = new Transaction[count]; for (int i = 0; i < Transactions.Length; i++) Transactions[i] = reader.ReadSerializable(); if (Transactions.Distinct().Count() != Transactions.Length) @@ -76,7 +76,8 @@ public bool Equals(Block other) public override bool Equals(object obj) { - return Equals(obj as Block); + if (!(obj is Block b)) return false; + return Equals(b); } public override int GetHashCode() @@ -92,7 +93,7 @@ public void RebuildMerkleRoot() public override void Serialize(BinaryWriter writer) { base.Serialize(writer); - writer.WriteVarInt(Transactions.Length + 1); + writer.WriteVarInt(Transactions.Length); writer.Write(ConsensusData); foreach (Transaction tx in Transactions) writer.Write(tx); From 0730e080508d17afaed2e163fea677f640b0e873 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 22 Jul 2019 12:03:41 +0200 Subject: [PATCH 048/305] Revert "Remove minner tx hole (#934)" (#944) This reverts commit 5b321fbcf97f9fff43980d9e0108c1cbf374968c. --- neo.UnitTests/UT_Block.cs | 27 +++++++++++++++++++-------- neo/Network/P2P/Payloads/Block.cs | 13 ++++++------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/neo.UnitTests/UT_Block.cs b/neo.UnitTests/UT_Block.cs index f6b9218070..886f5099f0 100644 --- a/neo.UnitTests/UT_Block.cs +++ b/neo.UnitTests/UT_Block.cs @@ -1,6 +1,5 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; using Neo.IO.Json; using Neo.Network.P2P.Payloads; using System.IO; @@ -87,13 +86,21 @@ public void Serialize() byte[] data; using (MemoryStream stream = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, true)) { - uut.Serialize(writer); - data = stream.ToArray(); + using (BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, true)) + { + uut.Serialize(writer); + data = stream.ToArray(); + } } - Assert.AreEqual(data.ToHexString(), "0000000000000000000000000000000000000000000000000000000000000000000000000f29b0d748a9ccf8c5af3cde10db3e36ec9a5f720643a2bcb4add76b3daf41d880ab04fd0000000000000000000000000000000000000000000000000100015101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"); + byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 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, 1, 0, 1, 0, 0 }; + + data.Length.Should().Be(requiredData.Length); + for (int i = 0; i < data.Length; i++) + { + data[i].Should().Be(requiredData[i]); + } } [TestMethod] @@ -104,10 +111,14 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - using (MemoryStream ms = new MemoryStream("0000000000000000000000000000000000000000000000000000000000000000000000000f29b0d748a9ccf8c5af3cde10db3e36ec9a5f720643a2bcb4add76b3daf41d880ab04fd0000000000000000000000000000000000000000000000000100015101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000".HexToBytes(), false)) - using (BinaryReader reader = new BinaryReader(ms)) + byte[] data = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 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, 1, 0, 0 }; + int index = 0; + using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) { - uut.Deserialize(reader); + using (BinaryReader reader = new BinaryReader(ms)) + { + uut.Deserialize(reader); + } } assertStandardBlockTestVals(val256, merkRoot, val160, timestampVal, indexVal, scriptVal, transactionsVal); diff --git a/neo/Network/P2P/Payloads/Block.cs b/neo/Network/P2P/Payloads/Block.cs index b63035e0ed..58faf54cb1 100644 --- a/neo/Network/P2P/Payloads/Block.cs +++ b/neo/Network/P2P/Payloads/Block.cs @@ -12,7 +12,7 @@ namespace Neo.Network.P2P.Payloads public class Block : BlockBase, IInventory, IEquatable { public const int MaxContentsPerBlock = ushort.MaxValue; - public const int MaxTransactionsPerBlock = MaxContentsPerBlock; + public const int MaxTransactionsPerBlock = MaxContentsPerBlock - 1; public ConsensusData ConsensusData; public Transaction[] Transactions; @@ -41,13 +41,13 @@ public Header Header InventoryType IInventory.InventoryType => InventoryType.Block; public override int Size => base.Size - + IO.Helper.GetVarSize(Transactions.Length) //Count + + IO.Helper.GetVarSize(Transactions.Length + 1) //Count + ConsensusData.Size //ConsensusData + Transactions.Sum(p => p.Size); //Transactions public static UInt256 CalculateMerkleRoot(UInt256 consensusDataHash, params UInt256[] transactionHashes) { - List hashes = new List(transactionHashes.Length) { consensusDataHash }; + List hashes = new List(transactionHashes.Length + 1) { consensusDataHash }; hashes.AddRange(transactionHashes); return MerkleTree.ComputeRoot(hashes); } @@ -58,7 +58,7 @@ public override void Deserialize(BinaryReader reader) int count = (int)reader.ReadVarInt(MaxContentsPerBlock); if (count == 0) throw new FormatException(); ConsensusData = reader.ReadSerializable(); - Transactions = new Transaction[count]; + Transactions = new Transaction[count - 1]; for (int i = 0; i < Transactions.Length; i++) Transactions[i] = reader.ReadSerializable(); if (Transactions.Distinct().Count() != Transactions.Length) @@ -76,8 +76,7 @@ public bool Equals(Block other) public override bool Equals(object obj) { - if (!(obj is Block b)) return false; - return Equals(b); + return Equals(obj as Block); } public override int GetHashCode() @@ -93,7 +92,7 @@ public void RebuildMerkleRoot() public override void Serialize(BinaryWriter writer) { base.Serialize(writer); - writer.WriteVarInt(Transactions.Length); + writer.WriteVarInt(Transactions.Length + 1); writer.Write(ConsensusData); foreach (Transaction tx in Transactions) writer.Write(tx); From 7a0e82f0650a50bcf9543c42e7d16e179de79ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Mon, 22 Jul 2019 14:21:38 -0300 Subject: [PATCH 049/305] Seconds to milliseconds (#918) * Seconds to milliseconds * Sending unsaved files * fixing formula of consensus bonification * Refactoring milliseconds * Refactoring milliseconds from capeslock * Refactoring milliseconds from capeslock II * Adjusting UT * Adjusting UT II * Super fast protocol to 2s * Fixing timestamps to long and tests * Minor adjusts * Change view deserialization fix * Timestamp to ulong * Update neo/Helper.cs Co-Authored-By: Shargon * Update JNumber.cs * Optimize and remove TODO * Update ApplicationEngine.cs * Fixing ExtendTimerByFactor This was already from milliseconds, thus, just removing the multiplier --- neo.UnitTests/TestUtils.cs | 9 +++--- neo.UnitTests/UT_Block.cs | 31 ++++++++++--------- neo.UnitTests/UT_Consensus.cs | 18 +++++------ neo.UnitTests/UT_Header.cs | 16 +++++----- neo.UnitTests/protocol.json | 2 +- neo/Consensus/ChangeView.cs | 6 ++-- neo/Consensus/ConsensusContext.cs | 8 ++--- neo/Consensus/ConsensusService.cs | 18 +++++------ neo/Consensus/PrepareRequest.cs | 6 ++-- ...ecoveryMessage.ChangeViewPayloadCompact.cs | 6 ++-- neo/Consensus/RecoveryRequest.cs | 6 ++-- neo/Helper.cs | 14 +++------ neo/IO/Json/JNumber.cs | 7 ----- neo/Ledger/Blockchain.cs | 11 ++++--- neo/Ledger/MemoryPool.cs | 16 +++++----- neo/Network/P2P/Payloads/BlockBase.cs | 16 +++++++--- neo/ProtocolSettings.cs | 4 +-- neo/SmartContract/ApplicationEngine.cs | 18 +++++++---- 18 files changed, 111 insertions(+), 101 deletions(-) diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index 9bf06acbf7..fd06f0ba1b 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -36,12 +36,12 @@ public static Transaction GetTransaction() }; } - public static void SetupHeaderWithValues(Header header, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal) + public static void SetupHeaderWithValues(Header header, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal) { setupBlockBaseWithValues(header, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal); } - public static void SetupBlockWithValues(Block block, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, int numberOfTransactions) + public static void SetupBlockWithValues(Block block, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, int numberOfTransactions) { setupBlockBaseWithValues(block, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal); @@ -58,13 +58,12 @@ public static void SetupBlockWithValues(Block block, UInt256 val256, out UInt256 block.Transactions = transactionsVal; } - private static void setupBlockBaseWithValues(BlockBase bb, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal) + private static void setupBlockBaseWithValues(BlockBase bb, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal) { bb.PrevHash = val256; merkRootVal = UInt256.Parse("0xd841af3d6bd7adb4bca24306725f9aec363edb10de3cafc5f8cca948d7b0290f"); - bb.MerkleRoot = merkRootVal; - timestampVal = new DateTime(1968, 06, 01, 0, 0, 0, DateTimeKind.Utc).ToTimestamp(); + timestampVal = new DateTime(1980, 06, 01, 0, 0, 1, 001, DateTimeKind.Utc).ToTimestampMS(); // GMT: Sunday, June 1, 1980 12:00:01.001 AM bb.Timestamp = timestampVal; indexVal = 0; bb.Index = indexVal; diff --git a/neo.UnitTests/UT_Block.cs b/neo.UnitTests/UT_Block.cs index 886f5099f0..c91776f0c0 100644 --- a/neo.UnitTests/UT_Block.cs +++ b/neo.UnitTests/UT_Block.cs @@ -43,9 +43,9 @@ public void Size_Get() { UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 0); - // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 4 + // blockbase 4 + 64 + 32 + 4 + 4 + 20 + 4 // block 9 + 1 - uut.Size.Should().Be(110); + uut.Size.Should().Be(114); } [TestMethod] @@ -59,7 +59,7 @@ public void Size_Get_1_Transaction() TestUtils.GetTransaction() }; - uut.Size.Should().Be(161); + uut.Size.Should().Be(165); } [TestMethod] @@ -75,7 +75,7 @@ public void Size_Get_3_Transaction() TestUtils.GetTransaction() }; - uut.Size.Should().Be(263); + uut.Size.Should().Be(267); } [TestMethod] @@ -94,8 +94,7 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 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, 1, 0, 1, 0, 0 }; - + byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 233, 19, 255, 133, 76, 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, 1, 0, 1, 81, 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, 1, 0, 1, 0, 0 }; data.Length.Should().Be(requiredData.Length); for (int i = 0; i < data.Length; i++) { @@ -111,7 +110,8 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - byte[] data = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 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, 1, 0, 0 }; + byte[] data = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 233, 19, 255, 133, 76, 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, 1, 0, 1, 81, 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, 1, 0, 1, 0, 0 }; + int index = 0; using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) { @@ -124,7 +124,7 @@ public void Deserialize() assertStandardBlockTestVals(val256, merkRoot, val160, timestampVal, indexVal, scriptVal, transactionsVal); } - private void assertStandardBlockTestVals(UInt256 val256, UInt256 merkRoot, UInt160 val160, uint timestampVal, uint indexVal, Witness scriptVal, Transaction[] transactionsVal, bool testTransactions = true) + private void assertStandardBlockTestVals(UInt256 val256, UInt256 merkRoot, UInt160 val160, ulong timestampVal, uint indexVal, Witness scriptVal, Transaction[] transactionsVal, bool testTransactions = true) { uut.PrevHash.Should().Be(val256); uut.MerkleRoot.Should().Be(merkRoot); @@ -155,7 +155,8 @@ public void Equals_DiffObj() UInt256 prevHash = new UInt256(TestUtils.GetByteArray(32, 0x42)); UInt256 merkRoot; UInt160 val160; - uint timestampVal, indexVal; + 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); @@ -178,7 +179,8 @@ public void Equals_SameHash() UInt256 prevHash = new UInt256(TestUtils.GetByteArray(32, 0x42)); UInt256 merkRoot; UInt160 val160; - uint timestampVal, indexVal; + 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); @@ -193,7 +195,8 @@ public void RebuildMerkleRoot_Updates() UInt256 val256 = UInt256.Zero; UInt256 merkRoot; UInt160 val160; - uint timestampVal, indexVal; + 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); @@ -214,12 +217,12 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0x1d8642796276c8ce3c5c03b8984a1b593d99b49a63d830bb06f800b8c953be77"); - jObj["size"].AsNumber().Should().Be(161); + jObj["hash"].AsString().Should().Be("0x4e1d8392e7c44830e7e45c18e5e0e3ef3c36af883868846d3691a436a62494b2"); + jObj["size"].AsNumber().Should().Be(165); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); jObj["merkleroot"].AsString().Should().Be("0xd841af3d6bd7adb4bca24306725f9aec363edb10de3cafc5f8cca948d7b0290f"); - jObj["time"].AsNumber().Should().Be(4244941696); + jObj["time"].AsNumber().Should().Be(328665601001); jObj["index"].AsNumber().Should().Be(0); jObj["nextconsensus"].AsString().Should().Be("AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM"); diff --git a/neo.UnitTests/UT_Consensus.cs b/neo.UnitTests/UT_Consensus.cs index d410bb5142..c5bf1828f8 100644 --- a/neo.UnitTests/UT_Consensus.cs +++ b/neo.UnitTests/UT_Consensus.cs @@ -47,10 +47,10 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() int timeIndex = 0; var timeValues = new[] { //new DateTime(1968, 06, 01, 0, 0, 15, DateTimeKind.Utc), // For tests here - new DateTime(1968, 06, 01, 0, 0, 1, DateTimeKind.Utc), // For receiving block - new DateTime(1968, 06, 01, 0, 0, (int) Blockchain.SecondsPerBlock, DateTimeKind.Utc), // For Initialize - new DateTime(1968, 06, 01, 0, 0, 15, DateTimeKind.Utc), // unused - new DateTime(1968, 06, 01, 0, 0, 15, DateTimeKind.Utc) // unused + new DateTime(1980, 06, 01, 0, 0, 1, 001, DateTimeKind.Utc), // For receiving block + new DateTime(1980, 06, 01, 0, 0, (int) Blockchain.MillisecondsPerBlock / 1000, 100, DateTimeKind.Utc), // For Initialize + new DateTime(1980, 06, 01, 0, 0, 15, 001, DateTimeKind.Utc), // unused + new DateTime(1980, 06, 01, 0, 0, 15, 001, DateTimeKind.Utc) // unused }; //TimeProvider.Current.UtcNow.ToTimestamp().Should().Be(4244941711); //1968-06-01 00:00:15 @@ -74,12 +74,12 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() // Creating proposed block Header header = new Header(); - TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal); - header.Size.Should().Be(101); + 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} timstamp {timestampVal}"); + Console.WriteLine($"header {header} hash {header.Hash} timestamp {timestampVal}"); - timestampVal.Should().Be(4244941696); //1968-06-01 00:00:00 + timestampVal.Should().Be(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 @@ -197,7 +197,7 @@ public void TestSerializeAndDeserializeConsensusContext() consensusContext.CommitPayloads[6] = MakeSignedPayload(consensusContext, new Commit { Signature = sha256.ComputeHash(testTx2.Hash.ToArray()) }, 3, new[] { (byte)'6', (byte)'7' }); } - consensusContext.Block.Timestamp = TimeProvider.Current.UtcNow.ToTimestamp(); + consensusContext.Block.Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS(); consensusContext.ChangeViewPayloads = new ConsensusPayload[consensusContext.Validators.Length]; consensusContext.ChangeViewPayloads[0] = MakeSignedPayload(consensusContext, new ChangeView { ViewNumber = 1, Timestamp = 6 }, 0, new[] { (byte)'A' }); diff --git a/neo.UnitTests/UT_Header.cs b/neo.UnitTests/UT_Header.cs index 82744a7f8b..f07be59051 100644 --- a/neo.UnitTests/UT_Header.cs +++ b/neo.UnitTests/UT_Header.cs @@ -22,22 +22,23 @@ public void Size_Get() { UInt256 val256 = UInt256.Zero; TestUtils.SetupHeaderWithValues(uut, val256, out _, out _, out _, out _, out _); - // blockbase 4 + 32 + 32 + 4 + 4 + 20 + 4 + // blockbase 4 + 64 + 32 + 4 + 4 + 20 + 4 // header 1 - uut.Size.Should().Be(101); + uut.Size.Should().Be(105); } [TestMethod] public void Deserialize() { UInt256 val256 = UInt256.Zero; - TestUtils.SetupHeaderWithValues(new Header(), val256, out UInt256 merkRoot, out UInt160 val160, out uint timestampVal, out uint indexVal, out Witness scriptVal); + TestUtils.SetupHeaderWithValues(new Header(), val256, out UInt256 merkRoot, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal); uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - byte[] data = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 0 }; + byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 233, 19, 255, 133, 76, 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, 1, 0, 1, 81, 0 }; + int index = 0; - using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) + using (MemoryStream ms = new MemoryStream(requiredData, index, requiredData.Length - index, false)) { using (BinaryReader reader = new BinaryReader(ms)) { @@ -48,7 +49,7 @@ public void Deserialize() assertStandardHeaderTestVals(val256, merkRoot, val160, timestampVal, indexVal, scriptVal); } - private void assertStandardHeaderTestVals(UInt256 val256, UInt256 merkRoot, UInt160 val160, uint 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); @@ -106,9 +107,10 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 128, 171, 4, 253, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 81, 0 }; + byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 233, 19, 255, 133, 76, 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, 1, 0, 1, 81, 0 }; data.Length.Should().Be(requiredData.Length); + for (int i = 0; i < data.Length; i++) { data[i].Should().Be(requiredData[i]); diff --git a/neo.UnitTests/protocol.json b/neo.UnitTests/protocol.json index 476a25d4aa..06aec5ccce 100644 --- a/neo.UnitTests/protocol.json +++ b/neo.UnitTests/protocol.json @@ -1,5 +1,5 @@ { "ProtocolConfiguration": { - "SecondsPerBlock": 2 + "MillisecondsPerBlock": 2000 } } diff --git a/neo/Consensus/ChangeView.cs b/neo/Consensus/ChangeView.cs index 7d5b3e96eb..4c18b3fb3f 100644 --- a/neo/Consensus/ChangeView.cs +++ b/neo/Consensus/ChangeView.cs @@ -14,7 +14,7 @@ public class ChangeView : ConsensusMessage /// they only respond once to a specific ChangeView request (it thus prevents replay of the ChangeView /// message from repeatedly broadcasting RecoveryMessages). ///
- public uint Timestamp; + public ulong Timestamp; /// /// Reason @@ -22,7 +22,7 @@ public class ChangeView : ConsensusMessage public ChangeViewReason Reason; public override int Size => base.Size + - sizeof(uint) + // Timestamp + sizeof(ulong) + // Timestamp sizeof(ChangeViewReason); // Reason public ChangeView() : base(ConsensusMessageType.ChangeView) { } @@ -30,7 +30,7 @@ public class ChangeView : ConsensusMessage public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); - Timestamp = reader.ReadUInt32(); + Timestamp = reader.ReadUInt64(); Reason = (ChangeViewReason)reader.ReadByte(); } diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 0d6855a623..270536fc89 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -95,7 +95,7 @@ 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.ReadUInt32(); + Block.Timestamp = reader.ReadUInt64(); Block.NextConsensus = reader.ReadSerializable(); if (Block.NextConsensus.Equals(UInt160.Zero)) Block.NextConsensus = null; @@ -164,7 +164,7 @@ public ConsensusPayload MakeChangeView(ChangeViewReason reason) return ChangeViewPayloads[MyIndex] = MakeSignedPayload(new ChangeView { Reason = reason, - Timestamp = TimeProvider.Current.UtcNow.ToTimestamp() + Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS() }); } @@ -216,7 +216,7 @@ public ConsensusPayload MakePrepareRequest() List transactions = memoryPoolTransactions.ToList(); TransactionHashes = transactions.Select(p => p.Hash).ToArray(); Transactions = transactions.ToDictionary(p => p.Hash); - Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestamp(), PrevHeader.Timestamp + 1); + Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1); Block.ConsensusData.Nonce = BitConverter.ToUInt64(buffer, 0); return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareRequest { @@ -230,7 +230,7 @@ public ConsensusPayload MakeRecoveryRequest() { return MakeSignedPayload(new RecoveryRequest { - Timestamp = TimeProvider.Current.UtcNow.ToTimestamp() + Timestamp = TimeProvider.Current.UtcNow.ToTimestampMS() }); } diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index b8e594d642..2e850807e4 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -139,7 +139,7 @@ private void CheckPreparations() 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.FromSeconds(Blockchain.SecondsPerBlock)); + ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock)); CheckCommits(); } } @@ -155,7 +155,7 @@ private void InitializeConsensus(byte viewNumber) { if (isRecovering) { - ChangeTimer(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (viewNumber + 1))); + ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << (viewNumber + 1))); } else { @@ -168,7 +168,7 @@ private void InitializeConsensus(byte viewNumber) } else { - ChangeTimer(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (viewNumber + 1))); + ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << (viewNumber + 1))); } } @@ -229,10 +229,10 @@ private void OnCommitReceived(ConsensusPayload payload, Commit commit) existingCommitPayload = payload; } - // this function increases existing timer (never decreases) with a value proportional to `maxDelayInBlockTimes`*`Blockchain.SecondsPerBlock` + // 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.SecondsPerBlock * 1000.0 / context.M); + TimeSpan nextDelay = expected_delay - (TimeProvider.Current.UtcNow - clock_started) + TimeSpan.FromMilliseconds(maxDelayInBlockTimes * Blockchain.MillisecondsPerBlock / context.M); if (!context.WatchOnly && !context.ViewChanging && !context.CommitSent && (nextDelay > TimeSpan.Zero)) ChangeTimer(nextDelay); } @@ -390,7 +390,7 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m 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.Timestamp <= context.PrevHeader.Timestamp || message.Timestamp > TimeProvider.Current.UtcNow.AddMinutes(10).ToTimestamp()) + if (message.Timestamp <= context.PrevHeader.Timestamp || message.Timestamp > TimeProvider.Current.UtcNow.AddMinutes(10).ToTimestampMS()) { Log($"Timestamp incorrect: {message.Timestamp}", LogLevel.Warning); return; @@ -543,7 +543,7 @@ private void OnTimer(Timer timer) // 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.FromSeconds(Blockchain.SecondsPerBlock << 1)); + ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << 1)); } else { @@ -590,7 +590,7 @@ private void RequestChangeView(ChangeViewReason reason) // The latter may happen by nodes in higher views with, at least, `M` proofs byte expectedView = context.ViewNumber; expectedView++; - ChangeTimer(TimeSpan.FromSeconds(Blockchain.SecondsPerBlock << (expectedView + 1))); + ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << (expectedView + 1))); if ((context.CountCommitted + context.CountFailed) > context.F) { Log($"Skip requesting change view to nv={expectedView} because nc={context.CountCommitted} nf={context.CountFailed}"); @@ -622,7 +622,7 @@ private void SendPrepareRequest() foreach (InvPayload payload in InvPayload.CreateGroup(InventoryType.TX, context.TransactionHashes)) localNode.Tell(Message.Create(MessageCommand.Inv, payload)); } - ChangeTimer(TimeSpan.FromSeconds((Blockchain.SecondsPerBlock << (context.ViewNumber + 1)) - (context.ViewNumber == 0 ? Blockchain.SecondsPerBlock : 0))); + ChangeTimer(TimeSpan.FromMilliseconds((Blockchain.MillisecondsPerBlock << (context.ViewNumber + 1)) - (context.ViewNumber == 0 ? Blockchain.MillisecondsPerBlock : 0))); } } diff --git a/neo/Consensus/PrepareRequest.cs b/neo/Consensus/PrepareRequest.cs index 299b0f6a72..85823fe957 100644 --- a/neo/Consensus/PrepareRequest.cs +++ b/neo/Consensus/PrepareRequest.cs @@ -8,12 +8,12 @@ namespace Neo.Consensus { public class PrepareRequest : ConsensusMessage { - public uint Timestamp; + public ulong Timestamp; public ulong Nonce; public UInt256[] TransactionHashes; public override int Size => base.Size - + sizeof(uint) //Timestamp + + sizeof(ulong) //Timestamp + sizeof(ulong) //Nonce + TransactionHashes.GetVarSize(); //TransactionHashes @@ -25,7 +25,7 @@ public PrepareRequest() public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); - Timestamp = reader.ReadUInt32(); + Timestamp = reader.ReadUInt64(); Nonce = reader.ReadUInt64(); TransactionHashes = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); if (TransactionHashes.Distinct().Count() != TransactionHashes.Length) diff --git a/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs b/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs index 373e7e75a3..6d4d6a7ae2 100644 --- a/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs +++ b/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs @@ -10,20 +10,20 @@ public class ChangeViewPayloadCompact : ISerializable { public ushort ValidatorIndex; public byte OriginalViewNumber; - public uint Timestamp; + public ulong Timestamp; public byte[] InvocationScript; int ISerializable.Size => sizeof(ushort) + //ValidatorIndex sizeof(byte) + //OriginalViewNumber - sizeof(uint) + //Timestamp + sizeof(ulong) + //Timestamp InvocationScript.GetVarSize(); //InvocationScript void ISerializable.Deserialize(BinaryReader reader) { ValidatorIndex = reader.ReadUInt16(); OriginalViewNumber = reader.ReadByte(); - Timestamp = reader.ReadUInt32(); + Timestamp = reader.ReadUInt64(); InvocationScript = reader.ReadVarBytes(1024); } diff --git a/neo/Consensus/RecoveryRequest.cs b/neo/Consensus/RecoveryRequest.cs index 971b49a324..c02ceb176c 100644 --- a/neo/Consensus/RecoveryRequest.cs +++ b/neo/Consensus/RecoveryRequest.cs @@ -9,17 +9,17 @@ public class RecoveryRequest : ConsensusMessage /// 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 uint Timestamp; + public ulong Timestamp; public override int Size => base.Size - + sizeof(uint); //Timestamp + + sizeof(ulong); //Timestamp public RecoveryRequest() : base(ConsensusMessageType.RecoveryRequest) { } public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); - Timestamp = reader.ReadUInt32(); + Timestamp = reader.ReadUInt64(); } public override void Serialize(BinaryWriter writer) diff --git a/neo/Helper.cs b/neo/Helper.cs index 8c562740bd..d8409b64bd 100644 --- a/neo/Helper.cs +++ b/neo/Helper.cs @@ -140,15 +140,6 @@ internal static bool TestBit(this BigInteger i, int index) return (i & (BigInteger.One << index)) > BigInteger.Zero; } - public static DateTime ToDateTime(this uint timestamp) - { - return unixEpoch.AddSeconds(timestamp).ToLocalTime(); - } - - public static DateTime ToDateTime(this ulong timestamp) - { - return unixEpoch.AddSeconds(timestamp).ToLocalTime(); - } public static string ToHexString(this IEnumerable value) { @@ -181,6 +172,11 @@ public static uint ToTimestamp(this DateTime time) return (uint)(time.ToUniversalTime() - unixEpoch).TotalSeconds; } + public static ulong ToTimestampMS(this DateTime time) + { + return (ulong)(time.ToUniversalTime() - unixEpoch).TotalMilliseconds; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] unsafe internal static ushort ToUInt16(this byte[] value, int startIndex) { diff --git a/neo/IO/Json/JNumber.cs b/neo/IO/Json/JNumber.cs index f9fa5e05e5..2a23dc3157 100644 --- a/neo/IO/Json/JNumber.cs +++ b/neo/IO/Json/JNumber.cs @@ -101,13 +101,6 @@ public override string ToString() return AsString(); } - public DateTime ToTimestamp() - { - if (Value < 0 || Value > ulong.MaxValue) - throw new InvalidCastException(); - return ((ulong)Value).ToDateTime(); - } - public override T TryGetEnum(T defaultValue = default, bool ignoreCase = false) { Type enumType = typeof(T); diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 5d9ad00ee9..854583423d 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -27,17 +27,17 @@ public class ImportCompleted { } public class FillMemoryPool { public IEnumerable Transactions; } public class FillCompleted { } - public static readonly uint SecondsPerBlock = ProtocolSettings.Default.SecondsPerBlock; + public static readonly uint MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock; public const uint DecrementInterval = 2000000; public const int MaxValidators = 1024; public static readonly uint[] GenerationAmount = { 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - public static readonly TimeSpan TimePerBlock = TimeSpan.FromSeconds(SecondsPerBlock); + public static readonly TimeSpan TimePerBlock = TimeSpan.FromMilliseconds(MillisecondsPerBlock); public static readonly ECPoint[] StandbyValidators = ProtocolSettings.Default.StandbyValidators.OfType().Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); public static readonly Block GenesisBlock = new Block { PrevHash = UInt256.Zero, - Timestamp = (new DateTime(2016, 7, 15, 15, 8, 21, DateTimeKind.Utc)).ToTimestamp(), + Timestamp = (new DateTime(2016, 7, 15, 15, 8, 21, DateTimeKind.Utc)).ToTimestampMS(), Index = 0, NextConsensus = GetConsensusAddress(StandbyValidators), Witness = new Witness @@ -283,7 +283,10 @@ private RelayResultReason OnNewBlock(Block block) block_cache_unverified.Remove(blockToPersist.Index); Persist(blockToPersist); - if (blocksPersisted++ < blocksToPersistList.Count - (2 + Math.Max(0, (15 - SecondsPerBlock)))) continue; + // 15000 is the default among of seconds per block, while MilliSecondsPerBlock is the current + uint extraBlocks = (15000 - MillisecondsPerBlock) / 1000; + + 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 diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 96e17dc34f..912ea69c73 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -21,10 +21,10 @@ public class MemoryPool : IReadOnlyCollection private const int BlocksTillRebroadcastHighPriorityPoolTx = 10; private int RebroadcastMultiplierThreshold => Capacity / 10; - private static readonly double MaxSecondsToReverifyTx = (double)Blockchain.SecondsPerBlock / 3; + private static readonly double MaxMillisecondsToReverifyTx = (double)Blockchain.MillisecondsPerBlock / 3; // These two are not expected to be hit, they are just safegaurds. - private static readonly double MaxSecondsToReverifyTxPerIdle = (double)Blockchain.SecondsPerBlock / 15; + private static readonly double MaxMillisecondsToReverifyTxPerIdle = (double)Blockchain.MillisecondsPerBlock / 15; private readonly NeoSystem _system; @@ -378,7 +378,7 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) return; ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, - _maxTxPerBlock, MaxSecondsToReverifyTx, snapshot); + _maxTxPerBlock, MaxMillisecondsToReverifyTx, snapshot); } internal void InvalidateAllTransactions() @@ -395,9 +395,9 @@ internal void InvalidateAllTransactions() } private int ReverifyTransactions(SortedSet verifiedSortedTxPool, - SortedSet unverifiedSortedTxPool, int count, double secondsTimeout, Snapshot snapshot) + SortedSet unverifiedSortedTxPool, int count, double millisecondsTimeout, Snapshot snapshot) { - DateTime reverifyCutOffTimeStamp = DateTime.UtcNow.AddSeconds(secondsTimeout); + DateTime reverifyCutOffTimeStamp = DateTime.UtcNow.AddMilliseconds(millisecondsTimeout); List reverifiedItems = new List(count); List invalidItems = new List(); @@ -421,8 +421,8 @@ internal void InvalidateAllTransactions() if (Count > RebroadcastMultiplierThreshold) blocksTillRebroadcast = blocksTillRebroadcast * Count / RebroadcastMultiplierThreshold; - var rebroadcastCutOffTime = DateTime.UtcNow.AddSeconds( - -Blockchain.SecondsPerBlock * blocksTillRebroadcast); + var rebroadcastCutOffTime = DateTime.UtcNow.AddMilliseconds( + -Blockchain.MillisecondsPerBlock * blocksTillRebroadcast); foreach (PoolItem item in reverifiedItems) { if (_unsortedTransactions.TryAdd(item.Tx.Hash, item)) @@ -477,7 +477,7 @@ internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, Snapsho { int verifyCount = _sortedTransactions.Count > _maxTxPerBlock ? 1 : maxToVerify; ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, - verifyCount, MaxSecondsToReverifyTxPerIdle, snapshot); + verifyCount, MaxMillisecondsToReverifyTxPerIdle, snapshot); } return _unverifiedTransactions.Count > 0; diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 9f56ca0d97..22ef72d021 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -14,7 +14,7 @@ public abstract class BlockBase : IVerifiable public uint Version; public UInt256 PrevHash; public UInt256 MerkleRoot; - public uint Timestamp; + public ulong Timestamp; public uint Index; public UInt160 NextConsensus; public Witness Witness; @@ -32,8 +32,16 @@ public UInt256 Hash } } - public virtual int Size => sizeof(uint) + PrevHash.Size + MerkleRoot.Size + sizeof(uint) + sizeof(uint) + NextConsensus.Size + 1 + Witness.Size; - + public virtual int Size => + sizeof(uint) + //Version + PrevHash.Size + //PrevHash + MerkleRoot.Size + //MerkleRoot + sizeof(ulong) + //Timestamp + sizeof(uint) + //Index + NextConsensus.Size + //NextConsensus + 1 + // + Witness.Size; //Witness + Witness[] IVerifiable.Witnesses { get @@ -59,7 +67,7 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) Version = reader.ReadUInt32(); PrevHash = reader.ReadSerializable(); MerkleRoot = reader.ReadSerializable(); - Timestamp = reader.ReadUInt32(); + Timestamp = reader.ReadUInt64(); Index = reader.ReadUInt32(); NextConsensus = reader.ReadSerializable(); } diff --git a/neo/ProtocolSettings.cs b/neo/ProtocolSettings.cs index ba4c192e8b..b690cd7602 100644 --- a/neo/ProtocolSettings.cs +++ b/neo/ProtocolSettings.cs @@ -10,7 +10,7 @@ public class ProtocolSettings public byte AddressVersion { get; } public string[] StandbyValidators { get; } public string[] SeedList { get; } - public uint SecondsPerBlock { get; } + public uint MillisecondsPerBlock { get; } static ProtocolSettings _default; @@ -69,7 +69,7 @@ private ProtocolSettings(IConfigurationSection section) "seed4.neo.org:10333", "seed5.neo.org:10333" }; - this.SecondsPerBlock = section.GetValue("SecondsPerBlock", 15u); + this.MillisecondsPerBlock = section.GetValue("SecondsPerBlock", 15000u); } } } diff --git a/neo/SmartContract/ApplicationEngine.cs b/neo/SmartContract/ApplicationEngine.cs index ced43d379e..03a098530a 100644 --- a/neo/SmartContract/ApplicationEngine.cs +++ b/neo/SmartContract/ApplicationEngine.cs @@ -83,18 +83,18 @@ protected override bool PreExecuteInstruction() return true; return AddGas(OpCodePrices[CurrentContext.CurrentInstruction.OpCode]); } - - public static ApplicationEngine Run(byte[] script, Snapshot snapshot, - IVerifiable container = null, Block persistingBlock = null, bool testMode = false, long extraGAS = default) + + private static Block CreateDummyBlock(Snapshot snapshot) { - snapshot.PersistingBlock = persistingBlock ?? snapshot.PersistingBlock ?? new Block + var currentBlock = snapshot.Blocks[snapshot.CurrentBlockHash]; + return new Block { Version = 0, PrevHash = snapshot.CurrentBlockHash, MerkleRoot = new UInt256(), - Timestamp = snapshot.Blocks[snapshot.CurrentBlockHash].Timestamp + Blockchain.SecondsPerBlock, + Timestamp = currentBlock.Timestamp + Blockchain.MillisecondsPerBlock, Index = snapshot.Height + 1, - NextConsensus = snapshot.Blocks[snapshot.CurrentBlockHash].NextConsensus, + NextConsensus = currentBlock.NextConsensus, Witness = new Witness { InvocationScript = new byte[0], @@ -103,6 +103,12 @@ protected override bool PreExecuteInstruction() ConsensusData = new ConsensusData(), Transactions = new Transaction[0] }; + } + + public static ApplicationEngine Run(byte[] script, Snapshot snapshot, + IVerifiable container = null, Block persistingBlock = null, bool testMode = false, long extraGAS = default) + { + snapshot.PersistingBlock = persistingBlock ?? snapshot.PersistingBlock ?? CreateDummyBlock(snapshot); ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, container, snapshot, extraGAS, testMode); engine.LoadScript(script); engine.Execute(); From d3dee6b39c47639f9a6db05e1941cbf633c3aaeb Mon Sep 17 00:00:00 2001 From: Nikolai Perevozchikov Date: Tue, 23 Jul 2019 15:39:05 +0900 Subject: [PATCH 050/305] allow CheckWitness to work when using invokescript (#335) * allow CheckWitness to work when using invokescript * Fix * Update RpcServer.cs * Allow more than one signature * Reorder properties --- neo/Network/RPC/RpcServer.cs | 49 +++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/neo/Network/RPC/RpcServer.cs b/neo/Network/RPC/RpcServer.cs index 6bfdd15618..c47e6644e7 100644 --- a/neo/Network/RPC/RpcServer.cs +++ b/neo/Network/RPC/RpcServer.cs @@ -31,6 +31,43 @@ namespace Neo.Network.RPC { public sealed class RpcServer : IDisposable { + private class CheckWitnessHashes : IVerifiable + { + private readonly UInt160[] _scriptHashesForVerifying; + public Witness[] Witnesses { get; set; } + public int Size { get; } + + public CheckWitnessHashes(UInt160[] scriptHashesForVerifying) + { + _scriptHashesForVerifying = scriptHashesForVerifying; + } + + public void Serialize(BinaryWriter writer) + { + throw new NotImplementedException(); + } + + public void Deserialize(BinaryReader reader) + { + throw new NotImplementedException(); + } + + public void DeserializeUnsigned(BinaryReader reader) + { + throw new NotImplementedException(); + } + + public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) + { + return _scriptHashesForVerifying; + } + + public void SerializeUnsigned(BinaryWriter writer) + { + throw new NotImplementedException(); + } + } + public Wallet Wallet { get; set; } public long MaxGasInvoke { get; } @@ -72,9 +109,9 @@ public void Dispose() } } - private JObject GetInvokeResult(byte[] script) + private JObject GetInvokeResult(byte[] script, IVerifiable checkWitnessHashes = null) { - ApplicationEngine engine = ApplicationEngine.Run(script, extraGAS: MaxGasInvoke); + ApplicationEngine engine = ApplicationEngine.Run(script, checkWitnessHashes, extraGAS: MaxGasInvoke); JObject json = new JObject(); json["script"] = script.ToHexString(); json["state"] = engine.State; @@ -202,7 +239,13 @@ private JObject Process(string method, JArray _params) case "invokescript": { byte[] script = _params[0].AsString().HexToBytes(); - return InvokeScript(script); + CheckWitnessHashes checkWitnessHashes = null; + if (_params.Count > 1) + { + UInt160[] scriptHashesForVerifying = _params.Skip(1).Select(u => UInt160.Parse(u.AsString())).ToArray(); + checkWitnessHashes = new CheckWitnessHashes(scriptHashesForVerifying); + } + return GetInvokeResult(script, checkWitnessHashes); } case "listplugins": { From e0f6455081217018c04f6eaf3ffec19caf05023b Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 23 Jul 2019 20:50:38 +0200 Subject: [PATCH 051/305] Allow to configure the memory pool capacity (#948) Making memory pool capacity configurable --- neo/Ledger/Blockchain.cs | 3 +-- neo/ProtocolSettings.cs | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 854583423d..ee8e114701 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -53,7 +53,6 @@ public class FillCompleted { } Transactions = new[] { DeployNativeContracts() } }; - private const int MemoryPoolMaxTransactions = 50_000; private const int MaxTxToReverifyPerIdle = 10; private static readonly object lockObj = new object(); private readonly NeoSystem system; @@ -89,7 +88,7 @@ static Blockchain() public Blockchain(NeoSystem system, Store store) { this.system = system; - this.MemPool = new MemoryPool(system, MemoryPoolMaxTransactions); + this.MemPool = new MemoryPool(system, ProtocolSettings.Default.MemoryPoolMaxTransactions); this.Store = store; lock (lockObj) { diff --git a/neo/ProtocolSettings.cs b/neo/ProtocolSettings.cs index b690cd7602..478d5b71b3 100644 --- a/neo/ProtocolSettings.cs +++ b/neo/ProtocolSettings.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using System; using System.Linq; using System.Threading; @@ -11,6 +12,7 @@ public class ProtocolSettings public string[] StandbyValidators { get; } public string[] SeedList { get; } public uint MillisecondsPerBlock { get; } + public int MemoryPoolMaxTransactions { get; } static ProtocolSettings _default; @@ -70,6 +72,7 @@ private ProtocolSettings(IConfigurationSection section) "seed5.neo.org:10333" }; this.MillisecondsPerBlock = section.GetValue("SecondsPerBlock", 15000u); + this.MemoryPoolMaxTransactions = Math.Max(1, section.GetValue("MemoryPoolMaxTransactions", 50_000)); } } } From 3151e48c7ea7d87df9944fce2aa82008869359bc Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 25 Jul 2019 20:16:33 +0800 Subject: [PATCH 052/305] Update dependency: Neo.VM v3.0.0-CI00030 (#956) --- neo/SmartContract/ApplicationEngine.OpCodePrices.cs | 2 -- neo/neo.csproj | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index 05105e2261..9da0f6c75d 100644 --- a/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -162,8 +162,6 @@ partial class ApplicationEngine [OpCode.MIN] = 200, [OpCode.MAX] = 200, [OpCode.WITHIN] = 200, - [OpCode.SHA1] = 300000, - [OpCode.SHA256] = 1000000, [OpCode.ARRAYSIZE] = 150, [OpCode.PACK] = 7000, [OpCode.UNPACK] = 7000, diff --git a/neo/neo.csproj b/neo/neo.csproj index e811c280dd..df29d088f2 100644 --- a/neo/neo.csproj +++ b/neo/neo.csproj @@ -29,7 +29,7 @@ - + From 30edffa6c82b9f8fd85fb01f773bbc220f2dde4f Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Fri, 26 Jul 2019 18:46:28 +0800 Subject: [PATCH 053/305] Unit tests for IO, Cryptography, Wallets and VM modules (#943) * testDemo * add dll lib * add dbsnapshot dispose * test get blocks in levelDBStore * add levelDBStore test funcs * fix levelDBStore funcs * add DbCache addInternal * differ db path * space * fix delete internal test * add test getInternal tryGetInternal move libleveldb.dll * add dbCache method test * add store test * add cache unit tests * add cache unit tests * up readonly max_capacity * fix leveldbexception * fix comment on UT_Cache * format * fix multithread test problem * up cache * update travis config * update travis.yml * test DbMetaDataCache * fix db directory * format and update travis for maxos * fix mac env travis * 2019/7/12 10:34 * 2019/7/12 11:01 * remove commented line * test BigDecimal * fix format and csproj * rm coverage.opencover.xml * update method name * add UT_P_Helper * modify UT_P_Helper * modify UT_P_helper * Clean ut * test Base58 & BloomFilter * Update UT_Cache.cs * Correct Typo * test JsonArray * update namespace * update namespace * update format * update format * organise folder structure * add UT_JString * test JBoolean JNumber & JObject * 2019/7/16 10:30 add some test case for UInt32Wrapper and SerializableWrapper * fix timestamp * test ECDsa and Crypto * test OrderedDictionary & complete IO.Json tests * 2019/7/16 17:33 add some test case of SQLiteWallet * test FIFOSet * add CloneCache and DataCache unit tests * fix namespace * add UT_Cryptography_Helper * format UT_CloneCache and UT_DataCache * add UT_DataCache.GetAndChange unit test * update namespace * remove comment code * delete Persistence part * 2019/7/19 11:07 add some test case for Helper in VM * Fix Base58 Test * 2019/7/19 11:33 change some format * update IOHelper exception assert * 2019/7/19 14:22 change format * format IOHelper * review IO.Wrapper * review Wallets.SQLite UT * Test ECFieldElement ECPoint * refactor package * format ECDsa * update namespace * Code fix * review cache * modify UT_JString * fomat * using Actin replace with try-catch * add UT_CloneMetaCache and UT_MetaDataCache * update namespace * format UT_DataCache.cs * Code Fix * format * update csproj * Code fix for UT_ECFieldElement and UT_ECPoint * Code fix * format * update travis * delete deleteFiles * fix path and comment * update travis * delete test ToTimeStamp * format UT_*Cache * update format * fomat * use hex extensions in Cryptography_Helper * remove reflection * optimization of UT_DataCache * update namespace * modify TestSha256 --- neo.UnitTests/{ => Consensus}/UT_Consensus.cs | 16 +- .../UT_ConsensusServiceMailbox.cs | 2 +- neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs | 55 ++ .../Cryptography/ECC/UT_ECFieldElement.cs | 69 +++ neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs | 310 ++++++++++ neo.UnitTests/Cryptography/UT_Base58.cs | 31 + neo.UnitTests/Cryptography/UT_BloomFilter.cs | 69 +++ neo.UnitTests/Cryptography/UT_Crypto.cs | 64 ++ .../Cryptography/UT_Cryptography_Helper.cs | 225 +++++++ neo.UnitTests/{ => Cryptography}/UT_Scrypt.cs | 4 +- .../Extensions/NativeContractExtensions.cs | 4 +- .../Nep5NativeContractExtensions.cs | 8 +- neo.UnitTests/IO/Caching/UT_Cache.cs | 214 +++++++ neo.UnitTests/IO/Caching/UT_CloneCache.cs | 126 ++++ neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs | 48 ++ neo.UnitTests/IO/Caching/UT_DataCache.cs | 347 +++++++++++ neo.UnitTests/IO/Caching/UT_FifoSet.cs | 120 ++++ neo.UnitTests/IO/Caching/UT_MetaDataCache.cs | 76 +++ .../IO/Caching/UT_OrderedDictionary.cs | 87 +++ neo.UnitTests/IO/Json/UT_JArray.cs | 249 ++++++++ neo.UnitTests/IO/Json/UT_JBoolean.cs | 77 +++ neo.UnitTests/IO/Json/UT_JNumber.cs | 67 +++ neo.UnitTests/IO/Json/UT_JObject.cs | 119 ++++ neo.UnitTests/IO/Json/UT_JString.cs | 73 +++ neo.UnitTests/IO/UT_IOHelper.cs | 549 ++++++++++++++++++ .../IO/Wrappers/UT_SerializableWrapper.cs | 50 ++ neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs | 97 ++++ neo.UnitTests/{ => Ledger}/UT_MemoryPool.cs | 2 +- neo.UnitTests/{ => Ledger}/UT_PoolItem.cs | 2 +- neo.UnitTests/{ => Ledger}/UT_StorageItem.cs | 2 +- neo.UnitTests/{ => Ledger}/UT_StorageKey.cs | 2 +- .../{ => Network/P2P/Payloads}/UT_Block.cs | 2 +- .../{ => Network/P2P/Payloads}/UT_Header.cs | 2 +- .../P2P/Payloads}/UT_Transaction.cs | 2 +- .../{ => Network/P2P/Payloads}/UT_Witness.cs | 2 +- .../P2P/UT_Message.cs} | 4 +- .../{ => Network/P2P}/UT_ProtocolHandler.cs | 2 +- .../P2P}/UT_ProtocolHandlerMailbox.cs | 15 +- .../{ => Network/P2P}/UT_RemoteNode.cs | 2 +- .../{ => Network/P2P}/UT_RemoteNodeMailbox.cs | 6 +- .../P2P}/UT_TaskManagerMailbox.cs | 12 +- .../Iterators}/UT_ConcatenatedIterator.cs | 2 +- .../Manifest}/UT_ContractManifest.cs | 2 +- .../Native/Tokens}/UT_GasToken.cs | 2 +- .../Native/Tokens}/UT_NeoToken.cs | 2 +- .../Native/UT_PolicyContract.cs} | 10 +- .../{ => SmartContract}/UT_InteropPrices.cs | 2 +- .../{ => SmartContract}/UT_InteropService.cs | 5 +- .../{ => SmartContract}/UT_JsonSerializer.cs | 2 +- .../{ => SmartContract}/UT_OpCodePrices.cs | 2 +- .../{ => SmartContract}/UT_Syscalls.cs | 2 +- neo.UnitTests/UT_BigDecimal.cs | 191 ++++++ neo.UnitTests/UT_FifoSet.cs | 54 -- neo.UnitTests/UT_ProtocolSettings.cs | 2 +- neo.UnitTests/VM/UT_Helper.cs | 82 +++ .../{ => Wallets/NEP6}/UT_NEP6Wallet.cs | 2 +- .../{ => Wallets/NEP6}/UT_ScryptParameters.cs | 2 +- neo.UnitTests/Wallets/SQLite/UT_Account.cs | 37 ++ neo.UnitTests/Wallets/SQLite/UT_Address.cs | 27 + neo.UnitTests/Wallets/SQLite/UT_Contract.cs | 65 +++ neo.UnitTests/Wallets/SQLite/UT_Key.cs | 37 ++ .../UT_AssetDescriptor.cs} | 8 +- neo.UnitTests/neo.UnitTests.csproj | 1 - neo/Ledger/MemoryPool.cs | 2 +- 64 files changed, 3625 insertions(+), 129 deletions(-) rename neo.UnitTests/{ => Consensus}/UT_Consensus.cs (97%) rename neo.UnitTests/{ => Consensus}/UT_ConsensusServiceMailbox.cs (97%) create mode 100644 neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs create mode 100644 neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs create mode 100644 neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs create mode 100644 neo.UnitTests/Cryptography/UT_Base58.cs create mode 100644 neo.UnitTests/Cryptography/UT_BloomFilter.cs create mode 100644 neo.UnitTests/Cryptography/UT_Crypto.cs create mode 100644 neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs rename neo.UnitTests/{ => Cryptography}/UT_Scrypt.cs (93%) create mode 100644 neo.UnitTests/IO/Caching/UT_Cache.cs create mode 100644 neo.UnitTests/IO/Caching/UT_CloneCache.cs create mode 100644 neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs create mode 100644 neo.UnitTests/IO/Caching/UT_DataCache.cs create mode 100644 neo.UnitTests/IO/Caching/UT_FifoSet.cs create mode 100644 neo.UnitTests/IO/Caching/UT_MetaDataCache.cs create mode 100644 neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs create mode 100644 neo.UnitTests/IO/Json/UT_JArray.cs create mode 100644 neo.UnitTests/IO/Json/UT_JBoolean.cs create mode 100644 neo.UnitTests/IO/Json/UT_JNumber.cs create mode 100644 neo.UnitTests/IO/Json/UT_JObject.cs create mode 100644 neo.UnitTests/IO/Json/UT_JString.cs create mode 100644 neo.UnitTests/IO/UT_IOHelper.cs create mode 100644 neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs create mode 100644 neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs rename neo.UnitTests/{ => Ledger}/UT_MemoryPool.cs (99%) rename neo.UnitTests/{ => Ledger}/UT_PoolItem.cs (99%) rename neo.UnitTests/{ => Ledger}/UT_StorageItem.cs (98%) rename neo.UnitTests/{ => Ledger}/UT_StorageKey.cs (99%) rename neo.UnitTests/{ => Network/P2P/Payloads}/UT_Block.cs (99%) rename neo.UnitTests/{ => Network/P2P/Payloads}/UT_Header.cs (99%) rename neo.UnitTests/{ => Network/P2P/Payloads}/UT_Transaction.cs (99%) rename neo.UnitTests/{ => Network/P2P/Payloads}/UT_Witness.cs (98%) rename neo.UnitTests/{UT_P2PMessage.cs => Network/P2P/UT_Message.cs} (98%) rename neo.UnitTests/{ => Network/P2P}/UT_ProtocolHandler.cs (97%) rename neo.UnitTests/{ => Network/P2P}/UT_ProtocolHandlerMailbox.cs (98%) rename neo.UnitTests/{ => Network/P2P}/UT_RemoteNode.cs (98%) rename neo.UnitTests/{ => Network/P2P}/UT_RemoteNodeMailbox.cs (94%) rename neo.UnitTests/{ => Network/P2P}/UT_TaskManagerMailbox.cs (93%) rename neo.UnitTests/{ => SmartContract/Iterators}/UT_ConcatenatedIterator.cs (97%) rename neo.UnitTests/{ => SmartContract/Manifest}/UT_ContractManifest.cs (99%) rename neo.UnitTests/{ => SmartContract/Native/Tokens}/UT_GasToken.cs (99%) rename neo.UnitTests/{ => SmartContract/Native/Tokens}/UT_NeoToken.cs (99%) rename neo.UnitTests/{UT_Policy.cs => SmartContract/Native/UT_PolicyContract.cs} (94%) rename neo.UnitTests/{ => SmartContract}/UT_InteropPrices.cs (99%) rename neo.UnitTests/{ => SmartContract}/UT_InteropService.cs (98%) rename neo.UnitTests/{ => SmartContract}/UT_JsonSerializer.cs (99%) rename neo.UnitTests/{ => SmartContract}/UT_OpCodePrices.cs (91%) rename neo.UnitTests/{ => SmartContract}/UT_Syscalls.cs (98%) create mode 100644 neo.UnitTests/UT_BigDecimal.cs delete mode 100644 neo.UnitTests/UT_FifoSet.cs create mode 100644 neo.UnitTests/VM/UT_Helper.cs rename neo.UnitTests/{ => Wallets/NEP6}/UT_NEP6Wallet.cs (97%) rename neo.UnitTests/{ => Wallets/NEP6}/UT_ScryptParameters.cs (97%) create mode 100644 neo.UnitTests/Wallets/SQLite/UT_Account.cs create mode 100644 neo.UnitTests/Wallets/SQLite/UT_Address.cs create mode 100644 neo.UnitTests/Wallets/SQLite/UT_Contract.cs create mode 100644 neo.UnitTests/Wallets/SQLite/UT_Key.cs rename neo.UnitTests/{UT_AssetDescription.cs => Wallets/UT_AssetDescriptor.cs} (77%) diff --git a/neo.UnitTests/UT_Consensus.cs b/neo.UnitTests/Consensus/UT_Consensus.cs similarity index 97% rename from neo.UnitTests/UT_Consensus.cs rename to neo.UnitTests/Consensus/UT_Consensus.cs index c5bf1828f8..34e8322615 100644 --- a/neo.UnitTests/UT_Consensus.cs +++ b/neo.UnitTests/Consensus/UT_Consensus.cs @@ -18,7 +18,7 @@ using System.Security.Cryptography; using ECPoint = Neo.Cryptography.ECC.ECPoint; -namespace Neo.UnitTests +namespace Neo.UnitTests.Consensus { [TestClass] @@ -151,13 +151,13 @@ public void TestSerializeAndDeserializeConsensusContext() ViewNumber = 2, Validators = new ECPoint[7] { - ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", Cryptography.ECC.ECCurve.Secp256r1), - ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", Cryptography.ECC.ECCurve.Secp256r1) + 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 }; diff --git a/neo.UnitTests/UT_ConsensusServiceMailbox.cs b/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs similarity index 97% rename from neo.UnitTests/UT_ConsensusServiceMailbox.cs rename to neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs index fded2ab54e..fec1ee6bc9 100644 --- a/neo.UnitTests/UT_ConsensusServiceMailbox.cs +++ b/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs @@ -10,7 +10,7 @@ using Akka.Configuration; using Neo.Consensus; -namespace Neo.UnitTests +namespace Neo.UnitTests.Consensus { [TestClass] public class UT_ConsensusServiceMailbox : TestKit diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs b/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs new file mode 100644 index 0000000000..dcd394fa4f --- /dev/null +++ b/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs @@ -0,0 +1,55 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Wallets; +using System; +using System.Numerics; +using ECDsa = Neo.Cryptography.ECC.ECDsa; + +namespace Neo.UnitTests.Cryptography +{ + [TestClass] + public class UT_ECDsa + { + private KeyPair key = null; + + [TestInitialize] + public void TestSetup() + { + key = UT_Crypto.generateCertainKey(32); + } + + [TestMethod] + public void TestECDsaConstructor() + { + Action action = () => new ECDsa(key.PublicKey); + action.ShouldNotThrow(); + action = () => new ECDsa(key.PrivateKey, key.PublicKey.Curve); + action.ShouldNotThrow(); + } + + [TestMethod] + public void TestGenerateSignature() + { + ECDsa sa = new ECDsa(key.PrivateKey, key.PublicKey.Curve); + byte[] message = System.Text.Encoding.Default.GetBytes("HelloWorld"); + for (int i = 0; i < 30; i++) + { + BigInteger[] result = sa.GenerateSignature(message); + result.Length.Should().Be(2); + } + sa = new ECDsa(key.PublicKey); + Action action = () => sa.GenerateSignature(message); + action.ShouldThrow(); + } + + [TestMethod] + public void TestVerifySignature() + { + ECDsa sa = new ECDsa(key.PrivateKey, key.PublicKey.Curve); + byte[] message = System.Text.Encoding.Default.GetBytes("HelloWorld"); + BigInteger[] result = sa.GenerateSignature(message); + sa.VerifySignature(message, result[0], result[1]).Should().BeTrue(); + sa.VerifySignature(message, new BigInteger(-100), result[1]).Should().BeFalse(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs b/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs new file mode 100644 index 0000000000..035e4dc469 --- /dev/null +++ b/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs @@ -0,0 +1,69 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; +using System; +using System.Globalization; +using System.Numerics; +using System.Reflection; + +namespace Neo.UnitTests.Cryptography.ECC +{ + [TestClass] + public class UT_ECFieldElement + { + [TestMethod] + public void TestECFieldElementConstructor() + { + BigInteger input = new BigInteger(100); + Action action = () => new ECFieldElement(input, ECCurve.Secp256k1); + action.ShouldNotThrow(); + + input = ECCurve.Secp256k1.Q; + action = () => new ECFieldElement(input, ECCurve.Secp256k1); + action.ShouldThrow(); + } + + [TestMethod] + public void TestEquals() + { + BigInteger input = new BigInteger(100); + object element = new ECFieldElement(input, ECCurve.Secp256k1); + element.Equals(element).Should().BeTrue(); + element.Equals(1).Should().BeFalse(); + + input = new BigInteger(200); + element.Equals(new ECFieldElement(input, ECCurve.Secp256k1)).Should().BeFalse(); + } + + [TestMethod] + public void TestSqrt() + { + ECFieldElement element = new ECFieldElement(new BigInteger(100), ECCurve.Secp256k1); + element.Sqrt().Should().Be(new ECFieldElement(BigInteger.Parse("115792089237316195423570985008687907853269984665640564039457584007908834671653"), ECCurve.Secp256k1)); + + ConstructorInfo constructor = typeof(ECCurve).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(BigInteger), typeof(BigInteger), typeof(BigInteger), typeof(BigInteger), typeof(byte[]) }, null); + ECCurve testCruve = constructor.Invoke(new object[] { + BigInteger.Parse("00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFF0", NumberStyles.AllowHexSpecifier), + BigInteger.Parse("00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFF00", NumberStyles.AllowHexSpecifier), + BigInteger.Parse("005AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", NumberStyles.AllowHexSpecifier), + BigInteger.Parse("00FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", NumberStyles.AllowHexSpecifier), + ("04" + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296" + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5").HexToBytes() }) as ECCurve; + element = new ECFieldElement(new BigInteger(200), testCruve); + element.Sqrt().Should().Be(null); + } + + [TestMethod] + public void TestToByteArray() + { + byte[] result = new byte[32]; + result[31] = 100; + new ECFieldElement(new BigInteger(100), ECCurve.Secp256k1).ToByteArray().Should().BeEquivalentTo(result); + + byte[] result2 = { 2, 53, 250, 221, 129, 194, 130, 43, 179, 240, 120, 119, 151, 61, 80, 242, 139, 242, 42, 49, 190, 142, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + new ECFieldElement(BigInteger.Pow(new BigInteger(10), 75), ECCurve.Secp256k1).ToByteArray().Should().BeEquivalentTo(result2); + + byte[] result3 = { 221, 21, 254, 134, 175, 250, 217, 18, 73, 239, 14, 183, 19, 243, 158, 190, 170, 152, 123, 110, 111, 210, 160, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + new ECFieldElement(BigInteger.Pow(new BigInteger(10), 77), ECCurve.Secp256k1).ToByteArray().Should().BeEquivalentTo(result3); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs b/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs new file mode 100644 index 0000000000..9de751e851 --- /dev/null +++ b/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs @@ -0,0 +1,310 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; +using Neo.IO; +using System; +using System.IO; +using System.Linq; +using System.Numerics; +using ECCurve = Neo.Cryptography.ECC.ECCurve; +using ECPoint = Neo.Cryptography.ECC.ECPoint; + +namespace Neo.UnitTests.Cryptography.ECC +{ + [TestClass] + public class UT_ECPoint + { + public static byte[] generatePrivateKey(int privateKeyLength) + { + byte[] privateKey = new byte[privateKeyLength]; + for (int i = 0; i < privateKeyLength; i++) + { + privateKey[i] = (byte)((byte)i % byte.MaxValue); + } + return privateKey; + } + + [TestMethod] + public void TestCompareTo() + { + ECFieldElement X1 = new ECFieldElement(new BigInteger(100), ECCurve.Secp256k1); + ECFieldElement Y1 = new ECFieldElement(new BigInteger(200), ECCurve.Secp256k1); + ECFieldElement X2 = new ECFieldElement(new BigInteger(300), ECCurve.Secp256k1); + ECFieldElement Y2 = new ECFieldElement(new BigInteger(400), ECCurve.Secp256k1); + ECPoint point1 = new ECPoint(X1, Y1, ECCurve.Secp256k1); + ECPoint point2 = new ECPoint(X2, Y1, ECCurve.Secp256k1); + ECPoint point3 = new ECPoint(X1, Y2, ECCurve.Secp256k1); + + point1.CompareTo(point1).Should().Be(0); + point1.CompareTo(point2).Should().Be(-1); + point2.CompareTo(point1).Should().Be(1); + point1.CompareTo(point3).Should().Be(-1); + point3.CompareTo(point1).Should().Be(1); + } + + [TestMethod] + public void TestECPointConstructor() + { + ECPoint point = new ECPoint(); + point.X.Should().BeNull(); + point.Y.Should().BeNull(); + point.Curve.Should().Be(ECCurve.Secp256r1); + + ECFieldElement X = new ECFieldElement(new BigInteger(100), ECCurve.Secp256k1); + ECFieldElement Y = new ECFieldElement(new BigInteger(200), ECCurve.Secp256k1); + point = new ECPoint(X, Y, ECCurve.Secp256k1); + point.X.Should().Be(X); + point.Y.Should().Be(Y); + point.Curve.Should().Be(ECCurve.Secp256k1); + } + + [TestMethod] + public void TestDecodePoint() + { + byte[] input1 = { 0 }; + Action action = () => ECPoint.DecodePoint(input1, ECCurve.Secp256k1); + action.ShouldThrow(); + + byte[] input2 = { 4, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 72, + 58, 218, 119, 38, 163, 196, 101, 93, 164, 251, 252, 14, 17, 8, 168, 253, 23, 180, 72, 166, 133, 84, 25, 156, 71, 208, 143, 251, 16, 212, 184 }; + ECPoint.DecodePoint(input2, ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + action = () => ECPoint.DecodePoint(input2.Take(32).ToArray(), ECCurve.Secp256k1); + action.ShouldThrow(); + + byte[] input3 = { 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152 }; + byte[] input4 = { 3, 107, 23, 209, 242, 225, 44, 66, 71, 248, 188, 230, 229, 99, 164, 64, 242, 119, 3, 125, 129, 45, 235, 51, 160, 244, 161, 57, 69, 216, 152, 194, 150 }; + ECPoint.DecodePoint(input3, ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + ECPoint.DecodePoint(input4, ECCurve.Secp256r1).Should().Be(ECCurve.Secp256r1.G); + + action = () => ECPoint.DecodePoint(input3.Take(input3.Length - 1).ToArray(), ECCurve.Secp256k1); + action.ShouldThrow(); + } + + [TestMethod] + public void TestDeserializeFrom() + { + byte[] input1 = { 0 }; + Action action = () => ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input1)), ECCurve.Secp256k1); + action.ShouldThrow(); + + byte[] input2 = { 4, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 72, + 58, 218, 119, 38, 163, 196, 101, 93, 164, 251, 252, 14, 17, 8, 168, 253, 23, 180, 72, 166, 133, 84, 25, 156, 71, 208, 143, 251, 16, 212, 184 }; + ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input2)), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + action = () => ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input2.Take(32).ToArray())), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + action.ShouldThrow(); + + byte[] input3 = { 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152 }; + ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input3)), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + byte[] input4 = { 3, 107, 23, 209, 242, 225, 44, 66, 71, 248, 188, 230, 229, 99, 164, 64, 242, 119, 3, 125, 129, 45, 235, 51, 160, 244, 161, 57, 69, 216, 152, 194, 150 }; + ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input4)), ECCurve.Secp256r1).Should().Be(ECCurve.Secp256r1.G); + + action = () => ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input3.Take(input3.Length - 1).ToArray())), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + action.ShouldThrow(); + } + + [TestMethod] + public void TestEncodePoint() + { + ECPoint point = new ECPoint(null, null, ECCurve.Secp256k1); + byte[] result1 = { 0 }; + point.EncodePoint(true).Should().BeEquivalentTo(result1); + + point = ECCurve.Secp256k1.G; + byte[] result2 = { 4, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 72, + 58, 218, 119, 38, 163, 196, 101, 93, 164, 251, 252, 14, 17, 8, 168, 253, 23, 180, 72, 166, 133, 84, 25, 156, 71, 208, 143, 251, 16, 212, 184 }; + point.EncodePoint(false).Should().BeEquivalentTo(result2); + + byte[] result3 = { 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152 }; + point.EncodePoint(true).Should().BeEquivalentTo(result3); + + point = ECCurve.Secp256r1.G; + byte[] result4 = { 3, 107, 23, 209, 242, 225, 44, 66, 71, 248, 188, 230, 229, 99, 164, 64, 242, 119, 3, 125, 129, 45, 235, 51, 160, 244, 161, 57, 69, 216, 152, 194, 150 }; + point.EncodePoint(true).Should().BeEquivalentTo(result4); + } + + [TestMethod] + public void TestEquals() + { + ECPoint point = ECCurve.Secp256k1.G; + point.Equals(point).Should().BeTrue(); + point.Equals(null).Should().BeFalse(); + + point = new ECPoint(null, null, ECCurve.Secp256k1); + point.Equals(new ECPoint(null, null, ECCurve.Secp256r1)).Should().BeTrue(); + point.Equals(ECCurve.Secp256r1.G).Should().BeFalse(); + ECCurve.Secp256r1.G.Equals(point).Should().BeFalse(); + + ECFieldElement X1 = new ECFieldElement(new BigInteger(100), ECCurve.Secp256k1); + ECFieldElement Y1 = new ECFieldElement(new BigInteger(200), ECCurve.Secp256k1); + ECFieldElement X2 = new ECFieldElement(new BigInteger(300), ECCurve.Secp256k1); + ECFieldElement Y2 = new ECFieldElement(new BigInteger(400), ECCurve.Secp256k1); + ECPoint point1 = new ECPoint(X1, Y1, ECCurve.Secp256k1); + ECPoint point2 = new ECPoint(X2, Y1, ECCurve.Secp256k1); + ECPoint point3 = new ECPoint(X1, Y2, ECCurve.Secp256k1); + point1.Equals(point2).Should().BeFalse(); + point1.Equals(point3).Should().BeFalse(); + } + + [TestMethod] + public void TestFromBytes() + { + byte[] input1 = { 0 }; + Action action = () => ECPoint.FromBytes(input1, ECCurve.Secp256k1); + action.ShouldThrow(); + + byte[] input2 = { 4, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 72, + 58, 218, 119, 38, 163, 196, 101, 93, 164, 251, 252, 14, 17, 8, 168, 253, 23, 180, 72, 166, 133, 84, 25, 156, 71, 208, 143, 251, 16, 212, 184 }; + ECPoint.FromBytes(input2, ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + + byte[] input3 = { 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152 }; + ECPoint.FromBytes(input3, ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + ECPoint.FromBytes(input2.Skip(1).ToArray(), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); + + byte[] input4 = generatePrivateKey(72); + ECPoint.FromBytes(input4, ECCurve.Secp256k1).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("3634473727541135791764834762056624681715094789735830699031648" + + "273128038409767"), ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("18165245710263168158644330920009617039772504630129940696140050972160274286151"), + ECCurve.Secp256k1), ECCurve.Secp256k1)); + + byte[] input5 = generatePrivateKey(96); + ECPoint.FromBytes(input5, ECCurve.Secp256k1).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("1780731860627700044960722568376592200742329637303199754547598" + + "369979440671"), ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("14532552714582660066924456880521368950258152170031413196862950297402215317055"), + ECCurve.Secp256k1), ECCurve.Secp256k1)); + + byte[] input6 = generatePrivateKey(104); + ECPoint.FromBytes(input6, ECCurve.Secp256k1).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("3634473727541135791764834762056624681715094789735830699031648" + + "273128038409767"), ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("18165245710263168158644330920009617039772504630129940696140050972160274286151"), + ECCurve.Secp256k1), ECCurve.Secp256k1)); + } + + [TestMethod] + public void TestMultiply() + { + ECPoint p = ECCurve.Secp256k1.G; + BigInteger k = BigInteger.Parse("100"); + ECPoint.Multiply(p, k).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("107303582290733097924842193972465022053148211775194373671539518313500194639752"), + ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("103795966108782717446806684023742168462365449272639790795591544606836007446638"), ECCurve.Secp256k1), + ECCurve.Secp256k1)); + + k = BigInteger.Parse("10000"); + ECPoint.Multiply(p, k).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("55279067612272658004429375184716238028207484982037227804583126224321918234542"), + ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("93139664895507357192565643142424306097487832058389223752321585898830257071353"), ECCurve.Secp256k1), + ECCurve.Secp256k1)); + + k = BigInteger.Parse("10000000000000"); + ECPoint.Multiply(p, k).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("115045167963494515061513744671884131783397561769819471159495798754884242293003"), + ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("93759167105263077270762304290738437383691912799231615884447658154878797241853"), ECCurve.Secp256k1), + ECCurve.Secp256k1)); + + k = BigInteger.Parse("1000000000000000000000000000000000000000"); + ECPoint.Multiply(p, k).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("114831276968810911840931876895388845736099852671055832194631099067239418074350"), + ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("16721517996619732311261078486295444964227498319433363271180755596201863690708"), ECCurve.Secp256k1), + ECCurve.Secp256k1)); + + k = new BigInteger(generatePrivateKey(100)); + ECPoint.Multiply(p, k).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("19222995016448259376216431079553428738726180595337971417371897285865264889977"), + ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("6637081904924493791520919212064582313497884724460823966446023080706723904419"), ECCurve.Secp256k1), + ECCurve.Secp256k1)); + + k = new BigInteger(generatePrivateKey(120)); + ECPoint.Multiply(p, k).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("79652345192111851576650978679091010173409410384772942769927955775006682639778"), + ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("6460429961979335115790346961011058418773289452368186110818621539624566803831"), ECCurve.Secp256k1), + ECCurve.Secp256k1)); + + k = new BigInteger(generatePrivateKey(300)); + ECPoint.Multiply(p, k).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("105331914562708556186724786757483927866790351460145374033180496740107603569412"), + ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("60523670886755698512704385951571322569877668383890769288780681319304421873758"), ECCurve.Secp256k1), + ECCurve.Secp256k1)); + } + + [TestMethod] + public void TestDeserialize() + { + ECPoint point = new ECPoint(null, null, ECCurve.Secp256k1); + ISerializable serializable = point; + byte[] input = { 4, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, + 72, 58, 218, 119, 38, 163, 196, 101, 93, 164, 251, 252, 14, 17, 8, 168, 253, 23, 180, 72, 166, 133, 84, 25, 156, 71, 208, 143, 251, 16, 212, 184 }; + serializable.Deserialize(new BinaryReader(new MemoryStream(input))); + point.X.Should().Be(ECCurve.Secp256k1.G.X); + point.Y.Should().Be(ECCurve.Secp256k1.G.Y); + } + + [TestMethod] + public void TestSerialize() + { + MemoryStream stream = new MemoryStream(); + ECPoint point = new ECPoint(null, null, ECCurve.Secp256k1); + ISerializable serializable = point; + serializable.Serialize(new BinaryWriter(stream)); + stream.ToArray().Should().BeEquivalentTo(new byte[] { 0 }); + } + + [TestMethod] + public void TestOpAddition() + { + (ECCurve.Secp256k1.Infinity + ECCurve.Secp256k1.G).Should().Be(ECCurve.Secp256k1.G); + (ECCurve.Secp256k1.G + ECCurve.Secp256k1.Infinity).Should().Be(ECCurve.Secp256k1.G); + (ECCurve.Secp256k1.G + ECCurve.Secp256k1.G).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("8956589192654700423125292042593569236064414582962220983368432" + + "9913297188986597"), ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("12158399299693830322967808612713398636155367887041628176798871954788371653930"), + ECCurve.Secp256k1), ECCurve.Secp256k1)); + (ECCurve.Secp256k1.G + new ECPoint(ECCurve.Secp256k1.G.X, new ECFieldElement(BigInteger.One, ECCurve.Secp256k1), ECCurve.Secp256k1)).Should().Be(ECCurve.Secp256k1.Infinity); + (ECCurve.Secp256k1.G + ECCurve.Secp256k1.G + ECCurve.Secp256k1.G).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("112711660439710606056748659173929673102" + + "114977341539408544630613555209775888121"), ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("2558302798057088369165690587740197640644886825481629506991988" + + "8960541586679410"), ECCurve.Secp256k1), ECCurve.Secp256k1)); + } + + [TestMethod] + public void TestOpMultiply() + { + ECPoint p = null; + byte[] n = new byte[] { 1 }; + Action action = () => p = p * n; + action.ShouldThrow(); + + p = ECCurve.Secp256k1.G; + n = null; + action.ShouldThrow(); + + n = new byte[] { 1 }; + action.ShouldThrow(); + + p = ECCurve.Secp256k1.Infinity; + n = new byte[32]; + (p * n).Should().Be(p); + + p = ECCurve.Secp256k1.G; + (p * n).Should().Be(ECCurve.Secp256k1.Infinity); + + n[0] = 1; + (p * n).Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("63395642421589016740518975608504846303065672135176650115036476193363423546538"), ECCurve.Secp256k1), + new ECFieldElement(BigInteger.Parse("29236048674093813394523910922582374630829081423043497254162533033164154049666"), ECCurve.Secp256k1), ECCurve.Secp256k1)); + } + + [TestMethod] + public void TestOpUnaryNegation() + { + (-ECCurve.Secp256k1.G).Should().Be(new ECPoint(ECCurve.Secp256k1.G.X, -ECCurve.Secp256k1.G.Y, ECCurve.Secp256k1)); + } + + [TestMethod] + public void TestTryParse() + { + ECPoint.TryParse("00", ECCurve.Secp256k1, out ECPoint result).Should().BeFalse(); + result.Should().BeNull(); + + ECPoint.TryParse("0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", ECCurve.Secp256k1, + out result).Should().BeTrue(); + result.Should().Be(ECCurve.Secp256k1.G); + } + + [TestMethod] + public void TestTwice() + { + ECCurve.Secp256k1.Infinity.Twice().Should().Be(ECCurve.Secp256k1.Infinity); + new ECPoint(new ECFieldElement(BigInteger.Zero, ECCurve.Secp256k1), new ECFieldElement(BigInteger.Zero, ECCurve.Secp256k1), + ECCurve.Secp256k1).Twice().Should().Be(ECCurve.Secp256k1.Infinity); + ECCurve.Secp256k1.G.Twice().Should().Be(new ECPoint(new ECFieldElement(BigInteger.Parse("89565891926547004231252920425935692360644145829622209833684329913297188986597"), + ECCurve.Secp256k1), new ECFieldElement(BigInteger.Parse("12158399299693830322967808612713398636155367887041628176798871954788371653930"), ECCurve.Secp256k1), + ECCurve.Secp256k1)); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Cryptography/UT_Base58.cs b/neo.UnitTests/Cryptography/UT_Base58.cs new file mode 100644 index 0000000000..2d18975445 --- /dev/null +++ b/neo.UnitTests/Cryptography/UT_Base58.cs @@ -0,0 +1,31 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using System; + +namespace Neo.UnitTests.Cryptography +{ + [TestClass] + public class UT_Base58 + { + byte[] decoded1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + string encoded1 = "1kA3B2yGe2z4"; + byte[] decoded2 = { 0, 0, 0, 0, 0}; + string encoded2 = "1111"; + + [TestMethod] + public void TestEncode() + { + Base58.Encode(decoded1).Should().Be(encoded1); + } + + [TestMethod] + public void TestDecode() + { + Base58.Decode(encoded1).Should().BeEquivalentTo(decoded1); + Base58.Decode(encoded2).Should().BeEquivalentTo(decoded2); + Action action = () => Base58.Decode(encoded1 + "l").Should().BeEquivalentTo(decoded1); + action.ShouldThrow(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Cryptography/UT_BloomFilter.cs b/neo.UnitTests/Cryptography/UT_BloomFilter.cs new file mode 100644 index 0000000000..d611e0965d --- /dev/null +++ b/neo.UnitTests/Cryptography/UT_BloomFilter.cs @@ -0,0 +1,69 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using System; + +namespace Neo.UnitTests.Cryptography +{ + [TestClass] + public class UT_BloomFilter + { + [TestMethod] + public void TestAddCheck() + { + int m = 7, n = 10; + uint nTweak = 123456; + byte[] elements = { 0, 1, 2, 3, 4 }; + BloomFilter filter = new BloomFilter(m, n, nTweak); + filter.Add(elements); + filter.Check(elements).Should().BeTrue(); + byte[] anotherElements = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + filter.Check(anotherElements).Should().BeFalse(); + } + + [TestMethod] + public void TestBloomFIlterConstructorGetKMTweak() + { + int m = -7, n = 10; + uint nTweak = 123456; + Action action = () => new BloomFilter(m, n, nTweak); + action.ShouldThrow(); + + m = 7; + n = -10; + action = () => new BloomFilter(m, n, nTweak); + action.ShouldThrow(); + + n = 10; + BloomFilter filter = new BloomFilter(m, n, nTweak); + filter.M.Should().Be(m); + filter.K.Should().Be(n); + filter.Tweak.Should().Be(nTweak); + + byte[] shorterElements = { 0, 1, 2, 3, 4 }; + filter = new BloomFilter(m, n, nTweak, shorterElements); + filter.M.Should().Be(m); + filter.K.Should().Be(n); + filter.Tweak.Should().Be(nTweak); + + byte[] longerElements = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + filter = new BloomFilter(m, n, nTweak, longerElements); + filter.M.Should().Be(m); + filter.K.Should().Be(n); + filter.Tweak.Should().Be(nTweak); + } + + [TestMethod] + public void TestGetBits() + { + int m = 7, n = 10; + uint nTweak = 123456; + byte[] elements = { 0, 1, 2, 3, 4 }; + BloomFilter filter = new BloomFilter(m, n, nTweak); + byte[] result = new byte[m]; + filter.GetBits(result); + foreach (byte value in result) + value.Should().Be(0); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Cryptography/UT_Crypto.cs b/neo.UnitTests/Cryptography/UT_Crypto.cs new file mode 100644 index 0000000000..ec0d95add6 --- /dev/null +++ b/neo.UnitTests/Cryptography/UT_Crypto.cs @@ -0,0 +1,64 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.Wallets; +using System; +using System.Linq; +using System.Security.Cryptography; + +namespace Neo.UnitTests.Cryptography +{ + [TestClass] + public class UT_Crypto + { + private KeyPair key = null; + + public static KeyPair generateKey(int privateKeyLength) + { + byte[] privateKey = new byte[privateKeyLength]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + return new KeyPair(privateKey); + } + + public static KeyPair generateCertainKey(int privateKeyLength) + { + byte[] privateKey = new byte[privateKeyLength]; + for (int i = 0; i < privateKeyLength; i++) + { + privateKey[i] = (byte)((byte)i % byte.MaxValue); + } + return new KeyPair(privateKey); + } + + [TestInitialize] + public void TestSetup() + { + key = generateKey(32); + } + + [TestMethod] + public void TestVerifySignature() + { + byte[] message = System.Text.Encoding.Default.GetBytes("HelloWorld"); + byte[] signature = Crypto.Default.Sign(message, key.PrivateKey, key.PublicKey.EncodePoint(false).Skip(1).ToArray()); + Crypto.Default.VerifySignature(message, signature, key.PublicKey.EncodePoint(false)).Should().BeTrue(); + Crypto.Default.VerifySignature(message, signature, key.PublicKey.EncodePoint(false).Skip(1).ToArray()).Should().BeTrue(); + Crypto.Default.VerifySignature(message, signature, key.PublicKey.EncodePoint(false).Skip(1).ToArray()).Should().BeTrue(); + + byte[] wrongKey = new byte[33]; + wrongKey[0] = 0x02; + Crypto.Default.VerifySignature(message, signature, wrongKey).Should().BeFalse(); + + wrongKey[0] = 0x03; + for (int i = 1; i < 33; i++) wrongKey[i] = byte.MaxValue; + Crypto.Default.VerifySignature(message, signature, wrongKey).Should().BeFalse(); + + wrongKey = new byte[36]; + Action action = () => Crypto.Default.VerifySignature(message, signature, wrongKey).Should().BeFalse(); + action.ShouldThrow(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs new file mode 100644 index 0000000000..af909e3d71 --- /dev/null +++ b/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -0,0 +1,225 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.Network.P2P.Payloads; +using System; +using System.Security; +using System.Text; + +namespace Neo.UnitTests.Cryptography +{ + [TestClass] + public class UT_Cryptography_Helper + { + [TestMethod] + public void TestAES256Encrypt() + { + byte[] block = Encoding.ASCII.GetBytes("00000000000000000000000000000000"); + byte[] key = Encoding.ASCII.GetBytes("1234567812345678"); + byte[] result = block.AES256Encrypt(key); + string encryptString = result.ToHexString(); + encryptString.Should().Be("f69e0923d8247eef417d6a78944a4b39f69e0923d8247eef417d6a78944a4b39"); + } + + [TestMethod] + public void TestAES256Decrypt() + { + byte[] block = new byte[32]; + byte[] key = Encoding.ASCII.GetBytes("1234567812345678"); + string decryptString = "f69e0923d8247eef417d6a78944a4b39f69e0923d8247eef417d6a78944a4b399ae8fd02b340288a0e7bbff0f0ba54d6"; + for (int i = 0; i < 32; i++) + block[i] = Convert.ToByte(decryptString.Substring(i * 2, 2), 16); + string str = System.Text.Encoding.Default.GetString(block.AES256Decrypt(key)); + str.Should().Be("00000000000000000000000000000000"); + } + + [TestMethod] + public void TestAesEncrypt() + { + byte[] data = Encoding.ASCII.GetBytes("00000000000000000000000000000000"); + byte[] key = Encoding.ASCII.GetBytes("12345678123456781234567812345678"); + byte[] iv = Encoding.ASCII.GetBytes("1234567812345678"); + byte[] result = data.AesEncrypt(key, iv); + + string encryptString = result.ToHexString(); + encryptString.Should().Be("07c748cf7d326782f82e60ebe60e2fac289e84e9ce91c1bc41565d14ecb53640"); + + byte[] nullData = null; + Action action = () => nullData.AesEncrypt(key, iv); + action.ShouldThrow(); + + byte[] nullKey = null; + action = () => data.AesEncrypt(nullKey, iv); + action.ShouldThrow(); + + byte[] nullIv = null; + action = () => data.AesEncrypt(key, nullIv); + action.ShouldThrow(); + + byte[] wrongData = Encoding.ASCII.GetBytes("000000000000000000000000000000001"); ; + action = () => wrongData.AesEncrypt(key, iv); + action.ShouldThrow(); + + byte[] wrongKey = Encoding.ASCII.GetBytes("123456781234567812345678123456780"); ; + action = () => data.AesEncrypt(wrongKey, iv); + action.ShouldThrow(); + + byte[] wrongIv = Encoding.ASCII.GetBytes("12345678123456780"); ; + action = () => data.AesEncrypt(key, wrongIv); + action.ShouldThrow(); + } + + [TestMethod] + public void TestAesDecrypt() + { + byte[] data = new byte[32]; + byte[] key = Encoding.ASCII.GetBytes("12345678123456781234567812345678"); + byte[] iv = Encoding.ASCII.GetBytes("1234567812345678"); + string decryptString = "07c748cf7d326782f82e60ebe60e2fac289e84e9ce91c1bc41565d14ecb5364073f28c9aa7bd6b069e44d8a97beb2b58"; + for (int i = 0; i < 32; i++) + data[i] = Convert.ToByte(decryptString.Substring(i * 2, 2), 16); + string str = System.Text.Encoding.Default.GetString(data.AesDecrypt(key, iv)); + str.Should().Be("00000000000000000000000000000000"); + + byte[] nullData = null; + Action action = () => nullData.AesDecrypt(key, iv); + action.ShouldThrow(); + + byte[] nullKey = null; + action = () => data.AesDecrypt(nullKey, iv); + action.ShouldThrow(); + + byte[] nullIv = null; + action = () => data.AesDecrypt(key, nullIv); + action.ShouldThrow(); + + byte[] wrongData = Encoding.ASCII.GetBytes("00000000000000001"); ; + action = () => wrongData.AesDecrypt(key, iv); + action.ShouldThrow(); + + byte[] wrongKey = Encoding.ASCII.GetBytes("123456781234567812345678123456780"); ; + action = () => data.AesDecrypt(wrongKey, iv); + action.ShouldThrow(); + + byte[] wrongIv = Encoding.ASCII.GetBytes("12345678123456780"); ; + action = () => data.AesDecrypt(key, wrongIv); + action.ShouldThrow(); + } + + [TestMethod] + public void TestBase58CheckDecode() + { + string input = "3vQB7B6MrGQZaxCuFg4oh"; + byte[] result = input.Base58CheckDecode(); + byte[] helloWorld = { 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100 }; + result.Should().Equal(helloWorld); + + input = "3v"; + Action action = () => input.Base58CheckDecode(); + action.ShouldThrow(); + + input = "3vQB7B6MrGQZaxCuFg4og"; + action = () => input.Base58CheckDecode(); + action.ShouldThrow(); + } + + [TestMethod] + public void TestSha256() + { + byte[] value = Encoding.ASCII.GetBytes("hello world"); + byte[] result = value.Sha256(0, value.Length); + string resultStr = result.ToHexString(); + resultStr.Should().Be("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"); + } + + [TestMethod] + public void TestTest() + { + int m = 7, n = 10; + uint nTweak = 123456; + BloomFilter filter = new BloomFilter(m, n, nTweak); + + Transaction tx = new Transaction + { + Script = TestUtils.GetByteArray(32, 0x42), + Sender = UInt160.Zero, + SystemFee = 4200000000, + Attributes = new TransactionAttribute[0], + Witnesses = new[] + { + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } + } + }; + filter.Test(tx).Should().BeFalse(); + + filter.Add(tx.Witnesses[0].ScriptHash.ToArray()); + filter.Test(tx).Should().BeTrue(); + + filter.Add(tx.Hash.ToArray()); + filter.Test(tx).Should().BeTrue(); + } + + [TestMethod] + public void TestStringToAesKey() + { + string password = "hello world"; + string string1 = "bc62d4b80d9e36da29c16c5d4d9f11731f36052c72401a76c23c0fb5a9b74423"; + byte[] byteArray = new byte[string1.Length / 2]; + byteArray = string1.HexToBytes(); + password.ToAesKey().Should().Equal(byteArray); + } + + [TestMethod] + public void TestSecureStringToAesKey() + { + var password = new SecureString(); + password.AppendChar('h'); + password.AppendChar('e'); + password.AppendChar('l'); + password.AppendChar('l'); + password.AppendChar('o'); + password.AppendChar(' '); + password.AppendChar('w'); + password.AppendChar('o'); + password.AppendChar('r'); + password.AppendChar('l'); + password.AppendChar('d'); + string string1 = "bc62d4b80d9e36da29c16c5d4d9f11731f36052c72401a76c23c0fb5a9b74423"; + byte[] byteArray = new byte[string1.Length / 2]; + byteArray = string1.HexToBytes(); + password.ToAesKey().Should().Equal(byteArray); + } + + [TestMethod] + public void TestToArray() + { + var password = new SecureString(); + password.AppendChar('h'); + password.AppendChar('e'); + password.AppendChar('l'); + password.AppendChar('l'); + password.AppendChar('o'); + password.AppendChar(' '); + password.AppendChar('w'); + password.AppendChar('o'); + password.AppendChar('r'); + password.AppendChar('l'); + password.AppendChar('d'); + byte[] byteArray = { 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64 }; + password.ToArray().Should().Equal(byteArray); + + SecureString nullString = null; + Action action = () => nullString.ToArray(); + action.ShouldThrow(); + + var zeroString = new SecureString(); + var result = zeroString.ToArray(); + byteArray = new byte[0]; + result.Should().Equal(byteArray); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/UT_Scrypt.cs b/neo.UnitTests/Cryptography/UT_Scrypt.cs similarity index 93% rename from neo.UnitTests/UT_Scrypt.cs rename to neo.UnitTests/Cryptography/UT_Scrypt.cs index 8e2bf1e30a..8ec603da48 100644 --- a/neo.UnitTests/UT_Scrypt.cs +++ b/neo.UnitTests/Cryptography/UT_Scrypt.cs @@ -2,7 +2,7 @@ using Neo.Cryptography; using System; -namespace Neo.UnitTests +namespace Neo.UnitTests.Cryptography { [TestClass] public class UT_Scrypt @@ -16,4 +16,4 @@ public void DeriveKeyTest() Assert.AreEqual("b6274d3a81892c24335ab46a08ec16d040ac00c5943b212099a44b76a9b8102631ab988fa07fb35357cee7b0e3910098c0774c0e97399997676d890b2bf2bb25", derivedkey); } } -} +} \ No newline at end of file diff --git a/neo.UnitTests/Extensions/NativeContractExtensions.cs b/neo.UnitTests/Extensions/NativeContractExtensions.cs index 7d33841ef8..6a641c2387 100644 --- a/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -8,12 +8,12 @@ namespace Neo.UnitTests.Extensions { public static class NativeContractExtensions { - public static StackItem Call(this NativeContract contract, Persistence.Snapshot snapshot, string method, params ContractParameter[] args) + public static StackItem Call(this NativeContract contract, Neo.Persistence.Snapshot snapshot, string method, params ContractParameter[] args) { return Call(contract, snapshot, null, method, args); } - public static StackItem Call(this NativeContract contract, Persistence.Snapshot snapshot, IVerifiable container, string method, params ContractParameter[] args) + public static StackItem Call(this NativeContract contract, Neo.Persistence.Snapshot snapshot, IVerifiable container, string method, params ContractParameter[] args) { var engine = new ApplicationEngine(TriggerType.Application, container, snapshot, 0, true); diff --git a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs index a87fd7c3e1..42c5c87543 100644 --- a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs +++ b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs @@ -34,14 +34,14 @@ public ManualWitness(params UInt160[] hashForVerify) public void DeserializeUnsigned(BinaryReader reader) { } - public UInt160[] GetScriptHashesForVerifying(Persistence.Snapshot snapshot) => _hashForVerify; + public UInt160[] GetScriptHashesForVerifying(Neo.Persistence.Snapshot snapshot) => _hashForVerify; public void Serialize(BinaryWriter writer) { } public void SerializeUnsigned(BinaryWriter writer) { } } - public static bool Transfer(this NativeContract contract, Persistence.Snapshot snapshot, byte[] from, byte[] to, BigInteger amount, bool signFrom) + public static bool Transfer(this NativeContract contract, Neo.Persistence.Snapshot snapshot, byte[] from, byte[] to, BigInteger amount, bool signFrom) { var engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(signFrom ? new UInt160(from) : null), snapshot, 0, true); @@ -90,7 +90,7 @@ public static string[] SupportedStandards(this NativeContract contract) .ToArray(); } - public static BigInteger TotalSupply(this NativeContract contract, Persistence.Snapshot snapshot) + public static BigInteger TotalSupply(this NativeContract contract, Neo.Persistence.Snapshot snapshot) { var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); @@ -110,7 +110,7 @@ public static BigInteger TotalSupply(this NativeContract contract, Persistence.S return (result as VM.Types.Integer).GetBigInteger(); } - public static BigInteger BalanceOf(this NativeContract contract, Persistence.Snapshot snapshot, byte[] account) + public static BigInteger BalanceOf(this NativeContract contract, Neo.Persistence.Snapshot snapshot, byte[] account) { var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); diff --git a/neo.UnitTests/IO/Caching/UT_Cache.cs b/neo.UnitTests/IO/Caching/UT_Cache.cs new file mode 100644 index 0000000000..01484ae65e --- /dev/null +++ b/neo.UnitTests/IO/Caching/UT_Cache.cs @@ -0,0 +1,214 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Caching; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Neo.UnitTests.IO.Caching +{ + class MyCache : Cache + { + public MyCache(int max_capacity) : base(max_capacity) { } + + protected override int GetKeyForItem(string item) + { + return item.GetHashCode(); + } + + protected override void OnAccess(CacheItem item) { } + + public IEnumerator MyGetEnumerator() + { + IEnumerable enumerable = this; + return enumerable.GetEnumerator(); + } + } + + [TestClass] + public class UT_Cache + { + MyCache cache; + readonly int max_capacity = 4; + + [TestInitialize] + public void init() + { + cache = new MyCache(max_capacity); + } + + [TestMethod] + public void TestCount() + { + cache.Count.Should().Be(0); + + cache.Add("hello"); + cache.Add("world"); + cache.Count.Should().Be(2); + + cache.Remove("hello"); + cache.Count.Should().Be(1); + } + + [TestMethod] + public void TestIsReadOnly() + { + cache.IsReadOnly.Should().BeFalse(); + } + + [TestMethod] + public void TestAddAndAddInternal() + { + cache.Add("hello"); + cache.Contains("hello").Should().BeTrue(); + cache.Contains("world").Should().BeFalse(); + cache.Add("hello"); + cache.Count.Should().Be(1); + } + + [TestMethod] + public void TestAddRange() + { + string[] range = { "hello", "world" }; + cache.AddRange(range); + cache.Count.Should().Be(2); + cache.Contains("hello").Should().BeTrue(); + cache.Contains("world").Should().BeTrue(); + cache.Contains("non exist string").Should().BeFalse(); + } + + [TestMethod] + public void TestClear() + { + cache.Add("hello"); + cache.Add("world"); + cache.Count.Should().Be(2); + cache.Clear(); + cache.Count.Should().Be(0); + } + + [TestMethod] + public void TestContainsKey() + { + cache.Add("hello"); + cache.Contains("hello").Should().BeTrue(); + cache.Contains("world").Should().BeFalse(); + } + + [TestMethod] + public void TestContainsValue() + { + cache.Add("hello"); + cache.Contains("hello".GetHashCode()).Should().BeTrue(); + cache.Contains("world".GetHashCode()).Should().BeFalse(); + } + + [TestMethod] + public void TestCopyTo() + { + cache.Add("hello"); + cache.Add("world"); + string[] temp = new string[2]; + + Action action = () => cache.CopyTo(null, 1); + action.ShouldThrow(); + + action = () => cache.CopyTo(temp, -1); + action.ShouldThrow(); + + action = () => cache.CopyTo(temp, 1); + action.ShouldThrow(); + + cache.CopyTo(temp, 0); + temp[0].Should().Be("hello"); + temp[1].Should().Be("world"); + } + + [TestMethod] + public void TestRemoveKey() + { + cache.Add("hello"); + cache.Remove("hello".GetHashCode()).Should().BeTrue(); + cache.Remove("world".GetHashCode()).Should().BeFalse(); + cache.Contains("hello").Should().BeFalse(); + } + + [TestMethod] + public void TestRemoveValue() + { + cache.Add("hello"); + cache.Remove("hello").Should().BeTrue(); + cache.Remove("world").Should().BeFalse(); + cache.Contains("hello").Should().BeFalse(); + } + + [TestMethod] + public void TestTryGet() + { + cache.Add("hello"); + cache.TryGet("hello".GetHashCode(), out string output).Should().BeTrue(); + output.Should().Be("hello"); + cache.TryGet("world".GetHashCode(), out string output2).Should().BeFalse(); + output2.Should().NotBe("world"); + output2.Should().BeNull(); + } + + [TestMethod] + public void TestArrayIndexAccess() + { + cache.Add("hello"); + cache.Add("world"); + cache["hello".GetHashCode()].Should().Be("hello"); + cache["world".GetHashCode()].Should().Be("world"); + + Action action = () => + { + string temp = cache["non exist string".GetHashCode()]; + }; + action.ShouldThrow(); + } + + [TestMethod] + public void TestGetEnumerator() + { + cache.Add("hello"); + cache.Add("world"); + int i = 0; + foreach (string item in cache) + { + if (i == 0) item.Should().Be("hello"); + if (i == 1) item.Should().Be("world"); + i++; + } + i.Should().Be(2); + cache.MyGetEnumerator().Should().NotBeNull(); + } + + [TestMethod] + public void TestOverMaxCapacity() + { + int i = 1; + for (; i <= max_capacity; i++) + { + cache.Add(i.ToString()); + } + cache.Add(i.ToString()); // The first one will be deleted + cache.Count.Should().Be(max_capacity); + cache.Contains((max_capacity + 1).ToString()).Should().BeTrue(); + } + + [TestMethod] + public void TestDispose() + { + cache.Add("hello"); + cache.Add("world"); + cache.Dispose(); + + Action action = () => + { + int count = cache.Count; + }; + action.ShouldThrow(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Caching/UT_CloneCache.cs b/neo.UnitTests/IO/Caching/UT_CloneCache.cs new file mode 100644 index 0000000000..1c9c2db74d --- /dev/null +++ b/neo.UnitTests/IO/Caching/UT_CloneCache.cs @@ -0,0 +1,126 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.IO.Caching; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.UnitTests.IO.Caching +{ + [TestClass] + public class UT_CloneCache + { + CloneCache cloneCache; + MyDataCache myDataCache; + + [TestInitialize] + public void Init() + { + myDataCache = new MyDataCache(); + cloneCache = new CloneCache(myDataCache); + } + + [TestMethod] + public void TestCloneCache() + { + cloneCache.Should().NotBeNull(); + } + + [TestMethod] + public void TestAddInternal() + { + cloneCache.Add(new MyKey("key1"), new MyValue("value1")); + cloneCache[new MyKey("key1")].Should().Be(new MyValue("value1")); + + cloneCache.Commit(); + myDataCache[new MyKey("key1")].Should().Be(new MyValue("value1")); + } + + [TestMethod] + public void TestDeleteInternal() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); + cloneCache.Delete(new MyKey("key1")); // trackable.State = TrackState.Deleted + cloneCache.Commit(); + + cloneCache.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")); + 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()); + 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.ElementAt(0).Key.Should().Be(new MyKey("key2")); + items.ElementAt(0).Value.Should().Be(new MyValue("value2")); + items.Count().Should().Be(1); + + items = cloneCache.Find(new MyKey("key3").ToArray()); + items.ElementAt(0).Key.Should().Be(new MyKey("key3")); + items.ElementAt(0).Value.Should().Be(new MyValue("value3")); + items.Count().Should().Be(1); + + items = cloneCache.Find(new MyKey("key4").ToArray()); + items.Count().Should().Be(0); + } + + [TestMethod] + public void TestGetInternal() + { + cloneCache.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")); + + Action action = () => { + var item = cloneCache[new MyKey("key4")]; + }; + action.ShouldThrow(); + } + + [TestMethod] + public void TestTryGetInternal() + { + cloneCache.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(); + } + + [TestMethod] + public void TestUpdateInternal() + { + cloneCache.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"; + + cloneCache.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")); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs b/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs new file mode 100644 index 0000000000..812cbc26ba --- /dev/null +++ b/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs @@ -0,0 +1,48 @@ +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"); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Caching/UT_DataCache.cs b/neo.UnitTests/IO/Caching/UT_DataCache.cs new file mode 100644 index 0000000000..d93316022d --- /dev/null +++ b/neo.UnitTests/IO/Caching/UT_DataCache.cs @@ -0,0 +1,347 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.IO.Caching; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace Neo.UnitTests.IO.Caching +{ + class MyKey : ISerializable, IEquatable + { + public string Key; + + public int Size => Key.Length; + + public MyKey() { } + + public MyKey(string val) + { + Key = val; + } + + public void Deserialize(BinaryReader reader) + { + Key = reader.ReadString(); + } + + public void Serialize(BinaryWriter writer) + { + writer.Write(Key); + } + + public bool Equals(MyKey other) + { + return Key.Equals(other.Key); + } + + public override bool Equals(object obj) + { + if (!(obj is MyKey key)) return false; + return Equals(key); + } + + public override int GetHashCode() + { + return Key.GetHashCode(); + } + } + + public class MyValue : ISerializable, ICloneable + { + public string Value; + + public int Size => Value.Length; + + public MyValue() { } + + public MyValue(string val) + { + Value = val; + } + + public void Deserialize(BinaryReader reader) + { + Value = reader.ReadString(); + } + + public void Serialize(BinaryWriter writer) + { + writer.Write(Value); + } + + MyValue ICloneable.Clone() + { + return new MyValue(Value); + } + + void ICloneable.FromReplica(MyValue replica) + { + Value = replica.Value; + } + + public bool Equals(MyValue other) + { + return (Value == null && other.Value == null) || Value.Equals(other.Value); + } + + public override bool Equals(object obj) + { + if (!(obj is MyValue key)) return false; + return Equals(key); + } + + public override int GetHashCode() + { + return Value.GetHashCode(); + } + } + + class MyDataCache : DataCache + where TKey : IEquatable, ISerializable, new() + where TValue : class, ICloneable, ISerializable, new() + { + public Dictionary InnerDict = new Dictionary(); + + public override void DeleteInternal(TKey key) + { + InnerDict.Remove(key); + } + + protected override void AddInternal(TKey key, TValue value) + { + InnerDict.Add(key, value); + } + + protected override IEnumerable> FindInternal(byte[] key_prefix) + { + return InnerDict.Where(kvp => kvp.Key.ToArray().Take(key_prefix.Length).SequenceEqual(key_prefix)); + } + + protected override TValue GetInternal(TKey key) + { + if (InnerDict.TryGetValue(key, out TValue value)) + { + return value.Clone(); + } + throw new KeyNotFoundException(); + } + + protected override TValue TryGetInternal(TKey key) + { + if (InnerDict.TryGetValue(key, out TValue value)) + { + return value.Clone(); + } + return null; + } + + protected override void UpdateInternal(TKey key, TValue value) + { + InnerDict[key] = value; + } + } + + [TestClass] + public class UT_DataCache + { + MyDataCache myDataCache; + + [TestInitialize] + public void Initialize() + { + myDataCache = new MyDataCache(); + } + + [TestMethod] + public void TestAccessByKey() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); + myDataCache.Add(new MyKey("key2"), new MyValue("value2")); + + myDataCache[new MyKey("key1")].Should().Be(new MyValue("value1")); + + // case 2 read from inner + myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); + myDataCache[new MyKey("key3")].Should().Be(new MyValue("value3")); + } + + [TestMethod] + public void TestAccessByNotFoundKey() + { + Action action = () => + { + var item = myDataCache[new MyKey("key1")]; + }; + action.ShouldThrow(); + } + + [TestMethod] + public void TestAccessByDeletedKey() + { + myDataCache.InnerDict.Add(new MyKey("key1"), new MyValue("value1")); + myDataCache.Delete(new MyKey("key1")); + + Action action = () => + { + var item = myDataCache[new MyKey("key1")]; + }; + action.ShouldThrow(); + } + + [TestMethod] + public void TestAdd() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); + myDataCache[new MyKey("key1")].Should().Be(new MyValue("value1")); + + Action action = () => myDataCache.Add(new MyKey("key1"), new MyValue("value1")); + action.ShouldThrow(); + + myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); + myDataCache.Delete(new MyKey("key2")); // trackable.State = TrackState.Deleted + myDataCache.Add(new MyKey("key2"), new MyValue("value2")); // trackable.State = TrackState.Changed + + action = () => myDataCache.Add(new MyKey("key2"), new MyValue("value2")); + action.ShouldThrow(); + } + + [TestMethod] + public void TestCommit() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); // trackable.State = TrackState.Added + + myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); + myDataCache.Delete(new MyKey("key2")); // trackable.State = TrackState.Deleted + + myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); + myDataCache.Delete(new MyKey("key3")); // trackable.State = TrackState.Deleted + myDataCache.Add(new MyKey("key3"), new MyValue("value4")); // trackable.State = TrackState.Changed + + myDataCache.Commit(); + + myDataCache.InnerDict[new MyKey("key1")].Should().Be(new MyValue("value1")); + myDataCache.InnerDict.ContainsKey(new MyKey("key2")).Should().BeFalse(); + myDataCache.InnerDict[new MyKey("key3")].Should().Be(new MyValue("value4")); + } + + [TestMethod] + public void TestCreateSnapshot() + { + myDataCache.CreateSnapshot().Should().NotBeNull(); + } + + [TestMethod] + public void TestDelete() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); + myDataCache.Delete(new MyKey("key1")); + myDataCache.InnerDict.ContainsKey(new MyKey("key1")).Should().BeFalse(); + + myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); + myDataCache.Delete(new MyKey("key2")); + myDataCache.Commit(); + myDataCache.InnerDict.ContainsKey(new MyKey("key2")).Should().BeFalse(); + } + + [TestMethod] + public void TestDeleteWhere() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); + myDataCache.Add(new MyKey("key2"), new MyValue("value2")); + + myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); + myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); + + myDataCache.DeleteWhere((k, v) => k.Key.StartsWith("key")); + myDataCache.Commit(); + myDataCache.TryGet(new MyKey("key1")).Should().BeNull(); + myDataCache.TryGet(new MyKey("key2")).Should().BeNull(); + myDataCache.InnerDict.ContainsKey(new MyKey("key1")).Should().BeFalse(); + myDataCache.InnerDict.ContainsKey(new MyKey("key2")).Should().BeFalse(); + } + + [TestMethod] + public void TestFind() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); + myDataCache.Add(new MyKey("key2"), new MyValue("value2")); + + myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); + 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")); + items.Count().Should().Be(1); + + items = myDataCache.Find(new MyKey("key5").ToArray()); + items.Count().Should().Be(0); + } + + [TestMethod] + public void TestGetChangeSet() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); // trackable.State = TrackState.Added + myDataCache.Add(new MyKey("key2"), new MyValue("value2")); // trackable.State = TrackState.Added + + myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); + myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); + myDataCache.Delete(new MyKey("key3")); // trackable.State = TrackState.Deleted + myDataCache.Delete(new MyKey("key4")); // trackable.State = TrackState.Deleted + + var items = myDataCache.GetChangeSet(); + int i = 0; + foreach (var item in items) + { + i++; + item.Key.Should().Be(new MyKey("key" + i)); + item.Item.Should().Be(new MyValue("value" + i)); + } + i.Should().Be(4); + } + + [TestMethod] + public void TestGetAndChange() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); // trackable.State = TrackState.Added + myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); + myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); + myDataCache.Delete(new MyKey("key3")); // trackable.State = TrackState.Deleted + + myDataCache.GetAndChange(new MyKey("key1"), () => new MyValue("value_bk_1")).Should().Be(new MyValue("value1")); + myDataCache.GetAndChange(new MyKey("key2"), () => new MyValue("value_bk_2")).Should().Be(new MyValue("value2")); + myDataCache.GetAndChange(new MyKey("key3"), () => new MyValue("value_bk_3")).Should().Be(new MyValue("value_bk_3")); + myDataCache.GetAndChange(new MyKey("key4"), () => new MyValue("value_bk_4")).Should().Be(new MyValue("value_bk_4")); + } + + [TestMethod] + public void TestGetOrAdd() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); // trackable.State = TrackState.Added + myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); + myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); + myDataCache.Delete(new MyKey("key3")); // trackable.State = TrackState.Deleted + + myDataCache.GetOrAdd(new MyKey("key1"), () => new MyValue("value_bk_1")).Should().Be(new MyValue("value1")); + myDataCache.GetOrAdd(new MyKey("key2"), () => new MyValue("value_bk_2")).Should().Be(new MyValue("value2")); + myDataCache.GetOrAdd(new MyKey("key3"), () => new MyValue("value_bk_3")).Should().Be(new MyValue("value_bk_3")); + myDataCache.GetOrAdd(new MyKey("key4"), () => new MyValue("value_bk_4")).Should().Be(new MyValue("value_bk_4")); + } + + [TestMethod] + public void TestTryGet() + { + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); // trackable.State = TrackState.Added + myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); + myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); + myDataCache.Delete(new MyKey("key3")); // trackable.State = TrackState.Deleted + + myDataCache.TryGet(new MyKey("key1")).Should().Be(new MyValue("value1")); + myDataCache.TryGet(new MyKey("key2")).Should().Be(new MyValue("value2")); + myDataCache.TryGet(new MyKey("key3")).Should().BeNull(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Caching/UT_FifoSet.cs b/neo.UnitTests/IO/Caching/UT_FifoSet.cs new file mode 100644 index 0000000000..8242572e86 --- /dev/null +++ b/neo.UnitTests/IO/Caching/UT_FifoSet.cs @@ -0,0 +1,120 @@ +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_FIFOSet + { + [TestMethod] + public void FIFOSetTest() + { + var a = UInt256.Zero; + var b = new UInt256(); + var c = new UInt256(new byte[32] { + 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 + }); + + var set = new FIFOSet(3); + + Assert.IsTrue(set.Add(a)); + Assert.IsFalse(set.Add(a)); + Assert.IsFalse(set.Add(b)); + Assert.IsTrue(set.Add(c)); + + CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a, c }); + + var d = new UInt256(new byte[32] { + 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, 0x02 + }); + + // Testing Fifo max size + Assert.IsTrue(set.Add(d)); + CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a, c, d }); + + var e = new UInt256(new byte[32] { + 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, 0x03 + }); + + Assert.IsTrue(set.Add(e)); + Assert.IsFalse(set.Add(e)); + CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { c, d, e }); + } + + [TestMethod] + public void TestConstructor() + { + Action action1 = () => new FIFOSet(-1); + action1.ShouldThrow(); + + Action action2 = () => new FIFOSet(1,-1); + action2.ShouldThrow(); + + Action action3 = () => new FIFOSet(1,2); + action3.ShouldThrow(); + } + + [TestMethod] + public void TestAdd() + { + var a = new UInt256(new byte[32] { + 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 + }); + var b = new UInt256(new byte[32] { + 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, 0x02 + }); + var set = new FIFOSet(1, 1); + set.Add(a); + set.Add(b); + CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { b }); + } + + [TestMethod] + public void TestExceptWith() + { + var a = new UInt256(new byte[32] { + 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 + }); + var b = new UInt256(new byte[32] { + 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, 0x02 + }); + var c = new UInt256(new byte[32] { + 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, 0x03 + }); + + var set = new FIFOSet(10); + set.Add(a); + set.Add(b); + set.Add(c); + set.ExceptWith(new UInt256[] { b, c }); + CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a }); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs b/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs new file mode 100644 index 0000000000..368e866f54 --- /dev/null +++ b/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs @@ -0,0 +1,76 @@ +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(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs b/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs new file mode 100644 index 0000000000..94d7b70ce1 --- /dev/null +++ b/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs @@ -0,0 +1,87 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Caching; + +namespace Neo.UnitTests.IO.Caching +{ + [TestClass] + public class UT_OrderedDictionary + { + private OrderedDictionary od; + + [TestInitialize] + public void SetUp() + { + od = new OrderedDictionary(); + od.Add("a", 1); + od.Add("b", 2); + od.Add("c", 3); + } + + [TestMethod] + public void TestClear() + { + od.Clear(); + od.Count.Should().Be(0); + od.TryGetValue("a", out uint i).Should().BeFalse(); + } + + [TestMethod] + public void TestCount() + { + od.Count.Should().Be(3); + od.Add("d", 4); + od.Count.Should().Be(4); + } + + [TestMethod] + public void TestIsReadOnly() + { + od.IsReadOnly.Should().BeFalse(); + } + + [TestMethod] + public void TestSetAndGetItem() + { + var val = od["a"]; + val.Should().Be(1); + od["d"] = 10; + od["d"].Should().Be(10); + od["d"] = 15; + od["d"].Should().Be(15); + } + + [TestMethod] + public void TestGetKeys() + { + var keys = od.Keys; + keys.Contains("a").Should().BeTrue(); + keys.Count.Should().Be(3); + } + + [TestMethod] + public void TestGetValues() + { + var values = od.Values; + values.Contains(1).Should().BeTrue(); + values.Count.Should().Be(3); + } + + [TestMethod] + public void TestRemove() + { + od.Remove("a"); + od.Count.Should().Be(2); + od.ContainsKey("a").Should().BeFalse(); + } + + [TestMethod] + public void TestTryGetValue() + { + od.TryGetValue("a", out uint i).Should().BeTrue(); + i.Should().Be(1); + od.TryGetValue("d", out uint j).Should().BeFalse(); + j.Should().Be(0); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Json/UT_JArray.cs b/neo.UnitTests/IO/Json/UT_JArray.cs new file mode 100644 index 0000000000..7e6e81c8d3 --- /dev/null +++ b/neo.UnitTests/IO/Json/UT_JArray.cs @@ -0,0 +1,249 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using FluentAssertions; +using System; +using System.Linq; +using System.Collections; + +namespace Neo.UnitTests.IO.Json +{ + enum Foo + { + male, + female + } + + [TestClass] + public class UT_JArray + { + private JObject alice; + private JObject bob; + + [TestInitialize] + public void SetUp() + { + alice = new JObject(); + alice["name"] = "alice"; + alice["age"] = 30; + alice["score"] = 100.001; + alice["gender"] = Foo.female; + alice["isMarried"] = true; + var pet1 = new JObject(); + pet1["name"] = "Tom"; + pet1["type"] = "cat"; + alice["pet"] = pet1; + + bob = new JObject(); + bob["name"] = "bob"; + bob["age"] = 100000; + bob["score"] = 0.001; + bob["gender"] = Foo.male; + bob["isMarried"] = false; + var pet2 = new JObject(); + pet2["name"] = "Paul"; + pet2["type"] = "dog"; + bob["pet"] = pet2; + } + + [TestMethod] + public void TestAdd() + { + var jArray = new JArray(); + jArray.Add(alice); + jArray.Add(bob); + var jAlice = jArray[0]; + var jBob = jArray[1]; + jAlice["name"].ToString().Should().Be(alice["name"].ToString()); + jAlice["age"].ToString().Should().Be(alice["age"].ToString()); + jAlice["score"].ToString().Should().Be(alice["score"].ToString()); + jAlice["gender"].ToString().Should().Be(alice["gender"].ToString()); + jAlice["isMarried"].ToString().Should().Be(alice["isMarried"].ToString()); + jAlice["pet"].ToString().Should().Be(alice["pet"].ToString()); + jBob["name"].ToString().Should().Be(bob["name"].ToString()); + jBob["age"].ToString().Should().Be(bob["age"].ToString()); + jBob["score"].ToString().Should().Be(bob["score"].ToString()); + jBob["gender"].ToString().Should().Be(bob["gender"].ToString()); + jBob["isMarried"].ToString().Should().Be(bob["isMarried"].ToString()); + jBob["pet"].ToString().Should().Be(bob["pet"].ToString()); + } + + [TestMethod] + public void TestSetItem() + { + var jArray = new JArray + { + alice + }; + jArray[0] = bob; + Assert.AreEqual(jArray[0], bob); + + Action action = () => jArray[1] = alice; + action.ShouldThrow(); + } + + [TestMethod] + public void TestClear() + { + var jArray = new JArray + { + alice + }; + var jAlice = jArray[0]; + jAlice["name"].ToString().Should().Be(alice["name"].ToString()); + jAlice["age"].ToString().Should().Be(alice["age"].ToString()); + jAlice["score"].ToString().Should().Be(alice["score"].ToString()); + jAlice["gender"].ToString().Should().Be(alice["gender"].ToString()); + jAlice["isMarried"].ToString().Should().Be(alice["isMarried"].ToString()); + jAlice["pet"].ToString().Should().Be(alice["pet"].ToString()); + + jArray.Clear(); + Action action = () => jArray[0].ToString(); + action.ShouldThrow(); + } + + [TestMethod] + public void TestContains() + { + var jArray = new JArray(); + jArray.Add(alice); + + jArray.Contains(alice).Should().BeTrue(); + jArray.Contains(bob).Should().BeFalse(); + } + + [TestMethod] + public void TestCopyTo() + { + var jArray = new JArray + { + alice, + bob + }; + + JObject[] jObjects1 = new JObject[2]; + jArray.CopyTo(jObjects1, 0); + var jAlice1 = jObjects1[0]; + var jBob1 = jObjects1[1]; + Assert.AreEqual(alice, jAlice1); + Assert.AreEqual(bob, jBob1); + + JObject[] jObjects2 = new JObject[4]; + jArray.CopyTo(jObjects2, 2); + var jAlice2 = jObjects2[2]; + var jBob2 = jObjects2[3]; + jObjects2[0].Should().BeNull(); + jObjects2[1].Should().BeNull(); + Assert.AreEqual(alice, jAlice2); + Assert.AreEqual(bob, jBob2); + } + + [TestMethod] + public void TestInsert() + { + var jArray = new JArray + { + alice, + alice, + alice, + alice + }; + + jArray.Insert(1, bob); + jArray.Count().Should().Be(5); + jArray[0].Should().Be(alice); + jArray[1].Should().Be(bob); + jArray[2].Should().Be(alice); + + jArray.Insert(5, bob); + jArray.Count().Should().Be(6); + jArray[5].Should().Be(bob); + } + + [TestMethod] + public void TestIndexOf() + { + var jArray = new JArray(); + jArray.IndexOf(alice).Should().Be(-1); + + jArray.Add(alice); + jArray.Add(alice); + jArray.Add(alice); + jArray.Add(alice); + jArray.IndexOf(alice).Should().Be(0); + + jArray.Insert(1, bob); + jArray.IndexOf(bob).Should().Be(1); + } + + [TestMethod] + public void TestIsReadOnly() + { + var jArray = new JArray(); + jArray.IsReadOnly.Should().BeFalse(); + } + + [TestMethod] + public void TestRemove() + { + var jArray = new JArray + { + alice + }; + jArray.Count().Should().Be(1); + jArray.Remove(alice); + jArray.Count().Should().Be(0); + + jArray.Add(alice); + jArray.Add(alice); + jArray.Count().Should().Be(2); + jArray.Remove(alice); + jArray.Count().Should().Be(1); + } + + [TestMethod] + public void TestRemoveAt() + { + var jArray = new JArray + { + alice, + bob, + alice + }; + jArray.RemoveAt(1); + jArray.Count().Should().Be(2); + jArray.Contains(bob).Should().BeFalse(); + } + + [TestMethod] + public void TestGetEnumerator() + { + var jArray = new JArray + { + alice, + bob, + alice, + bob + }; + int i = 0; + foreach (var item in jArray) + { + if (i % 2 == 0) item.Should().Be(alice); + if (i % 2 != 0) item.Should().Be(bob); + i++; + } + Assert.IsNotNull(((IEnumerable)jArray).GetEnumerator()); + } + + [TestMethod] + public void TestAsString() + { + var jArray = new JArray + { + alice, + bob, + }; + var s = jArray.AsString(); + Assert.AreEqual(s, "{\"name\":\"alice\",\"age\":30,\"score\":100.001,\"gender\":\"female\",\"isMarried\":true,\"pet\":{\"name\":\"Tom\",\"type\":\"cat\"}},{\"name\":\"bob\",\"age\":100000,\"score\":0.001,\"gender\":\"male\",\"isMarried\":false,\"pet\":{\"name\":\"Paul\",\"type\":\"dog\"}}"); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Json/UT_JBoolean.cs b/neo.UnitTests/IO/Json/UT_JBoolean.cs new file mode 100644 index 0000000000..cb44570e45 --- /dev/null +++ b/neo.UnitTests/IO/Json/UT_JBoolean.cs @@ -0,0 +1,77 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using System; +using System.IO; + +namespace Neo.UnitTests.IO.Json +{ + [TestClass] + public class UT_JBoolean + { + private JBoolean jFalse; + private JBoolean jTrue; + + [TestInitialize] + public void SetUp() + { + jFalse = new JBoolean(); + jTrue = new JBoolean(true); + } + + [TestMethod] + public void TestAsNumber() + { + jFalse.AsNumber().Should().Be(0); + jTrue.AsNumber().Should().Be(1); + } + + [TestMethod] + public void TestParse() + { + TextReader tr1 = new StringReader("true"); + JBoolean ret1 = JBoolean.Parse(tr1); + ret1.AsBoolean().Should().BeTrue(); + + TextReader tr2 = new StringReader("false"); + JBoolean ret2 = JBoolean.Parse(tr2); + ret2.AsBoolean().Should().BeFalse(); + + TextReader tr3 = new StringReader("aaa"); + Action action = () => JBoolean.Parse(tr3); + action.ShouldThrow(); + } + + [TestMethod] + public void TestParseFalse() + { + TextReader tr1 = new StringReader("false"); + JBoolean ret1 = JBoolean.ParseFalse(tr1); + ret1.AsBoolean().Should().BeFalse(); + + TextReader tr2 = new StringReader("aaa"); + Action action = () => JBoolean.ParseFalse(tr2); + action.ShouldThrow(); + + TextReader tr3 = new StringReader("\t\rfalse"); + JBoolean ret3 = JBoolean.ParseFalse(tr3); + ret3.AsBoolean().Should().BeFalse(); + } + + [TestMethod] + public void TestParseTrue() + { + TextReader tr1 = new StringReader("true"); + JBoolean ret1 = JBoolean.ParseTrue(tr1); + ret1.AsBoolean().Should().BeTrue(); + + TextReader tr2 = new StringReader("aaa"); + Action action = () => JBoolean.ParseTrue(tr2); + action.ShouldThrow(); + + TextReader tr3 = new StringReader(" true"); + JBoolean ret3 = JBoolean.ParseTrue(tr3); + ret3.AsBoolean().Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Json/UT_JNumber.cs b/neo.UnitTests/IO/Json/UT_JNumber.cs new file mode 100644 index 0000000000..7232e8b969 --- /dev/null +++ b/neo.UnitTests/IO/Json/UT_JNumber.cs @@ -0,0 +1,67 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using System; +using System.IO; + +namespace Neo.UnitTests.IO.Json +{ + enum Woo + { + Tom, + Jerry, + James + } + + [TestClass] + public class UT_JNumber + { + private JNumber maxInt; + private JNumber minInt; + private JNumber zero; + + [TestInitialize] + public void SetUp() + { + maxInt = new JNumber(JNumber.MAX_SAFE_INTEGER); + minInt = new JNumber(JNumber.MIN_SAFE_INTEGER); + zero = new JNumber(); + } + + [TestMethod] + public void TestAsBoolean() + { + maxInt.AsBoolean().Should().BeTrue(); + zero.AsBoolean().Should().BeFalse(); + } + + [TestMethod] + public void TestAsString() + { + Action action1 = () => new JNumber(double.PositiveInfinity).AsString(); + action1.ShouldThrow(); + + Action action2 = () => new JNumber(double.NegativeInfinity).AsString(); + action2.ShouldThrow(); + } + + [TestMethod] + public void TestTryGetEnum() + { + zero.TryGetEnum().Should().Be(Woo.Tom); + new JNumber(1).TryGetEnum().Should().Be(Woo.Jerry); + new JNumber(2).TryGetEnum().Should().Be(Woo.James); + new JNumber(3).TryGetEnum().Should().Be(Woo.Tom); + } + + [TestMethod] + public void TestParse() + { + Action action1 = () => JNumber.Parse(new StringReader("100.a")); + action1.ShouldThrow(); + + Action action2 = () => JNumber.Parse(new StringReader("100.+")); + action2.ShouldThrow(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Json/UT_JObject.cs b/neo.UnitTests/IO/Json/UT_JObject.cs new file mode 100644 index 0000000000..38781ed96a --- /dev/null +++ b/neo.UnitTests/IO/Json/UT_JObject.cs @@ -0,0 +1,119 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using System; +using System.IO; +using System.Reflection; + +namespace Neo.UnitTests.IO.Json +{ + [TestClass] + public class UT_JObject + { + private JObject alice; + private JObject bob; + + [TestInitialize] + public void SetUp() + { + alice = new JObject(); + alice["name"] = "alice"; + alice["age"] = 30; + alice["score"] = 100.001; + alice["gender"] = Foo.female; + alice["isMarried"] = true; + var pet1 = new JObject(); + pet1["name"] = "Tom"; + pet1["type"] = "cat"; + alice["pet"] = pet1; + + bob = new JObject(); + bob["name"] = "bob"; + bob["age"] = 100000; + bob["score"] = 0.001; + bob["gender"] = Foo.male; + bob["isMarried"] = false; + var pet2 = new JObject(); + pet2["name"] = "Paul"; + pet2["type"] = "dog"; + bob["pet"] = pet2; + } + + [TestMethod] + public void TestAsBoolean() + { + alice.AsBoolean().Should().BeTrue(); + } + + [TestMethod] + public void TestAsNumber() + { + alice.AsNumber().Should().Be(double.NaN); + } + + [TestMethod] + public void TestParse() + { + Action action = () => JObject.Parse(new StringReader(""), -1); + action.ShouldThrow(); + } + + [TestMethod] + public void TestParseNull() + { + Action action = () => JObject.Parse(new StringReader("naaa")); + action.ShouldThrow(); + + JObject.Parse(new StringReader("null")).Should().BeNull(); + } + + [TestMethod] + public void TestParseObject() + { + Action action1 = () => JObject.Parse(new StringReader("{\"k1\":\"v1\",\"k1\":\"v2\"}"), 100); + action1.ShouldThrow(); + + Action action2 = () => JObject.Parse(new StringReader("{\"k1\",\"k1\"}"), 100); + action2.ShouldThrow(); + + Action action3 = () => JObject.Parse(new StringReader("{\"k1\":\"v1\""), 100); + action3.ShouldThrow(); + + Action action4 = () => JObject.Parse(new StringReader("aaa"), 100); + action4.ShouldThrow(); + + JObject.Parse(new StringReader("{\"k1\":\"v1\"}"), 100).ToString().Should().Be("{\"k1\":\"v1\"}"); + } + + [TestMethod] + public void TestTryGetEnum() + { + alice.TryGetEnum().Should().Be(Woo.Tom); + } + + [TestMethod] + public void TestOpImplicitEnum() + { + var obj = new JObject(); + obj = Woo.Tom; + obj.AsString().Should().Be("Tom"); + } + + [TestMethod] + public void TestOpImplicitString() + { + var obj = new JObject(); + obj = null; + obj.Should().BeNull(); + + obj = "{\"aaa\":\"111\"}"; + obj.AsString().Should().Be("{\"aaa\":\"111\"}"); + } + + [TestMethod] + public void TestGetNull() + { + JObject.Null.Should().BeNull(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Json/UT_JString.cs b/neo.UnitTests/IO/Json/UT_JString.cs new file mode 100644 index 0000000000..07f231674f --- /dev/null +++ b/neo.UnitTests/IO/Json/UT_JString.cs @@ -0,0 +1,73 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.SmartContract; +using System; +using System.IO; + +namespace Neo.UnitTests.IO +{ + [TestClass] + public class UT_JString + { + [TestMethod] + public void TestConstructor() + { + String s = "hello world"; + JString jstring = new JString(s); + Assert.AreEqual(s, jstring.Value); + Assert.ThrowsException(() => new JString(null)); + } + + [TestMethod] + public void TestAsBoolean() + { + String s1 = "hello world"; + String s2 = ""; + JString jstring1 = new JString(s1); + JString jstring2 = new JString(s2); + Assert.AreEqual(true, jstring1.AsBoolean()); + Assert.AreEqual(false, jstring2.AsBoolean()); + } + + [TestMethod] + public void TestParse() + { + TextReader tr = new StringReader("\"hello world\""); + String s = JString.Parse(tr).Value; + Assert.AreEqual("hello world", s); + + tr = new StringReader("hello world"); + Assert.ThrowsException(() => JString.Parse(tr)); + + tr = new StringReader("\"\\s\""); + Assert.ThrowsException(() => JString.Parse(tr)); + + tr = new StringReader("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\""); + s = JString.Parse(tr).Value; + Assert.AreEqual("\"\\/\b\f\n\r\t", s); + + tr = new StringReader("\"\\u0030\""); + s = JString.Parse(tr).Value; + Assert.AreEqual("0", s); + + tr = new StringReader("\"a"); + Assert.ThrowsException(() => JString.Parse(tr)); + + byte[] byteArray = new byte[] { 0x22, 0x01, 0x22 }; + tr = new StringReader(System.Text.Encoding.ASCII.GetString(byteArray)); + Assert.ThrowsException(() => JString.Parse(tr)); + } + + [TestMethod] + public void TestTryGetEnum() + { + JString s = new JString("Signature"); + ContractParameterType cp = s.TryGetEnum(ContractParameterType.Void, false); + Assert.AreEqual(ContractParameterType.Signature, cp); + + s = new JString(""); + cp = s.TryGetEnum(ContractParameterType.Void, false); + Assert.AreEqual(ContractParameterType.Void, cp); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/UT_IOHelper.cs b/neo.UnitTests/IO/UT_IOHelper.cs new file mode 100644 index 0000000000..e113ce8cd4 --- /dev/null +++ b/neo.UnitTests/IO/UT_IOHelper.cs @@ -0,0 +1,549 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Neo.UnitTests.IO +{ + [TestClass] + public class UT_IOHelper + { + [TestMethod] + public void TestAsSerializableGeneric() + { + byte[] caseArray = new byte[] { 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00 }; + UInt160 result = Neo.IO.Helper.AsSerializable(caseArray); + Assert.AreEqual(UInt160.Zero, result); + } + + [TestMethod] + public void TestAsSerializable() + { + for (int i = 0; i < 2; i++) + { + if (i == 0) + { + byte[] caseArray = new byte[] { 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00 }; + ISerializable result = Neo.IO.Helper.AsSerializable(caseArray, typeof(UInt160)); + Assert.AreEqual(UInt160.Zero, result); + } + else + { + Action action = () => Neo.IO.Helper.AsSerializable(new byte[0], typeof(Double)); + action.ShouldThrow(); + } + } + } + + [TestMethod] + public void TestAsSerializableArray() + { + byte[] byteArray = Neo.IO.Helper.ToByteArray(new UInt160[] { UInt160.Zero }); + UInt160[] result = Neo.IO.Helper.AsSerializableArray(byteArray); + Assert.AreEqual(1, result.Length); + Assert.AreEqual(UInt160.Zero, result[0]); + } + + [TestMethod] + public void TestGetVarSizeInt() + { + for (int i = 0; i < 3; i++) + { + if (i == 0) + { + int result = Neo.IO.Helper.GetVarSize(1); + Assert.AreEqual(1, result); + } + else if (i == 1) + { + int result = Neo.IO.Helper.GetVarSize(0xFFFF); + Assert.AreEqual(3, result); + } + else + { + int result = Neo.IO.Helper.GetVarSize(0xFFFFFF); + Assert.AreEqual(5, result); + } + } + } + enum TestEnum0 : sbyte + { + case1 = 1, case2 = 2 + } + + enum TestEnum1 : byte + { + case1 = 1, case2 = 2 + } + + enum TestEnum2 : short + { + case1 = 1, case2 = 2 + } + + enum TestEnum3 : ushort + { + case1 = 1, case2 = 2 + } + + enum TestEnum4 : int + { + case1 = 1, case2 = 2 + } + + enum TestEnum5 : uint + { + case1 = 1, case2 = 2 + } + + enum TestEnum6 : long + { + case1 = 1, case2 = 2 + } + + [TestMethod] + public void TestGetVarSizeGeneric() + { + for (int i = 0; i < 9; i++) + { + if (i == 0) + { + int result = Neo.IO.Helper.GetVarSize(new UInt160[] { UInt160.Zero }); + Assert.AreEqual(21, result); + } + else if (i == 1)//sbyte + { + List initList = new List + { + TestEnum0.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = Neo.IO.Helper.GetVarSize(testList); + Assert.AreEqual(2, result); + } + else if (i == 2)//byte + { + List initList = new List + { + TestEnum1.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = Neo.IO.Helper.GetVarSize(testList); + Assert.AreEqual(2, result); + } + else if (i == 3)//short + { + List initList = new List + { + TestEnum2.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = Neo.IO.Helper.GetVarSize(testList); + Assert.AreEqual(3, result); + } + else if (i == 4)//ushort + { + List initList = new List + { + TestEnum3.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = Neo.IO.Helper.GetVarSize(testList); + Assert.AreEqual(3, result); + } + else if (i == 5)//int + { + List initList = new List + { + TestEnum4.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = Neo.IO.Helper.GetVarSize(testList); + Assert.AreEqual(5, result); + } + else if (i == 6)//uint + { + List initList = new List + { + TestEnum5.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = Neo.IO.Helper.GetVarSize(testList); + Assert.AreEqual(5, result); + } + else if (i == 7)//long + { + List initList = new List + { + TestEnum6.case1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = Neo.IO.Helper.GetVarSize(testList); + Assert.AreEqual(9, result); + } + else if (i == 8) + { + List initList = new List + { + 1 + }; + IReadOnlyCollection testList = initList.AsReadOnly(); + int result = Neo.IO.Helper.GetVarSize(testList); + Assert.AreEqual(5, result); + } + } + } + + [TestMethod] + public void TestGetVarSizeString() + { + int result = Neo.IO.Helper.GetVarSize("AA"); + Assert.AreEqual(3, result); + } + + [TestMethod] + public void TestReadBytesWithGrouping() + { + for (int i = 0; i < 2; i++) + { + if (i == 0) + { + byte[] caseArray = new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA}; + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteBytesWithGrouping(writer, caseArray); + stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(stream); + byte[] result = Neo.IO.Helper.ReadBytesWithGrouping(reader); + Assert.AreEqual(Encoding.Default.GetString(caseArray), Encoding.Default.GetString(result)); + } + else + { + byte[] caseArray = new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x11}; + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + writer.Write(caseArray); + stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(stream); + Action action = () => Neo.IO.Helper.ReadBytesWithGrouping(reader); + action.ShouldThrow(); + } + } + } + + [TestMethod] + public void TestReadFixedString() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteFixedString(writer, "AA", Encoding.UTF8.GetBytes("AA").Length + 1); + stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(stream); + string result = Neo.IO.Helper.ReadFixedString(reader, Encoding.UTF8.GetBytes("AA").Length + 1); + Assert.AreEqual("AA", result); + } + + [TestMethod] + public void TestReadSerializable() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.Write(writer, UInt160.Zero); + stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(stream); + UInt160 result = Neo.IO.Helper.ReadSerializable(reader); + Assert.AreEqual(UInt160.Zero, result); + } + + [TestMethod] + public void TestReadSerializableArray() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.Write(writer, new UInt160[] { UInt160.Zero }); + stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(stream); + UInt160[] resultArray = Neo.IO.Helper.ReadSerializableArray(reader); + Assert.AreEqual(1, resultArray.Length); + Assert.AreEqual(UInt160.Zero, resultArray[0]); + } + + [TestMethod] + public void TestReadVarBytes() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarBytes(writer, new byte[] { 0xAA, 0xAA }); + stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(stream); + byte[] byteArray = Neo.IO.Helper.ReadVarBytes(reader, 10); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0xAA, 0xAA }), Encoding.Default.GetString(byteArray)); + } + + [TestMethod] + public void TestReadVarInt() + { + for (int i = 0; i < 4; i++) + { + if (i == 0) + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarInt(writer, 0xFFFF); + stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(stream); + ulong result = Neo.IO.Helper.ReadVarInt(reader, 0xFFFF); + Assert.AreEqual((ulong)0xFFFF, result); + } + else if (i == 1) + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarInt(writer, 0xFFFFFFFF); + stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(stream); + ulong result = Neo.IO.Helper.ReadVarInt(reader, 0xFFFFFFFF); + Assert.AreEqual(0xFFFFFFFF, result); + } + else + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarInt(writer, 0xFFFFFFFFFF); + stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(stream); + Action action = () => Neo.IO.Helper.ReadVarInt(reader, 0xFFFFFFFF); + action.ShouldThrow(); + } + } + } + + [TestMethod] + public void TestReadVarString() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarString(writer, "AAAAAAA"); + stream.Seek(0, SeekOrigin.Begin); + BinaryReader reader = new BinaryReader(stream); + string result = Neo.IO.Helper.ReadVarString(reader, 10); + stream.Seek(0, SeekOrigin.Begin); + Assert.AreEqual("AAAAAAA", result); + } + + [TestMethod] + public void TestToArray() + { + byte[] byteArray = Neo.IO.Helper.ToArray(UInt160.Zero); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00}), Encoding.Default.GetString(byteArray)); + } + + [TestMethod] + public void TestToByteArrayGeneric() + { + byte[] byteArray = Neo.IO.Helper.ToByteArray(new UInt160[] { UInt160.Zero }); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00}), Encoding.Default.GetString(byteArray)); + } + + [TestMethod] + public void TestWrite() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.Write(writer, UInt160.Zero); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00}), Encoding.Default.GetString(byteArray)); + } + + [TestMethod] + public void TestWriteGeneric() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.Write(writer, new UInt160[] { UInt160.Zero }); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00}), Encoding.Default.GetString(byteArray)); + } + + + [TestMethod] + public void TestWriteBytesWithGrouping() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteBytesWithGrouping(writer, new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA}); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x0C}), Encoding.Default.GetString(byteArray)); + } + + [TestMethod] + public void TestWriteFixedString() + { + for (int i = 0; i < 5; i++) + { + if (i == 0) + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Action action = () => Neo.IO.Helper.WriteFixedString(writer, null, 0); + action.ShouldThrow(); + } + else if (i == 1) + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Action action = () => Neo.IO.Helper.WriteFixedString(writer, "AA", Encoding.UTF8.GetBytes("AA").Length - 1); + action.ShouldThrow(); + } + else if (i == 2) + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Action action = () => Neo.IO.Helper.WriteFixedString(writer, "拉拉", Encoding.UTF8.GetBytes("拉拉").Length - 1); + action.ShouldThrow(); + } + else if (i == 3) + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteFixedString(writer, "AA", Encoding.UTF8.GetBytes("AA").Length + 1); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + byte[] newArray = new byte[Encoding.UTF8.GetBytes("AA").Length + 1]; + Encoding.UTF8.GetBytes("AA").CopyTo(newArray, 0); + Assert.AreEqual(Encoding.Default.GetString(newArray), Encoding.Default.GetString(byteArray)); + } + } + } + + [TestMethod] + public void TestWriteVarBytes() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarBytes(writer, new byte[] { 0xAA }); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01, 0xAA }), Encoding.Default.GetString(byteArray)); + } + + [TestMethod] + public void TestWriteVarInt() + { + for (int i = 0; i < 5; i++) + { + if (i == 0) + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Action action = () => Neo.IO.Helper.WriteVarInt(writer, -1); + action.ShouldThrow(); + } + else if (i == 1) + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarInt(writer, 0xFC); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + Assert.AreEqual(0xFC, byteArray[0]); + } + else if (i == 2) + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarInt(writer, 0xFFFF); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + Assert.AreEqual(0xFD, byteArray[0]); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0xFF, 0xFF }), Encoding.Default.GetString(byteArray.Skip(1).Take(byteArray.Length - 1).ToArray())); + } + else if (i == 3) + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarInt(writer, 0xFFFFFFFF); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + Assert.AreEqual(0xFE, byteArray[0]); + Assert.AreEqual(0xFFFFFFFF, BitConverter.ToUInt32(byteArray, 1)); + } + else + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarInt(writer, 0xAEFFFFFFFF); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + Assert.AreEqual(0xFF, byteArray[0]); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00 }), Encoding.Default.GetString(byteArray.Skip(1).Take(byteArray.Length - 1).ToArray())); + } + } + } + + [TestMethod] + public void TestWriteVarString() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + Neo.IO.Helper.WriteVarString(writer, "a"); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + Assert.AreEqual(0x01, byteArray[0]); + Assert.AreEqual(0x61, byteArray[1]); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs b/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs new file mode 100644 index 0000000000..3f4f0c6d06 --- /dev/null +++ b/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs @@ -0,0 +1,50 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Wrappers; +using System.IO; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_SerializableWrapper + { + [TestMethod] + public void TestGetSize() + { + SerializableWrapper temp = new UInt32Wrapper(); + Assert.AreEqual(4, temp.Size); + } + + [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 UInt32Wrapper(); + temp.Deserialize(reader); + Assert.AreEqual(true, temp.Equals(1)); + } + + [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 UInt32Wrapper(); + 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 UInt32Wrapper(); + temp2.Deserialize(reader2); + Assert.AreEqual(true, temp.Equals(temp2)); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs b/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs new file mode 100644 index 0000000000..7762c56b7c --- /dev/null +++ b/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs @@ -0,0 +1,97 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Wrappers; +using System.IO; +using System.Text; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_UInt32Wrapper + { + [TestMethod] + public void TestGetSize() + { + UInt32Wrapper temp = new UInt32Wrapper(); + Assert.AreEqual(4, temp.Size); + } + + [TestMethod] + public void TestDeserialize() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + BinaryReader reader = new BinaryReader(stream); + writer.Write(new byte[] { 0x00, 0x00, 0x00, 0x01 }); + stream.Seek(0, SeekOrigin.Begin); + UInt32Wrapper temp = new UInt32Wrapper(); + temp.Deserialize(reader); + MemoryStream stream2 = new MemoryStream(); + BinaryWriter writer2 = new BinaryWriter(stream2); + temp.Serialize(writer2); + stream2.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream2.Length]; + stream2.Read(byteArray, 0, (int)stream2.Length); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00, 0x00, 0x00, 0x01 }), Encoding.Default.GetString(byteArray)); + } + + [TestMethod] + public void TestSerialize() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + BinaryReader reader = new BinaryReader(stream); + writer.Write(new byte[] { 0x00, 0x00, 0x00, 0x01 }); + stream.Seek(0, SeekOrigin.Begin); + UInt32Wrapper temp = new UInt32Wrapper(); + temp.Deserialize(reader); + MemoryStream stream2 = new MemoryStream(); + BinaryWriter writer2 = new BinaryWriter(stream2); + temp.Serialize(writer2); + stream2.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream2.Length]; + stream2.Read(byteArray, 0, (int)stream2.Length); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00, 0x00, 0x00, 0x01 }), Encoding.Default.GetString(byteArray)); + } + + [TestMethod] + public void TestEqualsUInt32Wrapper() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + BinaryReader reader = new BinaryReader(stream); + writer.Write((uint)1); + stream.Seek(0, SeekOrigin.Begin); + UInt32Wrapper temp = new UInt32Wrapper(); + 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); + UInt32Wrapper temp2 = new UInt32Wrapper(); + temp2.Deserialize(reader2); + Assert.AreEqual(true, temp.Equals(temp2)); + } + + [TestMethod] + public void TestOperatorUint() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + BinaryReader reader = new BinaryReader(stream); + writer.Write((uint)1); + stream.Seek(0, SeekOrigin.Begin); + UInt32Wrapper temp = new UInt32Wrapper(); + temp.Deserialize(reader); + uint result = temp; + Assert.AreEqual((uint)1, result); + } + + [TestMethod] + public void TestOperatorUInt32Wrapper() + { + UInt32Wrapper temp = 1; + Assert.AreEqual(true, temp.Equals(1)); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/UT_MemoryPool.cs b/neo.UnitTests/Ledger/UT_MemoryPool.cs similarity index 99% rename from neo.UnitTests/UT_MemoryPool.cs rename to neo.UnitTests/Ledger/UT_MemoryPool.cs index ee79d96842..1b5af16240 100644 --- a/neo.UnitTests/UT_MemoryPool.cs +++ b/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -8,7 +8,7 @@ using System.Collections.Generic; using System.Linq; -namespace Neo.UnitTests +namespace Neo.UnitTests.Ledger { [TestClass] public class UT_MemoryPool diff --git a/neo.UnitTests/UT_PoolItem.cs b/neo.UnitTests/Ledger/UT_PoolItem.cs similarity index 99% rename from neo.UnitTests/UT_PoolItem.cs rename to neo.UnitTests/Ledger/UT_PoolItem.cs index cedf7f3fa9..d8f4e4f696 100644 --- a/neo.UnitTests/UT_PoolItem.cs +++ b/neo.UnitTests/Ledger/UT_PoolItem.cs @@ -5,7 +5,7 @@ using Neo.Network.P2P.Payloads; using System; -namespace Neo.UnitTests +namespace Neo.UnitTests.Ledger { [TestClass] public class UT_PoolItem diff --git a/neo.UnitTests/UT_StorageItem.cs b/neo.UnitTests/Ledger/UT_StorageItem.cs similarity index 98% rename from neo.UnitTests/UT_StorageItem.cs rename to neo.UnitTests/Ledger/UT_StorageItem.cs index c7372eb455..9afab26372 100644 --- a/neo.UnitTests/UT_StorageItem.cs +++ b/neo.UnitTests/Ledger/UT_StorageItem.cs @@ -5,7 +5,7 @@ using System.IO; using System.Text; -namespace Neo.UnitTests +namespace Neo.UnitTests.Ledger { [TestClass] public class UT_StorageItem diff --git a/neo.UnitTests/UT_StorageKey.cs b/neo.UnitTests/Ledger/UT_StorageKey.cs similarity index 99% rename from neo.UnitTests/UT_StorageKey.cs rename to neo.UnitTests/Ledger/UT_StorageKey.cs index ab718f0f89..7e2ec9bdb2 100644 --- a/neo.UnitTests/UT_StorageKey.cs +++ b/neo.UnitTests/Ledger/UT_StorageKey.cs @@ -3,7 +3,7 @@ using Neo.IO; using Neo.Ledger; -namespace Neo.UnitTests +namespace Neo.UnitTests.Ledger { [TestClass] public class UT_StorageKey diff --git a/neo.UnitTests/UT_Block.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs similarity index 99% rename from neo.UnitTests/UT_Block.cs rename to neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index c91776f0c0..6736d8f936 100644 --- a/neo.UnitTests/UT_Block.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -5,7 +5,7 @@ using System.IO; using System.Text; -namespace Neo.UnitTests +namespace Neo.UnitTests.Network.P2P.Payloads { [TestClass] public class UT_Block diff --git a/neo.UnitTests/UT_Header.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs similarity index 99% rename from neo.UnitTests/UT_Header.cs rename to neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index f07be59051..f9dfd30d1e 100644 --- a/neo.UnitTests/UT_Header.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -4,7 +4,7 @@ using System.IO; using System.Text; -namespace Neo.UnitTests +namespace Neo.UnitTests.Network.P2P.Payloads { [TestClass] public class UT_Header diff --git a/neo.UnitTests/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs similarity index 99% rename from neo.UnitTests/UT_Transaction.cs rename to neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index dc41d3a0d8..2dfa8b4c35 100644 --- a/neo.UnitTests/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -13,7 +13,7 @@ using Neo.Wallets; using Neo.Wallets.NEP6; -namespace Neo.UnitTests +namespace Neo.UnitTests.Network.P2P.Payloads { [TestClass] public class UT_Transaction diff --git a/neo.UnitTests/UT_Witness.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs similarity index 98% rename from neo.UnitTests/UT_Witness.cs rename to neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index 4ca00b6643..ec3525931e 100644 --- a/neo.UnitTests/UT_Witness.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -3,7 +3,7 @@ using Neo.IO.Json; using Neo.Network.P2P.Payloads; -namespace Neo.UnitTests +namespace Neo.UnitTests.Network.P2P.Payloads { [TestClass] public class UT_Witness diff --git a/neo.UnitTests/UT_P2PMessage.cs b/neo.UnitTests/Network/P2P/UT_Message.cs similarity index 98% rename from neo.UnitTests/UT_P2PMessage.cs rename to neo.UnitTests/Network/P2P/UT_Message.cs index 99af976992..4bb37a8166 100644 --- a/neo.UnitTests/UT_P2PMessage.cs +++ b/neo.UnitTests/Network/P2P/UT_Message.cs @@ -6,10 +6,10 @@ using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; -namespace Neo.UnitTests +namespace Neo.UnitTests.Network.P2P { [TestClass] - public class UT_P2PMessage + public class UT_Message { [TestMethod] public void Serialize_Deserialize() diff --git a/neo.UnitTests/UT_ProtocolHandler.cs b/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs similarity index 97% rename from neo.UnitTests/UT_ProtocolHandler.cs rename to neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs index 63bfe90e44..4ea998ef40 100644 --- a/neo.UnitTests/UT_ProtocolHandler.cs +++ b/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs @@ -4,7 +4,7 @@ using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; -namespace Neo.UnitTests +namespace Neo.UnitTests.Network.P2P { [TestClass] public class UT_ProtocolHandler : TestKit diff --git a/neo.UnitTests/UT_ProtocolHandlerMailbox.cs b/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs similarity index 98% rename from neo.UnitTests/UT_ProtocolHandlerMailbox.cs rename to neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs index edd5a4191f..91caf65297 100644 --- a/neo.UnitTests/UT_ProtocolHandlerMailbox.cs +++ b/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs @@ -1,18 +1,13 @@ -using Akka.TestKit; -using Akka.TestKit.Xunit2; +using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using Moq; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.Network.P2P; -using Akka.Configuration; using Neo.IO; -using System.Linq; +using Neo.Network.P2P; +using System; using System.Collections.Generic; +using System.Linq; -namespace Neo.UnitTests +namespace Neo.UnitTests.Network.P2P { [TestClass] public class UT_ProtocolHandlerMailbox : TestKit diff --git a/neo.UnitTests/UT_RemoteNode.cs b/neo.UnitTests/Network/P2P/UT_RemoteNode.cs similarity index 98% rename from neo.UnitTests/UT_RemoteNode.cs rename to neo.UnitTests/Network/P2P/UT_RemoteNode.cs index 60d587ed13..03da2a3db8 100644 --- a/neo.UnitTests/UT_RemoteNode.cs +++ b/neo.UnitTests/Network/P2P/UT_RemoteNode.cs @@ -6,7 +6,7 @@ using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; -namespace Neo.UnitTests +namespace Neo.UnitTests.Network.P2P { [TestClass] [NotReRunnable] diff --git a/neo.UnitTests/UT_RemoteNodeMailbox.cs b/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs similarity index 94% rename from neo.UnitTests/UT_RemoteNodeMailbox.cs rename to neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs index ff3e6e17d9..860012a31e 100644 --- a/neo.UnitTests/UT_RemoteNodeMailbox.cs +++ b/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs @@ -1,11 +1,11 @@ -using System; -using Akka.IO; +using Akka.IO; using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P; +using System; -namespace Neo.UnitTests +namespace Neo.UnitTests.Network.P2P { [TestClass] [NotReRunnable] diff --git a/neo.UnitTests/UT_TaskManagerMailbox.cs b/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs similarity index 93% rename from neo.UnitTests/UT_TaskManagerMailbox.cs rename to neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs index 2ba4ff2d85..6d014cd9c7 100644 --- a/neo.UnitTests/UT_TaskManagerMailbox.cs +++ b/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs @@ -1,15 +1,11 @@ -using Akka.TestKit; -using Akka.TestKit.Xunit2; +using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using Moq; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; using Neo.Network.P2P; -using Akka.Configuration; +using Neo.Network.P2P.Payloads; +using System; -namespace Neo.UnitTests +namespace Neo.UnitTests.Network.P2P { [TestClass] public class UT_TaskManagerMailbox : TestKit diff --git a/neo.UnitTests/UT_ConcatenatedIterator.cs b/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs similarity index 97% rename from neo.UnitTests/UT_ConcatenatedIterator.cs rename to neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs index 7051f5f4f8..51db50ea31 100644 --- a/neo.UnitTests/UT_ConcatenatedIterator.cs +++ b/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs @@ -4,7 +4,7 @@ using Neo.VM.Types; using System.Numerics; -namespace Neo.UnitTests +namespace Neo.UnitTests.SmartContract.Iterators { [TestClass] diff --git a/neo.UnitTests/UT_ContractManifest.cs b/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs similarity index 99% rename from neo.UnitTests/UT_ContractManifest.cs rename to neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index 451791e9bd..bbb613c2be 100644 --- a/neo.UnitTests/UT_ContractManifest.cs +++ b/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -2,7 +2,7 @@ using Neo.Cryptography.ECC; using Neo.SmartContract.Manifest; -namespace Neo.UnitTests +namespace Neo.UnitTests.SmartContract.Manifest { [TestClass] public class UT_ContractManifest diff --git a/neo.UnitTests/UT_GasToken.cs b/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs similarity index 99% rename from neo.UnitTests/UT_GasToken.cs rename to neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index 72a0c4d7ae..0ad69a7198 100644 --- a/neo.UnitTests/UT_GasToken.cs +++ b/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -11,7 +11,7 @@ using System.Linq; using System.Numerics; -namespace Neo.UnitTests +namespace Neo.UnitTests.SmartContract.Native.Tokens { [TestClass] public class UT_GasToken diff --git a/neo.UnitTests/UT_NeoToken.cs b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs similarity index 99% rename from neo.UnitTests/UT_NeoToken.cs rename to neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 3997573c0f..9a22bab3c4 100644 --- a/neo.UnitTests/UT_NeoToken.cs +++ b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -15,7 +15,7 @@ using System.Linq; using System.Numerics; -namespace Neo.UnitTests +namespace Neo.UnitTests.SmartContract.Native.Tokens { [TestClass] public class UT_NeoToken diff --git a/neo.UnitTests/UT_Policy.cs b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs similarity index 94% rename from neo.UnitTests/UT_Policy.cs rename to neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 09a09b20ba..0f353e37da 100644 --- a/neo.UnitTests/UT_Policy.cs +++ b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -7,10 +7,10 @@ using Neo.UnitTests.Extensions; using System.Linq; -namespace Neo.UnitTests +namespace Neo.UnitTests.SmartContract.Native { [TestClass] - public class UT_Policy + public class UT_PolicyContract { Store Store; @@ -55,7 +55,7 @@ public void Check_SetMaxTransactionsPerBlock() // Fake blockchain snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); + snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); @@ -90,7 +90,7 @@ public void Check_SetFeePerByte() // Fake blockchain snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); + snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); @@ -125,7 +125,7 @@ public void Check_Block_UnblockAccount() // Fake blockchain snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; - snapshot.Blocks.Add(UInt256.Zero, new Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); + snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); diff --git a/neo.UnitTests/UT_InteropPrices.cs b/neo.UnitTests/SmartContract/UT_InteropPrices.cs similarity index 99% rename from neo.UnitTests/UT_InteropPrices.cs rename to neo.UnitTests/SmartContract/UT_InteropPrices.cs index 27c0a4cdc0..d6b9b8254b 100644 --- a/neo.UnitTests/UT_InteropPrices.cs +++ b/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -3,7 +3,7 @@ using Neo.SmartContract; using Neo.VM; -namespace Neo.UnitTests +namespace Neo.UnitTests.SmartContract { [TestClass] public class UT_InteropPrices diff --git a/neo.UnitTests/UT_InteropService.cs b/neo.UnitTests/SmartContract/UT_InteropService.cs similarity index 98% rename from neo.UnitTests/UT_InteropService.cs rename to neo.UnitTests/SmartContract/UT_InteropService.cs index b5e3626109..023ba4c8e5 100644 --- a/neo.UnitTests/UT_InteropService.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -1,10 +1,9 @@ -using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; using Neo.SmartContract.Manifest; using Neo.VM; -namespace Neo.UnitTests +namespace Neo.UnitTests.SmartContract { [TestClass] public class UT_InteropService @@ -42,7 +41,7 @@ public void Runtime_GetNotifications_Test() scriptHash2 = script.ToArray().ToScriptHash(); snapshot.Contracts.Delete(scriptHash2); - snapshot.Contracts.Add(scriptHash2, new Ledger.ContractState() + snapshot.Contracts.Add(scriptHash2, new Neo.Ledger.ContractState() { Script = script.ToArray(), Manifest = ContractManifest.CreateDefault(scriptHash2), diff --git a/neo.UnitTests/UT_JsonSerializer.cs b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs similarity index 99% rename from neo.UnitTests/UT_JsonSerializer.cs rename to neo.UnitTests/SmartContract/UT_JsonSerializer.cs index ed20b15392..adfbea8760 100644 --- a/neo.UnitTests/UT_JsonSerializer.cs +++ b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs @@ -7,7 +7,7 @@ using System.Linq; using System.Numerics; -namespace Neo.UnitTests +namespace Neo.UnitTests.SmartContract { [TestClass] public class UT_JsonSerializer diff --git a/neo.UnitTests/UT_OpCodePrices.cs b/neo.UnitTests/SmartContract/UT_OpCodePrices.cs similarity index 91% rename from neo.UnitTests/UT_OpCodePrices.cs rename to neo.UnitTests/SmartContract/UT_OpCodePrices.cs index b53d745d6a..3174d8494b 100644 --- a/neo.UnitTests/UT_OpCodePrices.cs +++ b/neo.UnitTests/SmartContract/UT_OpCodePrices.cs @@ -3,7 +3,7 @@ using Neo.VM; using System; -namespace Neo.UnitTests +namespace Neo.UnitTests.SmartContract { [TestClass] public class UT_OpCodePrices diff --git a/neo.UnitTests/UT_Syscalls.cs b/neo.UnitTests/SmartContract/UT_Syscalls.cs similarity index 98% rename from neo.UnitTests/UT_Syscalls.cs rename to neo.UnitTests/SmartContract/UT_Syscalls.cs index c6642c4e88..c80899b8f8 100644 --- a/neo.UnitTests/UT_Syscalls.cs +++ b/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -4,7 +4,7 @@ using Neo.VM; using System.Linq; -namespace Neo.UnitTests +namespace Neo.UnitTests.SmartContract { [TestClass] public class UT_Syscalls diff --git a/neo.UnitTests/UT_BigDecimal.cs b/neo.UnitTests/UT_BigDecimal.cs new file mode 100644 index 0000000000..3975d5503e --- /dev/null +++ b/neo.UnitTests/UT_BigDecimal.cs @@ -0,0 +1,191 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Numerics; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_BigDecimal + { + [TestMethod] + public void TestChangeDecimals() + { + BigDecimal originalValue = new BigDecimal(new BigInteger(12300), 5); + BigDecimal result1 = originalValue.ChangeDecimals(7); + result1.Value.Should().Be(new BigInteger(1230000)); + result1.Decimals.Should().Be(7); + BigDecimal result2 = originalValue.ChangeDecimals(3); + result2.Value.Should().Be(new BigInteger(123)); + result2.Decimals.Should().Be(3); + BigDecimal result3 = originalValue.ChangeDecimals(5); + result3.Value.Should().Be(originalValue.Value); + Action action = () => originalValue.ChangeDecimals(2); + action.ShouldThrow(); + } + + [TestMethod] + 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); + } + + [TestMethod] + public void TestGetDecimals() + { + BigDecimal value = new BigDecimal(new BigInteger(45600), 7); + value.Sign.Should().Be(1); + value = new BigDecimal(new BigInteger(0), 5); + value.Sign.Should().Be(0); + value = new BigDecimal(new BigInteger(-10), 0); + value.Sign.Should().Be(-1); + } + + [TestMethod] + public void TestGetSign() + { + BigDecimal value = new BigDecimal(new BigInteger(45600), 7); + value.Sign.Should().Be(1); + value = new BigDecimal(new BigInteger(0), 5); + value.Sign.Should().Be(0); + value = new BigDecimal(new BigInteger(-10), 0); + value.Sign.Should().Be(-1); + } + + [TestMethod] + public void TestParse() + { + string s = "12345"; + byte decimals = 0; + BigDecimal.Parse(s, decimals).Should().Be(new BigDecimal(new BigInteger(12345), 0)); + + s = "abcdEfg"; + Action action = () => BigDecimal.Parse(s, decimals); + action.ShouldThrow(); + } + + [TestMethod] + public void TestToString() + { + BigDecimal value = new BigDecimal(new BigInteger(100000), 5); + value.ToString().Should().Be("1"); + value = new BigDecimal(new BigInteger(123456), 5); + value.ToString().Should().Be("1.23456"); + } + + [TestMethod] + public void TestTryParse() + { + string s = ""; + byte decimals = 0; + BigDecimal result; + + s = "12345"; + decimals = 0; + BigDecimal.TryParse(s, decimals, out result).Should().BeTrue(); + result.Should().Be(new BigDecimal(new BigInteger(12345), 0)); + + s = "12345E-5"; + decimals = 5; + BigDecimal.TryParse(s, decimals, out result).Should().BeTrue(); + result.Should().Be(new BigDecimal(new BigInteger(12345), 5)); + + s = "abcdEfg"; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "123.45"; + decimals = 2; + BigDecimal.TryParse(s, decimals, out result).Should().BeTrue(); + result.Should().Be(new BigDecimal(new BigInteger(12345), 2)); + + s = "123.45E-5"; + decimals = 7; + BigDecimal.TryParse(s, decimals, out result).Should().BeTrue(); + result.Should().Be(new BigDecimal(new BigInteger(12345), 7)); + + s = "12345E-5"; + decimals = 3; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "1.2345"; + decimals = 3; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "1.2345E-5"; + decimals = 3; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "12345"; + decimals = 3; + BigDecimal.TryParse(s, decimals, out result).Should().BeTrue(); + result.Should().Be(new BigDecimal(new BigInteger(12345000), 3)); + + s = "12345E-2"; + decimals = 3; + BigDecimal.TryParse(s, decimals, out result).Should().BeTrue(); + result.Should().Be(new BigDecimal(new BigInteger(123450), 3)); + + s = "123.45"; + decimals = 3; + BigDecimal.TryParse(s, decimals, out result).Should().BeTrue(); + result.Should().Be(new BigDecimal(new BigInteger(123450), 3)); + + s = "123.45E3"; + decimals = 3; + BigDecimal.TryParse(s, decimals, out result).Should().BeTrue(); + result.Should().Be(new BigDecimal(new BigInteger(123450000), 3)); + + s = "a456bcdfg"; + decimals = 0; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "a456bce-5"; + decimals = 5; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "a4.56bcd"; + decimals = 5; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "a4.56bce3"; + decimals = 2; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "a456bcd"; + decimals = 2; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "a456bcdE3"; + decimals = 2; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "a456b.cd"; + decimals = 5; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + + s = "a456b.cdE3"; + decimals = 5; + BigDecimal.TryParse(s, decimals, out result).Should().BeFalse(); + result.Should().Be(default(BigDecimal)); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/UT_FifoSet.cs b/neo.UnitTests/UT_FifoSet.cs deleted file mode 100644 index ff5ccf9c27..0000000000 --- a/neo.UnitTests/UT_FifoSet.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Caching; -using System.Linq; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_FifoSet - { - [TestMethod] - public void FifoSetTest() - { - var a = UInt256.Zero; - var b = new UInt256(); - var c = new UInt256(new byte[32] { - 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 - }); - - var set = new FIFOSet(3); - - Assert.IsTrue(set.Add(a)); - Assert.IsFalse(set.Add(a)); - Assert.IsFalse(set.Add(b)); - Assert.IsTrue(set.Add(c)); - - CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a, c }); - - var d = new UInt256(new byte[32] { - 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, 0x02 - }); - - // Testing Fifo max size - Assert.IsTrue(set.Add(d)); - CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a, c, d }); - - var e = new UInt256(new byte[32] { - 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, 0x03 - }); - - Assert.IsTrue(set.Add(e)); - Assert.IsFalse(set.Add(e)); - CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { c, d, e }); - } - } -} diff --git a/neo.UnitTests/UT_ProtocolSettings.cs b/neo.UnitTests/UT_ProtocolSettings.cs index 92cabae19b..053c5d7e37 100644 --- a/neo.UnitTests/UT_ProtocolSettings.cs +++ b/neo.UnitTests/UT_ProtocolSettings.cs @@ -93,4 +93,4 @@ public void Cant_initialize_ProtocolSettings_twice() ProtocolSettings.Default.Magic.Should().Be(expectedMagic); } } -} +} \ No newline at end of file diff --git a/neo.UnitTests/VM/UT_Helper.cs b/neo.UnitTests/VM/UT_Helper.cs new file mode 100644 index 0000000000..7fc0088632 --- /dev/null +++ b/neo.UnitTests/VM/UT_Helper.cs @@ -0,0 +1,82 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.VM; +using System; +using System.Text; + +namespace Neo.UnitTests.IO +{ + [TestClass] + public class UT_Helper + { + [TestMethod] + public void TestEmit() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.Emit(new OpCode[] { OpCode.PUSH0 }); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00 }), Encoding.Default.GetString(sb.ToArray())); + } + + [TestMethod] + public void TestEmitAppCall1() + { + //format:(byte)0x00+(byte)OpCode.NEWARRAY+(string)operation+(Uint160)scriptHash+(uint)InteropService.System_Contract_Call + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitAppCall(UInt160.Zero, "AAAAA"); + byte[] tempArray = new byte[34]; + tempArray[0] = 0x00;//0 + tempArray[1] = 0xC5;//OpCode.NEWARRAY + tempArray[2] = 5;//operation.Length + Array.Copy(Encoding.UTF8.GetBytes("AAAAA"),0, tempArray,3, 5);//operation.data + tempArray[8] = 0x14;//scriptHash.Length + Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 9, 20);//operation.data + uint api = InteropService.System_Contract_Call; + tempArray[29] = 0x68;//OpCode.SYSCALL + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 30, 4);//api.data + byte[] resultArray = sb.ToArray(); + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(resultArray)); + } + + [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)}); + byte[] tempArray = new byte[35]; + tempArray[0] = 0x00;//0 + tempArray[1] = 0x51;//ContractParameter.Length + tempArray[2] = 0xC1;//OpCode.PACK + tempArray[3] = 0x05;//operation.Length + Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 4, 5);//operation.data + tempArray[9] = 0x14;//scriptHash.Length + Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 10, 20);//operation.data + uint api = InteropService.System_Contract_Call; + tempArray[30] = 0x68;//OpCode.SYSCALL + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 31, 4);//api.data + byte[] resultArray = sb.ToArray(); + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(resultArray)); + } + + [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); + byte[] tempArray = new byte[35]; + tempArray[0] = 0x51;//arg + tempArray[1] = 0x51;//args.Length + tempArray[2] = 0xC1;//OpCode.PACK + tempArray[3] = 0x05;//operation.Length + Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 4, 5);//operation.data + tempArray[9] = 0x14;//scriptHash.Length + Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 10, 20);//operation.data + uint api = InteropService.System_Contract_Call; + tempArray[30] = 0x68;//OpCode.SYSCALL + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 31, 4);//api.data + byte[] resultArray = sb.ToArray(); + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(resultArray)); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/UT_NEP6Wallet.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs similarity index 97% rename from neo.UnitTests/UT_NEP6Wallet.cs rename to neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index 187e487030..9bac3de4ac 100644 --- a/neo.UnitTests/UT_NEP6Wallet.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -4,7 +4,7 @@ using Neo.Wallets.NEP6; using System; -namespace Neo.UnitTests +namespace Neo.UnitTests.Wallets.NEP6 { [TestClass] public class UT_NEP6Wallet diff --git a/neo.UnitTests/UT_ScryptParameters.cs b/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs similarity index 97% rename from neo.UnitTests/UT_ScryptParameters.cs rename to neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs index e0021f0eb0..3d4550a48d 100644 --- a/neo.UnitTests/UT_ScryptParameters.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs @@ -3,7 +3,7 @@ using Neo.IO.Json; using Neo.Wallets.NEP6; -namespace Neo.UnitTests +namespace Neo.UnitTests.Wallets.NEP6 { [TestClass] public class UT_ScryptParameters diff --git a/neo.UnitTests/Wallets/SQLite/UT_Account.cs b/neo.UnitTests/Wallets/SQLite/UT_Account.cs new file mode 100644 index 0000000000..c60e90fd10 --- /dev/null +++ b/neo.UnitTests/Wallets/SQLite/UT_Account.cs @@ -0,0 +1,37 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Wallets.SQLite; +using System.Text; + +namespace Neo.UnitTests.Wallets.SQLite +{ + [TestClass] + public class UT_Account + { + [TestMethod] + public void TestGenerator() + { + Account account = new Account(); + Assert.IsNotNull(account); + } + + [TestMethod] + public void TestSetAndGetPrivateKeyEncrypted() + { + Account account = new Account + { + PrivateKeyEncrypted = new byte[] { 0x01 } + }; + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(account.PrivateKeyEncrypted)); + } + + [TestMethod] + public void TestSetAndGetPublicKeyHash() + { + Account account = new Account + { + PublicKeyHash = new byte[] { 0x01 } + }; + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(account.PublicKeyHash)); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Wallets/SQLite/UT_Address.cs b/neo.UnitTests/Wallets/SQLite/UT_Address.cs new file mode 100644 index 0000000000..4d3e2d6717 --- /dev/null +++ b/neo.UnitTests/Wallets/SQLite/UT_Address.cs @@ -0,0 +1,27 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Wallets.SQLite; +using System.Text; + +namespace Neo.UnitTests.Wallets.SQLite +{ + [TestClass] + public class UT_Address + { + [TestMethod] + public void TestGenerator() + { + Address address = new Address(); + Assert.IsNotNull(address); + } + + [TestMethod] + public void TestSetAndGetScriptHash() + { + Address address = new Address + { + ScriptHash = new byte[] { 0x01 } + }; + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(address.ScriptHash)); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Wallets/SQLite/UT_Contract.cs b/neo.UnitTests/Wallets/SQLite/UT_Contract.cs new file mode 100644 index 0000000000..52c197a778 --- /dev/null +++ b/neo.UnitTests/Wallets/SQLite/UT_Contract.cs @@ -0,0 +1,65 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Wallets.SQLite; +using System.Text; + +namespace Neo.UnitTests.Wallets.SQLite +{ + [TestClass] + public class UT_Contract + { + [TestMethod] + public void TestGenerator() + { + Contract contract = new Contract(); + Assert.IsNotNull(contract); + } + + [TestMethod] + public void TestSetAndGetRawData() + { + Contract contract = new Contract + { + RawData = new byte[] { 0x01 } + }; + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(contract.RawData)); + } + + [TestMethod] + public void TestSetAndGetScriptHash() + { + Contract contract = new Contract + { + ScriptHash = new byte[] { 0x01 } + }; + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(contract.ScriptHash)); + } + + [TestMethod] + public void TestSetAndGetPublicKeyHash() + { + Contract contract = new Contract + { + PublicKeyHash = new byte[] { 0x01 } + }; + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(contract.PublicKeyHash)); + } + + [TestMethod] + public void TestSetAndGetAccount() + { + Contract contract = new Contract(); + Account account = new Account(); + contract.Account = account; + Assert.AreEqual(account, contract.Account); + } + + [TestMethod] + public void TestSetAndGetAddress() + { + Contract contract = new Contract(); + Address address = new Address(); + contract.Address = address; + Assert.AreEqual(address, contract.Address); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Wallets/SQLite/UT_Key.cs b/neo.UnitTests/Wallets/SQLite/UT_Key.cs new file mode 100644 index 0000000000..e09896b72c --- /dev/null +++ b/neo.UnitTests/Wallets/SQLite/UT_Key.cs @@ -0,0 +1,37 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Wallets.SQLite; +using System.Text; + +namespace Neo.UnitTests.Wallets.SQLite +{ + [TestClass] + public class UT_Key + { + [TestMethod] + public void TestGenerator() + { + Key key = new Key(); + Assert.IsNotNull(key); + } + + [TestMethod] + public void TestSetAndGetName() + { + Key key = new Key + { + Name = "AAA" + }; + Assert.AreEqual("AAA", key.Name); + } + + [TestMethod] + public void TestSetAndGetValue() + { + Key key = new Key + { + Value = new byte[] { 0x01 } + }; + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(key.Value)); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/UT_AssetDescription.cs b/neo.UnitTests/Wallets/UT_AssetDescriptor.cs similarity index 77% rename from neo.UnitTests/UT_AssetDescription.cs rename to neo.UnitTests/Wallets/UT_AssetDescriptor.cs index 175814b91f..6630a8450b 100644 --- a/neo.UnitTests/UT_AssetDescription.cs +++ b/neo.UnitTests/Wallets/UT_AssetDescriptor.cs @@ -3,10 +3,10 @@ using Neo.Persistence; using Neo.SmartContract.Native; -namespace Neo.UnitTests +namespace Neo.UnitTests.Wallets { [TestClass] - public class UT_AssetDescription + public class UT_AssetDescriptor { private Store Store; @@ -20,7 +20,7 @@ public void TestSetup() [TestMethod] public void Check_GAS() { - var descriptor = new Wallets.AssetDescriptor(NativeContract.GAS.Hash); + var descriptor = new Neo.Wallets.AssetDescriptor(NativeContract.GAS.Hash); descriptor.AssetId.Should().Be(NativeContract.GAS.Hash); descriptor.AssetName.Should().Be("GAS"); descriptor.Decimals.Should().Be(8); @@ -29,7 +29,7 @@ public void Check_GAS() [TestMethod] public void Check_NEO() { - var descriptor = new Wallets.AssetDescriptor(NativeContract.NEO.Hash); + var descriptor = new Neo.Wallets.AssetDescriptor(NativeContract.NEO.Hash); descriptor.AssetId.Should().Be(NativeContract.NEO.Hash); descriptor.AssetName.Should().Be("NEO"); descriptor.Decimals.Should().Be(0); diff --git a/neo.UnitTests/neo.UnitTests.csproj b/neo.UnitTests/neo.UnitTests.csproj index 9f57166c69..5d967c0845 100644 --- a/neo.UnitTests/neo.UnitTests.csproj +++ b/neo.UnitTests/neo.UnitTests.csproj @@ -31,5 +31,4 @@ - diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 912ea69c73..739085f5b6 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -361,7 +361,7 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) if (policyChanged) { foreach (PoolItem item in _unverifiedSortedTransactions.Reverse()) - if(item.Tx.FeePerByte >= _feePerByte) + if (item.Tx.FeePerByte >= _feePerByte) _system.Blockchain.Tell(item.Tx, ActorRefs.NoSender); _unverifiedTransactions.Clear(); _unverifiedSortedTransactions.Clear(); From c47f2e329697caf10ab69841a8c030017b034676 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 29 Jul 2019 07:24:58 +0200 Subject: [PATCH 054/305] Script header (#903) * Script header * Script header draft * Clean files * Typo * Rename class and magic header * Clean hash * UT * Erik's suggestions * Change name * Typo * Check magic * 64 to 32 * Nef file only for neo-cli * Real Checksum * Clean * Clean again * Remove code * Update neo/SmartContract/NefFile.cs Co-Authored-By: Igor Machado Coelho * Enum to const * Remove unnecessary change * Apply Erik's suggestions --- neo.UnitTests/UT_NefFile.cs | 75 ++++++++++++++++++++ neo/SmartContract/NefFile.cs | 129 +++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 neo.UnitTests/UT_NefFile.cs create mode 100644 neo/SmartContract/NefFile.cs diff --git a/neo.UnitTests/UT_NefFile.cs b/neo.UnitTests/UT_NefFile.cs new file mode 100644 index 0000000000..ba14496a27 --- /dev/null +++ b/neo.UnitTests/UT_NefFile.cs @@ -0,0 +1,75 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.IO; +using Neo.SmartContract; +using System; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_NefFile + { + [TestMethod] + public void ParseTest() + { + var file = new NefFile() + { + Compiler = "".PadLeft(32, ' '), + Version = new Version(1, 2, 3, 4), + Script = new byte[] { 0x01, 0x02, 0x03 } + }; + + file.ScriptHash = file.Script.ToScriptHash(); + file.CheckSum = NefFile.ComputeChecksum(file); + + var data = file.ToArray(); + file = data.AsSerializable(); + + Assert.AreEqual("".PadLeft(32, ' '), file.Compiler); + Assert.AreEqual(new Version(1, 2, 3, 4), file.Version); + Assert.AreEqual(file.Script.ToScriptHash(), file.ScriptHash); + CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, file.Script); + } + + [TestMethod] + public void LimitTest() + { + var file = new NefFile() + { + Compiler = "".PadLeft(byte.MaxValue, ' '), + Version = new Version(1, 2, 3, 4), + Script = new byte[1024 * 1024], + ScriptHash = new byte[1024 * 1024].ToScriptHash(), + CheckSum = 0 + }; + + // Wrong compiler + + Assert.ThrowsException(() => file.ToArray()); + + // Wrong script + + file.Compiler = ""; + file.Script = new byte[(1024 * 1024) + 1]; + file.ScriptHash = file.Script.ToScriptHash(); + var data = file.ToArray(); + + Assert.ThrowsException(() => data.AsSerializable()); + + // Wrong script hash + + file.Script = new byte[1024 * 1024]; + data = file.ToArray(); + + Assert.ThrowsException(() => data.AsSerializable()); + + // Wrong checksum + + file.Script = new byte[1024]; + data = file.ToArray(); + file.CheckSum = NefFile.ComputeChecksum(file) + 1; + + Assert.ThrowsException(() => data.AsSerializable()); + } + } +} \ No newline at end of file diff --git a/neo/SmartContract/NefFile.cs b/neo/SmartContract/NefFile.cs new file mode 100644 index 0000000000..52507eda08 --- /dev/null +++ b/neo/SmartContract/NefFile.cs @@ -0,0 +1,129 @@ +using Neo.Cryptography; +using Neo.IO; +using System; +using System.IO; + +namespace Neo.SmartContract +{ + /// + /// +------------+-----------+------------------------------------------------------------+ + /// | Field | Length | Comment | + /// +------------+-----------+------------------------------------------------------------+ + /// | Magic | 4 bytes | Magic header | + /// | Compiler | 32 bytes | Compiler used | + /// | Version | 16 bytes | Compiler version (Mayor, Minor, Build, Version) | + /// | ScriptHash | 20 bytes | ScriptHash for the script | + /// +------------+-----------+------------------------------------------------------------+ + /// | Checksum | 4 bytes | Sha256 of the header (CRC) | + /// +------------+-----------+------------------------------------------------------------+ + /// | Script | Var bytes | Var bytes for the payload | + /// +------------+-----------+------------------------------------------------------------+ + /// + public class NefFile : ISerializable + { + /// + /// NEO Executable Format 3 (NEF3) + /// + private const uint Magic = 0x3346454E; + + /// + /// Compiler + /// + public string Compiler { get; set; } + + /// + /// Version + /// + public Version Version { get; set; } + + /// + /// Script Hash + /// + public UInt160 ScriptHash { get; set; } + + /// + /// Checksum + /// + public uint CheckSum { get; set; } + + /// + /// Script + /// + public byte[] Script { get; set; } + + private const int HeaderSize = + sizeof(uint) + // Magic + 32 + // Compiler + (sizeof(int) * 4) + // Version + UInt160.Length + // ScriptHash + sizeof(uint); // Checksum + + public int Size => + HeaderSize + // Header + Script.GetVarSize(); // Script + + public void Serialize(BinaryWriter writer) + { + writer.Write(Magic); + writer.WriteFixedString(Compiler, 32); + + // Version + writer.Write(Version.Major); + writer.Write(Version.Minor); + writer.Write(Version.Build); + writer.Write(Version.Revision); + + writer.Write(ScriptHash); + writer.Write(CheckSum); + writer.WriteVarBytes(Script ?? new byte[0]); + } + + public void Deserialize(BinaryReader reader) + { + if (reader.ReadUInt32() != Magic) + { + throw new FormatException("Wrong magic"); + } + + Compiler = reader.ReadFixedString(32); + Version = new Version(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); + ScriptHash = reader.ReadSerializable(); + CheckSum = reader.ReadUInt32(); + + if (CheckSum != ComputeChecksum(this)) + { + throw new FormatException("CRC verification fail"); + } + + Script = reader.ReadVarBytes(1024 * 1024); + + if (Script.ToScriptHash() != ScriptHash) + { + throw new FormatException("ScriptHash is different"); + } + } + + /// + /// Compute checksum for a file + /// + /// File + /// Return checksum + public static uint ComputeChecksum(NefFile file) + { + using (var ms = new MemoryStream()) + using (var wr = new BinaryWriter(ms)) + { + file.Serialize(wr); + wr.Flush(); + + // Read header without CRC + + var buffer = new byte[HeaderSize - sizeof(uint)]; + ms.Seek(0, SeekOrigin.Begin); + ms.Read(buffer, 0, buffer.Length); + + return BitConverter.ToUInt32(buffer.Sha256(), 0); + } + } + } +} From 1554050943986e80fe32cff767bf3b7a13be438a Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Mon, 29 Jul 2019 23:41:14 +0800 Subject: [PATCH 055/305] Unit Tests of Crypto Module (#962) * testDemo * add dll lib * add dbsnapshot dispose * test get blocks in levelDBStore * add levelDBStore test funcs * fix levelDBStore funcs * add DbCache addInternal * differ db path * space * fix delete internal test * add test getInternal tryGetInternal move libleveldb.dll * add dbCache method test * add store test * add cache unit tests * add cache unit tests * up readonly max_capacity * fix leveldbexception * fix comment on UT_Cache * format * fix multithread test problem * up cache * update travis config * update travis.yml * test DbMetaDataCache * fix db directory * format and update travis for maxos * fix mac env travis * 2019/7/12 10:34 * 2019/7/12 11:01 * remove commented line * test BigDecimal * fix format and csproj * rm coverage.opencover.xml * update method name * add UT_P_Helper * modify UT_P_Helper * modify UT_P_helper * Clean ut * test Base58 & BloomFilter * Update UT_Cache.cs * Correct Typo * test JsonArray * update namespace * update namespace * update format * update format * organise folder structure * add UT_JString * test JBoolean JNumber & JObject * 2019/7/16 10:30 add some test case for UInt32Wrapper and SerializableWrapper * fix timestamp * test ECDsa and Crypto * test OrderedDictionary & complete IO.Json tests * 2019/7/16 17:33 add some test case of SQLiteWallet * test FIFOSet * add CloneCache and DataCache unit tests * fix namespace * add UT_Cryptography_Helper * format UT_CloneCache and UT_DataCache * add UT_DataCache.GetAndChange unit test * update namespace * remove comment code * delete Persistence part * 2019/7/19 11:07 add some test case for Helper in VM * Fix Base58 Test * 2019/7/19 11:33 change some format * update IOHelper exception assert * 2019/7/19 14:22 change format * format IOHelper * review IO.Wrapper * review Wallets.SQLite UT * Test ECFieldElement ECPoint * refactor package * format ECDsa * update namespace * Code fix * review cache * modify UT_JString * fomat * using Actin replace with try-catch * add UT_CloneMetaCache and UT_MetaDataCache * update namespace * format UT_DataCache.cs * Code Fix * format * update csproj * Code fix for UT_ECFieldElement and UT_ECPoint * Code fix * format * update travis * delete deleteFiles * fix path and comment * update travis * delete test ToTimeStamp * format UT_*Cache * update format * fomat * use hex extensions in Cryptography_Helper * remove reflection * optimization of UT_DataCache * update namespace * modify TestSha256 * update UT in crypto module * Rename UT_Scrypt.cs to UT_SCrypt.cs * format * update UT_Murmur3 --- neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs | 42 ++++++++++ neo.UnitTests/Cryptography/UT_MerkleTree.cs | 80 +++++++++++++++++++ .../Cryptography/UT_MerkleTreeNode.cs | 51 ++++++++++++ neo.UnitTests/Cryptography/UT_Murmur3.cs | 24 ++++++ .../{UT_Scrypt.cs => UT_SCrypt.cs} | 3 +- 5 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 neo.UnitTests/Cryptography/UT_MerkleTree.cs create mode 100644 neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs create mode 100644 neo.UnitTests/Cryptography/UT_Murmur3.cs rename neo.UnitTests/Cryptography/{UT_Scrypt.cs => UT_SCrypt.cs} (93%) diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs b/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs index 9de751e851..cc6fa87d5a 100644 --- a/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs +++ b/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs @@ -56,6 +56,10 @@ public void TestECPointConstructor() point.X.Should().Be(X); point.Y.Should().Be(Y); point.Curve.Should().Be(ECCurve.Secp256k1); + Action action = () => new ECPoint(X, null, ECCurve.Secp256k1); + action.ShouldThrow(); + action = () => new ECPoint(null, Y, ECCurve.Secp256k1); + action.ShouldThrow(); } [TestMethod] @@ -145,6 +149,30 @@ public void TestEquals() point1.Equals(point3).Should().BeFalse(); } + [TestMethod] + public void TestEqualsObject() + { + object point = ECCurve.Secp256k1.G; + point.Equals(point).Should().BeTrue(); + point.Equals(null).Should().BeFalse(); + point.Equals(1u).Should().BeFalse(); + + point = new ECPoint(null, null, ECCurve.Secp256k1); + point.Equals(new ECPoint(null, null, ECCurve.Secp256r1)).Should().BeTrue(); + point.Equals(ECCurve.Secp256r1.G).Should().BeFalse(); + ECCurve.Secp256r1.G.Equals(point).Should().BeFalse(); + + ECFieldElement X1 = new ECFieldElement(new BigInteger(100), ECCurve.Secp256k1); + ECFieldElement Y1 = new ECFieldElement(new BigInteger(200), ECCurve.Secp256k1); + ECFieldElement X2 = new ECFieldElement(new BigInteger(300), ECCurve.Secp256k1); + ECFieldElement Y2 = new ECFieldElement(new BigInteger(400), ECCurve.Secp256k1); + object point1 = new ECPoint(X1, Y1, ECCurve.Secp256k1); + object point2 = new ECPoint(X2, Y1, ECCurve.Secp256k1); + object point3 = new ECPoint(X1, Y2, ECCurve.Secp256k1); + point1.Equals(point2).Should().BeFalse(); + point1.Equals(point3).Should().BeFalse(); + } + [TestMethod] public void TestFromBytes() { @@ -176,6 +204,13 @@ public void TestFromBytes() ECCurve.Secp256k1), ECCurve.Secp256k1)); } + [TestMethod] + public void TestGetSize() + { + ECCurve.Secp256k1.G.Size.Should().Be(33); + ECCurve.Secp256k1.Infinity.Size.Should().Be(1); + } + [TestMethod] public void TestMultiply() { @@ -279,6 +314,13 @@ public void TestOpMultiply() new ECFieldElement(BigInteger.Parse("29236048674093813394523910922582374630829081423043497254162533033164154049666"), ECCurve.Secp256k1), ECCurve.Secp256k1)); } + [TestMethod] + public void TestOpSubtraction() + { + (ECCurve.Secp256k1.G - ECCurve.Secp256k1.Infinity).Should().Be(ECCurve.Secp256k1.G); + (ECCurve.Secp256k1.G - ECCurve.Secp256k1.G).Should().Be(ECCurve.Secp256k1.Infinity); + } + [TestMethod] public void TestOpUnaryNegation() { diff --git a/neo.UnitTests/Cryptography/UT_MerkleTree.cs b/neo.UnitTests/Cryptography/UT_MerkleTree.cs new file mode 100644 index 0000000000..3edf6b4a8b --- /dev/null +++ b/neo.UnitTests/Cryptography/UT_MerkleTree.cs @@ -0,0 +1,80 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.UnitTests.Cryptography +{ + [TestClass] + public class UT_MerkleTree + { + public UInt256 GetByteArrayHash(byte[] byteArray) + { + if(byteArray == null || byteArray.Length == 0) throw new ArgumentNullException(); + var hash = new UInt256(Crypto.Default.Hash256(byteArray)); + return hash; + } + + [TestMethod] + public void TestBuildAndDepthFirstSearch() + { + IReadOnlyList hashNull = new UInt256[] { }; + Action action = () => new MerkleTree(hashNull); + action.ShouldThrow(); + + byte[] array1 = { 0x01 }; + var hash1 = GetByteArrayHash(array1); + + byte[] array2 = { 0x02 }; + var hash2 = GetByteArrayHash(array2); + + byte[] array3 = { 0x03 }; + var hash3 = GetByteArrayHash(array3); + + IReadOnlyList hashes = new UInt256[] { hash1, hash2, hash3 }; + MerkleTree tree = new MerkleTree(hashes); + var hashArray = tree.ToHashArray(); + hashArray[0].Should().Be(hash1); + hashArray[1].Should().Be(hash2); + hashArray[2].Should().Be(hash3); + hashArray[3].Should().Be(hash3); + + var rootHash = MerkleTree.ComputeRoot(hashes); + var hash4 = Crypto.Default.Hash256(hash1.ToArray().Concat(hash2.ToArray()).ToArray()); + var hash5 = Crypto.Default.Hash256(hash3.ToArray().Concat(hash3.ToArray()).ToArray()); + var result = new UInt256(Crypto.Default.Hash256(hash4.ToArray().Concat(hash5.ToArray()).ToArray())); + rootHash.Should().Be(result); + } + + [TestMethod] + public void TestTrim() + { + byte[] array1 = { 0x01 }; + var hash1 = GetByteArrayHash(array1); + + byte[] array2 = { 0x02 }; + var hash2 = GetByteArrayHash(array2); + + byte[] array3 = { 0x03 }; + var hash3 = GetByteArrayHash(array3); + + IReadOnlyList hashes = new UInt256[] { hash1, hash2, hash3 }; + MerkleTree tree = new MerkleTree(hashes); + + bool[] boolArray = { false, false, false }; + BitArray bitArray = new BitArray(boolArray); + tree.Trim(bitArray); + var hashArray = tree.ToHashArray(); + + hashArray.Length.Should().Be(1); + var rootHash = MerkleTree.ComputeRoot(hashes); + var hash4 = Crypto.Default.Hash256(hash1.ToArray().Concat(hash2.ToArray()).ToArray()); + var hash5 = Crypto.Default.Hash256(hash3.ToArray().Concat(hash3.ToArray()).ToArray()); + var result = new UInt256(Crypto.Default.Hash256(hash4.ToArray().Concat(hash5.ToArray()).ToArray())); + hashArray[0].Should().Be(result); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs b/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs new file mode 100644 index 0000000000..48fd05745d --- /dev/null +++ b/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs @@ -0,0 +1,51 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using System.Text; + +namespace Neo.UnitTests.Cryptography +{ + [TestClass] + public class UT_MerkleTreeNode + { + private MerkleTreeNode node = new MerkleTreeNode(); + + [TestInitialize] + public void TestSetup() + { + node.Hash = null; + node.Parent = null; + node.LeftChild = null; + node.RightChild = null; + } + + [TestMethod] + public void TestConstructor() + { + byte[] byteArray = Encoding.ASCII.GetBytes("hello world"); + var hash = new UInt256(Crypto.Default.Hash256(byteArray)); + node.Hash = hash; + + node.Hash.Should().Be(hash); + node.Parent.Should().BeNull(); + node.LeftChild.Should().BeNull(); + node.RightChild.Should().BeNull(); + } + + [TestMethod] + public void TestGetIsLeaf() + { + node.IsLeaf.Should().BeTrue(); + + MerkleTreeNode child = new MerkleTreeNode(); + node.LeftChild = child; + node.IsLeaf.Should().BeFalse(); + } + + [TestMethod] + public void TestGetIsRoot() + { + node.IsRoot.Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Cryptography/UT_Murmur3.cs b/neo.UnitTests/Cryptography/UT_Murmur3.cs new file mode 100644 index 0000000000..c59a53645a --- /dev/null +++ b/neo.UnitTests/Cryptography/UT_Murmur3.cs @@ -0,0 +1,24 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; + +namespace Neo.UnitTests.Cryptography +{ + [TestClass] + public class UT_Murmur3 + { + [TestMethod] + public void TestGetHashSize() + { + Murmur3 murmur3 = new Murmur3(1); + murmur3.HashSize.Should().Be(32); + } + + [TestMethod] + public void TestHashCore() + { + byte[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1 }; + array.Murmur32(10u).Should().Be(378574820u); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Cryptography/UT_Scrypt.cs b/neo.UnitTests/Cryptography/UT_SCrypt.cs similarity index 93% rename from neo.UnitTests/Cryptography/UT_Scrypt.cs rename to neo.UnitTests/Cryptography/UT_SCrypt.cs index 8ec603da48..afe41bf475 100644 --- a/neo.UnitTests/Cryptography/UT_Scrypt.cs +++ b/neo.UnitTests/Cryptography/UT_SCrypt.cs @@ -1,11 +1,10 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; -using System; namespace Neo.UnitTests.Cryptography { [TestClass] - public class UT_Scrypt + public class UT_SCrypt { [TestMethod] public void DeriveKeyTest() From 8768bf7f5a2b6e4754b64a44756cc0a5df6164b1 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Mon, 29 Jul 2019 23:44:55 +0800 Subject: [PATCH 056/305] Fix `System.Contract.Call` --- neo/SmartContract/InteropService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index fbb3a8655d..c4daa8a89b 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -54,7 +54,7 @@ public static partial class InteropService public static readonly uint System_Block_GetTransactions = Register("System.Block.GetTransactions", Block_GetTransactions, 0_00010000, TriggerType.Application); public static readonly uint System_Block_GetTransaction = Register("System.Block.GetTransaction", Block_GetTransaction, 0_00000400, TriggerType.Application); public static readonly uint System_Transaction_GetHash = Register("System.Transaction.GetHash", Transaction_GetHash, 0_00000400, TriggerType.All); - public static readonly uint System_Contract_Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.Application); + public static readonly uint System_Contract_Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application); public static readonly uint System_Contract_Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application); public static readonly uint System_Storage_GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application); public static readonly uint System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400, TriggerType.Application); From c7bfb10bb739376022a85c1cbc593707fbf2c915 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Mon, 29 Jul 2019 23:57:24 +0800 Subject: [PATCH 057/305] Fix ProtocolSettings.cs --- neo/ProtocolSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/ProtocolSettings.cs b/neo/ProtocolSettings.cs index 478d5b71b3..381e56c8a7 100644 --- a/neo/ProtocolSettings.cs +++ b/neo/ProtocolSettings.cs @@ -71,7 +71,7 @@ private ProtocolSettings(IConfigurationSection section) "seed4.neo.org:10333", "seed5.neo.org:10333" }; - this.MillisecondsPerBlock = section.GetValue("SecondsPerBlock", 15000u); + this.MillisecondsPerBlock = section.GetValue("MillisecondsPerBlock", 15000u); this.MemoryPoolMaxTransactions = Math.Max(1, section.GetValue("MemoryPoolMaxTransactions", 50_000)); } } From 1064e772bf1dfcaab3f0f7cca8a606e20847e6b1 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 30 Jul 2019 13:00:52 +0800 Subject: [PATCH 058/305] Fixes #946 (#950) --- neo.UnitTests/IO/UT_IOHelper.cs | 12 +-- neo.UnitTests/UT_DataCache.cs | 126 ++++++++++++++++++++++++ neo/IO/ByteArrayComparer.cs | 21 ++++ neo/IO/Caching/DataCache.cs | 54 ++++++++-- neo/IO/Helper.cs | 13 ++- neo/SmartContract/InteropService.NEO.cs | 2 +- 6 files changed, 208 insertions(+), 20 deletions(-) create mode 100644 neo.UnitTests/UT_DataCache.cs create mode 100644 neo/IO/ByteArrayComparer.cs diff --git a/neo.UnitTests/IO/UT_IOHelper.cs b/neo.UnitTests/IO/UT_IOHelper.cs index e113ce8cd4..b0593c02ec 100644 --- a/neo.UnitTests/IO/UT_IOHelper.cs +++ b/neo.UnitTests/IO/UT_IOHelper.cs @@ -234,9 +234,9 @@ public void TestReadBytesWithGrouping() else { byte[] caseArray = new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x10, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x10, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x11}; MemoryStream stream = new MemoryStream(); @@ -415,11 +415,11 @@ public void TestWriteBytesWithGrouping() byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x10, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x10, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x0C}), Encoding.Default.GetString(byteArray)); + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x04}), Encoding.Default.GetString(byteArray)); } [TestMethod] @@ -546,4 +546,4 @@ public void TestWriteVarString() Assert.AreEqual(0x61, byteArray[1]); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/UT_DataCache.cs b/neo.UnitTests/UT_DataCache.cs new file mode 100644 index 0000000000..85df272db5 --- /dev/null +++ b/neo.UnitTests/UT_DataCache.cs @@ -0,0 +1,126 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Caching; +using Neo.Ledger; +using System.Linq; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_DataCache + { + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + } + + [TestMethod] + public void TestCachedFind_Between() + { + var snapshot = TestBlockchain.GetStore().GetSnapshot(); + var storages = snapshot.Storages; + var cache = new CloneCache(storages); + + storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + + storages.Add + ( + new StorageKey() { Key = new byte[] { 0x01, 0x01 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + storages.Add + ( + new StorageKey() { Key = new byte[] { 0x00, 0x01 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + storages.Add + ( + new StorageKey() { Key = new byte[] { 0x00, 0x03 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + cache.Add + ( + new StorageKey() { Key = new byte[] { 0x01, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + cache.Add + ( + new StorageKey() { Key = new byte[] { 0x00, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + + CollectionAssert.AreEqual( + cache.Find(new byte[21]).Select(u => u.Key.Key[1]).ToArray(), + new byte[] { 0x01, 0x02, 0x03 } + ); + + storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + } + + [TestMethod] + public void TestCachedFind_Last() + { + var snapshot = TestBlockchain.GetStore().GetSnapshot(); + var storages = snapshot.Storages; + var cache = new CloneCache(storages); + + storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + + storages.Add + ( + new StorageKey() { Key = new byte[] { 0x00, 0x01 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + storages.Add + ( + new StorageKey() { Key = new byte[] { 0x01, 0x01 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + cache.Add + ( + new StorageKey() { Key = new byte[] { 0x00, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + cache.Add + ( + new StorageKey() { Key = new byte[] { 0x01, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + + CollectionAssert.AreEqual( + cache.Find(new byte[21]).Select(u => u.Key.Key[1]).ToArray(), + new byte[] { 0x01, 0x02 } + ); + + storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + } + + [TestMethod] + public void TestCachedFind_Empty() + { + var snapshot = TestBlockchain.GetStore().GetSnapshot(); + var storages = snapshot.Storages; + var cache = new CloneCache(storages); + + storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + + cache.Add + ( + new StorageKey() { Key = new byte[] { 0x00, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + cache.Add + ( + new StorageKey() { Key = new byte[] { 0x01, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageItem() { IsConstant = false, Value = new byte[] { } } + ); + + CollectionAssert.AreEqual( + cache.Find(new byte[21]).Select(u => u.Key.Key[1]).ToArray(), + new byte[] { 0x02 } + ); + + storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + } + } +} diff --git a/neo/IO/ByteArrayComparer.cs b/neo/IO/ByteArrayComparer.cs new file mode 100644 index 0000000000..956ab758e6 --- /dev/null +++ b/neo/IO/ByteArrayComparer.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; + +namespace Neo.IO +{ + internal class ByteArrayComparer : IComparer + { + public static readonly ByteArrayComparer Default = new ByteArrayComparer(); + + public int Compare(byte[] x, byte[] y) + { + int length = Math.Min(x.Length, y.Length); + for (int i = 0; i < length; i++) + { + int r = x[i].CompareTo(y[i]); + if (r != 0) return r; + } + return x.Length.CompareTo(y.Length); + } + } +} diff --git a/neo/IO/Caching/DataCache.cs b/neo/IO/Caching/DataCache.cs index 5e9886d10c..278c01d650 100644 --- a/neo/IO/Caching/DataCache.cs +++ b/neo/IO/Caching/DataCache.cs @@ -118,16 +118,58 @@ public void DeleteWhere(Func predicate) } } + /// + /// Find the entries that start with the `key_prefix` + /// + /// Must maintain the deserialized format of TKey + /// Entries found with the desired prefix public IEnumerable> Find(byte[] key_prefix = null) { + IEnumerable<(byte[], TKey, TValue)> cached; lock (dictionary) { - foreach (var pair in FindInternal(key_prefix ?? new byte[0])) - if (!dictionary.ContainsKey(pair.Key)) - yield return pair; - foreach (var pair in dictionary) - if (pair.Value.State != TrackState.Deleted && (key_prefix == null || pair.Key.ToArray().Take(key_prefix.Length).SequenceEqual(key_prefix))) - yield return new KeyValuePair(pair.Key, pair.Value.Item); + cached = dictionary + .Where(p => p.Value.State != TrackState.Deleted && (key_prefix == null || p.Key.ToArray().Take(key_prefix.Length).SequenceEqual(key_prefix))) + .Select(p => + ( + KeyBytes: p.Key.ToArray(), + p.Key, + p.Value.Item + )) + .OrderBy(p => p.KeyBytes, ByteArrayComparer.Default) + .ToArray(); + } + var uncached = FindInternal(key_prefix ?? new byte[0]) + .Where(p => !dictionary.ContainsKey(p.Key)) + .Select(p => + ( + KeyBytes: p.Key.ToArray(), + p.Key, + p.Value + )); + using (var e1 = cached.GetEnumerator()) + using (var e2 = uncached.GetEnumerator()) + { + (byte[] KeyBytes, TKey Key, TValue Item) i1, i2; + bool c1 = e1.MoveNext(); + bool c2 = e2.MoveNext(); + i1 = c1 ? e1.Current : default; + i2 = c2 ? e2.Current : default; + while (c1 || c2) + { + if (!c2 || (c1 && ByteArrayComparer.Default.Compare(i1.KeyBytes, i2.KeyBytes) < 0)) + { + yield return new KeyValuePair(i1.Key, i1.Item); + c1 = e1.MoveNext(); + i1 = c1 ? e1.Current : default; + } + else + { + yield return new KeyValuePair(i2.Key, i2.Item); + c2 = e2.MoveNext(); + i2 = c2 ? e2.Current : default; + } + } } } diff --git a/neo/IO/Helper.cs b/neo/IO/Helper.cs index 647ae2cfb1..a306e297d5 100644 --- a/neo/IO/Helper.cs +++ b/neo/IO/Helper.cs @@ -92,17 +92,16 @@ public static byte[] ReadBytesWithGrouping(this BinaryReader reader) { using (MemoryStream ms = new MemoryStream()) { - int padding = 0; + int count; do { byte[] group = reader.ReadBytes(GroupingSizeInBytes); - padding = reader.ReadByte(); - if (padding > GroupingSizeInBytes) + count = reader.ReadByte(); + if (count > GroupingSizeInBytes) throw new FormatException(); - int count = GroupingSizeInBytes - padding; if (count > 0) ms.Write(group, 0, count); - } while (padding == 0); + } while (count == GroupingSizeInBytes); return ms.ToArray(); } } @@ -200,7 +199,7 @@ public static void WriteBytesWithGrouping(this BinaryWriter writer, byte[] value while (remain >= GroupingSizeInBytes) { writer.Write(value, index, GroupingSizeInBytes); - writer.Write((byte)0); + writer.Write((byte)GroupingSizeInBytes); index += GroupingSizeInBytes; remain -= GroupingSizeInBytes; } @@ -209,7 +208,7 @@ public static void WriteBytesWithGrouping(this BinaryWriter writer, byte[] value int padding = GroupingSizeInBytes - remain; for (int i = 0; i < padding; i++) writer.Write((byte)0); - writer.Write((byte)padding); + writer.Write((byte)remain); } public static void WriteFixedString(this BinaryWriter writer, string value, int length) diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index 5787cebdb4..669a9b7073 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -353,7 +353,7 @@ private static bool Storage_Find(ApplicationEngine engine) while (remain >= 16) { ms.Write(prefix, index, 16); - ms.WriteByte(0); + ms.WriteByte(16); index += 16; remain -= 16; } From 81d6754e1d5aa61db272cab377ae5fa2cc928a0e Mon Sep 17 00:00:00 2001 From: Krain Chen Date: Tue, 30 Jul 2019 22:20:55 +0800 Subject: [PATCH 059/305] Add NEO SDK RPC module (#850) * Add NEO SDK based on RPC client * add rpc interface methods for neo3 * update unit test * add unit test * Update TransactionHelper.cs Changed for neo 3.0, not final yet. * implement sdk rpc client methods * backup files * change class name * remove uncompleted modules for pull request * change json deserialize method with Neo JObject * modified JSON implementation, added FromJson() * more RPC change * PR correction * RPC module fix, remove newton.json * fix * fix getblock issue * PR correction * PR Correction * PR Correction: rename RPC models * PR Correction * resolve conflicts * Clean code * Clean code * Clean code * Clean code * Update RpcValidateAddressResult.cs * Clean code * PR correction * Move test file to the right place --- neo.UnitTests/Network/RPC/UT_RpcClient.cs | 540 ++++++++++++++++++ neo/Ledger/ContractState.cs | 8 + neo/Network/P2P/Payloads/Block.cs | 11 + neo/Network/P2P/Payloads/BlockBase.cs | 16 +- neo/Network/P2P/Payloads/ConsensusData.cs | 10 + neo/Network/P2P/Payloads/Header.cs | 14 +- neo/Network/P2P/Payloads/Transaction.cs | 15 + .../P2P/Payloads/TransactionAttribute.cs | 8 + neo/Network/P2P/Payloads/Witness.cs | 8 + neo/Network/RPC/Models/RpcBlock.cs | 37 ++ neo/Network/RPC/Models/RpcBlockHeader.cs | 37 ++ neo/Network/RPC/Models/RpcInvokeResult.cs | 69 +++ neo/Network/RPC/Models/RpcNep5Balances.cs | 57 ++ neo/Network/RPC/Models/RpcPeers.cs | 55 ++ neo/Network/RPC/Models/RpcPlugin.cs | 32 ++ neo/Network/RPC/Models/RpcRawMemPool.cs | 32 ++ neo/Network/RPC/Models/RpcRequest.cs | 37 ++ neo/Network/RPC/Models/RpcResponse.cs | 72 +++ neo/Network/RPC/Models/RpcTransaction.cs | 41 ++ .../RPC/Models/RpcValidateAddressResult.cs | 27 + neo/Network/RPC/Models/RpcValidator.cs | 32 ++ neo/Network/RPC/Models/RpcVersion.cs | 35 ++ neo/Network/RPC/RpcClient.cs | 292 ++++++++++ 23 files changed, 1482 insertions(+), 3 deletions(-) create mode 100644 neo.UnitTests/Network/RPC/UT_RpcClient.cs create mode 100644 neo/Network/RPC/Models/RpcBlock.cs create mode 100644 neo/Network/RPC/Models/RpcBlockHeader.cs create mode 100644 neo/Network/RPC/Models/RpcInvokeResult.cs create mode 100644 neo/Network/RPC/Models/RpcNep5Balances.cs create mode 100644 neo/Network/RPC/Models/RpcPeers.cs create mode 100644 neo/Network/RPC/Models/RpcPlugin.cs create mode 100644 neo/Network/RPC/Models/RpcRawMemPool.cs create mode 100644 neo/Network/RPC/Models/RpcRequest.cs create mode 100644 neo/Network/RPC/Models/RpcResponse.cs create mode 100644 neo/Network/RPC/Models/RpcTransaction.cs create mode 100644 neo/Network/RPC/Models/RpcValidateAddressResult.cs create mode 100644 neo/Network/RPC/Models/RpcValidator.cs create mode 100644 neo/Network/RPC/Models/RpcVersion.cs create mode 100644 neo/Network/RPC/RpcClient.cs diff --git a/neo.UnitTests/Network/RPC/UT_RpcClient.cs b/neo.UnitTests/Network/RPC/UT_RpcClient.cs new file mode 100644 index 0000000000..955c481248 --- /dev/null +++ b/neo.UnitTests/Network/RPC/UT_RpcClient.cs @@ -0,0 +1,540 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Moq.Protected; +using Neo.IO; +using Neo.IO.Json; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Network.RPC; +using Neo.Network.RPC.Models; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; +using Neo.VM; +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Neo.UnitTests.Network.RPC +{ + [TestClass] + public class UT_RpcClient + { + RpcClient rpc; + Mock handlerMock; + + [TestInitialize] + public void TestSetup() + { + handlerMock = new Mock(MockBehavior.Strict); + + // use real http client with mocked handler here + var httpClient = new HttpClient(handlerMock.Object) + { + BaseAddress = new Uri("http://seed1.neo.org:10331"), + }; + + rpc = new RpcClient(httpClient); + } + + private void MockResponse(string content) + { + handlerMock.Protected() + // Setup the PROTECTED method to mock + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny() + ) + // prepare the expected response of the mocked http call + .ReturnsAsync(new HttpResponseMessage() + { + StatusCode = HttpStatusCode.OK, + Content = new StringContent(content), + }) + .Verifiable(); + } + + private JObject CreateErrorResponse(JObject id, int code, string message, JObject data = null) + { + JObject response = CreateResponse(id); + response["error"] = new JObject(); + response["error"]["code"] = code; + response["error"]["message"] = message; + if (data != null) + response["error"]["data"] = data; + return response; + } + + private JObject CreateResponse(JObject id) + { + JObject response = new JObject(); + response["jsonrpc"] = "2.0"; + response["id"] = id; + return response; + } + + [TestMethod] + public void TestErrorResponse() + { + JObject response = CreateErrorResponse(null, -32700, "Parse error"); + MockResponse(response.ToString()); + try + { + var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + } + catch (RpcException ex) + { + Assert.AreEqual(-32700, ex.HResult); + Assert.AreEqual("Parse error", ex.Message); + } + } + + [TestMethod] + public void TestGetBestBlockHash() + { + JObject response = CreateResponse(1); + response["result"] = "000000002deadfa82cbc4682f5800"; + MockResponse(response.ToString()); + + var result = rpc.GetBestBlockHash(); + Assert.AreEqual("000000002deadfa82cbc4682f5800", result); + } + + [TestMethod] + public void TestGetBlockHex() + { + JObject response = CreateResponse(1); + response["result"] = "000000002deadfa82cbc4682f5800"; + MockResponse(response.ToString()); + + var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + Assert.AreEqual("000000002deadfa82cbc4682f5800", result); + } + + [TestMethod] + public void TestGetBlock() + { + // create block + var block = new Block(); + TestUtils.SetupBlockWithValues(block, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, 0); + + block.Transactions = new[] + { + TestUtils.GetTransaction(), + TestUtils.GetTransaction(), + TestUtils.GetTransaction() + }; + + JObject json = block.ToJson(); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetBlock("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); + Assert.IsNull(result.Confirmations); + Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); + Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); + + // verbose with confirmations + json["confirmations"] = 20; + json["nextblockhash"] = "773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"; + MockResponse(response.ToString()); + result = rpc.GetBlock("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); + Assert.AreEqual(20, result.Confirmations); + Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); + Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); + } + + [TestMethod] + public void TestGetBlockCount() + { + JObject response = CreateResponse(1); + response["result"] = 100; + MockResponse(response.ToString()); + + var result = rpc.GetBlockCount(); + Assert.AreEqual(100, result); + } + + [TestMethod] + public void TestGetBlockHash() + { + JObject response = CreateResponse(1); + response["result"] = "0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; + MockResponse(response.ToString()); + + var result = rpc.GetBlockHash(100); + Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); + } + + [TestMethod] + public void TestGetBlockHeaderHex() + { + JObject response = CreateResponse(1); + response["result"] = "0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; + MockResponse(response.ToString()); + + var result = rpc.GetBlockHeaderHex("100"); + Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); + } + + [TestMethod] + public void TestGetBlockHeader() + { + Header header = new Header(); + TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal); + + JObject json = header.ToJson(); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetBlockHeader("100"); + Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); + Assert.IsNull(result.Confirmations); + + json["confirmations"] = 20; + json["nextblockhash"] = "4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; + MockResponse(response.ToString()); + result = rpc.GetBlockHeader("100"); + Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); + Assert.AreEqual(20, result.Confirmations); + } + + [TestMethod] + public void TestGetBlockSysFee() + { + JObject response = CreateResponse(1); + response["result"] = "195500"; + MockResponse(response.ToString()); + + var result = rpc.GetBlockSysFee(100); + Assert.AreEqual("195500", result); + } + + [TestMethod] + public void TestGetConnectionCount() + { + JObject response = CreateResponse(1); + response["result"] = 9; + MockResponse(response.ToString()); + + var result = rpc.GetConnectionCount(); + Assert.AreEqual(9, result); + } + + [TestMethod] + public void TestGetContractState() + { + var sb = new ScriptBuilder(); + sb.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); + + ContractState state = new ContractState + { + Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(sb.ToArray()).ToArray(), + Manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")) + }; + + JObject response = CreateResponse(1); + response["result"] = state.ToJson(); + MockResponse(response.ToString()); + + var result = rpc.GetContractState("17694b31cc7ee215cea2ded146e0b2b28768fc46"); + + Assert.AreEqual(state.Script.ToHexString(), result.Script.ToHexString()); + Assert.AreEqual(state.Manifest.Abi.EntryPoint.Name, result.Manifest.Abi.EntryPoint.Name); + } + + [TestMethod] + public void TestGetPeers() + { + JObject response = CreateResponse(1); + response["result"] = JObject.Parse(@"{ + ""unconnected"": [ + { + ""address"": ""::ffff:70.73.16.236"", + ""port"": 10333 + }, + { + ""address"": ""::ffff:82.95.77.148"", + ""port"": 10333 + }, + { + ""address"": ""::ffff:49.50.215.166"", + ""port"": 10333 + } + ], + ""bad"": [], + ""connected"": [ + { + ""address"": ""::ffff:139.219.106.33"", + ""port"": 10333 + }, + { + ""address"": ""::ffff:47.88.53.224"", + ""port"": 10333 + } + ] + }"); + MockResponse(response.ToString()); + + var result = rpc.GetPeers(); + Assert.AreEqual("::ffff:139.219.106.33", result.Connected[0].Address); + Assert.AreEqual("::ffff:82.95.77.148", result.Unconnected[1].Address); + } + + [TestMethod] + public void TestGetRawMempool() + { + JObject response = CreateResponse(1); + response["result"] = JObject.Parse(@"[ + ""0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"", + ""0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7"", + ""0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6"" + ]"); + MockResponse(response.ToString()); + + var result = rpc.GetRawMempool(); + Assert.AreEqual("0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7", result[1]); + } + + [TestMethod] + public void TestGetRawMempoolBoth() + { + JObject json = new JObject(); + json["height"] = 65535; + json["verified"] = new JArray(new[] { "0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e" }.Select(p => (JObject)p)); + json["unverified"] = new JArray(new[] { "0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7", "0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6" }.Select(p => (JObject)p)); + + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetRawMempoolBoth(); + Assert.AreEqual((uint)65535, result.Height); + Assert.AreEqual("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e", result.Verified[0]); + Assert.AreEqual("0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6", result.UnVerified[1]); + } + + [TestMethod] + public void TestGetRawTransactionHex() + { + var json = TestUtils.GetTransaction().ToArray().ToHexString(); + + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + //var result = rpc.GetRawTransactionHex("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); + var result = rpc.GetRawTransactionHex(TestUtils.GetTransaction().Hash.ToString()); + Assert.AreEqual(json, result); + } + + [TestMethod] + public void TestGetRawTransaction() + { + var transaction = TestUtils.GetTransaction(); + JObject json = transaction.ToJson(); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); + Assert.AreEqual(transaction.Hash, result.Transaction.Hash); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + + json["blockhash"] = UInt256.Zero.ToString(); + json["confirmations"] = 100; + json["blocktime"] = 10; + MockResponse(response.ToString()); + + result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); + Assert.AreEqual(transaction.Hash, result.Transaction.Hash); + Assert.AreEqual(100, result.Confirmations); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + } + + [TestMethod] + public void TestGetStorage() + { + JObject json = "4c696e"; + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetStorage("03febccf81ac85e3d795bc5cbd4e84e907812aa3", "5065746572"); + Assert.AreEqual("4c696e", result); + } + + [TestMethod] + public void TestGetTransactionHeight() + { + JObject json = 10000; + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetTransactionHeight("9c909e1e3ba03290553a68d862e002c7a21ba302e043fc492fe069bf6a134d29"); + Assert.AreEqual(json.ToString(), result.ToString()); + } + + [TestMethod] + public void TestGetValidators() + { + JObject json = JObject.Parse(@"[ + { + ""publickey"": ""02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"", + ""votes"": ""46632420"", + ""active"": true + }, + { + ""publickey"": ""024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"", + ""votes"": ""46632420"", + ""active"": true + } + ]"); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetValidators(); + Assert.AreEqual(((JArray)json)[0].ToString(), (result[0]).ToJson().ToString()); + } + + [TestMethod] + public void TestGetVersion() + { + JObject json = new JObject(); + json["tcpPort"] = 30001; + json["wsPort"] = 30002; + json["nonce"] = 1546258664; + json["useragent"] = "/NEO:2.7.5/"; + + var json1 = JObject.Parse(@"{ + ""tcpPort"": 30001, + ""wsPort"": 30002, + ""nonce"": 1546258664, + ""useragent"": ""/NEO:2.7.5/"" + }"); + Assert.AreEqual(json.ToString(), json1.ToString()); + + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.GetVersion(); + Assert.AreEqual(30001, result.TcpPort); + Assert.AreEqual("/NEO:2.7.5/", result.UserAgent); + } + + [TestMethod] + public void TestInvokeFunction() + { + JObject json = JObject.Parse(@" + { + ""script"": ""1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf"", + ""state"": ""HALT"", + ""gas_consumed"": ""0.311"", + ""stack"": [ + { + ""type"": ""ByteArray"", + ""value"": ""262bec084432"" + } + ], + ""tx"":""d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"" + }"); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.InvokeFunction("af7c7328eee5a275a3bcaee2bf0cf662b5e739be", "balanceOf", new[] { new RpcStack { Type = "Hash160", Value = "91b83e96f2a7c4fdf0c1688441ec61986c7cae26" } }); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + } + + [TestMethod] + public void TestInvokeScript() + { + JObject json = JObject.Parse(@" + { + ""script"": ""1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf"", + ""state"": ""HALT"", + ""gas_consumed"": ""0.311"", + ""stack"": [ + { + ""type"": ""ByteArray"", + ""value"": ""262bec084432"" + } + ], + ""tx"":""d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"" + }"); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191"); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + } + + [TestMethod] + public void TestListPlugins() + { + JObject json = JObject.Parse(@"[{ + ""name"": ""SimplePolicyPlugin"", + ""version"": ""2.10.1.0"", + ""interfaces"": [ + ""ILogPlugin"", + ""IPolicyPlugin"" + ] + }]"); + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.ListPlugins(); + Assert.AreEqual(((JArray)json)[0].ToString(), result[0].ToJson().ToString()); + } + + [TestMethod] + public void TestSendRawTransaction() + { + JObject json = true; + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.SendRawTransaction("80000001195876cb34364dc38b730077156c6bc3a7fc570044a66fbfeeea56f71327e8ab0000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500c65eaf440000000f9a23e06f74cf86b8827a9108ec2e0f89ad956c9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50092e14b5e00000030aab52ad93f6ce17ca07fa88fc191828c58cb71014140915467ecd359684b2dc358024ca750609591aa731a0b309c7fb3cab5cd0836ad3992aa0a24da431f43b68883ea5651d548feb6bd3c8e16376e6e426f91f84c58232103322f35c7819267e721335948d385fae5be66e7ba8c748ac15467dcca0693692dac"); + Assert.AreEqual(json.ToString(), ((JObject)result).ToString()); + } + + [TestMethod] + public void TestSubmitBlock() + { + JObject json = true; + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.SubmitBlock("03febccf81ac85e3d795bc5cbd4e84e907812aa3"); + Assert.AreEqual(json.ToString(), ((JObject)result).ToString()); + } + + [TestMethod] + public void TestValidateAddress() + { + JObject json = new JObject(); + json["address"] = "AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"; + json["isvalid"] = false; + JObject response = CreateResponse(1); + response["result"] = json; + MockResponse(response.ToString()); + + var result = rpc.ValidateAddress("AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + } + } +} diff --git a/neo/Ledger/ContractState.cs b/neo/Ledger/ContractState.cs index 8f97d4b363..da02db912f 100644 --- a/neo/Ledger/ContractState.cs +++ b/neo/Ledger/ContractState.cs @@ -64,5 +64,13 @@ public JObject ToJson() json["manifest"] = Manifest.ToJson(); return json; } + + public static ContractState FromJson(JObject json) + { + ContractState contractState = new ContractState(); + contractState.Script = json["script"].AsString().HexToBytes(); + contractState.Manifest = ContractManifest.FromJson(json["manifest"]); + return contractState; + } } } diff --git a/neo/Network/P2P/Payloads/Block.cs b/neo/Network/P2P/Payloads/Block.cs index 58faf54cb1..3f29bb9428 100644 --- a/neo/Network/P2P/Payloads/Block.cs +++ b/neo/Network/P2P/Payloads/Block.cs @@ -2,6 +2,7 @@ using Neo.IO; using Neo.IO.Json; using Neo.Ledger; +using Neo.Wallets; using System; using System.Collections.Generic; using System.IO; @@ -106,6 +107,16 @@ public override JObject ToJson() return json; } + public new static Block FromJson(JObject json) + { + Block block = new Block(); + BlockBase blockBase = block; + blockBase.FromJson(json); + block.ConsensusData = ConsensusData.FromJson(json["consensus_data"]); + block.Transactions = ((JArray)json["tx"]).Select(p => Transaction.FromJson(p)).ToArray(); + return block; + } + public TrimmedBlock Trim() { return new TrimmedBlock diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 22ef72d021..8820c95538 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -6,6 +6,7 @@ using Neo.Wallets; using System; using System.IO; +using System.Linq; namespace Neo.Network.P2P.Payloads { @@ -32,7 +33,7 @@ public UInt256 Hash } } - public virtual int Size => + public virtual int Size => sizeof(uint) + //Version PrevHash.Size + //PrevHash MerkleRoot.Size + //MerkleRoot @@ -41,7 +42,7 @@ public UInt256 Hash NextConsensus.Size + //NextConsensus 1 + // Witness.Size; //Witness - + Witness[] IVerifiable.Witnesses { get @@ -111,6 +112,17 @@ public virtual JObject ToJson() return json; } + public void FromJson(JObject json) + { + Version = (uint)json["version"].AsNumber(); + PrevHash = UInt256.Parse(json["previousblockhash"].AsString()); + MerkleRoot = UInt256.Parse(json["merkleroot"].AsString()); + Timestamp = (ulong)json["time"].AsNumber(); + Index = (uint)json["index"].AsNumber(); + NextConsensus = json["nextconsensus"].AsString().ToScriptHash(); + Witness = ((JArray)json["witnesses"]).Select(p => Witness.FromJson(p)).FirstOrDefault(); + } + public virtual bool Verify(Snapshot snapshot) { Header prev_header = snapshot.GetHeader(PrevHash); diff --git a/neo/Network/P2P/Payloads/ConsensusData.cs b/neo/Network/P2P/Payloads/ConsensusData.cs index a48a8f1304..622c42e464 100644 --- a/neo/Network/P2P/Payloads/ConsensusData.cs +++ b/neo/Network/P2P/Payloads/ConsensusData.cs @@ -2,6 +2,7 @@ using Neo.IO; using Neo.IO.Json; using Neo.Ledger; +using System.Globalization; using System.IO; namespace Neo.Network.P2P.Payloads @@ -45,5 +46,14 @@ public JObject ToJson() json["nonce"] = Nonce.ToString("x16"); return json; } + + public static ConsensusData FromJson(JObject json) + { + ConsensusData block = new ConsensusData(); + block.PrimaryIndex = (uint)json["primary"].AsNumber(); + block.Nonce = ulong.Parse(json["nonce"].AsString(), NumberStyles.HexNumber); + return block; + } + } } diff --git a/neo/Network/P2P/Payloads/Header.cs b/neo/Network/P2P/Payloads/Header.cs index 149f4a020f..abd7fbd370 100644 --- a/neo/Network/P2P/Payloads/Header.cs +++ b/neo/Network/P2P/Payloads/Header.cs @@ -1,6 +1,9 @@ -using Neo.Ledger; +using Neo.IO.Json; +using Neo.Ledger; +using Neo.Wallets; using System; using System.IO; +using System.Linq; namespace Neo.Network.P2P.Payloads { @@ -51,5 +54,14 @@ public TrimmedBlock Trim() Hashes = new UInt256[0] }; } + + public new static Header FromJson(JObject json) + { + Header header = new Header(); + BlockBase blockBase = header; + blockBase.FromJson(json); + return header; + } + } } diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 923fe6447f..40efe0e9d3 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -177,6 +177,21 @@ public JObject ToJson() return json; } + public static Transaction FromJson(JObject json) + { + Transaction tx = new Transaction(); + tx.Version = byte.Parse(json["version"].AsString()); + tx.Nonce = uint.Parse(json["nonce"].AsString()); + tx.Sender = json["sender"].AsString().ToScriptHash(); + tx.SystemFee = long.Parse(json["sys_fee"].AsString()); + tx.NetworkFee = long.Parse(json["net_fee"].AsString()); + tx.ValidUntilBlock = uint.Parse(json["valid_until_block"].AsString()); + tx.Attributes = ((JArray)json["attributes"]).Select(p => TransactionAttribute.FromJson(p)).ToArray(); + tx.Script = json["script"].AsString().HexToBytes(); + tx.Witnesses = ((JArray)json["witnesses"]).Select(p => Witness.FromJson(p)).ToArray(); + return tx; + } + bool IInventory.Verify(Snapshot snapshot) { return Verify(snapshot, Enumerable.Empty()); diff --git a/neo/Network/P2P/Payloads/TransactionAttribute.cs b/neo/Network/P2P/Payloads/TransactionAttribute.cs index 2f384fa046..99d1671ebd 100644 --- a/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -33,5 +33,13 @@ public JObject ToJson() json["data"] = Data.ToHexString(); return json; } + + public static TransactionAttribute FromJson(JObject json) + { + TransactionAttribute transactionAttribute = new TransactionAttribute(); + transactionAttribute.Usage = (TransactionAttributeUsage)(byte.Parse(json["usage"].AsString())); + transactionAttribute.Data = json["data"].AsString().HexToBytes(); + return transactionAttribute; + } } } diff --git a/neo/Network/P2P/Payloads/Witness.cs b/neo/Network/P2P/Payloads/Witness.cs index 237a954642..d352882e17 100644 --- a/neo/Network/P2P/Payloads/Witness.cs +++ b/neo/Network/P2P/Payloads/Witness.cs @@ -45,5 +45,13 @@ public JObject ToJson() json["verification"] = VerificationScript.ToHexString(); return json; } + + public static Witness FromJson(JObject json) + { + Witness witness = new Witness(); + witness.InvocationScript = json["invocation"].AsString().HexToBytes(); + witness.VerificationScript = json["verification"].AsString().HexToBytes(); + return witness; + } } } diff --git a/neo/Network/RPC/Models/RpcBlock.cs b/neo/Network/RPC/Models/RpcBlock.cs new file mode 100644 index 0000000000..0d49b3b53a --- /dev/null +++ b/neo/Network/RPC/Models/RpcBlock.cs @@ -0,0 +1,37 @@ +using Neo.IO.Json; +using Neo.Network.P2P.Payloads; + +namespace Neo.Network.RPC.Models +{ + public class RpcBlock + { + public Block Block { get; set; } + + public int? Confirmations { get; set; } + + public UInt256 NextBlockHash { get; set; } + + public JObject ToJson() + { + JObject json = Block.ToJson(); + if (Confirmations != null) + { + json["confirmations"] = Confirmations; + json["nextblockhash"] = NextBlockHash.ToString(); + } + return json; + } + + public static RpcBlock FromJson(JObject json) + { + RpcBlock block = new RpcBlock(); + block.Block = Block.FromJson(json); + if (json["confirmations"] != null) + { + block.Confirmations = (int)json["confirmations"].AsNumber(); + block.NextBlockHash = UInt256.Parse(json["nextblockhash"].AsString()); + } + return block; + } + } +} diff --git a/neo/Network/RPC/Models/RpcBlockHeader.cs b/neo/Network/RPC/Models/RpcBlockHeader.cs new file mode 100644 index 0000000000..250acfd818 --- /dev/null +++ b/neo/Network/RPC/Models/RpcBlockHeader.cs @@ -0,0 +1,37 @@ +using Neo.IO.Json; +using Neo.Network.P2P.Payloads; + +namespace Neo.Network.RPC.Models +{ + public class RpcBlockHeader + { + public Header Header { get; set; } + + public int? Confirmations { get; set; } + + public UInt256 NextBlockHash { get; set; } + + public JObject ToJson() + { + JObject json = Header.ToJson(); + if (Confirmations != null) + { + json["confirmations"] = Confirmations; + json["nextblockhash"] = NextBlockHash.ToString(); + } + return json; + } + + public static RpcBlockHeader FromJson(JObject json) + { + RpcBlockHeader block = new RpcBlockHeader(); + block.Header = Header.FromJson(json); + if (json["confirmations"] != null) + { + block.Confirmations = (int)json["confirmations"].AsNumber(); + block.NextBlockHash = UInt256.Parse(json["nextblockhash"].AsString()); + } + return block; + } + } +} diff --git a/neo/Network/RPC/Models/RpcInvokeResult.cs b/neo/Network/RPC/Models/RpcInvokeResult.cs new file mode 100644 index 0000000000..9a0f646588 --- /dev/null +++ b/neo/Network/RPC/Models/RpcInvokeResult.cs @@ -0,0 +1,69 @@ +using Neo.IO.Json; +using Newtonsoft.Json; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcInvokeResult + { + [JsonProperty(PropertyName = "script")] + public string Script { get; set; } + + [JsonProperty(PropertyName = "state")] + public string State { get; set; } + + [JsonProperty(PropertyName = "gas_consumed")] + public string GasConsumed { get; set; } + + [JsonProperty(PropertyName = "stack")] + public RpcStack[] Stack { get; set; } + + [JsonProperty(PropertyName = "tx")] + public string Tx { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["script"] = Script; + json["state"] = State; + json["gas_consumed"] = GasConsumed; + json["stack"] = new JArray(Stack.Select(p => p.ToJson())); + json["tx"] = Tx; + return json; + } + + public static RpcInvokeResult FromJson(JObject json) + { + RpcInvokeResult invokeScriptResult = new RpcInvokeResult(); + invokeScriptResult.Script = json["script"].AsString(); + invokeScriptResult.State = json["state"].AsString(); + invokeScriptResult.GasConsumed = json["gas_consumed"].AsString(); + invokeScriptResult.Tx = json["tx"].AsString(); + invokeScriptResult.Stack = ((JArray)json["stack"]).Select(p => RpcStack.FromJson(p)).ToArray(); + return invokeScriptResult; + } + } + + public class RpcStack + { + public string Type { get; set; } + + public string Value { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["type"] = Type; + json["value"] = Value; + return json; + } + + public static RpcStack FromJson(JObject json) + { + RpcStack stackJson = new RpcStack(); + stackJson.Type = json["type"].AsString(); + stackJson.Value = json["value"].AsString(); + return stackJson; + } + } +} diff --git a/neo/Network/RPC/Models/RpcNep5Balances.cs b/neo/Network/RPC/Models/RpcNep5Balances.cs new file mode 100644 index 0000000000..edcf8d7f5e --- /dev/null +++ b/neo/Network/RPC/Models/RpcNep5Balances.cs @@ -0,0 +1,57 @@ +using Neo.IO.Json; +using System.Linq; +using System.Numerics; + +namespace Neo.Network.RPC.Models +{ + public class RpcNep5Balances + { + public string Address { get; set; } + + public RpcNep5Balance[] Balances { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["address"] = Address; + json["balance"] = Balances.Select(p => p.ToJson()).ToArray(); + return json; + } + + public static RpcNep5Balances FromJson(JObject json) + { + RpcNep5Balances nep5Balance = new RpcNep5Balances(); + nep5Balance.Address = json["address"].AsString(); + //List listBalance = new List(); + nep5Balance.Balances = ((JArray)json["balance"]).Select(p => RpcNep5Balance.FromJson(p)).ToArray(); + return nep5Balance; + } + } + + public class RpcNep5Balance + { + public UInt160 AssetHash { get; set; } + + public BigInteger Amount { get; set; } + + public uint LastUpdatedBlock { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["asset_hash"] = AssetHash.ToArray().ToHexString(); + json["amount"] = Amount.ToString(); + json["last_updated_block"] = LastUpdatedBlock.ToString(); + return json; + } + + public static RpcNep5Balance FromJson(JObject json) + { + RpcNep5Balance balance = new RpcNep5Balance(); + balance.AssetHash = UInt160.Parse(json["asset_hash"].AsString()); + balance.Amount = BigInteger.Parse(json["amount"].AsString()); + balance.LastUpdatedBlock = uint.Parse(json["last_updated_block"].AsString()); + return balance; + } + } +} diff --git a/neo/Network/RPC/Models/RpcPeers.cs b/neo/Network/RPC/Models/RpcPeers.cs new file mode 100644 index 0000000000..5caa99a66a --- /dev/null +++ b/neo/Network/RPC/Models/RpcPeers.cs @@ -0,0 +1,55 @@ +using Neo.IO.Json; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcPeers + { + public RpcPeer[] Unconnected { get; set; } + + public RpcPeer[] Bad { get; set; } + + public RpcPeer[] Connected { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["unconnected"] = new JArray(Unconnected.Select(p => p.ToJson())); + json["bad"] = new JArray(Bad.Select(p => p.ToJson())); + json["connected"] = new JArray(Connected.Select(p => p.ToJson())); + return json; + } + + public static RpcPeers FromJson(JObject json) + { + RpcPeers result = new RpcPeers(); + result.Unconnected = ((JArray)json["unconnected"]).Select(p => RpcPeer.FromJson(p)).ToArray(); + result.Bad = ((JArray)json["bad"]).Select(p => RpcPeer.FromJson(p)).ToArray(); + result.Connected = ((JArray)json["connected"]).Select(p => RpcPeer.FromJson(p)).ToArray(); + return result; + } + } + + public class RpcPeer + { + public string Address { get; set; } + + public int Port { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["address"] = Address; + json["port"] = Port; + return json; + } + + public static RpcPeer FromJson(JObject json) + { + RpcPeer peer = new RpcPeer(); + peer.Address = json["address"].AsString(); + peer.Port = int.Parse(json["port"].AsString()); + return peer; + } + } +} diff --git a/neo/Network/RPC/Models/RpcPlugin.cs b/neo/Network/RPC/Models/RpcPlugin.cs new file mode 100644 index 0000000000..465b3f2d17 --- /dev/null +++ b/neo/Network/RPC/Models/RpcPlugin.cs @@ -0,0 +1,32 @@ +using Neo.IO.Json; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcPlugin + { + public string Name { get; set; } + + public string Version { get; set; } + + public string[] Interfaces { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["name"] = Name; + json["version"] = Version; + json["interfaces"] = new JArray(Interfaces.Select(p => (JObject)p)); + return json; + } + + public static RpcPlugin FromJson(JObject json) + { + RpcPlugin plugin = new RpcPlugin(); + plugin.Name = json["name"].AsString(); + plugin.Version = json["version"].AsString(); + plugin.Interfaces = ((JArray)json["interfaces"]).Select(p => p.AsString()).ToArray(); + return plugin; + } + } +} diff --git a/neo/Network/RPC/Models/RpcRawMemPool.cs b/neo/Network/RPC/Models/RpcRawMemPool.cs new file mode 100644 index 0000000000..1841916ff2 --- /dev/null +++ b/neo/Network/RPC/Models/RpcRawMemPool.cs @@ -0,0 +1,32 @@ +using Neo.IO.Json; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcRawMemPool + { + public uint Height { get; set; } + + public string[] Verified { get; set; } + + public string[] UnVerified { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["height"] = Height; + json["verified"] = new JArray(Verified.Select(p=>(JObject)p)); + json["unverified"] = new JArray(UnVerified.Select(p => (JObject)p)); + return json; + } + + public static RpcRawMemPool FromJson(JObject json) + { + RpcRawMemPool rawMemPool = new RpcRawMemPool(); + rawMemPool.Height = uint.Parse(json["height"].AsString()); + rawMemPool.Verified = ((JArray)json["verified"]).Select(p => p.AsString()).ToArray(); + rawMemPool.UnVerified = ((JArray)json["unverified"]).Select(p => p.AsString()).ToArray(); + return rawMemPool; + } + } +} diff --git a/neo/Network/RPC/Models/RpcRequest.cs b/neo/Network/RPC/Models/RpcRequest.cs new file mode 100644 index 0000000000..9c2d2f05ec --- /dev/null +++ b/neo/Network/RPC/Models/RpcRequest.cs @@ -0,0 +1,37 @@ +using Neo.IO.Json; +using System.Linq; + +namespace Neo.Network.RPC.Models +{ + public class RpcRequest + { + public int Id { get; set; } + + public string Jsonrpc { get; set; } + + public string Method { get; set; } + + public JObject[] Params { get; set; } + + public static RpcRequest FromJson(JObject json) + { + return new RpcRequest + { + Id = (int)json["id"].AsNumber(), + Jsonrpc = json["jsonrpc"].AsString(), + Method = json["method"].AsString(), + Params = ((JArray)json["params"]).ToArray() + }; + } + + public JObject ToJson() + { + var json = new JObject(); + json["id"] = Id; + json["jsonrpc"] = Jsonrpc; + json["method"] = Method; + json["params"] = new JArray(Params); + return json; + } + } +} diff --git a/neo/Network/RPC/Models/RpcResponse.cs b/neo/Network/RPC/Models/RpcResponse.cs new file mode 100644 index 0000000000..ff20200519 --- /dev/null +++ b/neo/Network/RPC/Models/RpcResponse.cs @@ -0,0 +1,72 @@ +using Neo.IO.Json; + +namespace Neo.Network.RPC.Models +{ + public class RpcResponse + { + public int? Id { get; set; } + + public string Jsonrpc { get; set; } + + public RpcResponseError Error { get; set; } + + public JObject Result { get; set; } + + public string RawResponse { get; set; } + + public static RpcResponse FromJson(JObject json) + { + var response = new RpcResponse + { + Id = (int?)json["id"]?.AsNumber(), + Jsonrpc = json["jsonrpc"].AsString(), + Result = json["result"] + }; + + if (json["error"] != null) + { + response.Error = RpcResponseError.FromJson(json["error"]); + } + + return response; + } + + public JObject ToJson() + { + var json = new JObject(); + json["id"] = Id; + json["jsonrpc"] = Jsonrpc; + json["error"] = Error.ToJson(); + json["result"] = Result; + return json; + } + } + + public class RpcResponseError + { + public int Code { get; set; } + + public string Message { get; set; } + + public JObject Data { get; set; } + + public static RpcResponseError FromJson(JObject json) + { + return new RpcResponseError + { + Code = (int)json["code"].AsNumber(), + Message = json["message"].AsString(), + Data = json["data"], + }; + } + + public JObject ToJson() + { + var json = new JObject(); + json["code"] = Code; + json["message"] = Message; + json["data"] = Data; + return json; + } + } +} diff --git a/neo/Network/RPC/Models/RpcTransaction.cs b/neo/Network/RPC/Models/RpcTransaction.cs new file mode 100644 index 0000000000..3e45830ae6 --- /dev/null +++ b/neo/Network/RPC/Models/RpcTransaction.cs @@ -0,0 +1,41 @@ +using Neo.IO.Json; +using Neo.Network.P2P.Payloads; + +namespace Neo.Network.RPC.Models +{ + public class RpcTransaction + { + public Transaction Transaction { get; set; } + + public UInt256 BlockHash { get; set; } + + public int? Confirmations { get; set; } + + public uint? BlockTime { get; set; } + + public JObject ToJson() + { + JObject json = Transaction.ToJson(); + if (Confirmations != null) + { + json["blockhash"] = BlockHash.ToString(); + json["confirmations"] = Confirmations; + json["blocktime"] = BlockTime; + } + return json; + } + + public static RpcTransaction FromJson(JObject json) + { + RpcTransaction transaction = new RpcTransaction(); + transaction.Transaction = Transaction.FromJson(json); + if (json["confirmations"] != null) + { + transaction.BlockHash = UInt256.Parse(json["blockhash"].AsString()); + transaction.Confirmations = (int)json["confirmations"].AsNumber(); + transaction.BlockTime = (uint)json["blocktime"].AsNumber(); + } + return transaction; + } + } +} diff --git a/neo/Network/RPC/Models/RpcValidateAddressResult.cs b/neo/Network/RPC/Models/RpcValidateAddressResult.cs new file mode 100644 index 0000000000..5e59bf8eaf --- /dev/null +++ b/neo/Network/RPC/Models/RpcValidateAddressResult.cs @@ -0,0 +1,27 @@ +using Neo.IO.Json; + +namespace Neo.Network.RPC.Models +{ + public class RpcValidateAddressResult + { + public string Address { get; set; } + + public bool IsValid { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["address"] = Address; + json["isvalid"] = IsValid; + return json; + } + + public static RpcValidateAddressResult FromJson(JObject json) + { + RpcValidateAddressResult validateAddress = new RpcValidateAddressResult(); + validateAddress.Address = json["address"].AsString(); + validateAddress.IsValid = json["isvalid"].AsBoolean(); + return validateAddress; + } + } +} diff --git a/neo/Network/RPC/Models/RpcValidator.cs b/neo/Network/RPC/Models/RpcValidator.cs new file mode 100644 index 0000000000..b694a6dfcd --- /dev/null +++ b/neo/Network/RPC/Models/RpcValidator.cs @@ -0,0 +1,32 @@ +using Neo.IO.Json; +using System.Numerics; + +namespace Neo.Network.RPC.Models +{ + public class RpcValidator + { + public string PublicKey { get; set; } + + public BigInteger Votes { get; set; } + + public bool Active { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["publickey"] = PublicKey; + json["votes"] = Votes.ToString(); + json["active"] = Active; + return json; + } + + public static RpcValidator FromJson(JObject json) + { + RpcValidator validator = new RpcValidator(); + validator.PublicKey = json["publickey"].AsString(); + validator.Votes = BigInteger.Parse(json["votes"].AsString()); + validator.Active = json["active"].AsBoolean(); + return validator; + } + } +} diff --git a/neo/Network/RPC/Models/RpcVersion.cs b/neo/Network/RPC/Models/RpcVersion.cs new file mode 100644 index 0000000000..02f4dc3407 --- /dev/null +++ b/neo/Network/RPC/Models/RpcVersion.cs @@ -0,0 +1,35 @@ +using Neo.IO.Json; + +namespace Neo.Network.RPC.Models +{ + public class RpcVersion + { + public int TcpPort { get; set; } + + public int WsPort { get; set; } + + public uint Nonce { get; set; } + + public string UserAgent { get; set; } + + public JObject ToJson() + { + JObject json = new JObject(); + json["topPort"] = TcpPort.ToString(); + json["wsPort"] = WsPort.ToString(); + json["nonce"] = Nonce.ToString(); + json["useragent"] = UserAgent; + return json; + } + + public static RpcVersion FromJson(JObject json) + { + RpcVersion version = new RpcVersion(); + version.TcpPort = int.Parse(json["tcpPort"].AsString()); + version.WsPort = int.Parse(json["wsPort"].AsString()); + version.Nonce = uint.Parse(json["nonce"].AsString()); + version.UserAgent = json["useragent"].AsString(); + return version; + } + } +} diff --git a/neo/Network/RPC/RpcClient.cs b/neo/Network/RPC/RpcClient.cs new file mode 100644 index 0000000000..4b968163c7 --- /dev/null +++ b/neo/Network/RPC/RpcClient.cs @@ -0,0 +1,292 @@ +using Neo.IO.Json; +using Neo.Ledger; +using Neo.Network.RPC.Models; +using System; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace Neo.Network.RPC +{ + public class RpcClient : IDisposable + { + private readonly HttpClient httpClient; + + public RpcClient(string url) + { + httpClient = new HttpClient() { BaseAddress = new Uri(url) }; + } + + public RpcClient(HttpClient client) + { + httpClient = client; + } + + public void Dispose() + { + httpClient?.Dispose(); + } + + public async Task SendAsync(RpcRequest request) + { + var requestJson = request.ToJson().ToString(); + var result = await httpClient.PostAsync(httpClient.BaseAddress, new StringContent(requestJson, Encoding.UTF8)); + var content = await result.Content.ReadAsStringAsync(); + var response = RpcResponse.FromJson(JObject.Parse(content)); + response.RawResponse = content; + + if (response.Error != null) + { + throw new RpcException(response.Error.Code, response.Error.Message); + } + + return response; + } + + public RpcResponse Send(RpcRequest request) + { + try + { + return SendAsync(request).Result; + } + catch (AggregateException ex) + { + throw ex.GetBaseException(); + } + } + + private JObject RpcSend(string method, params JObject[] paraArgs) + { + var request = new RpcRequest + { + Id = 1, + Jsonrpc = "2.0", + Method = method, + Params = paraArgs.Select(p => p).ToArray() + }; + return Send(request).Result; + } + + /// + /// Returns the hash of the tallest block in the main chain. + /// + public string GetBestBlockHash() + { + return RpcSend("getbestblockhash").AsString(); + } + + /// + /// Returns the hash of the tallest block in the main chain. + /// The serialized information of the block is returned, represented by a hexadecimal string. + /// + public string GetBlockHex(string hashOrIndex) + { + if (int.TryParse(hashOrIndex, out int index)) + { + return RpcSend("getblock", index).AsString(); + } + return RpcSend("getblock", hashOrIndex).AsString(); + } + + /// + /// Returns the hash of the tallest block in the main chain. + /// + public RpcBlock GetBlock(string hashOrIndex) + { + if (int.TryParse(hashOrIndex, out int index)) + { + return RpcBlock.FromJson(RpcSend("getblock", index, true)); + } + return RpcBlock.FromJson(RpcSend("getblock", hashOrIndex, true)); + } + + /// + /// Gets the number of blocks in the main chain. + /// + public int GetBlockCount() + { + return (int)RpcSend("getblockcount").AsNumber(); + } + + /// + /// Returns the hash value of the corresponding block, based on the specified index. + /// + public string GetBlockHash(int index) + { + return RpcSend("getblockhash", index).AsString(); + } + + /// + /// Returns the corresponding block header information according to the specified script hash. + /// + public string GetBlockHeaderHex(string hashOrIndex) + { + if (int.TryParse(hashOrIndex, out int index)) + { + return RpcSend("getblockheader", index).AsString(); + } + return RpcSend("getblockheader", hashOrIndex).AsString(); + } + + /// + /// Returns the corresponding block header information according to the specified script hash. + /// + public RpcBlockHeader GetBlockHeader(string hashOrIndex) + { + if (int.TryParse(hashOrIndex, out int index)) + { + return RpcBlockHeader.FromJson(RpcSend("getblockheader", index, true)); + } + return RpcBlockHeader.FromJson(RpcSend("getblockheader", hashOrIndex, true)); + } + + /// + /// Returns the system fees of the block, based on the specified index. + /// + public string GetBlockSysFee(int height) + { + return RpcSend("getblocksysfee", height).AsString(); + } + + /// + /// Gets the current number of connections for the node. + /// + public int GetConnectionCount() + { + return (int)RpcSend("getconnectioncount").AsNumber(); + } + + /// + /// Queries contract information, according to the contract script hash. + /// + public ContractState GetContractState(string hash) + { + return ContractState.FromJson(RpcSend("getcontractstate", hash)); + } + + /// + /// Gets the list of nodes that the node is currently connected/disconnected from. + /// + public RpcPeers GetPeers() + { + return RpcPeers.FromJson(RpcSend("getpeers")); + } + + /// + /// Obtains the list of unconfirmed transactions in memory. + /// + public string[] GetRawMempool() + { + return ((JArray)RpcSend("getrawmempool")).Select(p => p.AsString()).ToArray(); + } + + /// + /// Obtains the list of unconfirmed transactions in memory. + /// shouldGetUnverified = true + /// + public RpcRawMemPool GetRawMempoolBoth() + { + return RpcRawMemPool.FromJson(RpcSend("getrawmempool")); + } + + /// + /// Returns the corresponding transaction information, based on the specified hash value. + /// + public string GetRawTransactionHex(string txid) + { + return RpcSend("getrawtransaction", txid).AsString(); + } + + /// + /// Returns the corresponding transaction information, based on the specified hash value. + /// verbose = true + /// + public RpcTransaction GetRawTransaction(string txid) + { + return RpcTransaction.FromJson(RpcSend("getrawtransaction", txid, true)); + } + + /// + /// Returns the stored value, according to the contract script hash and the stored key. + /// + public string GetStorage(string script_hash, string key) + { + return RpcSend("getstorage", script_hash, key).AsString(); + } + + /// + /// Returns the block index in which the transaction is found. + /// + public uint GetTransactionHeight(string txid) + { + return uint.Parse(RpcSend("gettransactionheight", txid).AsString()); + } + + /// + /// Returns the current NEO consensus nodes information and voting status. + /// + public RpcValidator[] GetValidators() + { + return ((JArray)RpcSend("getvalidators")).Select(p => RpcValidator.FromJson(p)).ToArray(); + } + + /// + /// Returns the version information about the queried node. + /// + public RpcVersion GetVersion() + { + return RpcVersion.FromJson(RpcSend("getversion")); + } + + /// + /// Returns the result after calling a smart contract at scripthash with the given operation and parameters. + /// This RPC call does not affect the blockchain in any way. + /// + public RpcInvokeResult InvokeFunction(string address, string function, RpcStack[] stacks) + { + return RpcInvokeResult.FromJson(RpcSend("invokefunction", address, function, stacks.Select(p => p.ToJson()).ToArray())); + } + + /// + /// Returns the result after passing a script through the VM. + /// This RPC call does not affect the blockchain in any way. + /// + public RpcInvokeResult InvokeScript(string script) + { + return RpcInvokeResult.FromJson(RpcSend("invokescript", script)); + } + + /// + /// Returns a list of plugins loaded by the node. + /// + public RpcPlugin[] ListPlugins() + { + return ((JArray)RpcSend("listplugins")).Select(p => RpcPlugin.FromJson(p)).ToArray(); + } + + /// + /// Broadcasts a transaction over the NEO network. + /// + public bool SendRawTransaction(string rawTransaction) + { + return RpcSend("sendrawtransaction", rawTransaction).AsBoolean(); + } + + /// + /// Broadcasts a raw block over the NEO network. + /// + public bool SubmitBlock(string block) + { + return RpcSend("submitblock", block).AsBoolean(); + } + + /// + /// Verifies that the address is a correct NEO address. + /// + public RpcValidateAddressResult ValidateAddress(string address) + { + return RpcValidateAddressResult.FromJson(RpcSend("validateaddress", address)); + } + } +} From 0f1bb13968055e727b2e3224df6f382263edeec0 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 31 Jul 2019 20:29:24 +0800 Subject: [PATCH 060/305] Remove `IPolicyPlugin` (#971) --- neo/Consensus/ConsensusContext.cs | 8 +++----- neo/Consensus/ConsensusService.cs | 3 ++- neo/Ledger/Blockchain.cs | 4 ++-- neo/Plugins/IPolicyPlugin.cs | 11 ----------- neo/Plugins/Plugin.cs | 11 ----------- neo/SmartContract/Native/PolicyContract.cs | 9 +++++++++ 6 files changed, 16 insertions(+), 30 deletions(-) delete mode 100644 neo/Plugins/IPolicyPlugin.cs diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 270536fc89..ba7edf66c5 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -4,7 +4,6 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; -using Neo.Plugins; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.Wallets; @@ -210,10 +209,9 @@ public ConsensusPayload MakePrepareRequest() { byte[] buffer = new byte[sizeof(ulong)]; random.NextBytes(buffer); - IEnumerable memoryPoolTransactions = Blockchain.Singleton.MemPool.GetSortedVerifiedTransactions(); - foreach (IPolicyPlugin plugin in Plugin.Policies) - memoryPoolTransactions = plugin.FilterForBlock(memoryPoolTransactions); - List transactions = memoryPoolTransactions.ToList(); + List transactions = Blockchain.Singleton.MemPool.GetSortedVerifiedTransactions() + .Take((int)NativeContract.Policy.GetMaxTransactionsPerBlock(Snapshot)) + .ToList(); TransactionHashes = transactions.Select(p => p.Hash).ToArray(); Transactions = transactions.ToDictionary(p => p.Hash); Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1); diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 2e850807e4..08cf41e8b9 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -8,6 +8,7 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.Plugins; +using Neo.SmartContract.Native; using Neo.Wallets; using System; using System.Collections.Generic; @@ -66,7 +67,7 @@ private bool AddTransaction(Transaction tx, bool verify) RequestChangeView(ChangeViewReason.TxInvalid); return false; } - if (!Plugin.CheckPolicy(tx)) + if (!NativeContract.Policy.CheckPolicy(tx, context.Snapshot)) { Log($"reject tx: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning); RequestChangeView(ChangeViewReason.TxRejectedByPolicy); diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index ee8e114701..663d142d62 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -228,7 +228,7 @@ private void OnFillMemoryPool(IEnumerable transactions) { if (Store.ContainsTransaction(tx.Hash)) continue; - if (!Plugin.CheckPolicy(tx)) + if (!NativeContract.Policy.CheckPolicy(tx, currentSnapshot)) continue; // First remove the tx if it is unverified in the pool. MemPool.TryRemoveUnVerified(tx.Hash, out _); @@ -361,7 +361,7 @@ private RelayResultReason OnNewTransaction(Transaction transaction) return RelayResultReason.OutOfMemory; if (!transaction.Verify(currentSnapshot, MemPool.GetVerifiedTransactions())) return RelayResultReason.Invalid; - if (!Plugin.CheckPolicy(transaction)) + if (!NativeContract.Policy.CheckPolicy(transaction, currentSnapshot)) return RelayResultReason.PolicyFail; if (!MemPool.TryAdd(transaction.Hash, transaction)) diff --git a/neo/Plugins/IPolicyPlugin.cs b/neo/Plugins/IPolicyPlugin.cs deleted file mode 100644 index 812d418ee2..0000000000 --- a/neo/Plugins/IPolicyPlugin.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Neo.Network.P2P.Payloads; -using System.Collections.Generic; - -namespace Neo.Plugins -{ - public interface IPolicyPlugin - { - bool FilterForMemoryPool(Transaction tx); - IEnumerable FilterForBlock(IEnumerable transactions); - } -} diff --git a/neo/Plugins/Plugin.cs b/neo/Plugins/Plugin.cs index 862ff6ae9a..72c7ce3b14 100644 --- a/neo/Plugins/Plugin.cs +++ b/neo/Plugins/Plugin.cs @@ -1,5 +1,4 @@ using Microsoft.Extensions.Configuration; -using Neo.Network.P2P.Payloads; using System; using System.Collections.Generic; using System.IO; @@ -13,7 +12,6 @@ public abstract class Plugin { public static readonly List Plugins = new List(); private static readonly List Loggers = new List(); - internal static readonly List Policies = new List(); internal static readonly List RpcPlugins = new List(); internal static readonly List PersistencePlugins = new List(); internal static readonly List P2PPlugins = new List(); @@ -51,7 +49,6 @@ protected Plugin() if (this is ILogPlugin logger) Loggers.Add(logger); if (this is IP2PPlugin p2p) P2PPlugins.Add(p2p); - if (this is IPolicyPlugin policy) Policies.Add(policy); if (this is IRpcPlugin rpc) RpcPlugins.Add(rpc); if (this is IPersistencePlugin persistence) PersistencePlugins.Add(persistence); if (this is IMemoryPoolTxObserverPlugin txObserver) TxObserverPlugins.Add(txObserver); @@ -59,14 +56,6 @@ protected Plugin() Configure(); } - public static bool CheckPolicy(Transaction tx) - { - foreach (IPolicyPlugin plugin in Policies) - if (!plugin.FilterForMemoryPool(tx)) - return false; - return true; - } - public abstract void Configure(); protected virtual void OnPluginsLoaded() diff --git a/neo/SmartContract/Native/PolicyContract.cs b/neo/SmartContract/Native/PolicyContract.cs index 2109e8b65d..239cd92200 100644 --- a/neo/SmartContract/Native/PolicyContract.cs +++ b/neo/SmartContract/Native/PolicyContract.cs @@ -3,6 +3,7 @@ using Neo.IO; using Neo.Ledger; +using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.VM; @@ -26,6 +27,14 @@ public PolicyContract() Manifest.Features = ContractFeatures.HasStorage; } + internal bool CheckPolicy(Transaction tx, Snapshot snapshot) + { + UInt160[] blockedAccounts = GetBlockedAccounts(snapshot); + if (blockedAccounts.Intersect(tx.GetScriptHashesForVerifying(snapshot)).Any()) + return false; + return true; + } + private bool CheckValidators(ApplicationEngine engine) { UInt256 prev_hash = engine.Snapshot.PersistingBlock.PrevHash; From ef29b9323d731177ea1232774f2151f747722af6 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 31 Jul 2019 23:48:31 +0800 Subject: [PATCH 061/305] Add `Console.WriteLine()` in `ConsensusService.Log()` (#974) --- neo/Consensus/ConsensusService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 08cf41e8b9..e4983830a4 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -175,6 +175,7 @@ private void InitializeConsensus(byte viewNumber) private void Log(string message, LogLevel level = LogLevel.Info) { + Console.WriteLine($"[{DateTime.Now.TimeOfDay:hh\\:mm\\:ss\\.fff}] {message}"); Plugin.Log(nameof(ConsensusService), level, message); } From 50527ebe6fbed6fac08519d76819799b55407760 Mon Sep 17 00:00:00 2001 From: Igor Machado Coelho Date: Fri, 2 Aug 2019 05:01:10 -0300 Subject: [PATCH 062/305] dotnet format (#983) --- neo.UnitTests/Consensus/UT_Consensus.cs | 4 ++-- neo.UnitTests/Cryptography/UT_Base58.cs | 2 +- neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs | 8 ++++---- neo.UnitTests/Cryptography/UT_MerkleTree.cs | 6 +++--- neo.UnitTests/IO/Caching/UT_CloneCache.cs | 3 ++- neo.UnitTests/IO/Caching/UT_FifoSet.cs | 4 ++-- neo.UnitTests/Network/P2P/Payloads/UT_Header.cs | 2 +- neo.UnitTests/VM/UT_Helper.cs | 4 ++-- neo/Network/RPC/Models/RpcInvokeResult.cs | 2 +- neo/Network/RPC/Models/RpcNep5Balances.cs | 6 +++--- neo/Network/RPC/Models/RpcPlugin.cs | 4 ++-- neo/Network/RPC/Models/RpcRawMemPool.cs | 6 +++--- neo/Network/RPC/Models/RpcRequest.cs | 6 +++--- neo/Network/RPC/Models/RpcValidateAddressResult.cs | 2 +- neo/SmartContract/ApplicationEngine.cs | 2 +- 15 files changed, 31 insertions(+), 30 deletions(-) diff --git a/neo.UnitTests/Consensus/UT_Consensus.cs b/neo.UnitTests/Consensus/UT_Consensus.cs index 34e8322615..14c287bcff 100644 --- a/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/neo.UnitTests/Consensus/UT_Consensus.cs @@ -80,8 +80,8 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() Console.WriteLine($"header {header} hash {header.Hash} timestamp {timestampVal}"); timestampVal.Should().Be(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 + // check basic ConsensusContext + //mockConsensusContext.Object.block_received_time.ToTimestamp().Should().Be(4244941697); //1968-06-01 00:00:01 // ============================================================================ // creating ConsensusService actor diff --git a/neo.UnitTests/Cryptography/UT_Base58.cs b/neo.UnitTests/Cryptography/UT_Base58.cs index 2d18975445..21cc63dda2 100644 --- a/neo.UnitTests/Cryptography/UT_Base58.cs +++ b/neo.UnitTests/Cryptography/UT_Base58.cs @@ -10,7 +10,7 @@ public class UT_Base58 { byte[] decoded1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; string encoded1 = "1kA3B2yGe2z4"; - byte[] decoded2 = { 0, 0, 0, 0, 0}; + byte[] decoded2 = { 0, 0, 0, 0, 0 }; string encoded2 = "1111"; [TestMethod] diff --git a/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index af909e3d71..06f672ff77 100644 --- a/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -18,7 +18,7 @@ public void TestAES256Encrypt() byte[] key = Encoding.ASCII.GetBytes("1234567812345678"); byte[] result = block.AES256Encrypt(key); string encryptString = result.ToHexString(); - encryptString.Should().Be("f69e0923d8247eef417d6a78944a4b39f69e0923d8247eef417d6a78944a4b39"); + encryptString.Should().Be("f69e0923d8247eef417d6a78944a4b39f69e0923d8247eef417d6a78944a4b39"); } [TestMethod] @@ -104,7 +104,7 @@ public void TestAesDecrypt() byte[] wrongIv = Encoding.ASCII.GetBytes("12345678123456780"); ; action = () => data.AesDecrypt(key, wrongIv); action.ShouldThrow(); - } + } [TestMethod] public void TestBase58CheckDecode() @@ -126,7 +126,7 @@ public void TestBase58CheckDecode() [TestMethod] public void TestSha256() { - byte[] value = Encoding.ASCII.GetBytes("hello world"); + byte[] value = Encoding.ASCII.GetBytes("hello world"); byte[] result = value.Sha256(0, value.Length); string resultStr = result.ToHexString(); resultStr.Should().Be("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"); @@ -169,7 +169,7 @@ public void TestStringToAesKey() string password = "hello world"; string string1 = "bc62d4b80d9e36da29c16c5d4d9f11731f36052c72401a76c23c0fb5a9b74423"; byte[] byteArray = new byte[string1.Length / 2]; - byteArray = string1.HexToBytes(); + byteArray = string1.HexToBytes(); password.ToAesKey().Should().Equal(byteArray); } diff --git a/neo.UnitTests/Cryptography/UT_MerkleTree.cs b/neo.UnitTests/Cryptography/UT_MerkleTree.cs index 3edf6b4a8b..75ab2330b2 100644 --- a/neo.UnitTests/Cryptography/UT_MerkleTree.cs +++ b/neo.UnitTests/Cryptography/UT_MerkleTree.cs @@ -13,7 +13,7 @@ public class UT_MerkleTree { public UInt256 GetByteArrayHash(byte[] byteArray) { - if(byteArray == null || byteArray.Length == 0) throw new ArgumentNullException(); + if (byteArray == null || byteArray.Length == 0) throw new ArgumentNullException(); var hash = new UInt256(Crypto.Default.Hash256(byteArray)); return hash; } @@ -26,13 +26,13 @@ public void TestBuildAndDepthFirstSearch() action.ShouldThrow(); byte[] array1 = { 0x01 }; - var hash1 = GetByteArrayHash(array1); + var hash1 = GetByteArrayHash(array1); byte[] array2 = { 0x02 }; var hash2 = GetByteArrayHash(array2); byte[] array3 = { 0x03 }; - var hash3 = GetByteArrayHash(array3); + var hash3 = GetByteArrayHash(array3); IReadOnlyList hashes = new UInt256[] { hash1, hash2, hash3 }; MerkleTree tree = new MerkleTree(hashes); diff --git a/neo.UnitTests/IO/Caching/UT_CloneCache.cs b/neo.UnitTests/IO/Caching/UT_CloneCache.cs index 1c9c2db74d..52c665a95d 100644 --- a/neo.UnitTests/IO/Caching/UT_CloneCache.cs +++ b/neo.UnitTests/IO/Caching/UT_CloneCache.cs @@ -85,7 +85,8 @@ public void TestGetInternal() cloneCache[new MyKey("key2")].Should().Be(new MyValue("value2")); cloneCache[new MyKey("key3")].Should().Be(new MyValue("value3")); - Action action = () => { + Action action = () => + { var item = cloneCache[new MyKey("key4")]; }; action.ShouldThrow(); diff --git a/neo.UnitTests/IO/Caching/UT_FifoSet.cs b/neo.UnitTests/IO/Caching/UT_FifoSet.cs index 8242572e86..cbbafae402 100644 --- a/neo.UnitTests/IO/Caching/UT_FifoSet.cs +++ b/neo.UnitTests/IO/Caching/UT_FifoSet.cs @@ -59,10 +59,10 @@ public void TestConstructor() Action action1 = () => new FIFOSet(-1); action1.ShouldThrow(); - Action action2 = () => new FIFOSet(1,-1); + Action action2 = () => new FIFOSet(1, -1); action2.ShouldThrow(); - Action action3 = () => new FIFOSet(1,2); + Action action3 = () => new FIFOSet(1, 2); action3.ShouldThrow(); } diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index f9dfd30d1e..8002f4b196 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -110,7 +110,7 @@ public void Serialize() byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 233, 19, 255, 133, 76, 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, 1, 0, 1, 81, 0 }; data.Length.Should().Be(requiredData.Length); - + for (int i = 0; i < data.Length; i++) { data[i].Should().Be(requiredData[i]); diff --git a/neo.UnitTests/VM/UT_Helper.cs b/neo.UnitTests/VM/UT_Helper.cs index 7fc0088632..b57ca02a7c 100644 --- a/neo.UnitTests/VM/UT_Helper.cs +++ b/neo.UnitTests/VM/UT_Helper.cs @@ -27,7 +27,7 @@ public void TestEmitAppCall1() tempArray[0] = 0x00;//0 tempArray[1] = 0xC5;//OpCode.NEWARRAY tempArray[2] = 5;//operation.Length - Array.Copy(Encoding.UTF8.GetBytes("AAAAA"),0, tempArray,3, 5);//operation.data + Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 3, 5);//operation.data tempArray[8] = 0x14;//scriptHash.Length Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 9, 20);//operation.data uint api = InteropService.System_Contract_Call; @@ -42,7 +42,7 @@ 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.EmitAppCall(UInt160.Zero, "AAAAA", new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) }); byte[] tempArray = new byte[35]; tempArray[0] = 0x00;//0 tempArray[1] = 0x51;//ContractParameter.Length diff --git a/neo/Network/RPC/Models/RpcInvokeResult.cs b/neo/Network/RPC/Models/RpcInvokeResult.cs index 9a0f646588..9df9492e2b 100644 --- a/neo/Network/RPC/Models/RpcInvokeResult.cs +++ b/neo/Network/RPC/Models/RpcInvokeResult.cs @@ -47,7 +47,7 @@ public static RpcInvokeResult FromJson(JObject json) public class RpcStack { public string Type { get; set; } - + public string Value { get; set; } public JObject ToJson() diff --git a/neo/Network/RPC/Models/RpcNep5Balances.cs b/neo/Network/RPC/Models/RpcNep5Balances.cs index edcf8d7f5e..f29f0c5564 100644 --- a/neo/Network/RPC/Models/RpcNep5Balances.cs +++ b/neo/Network/RPC/Models/RpcNep5Balances.cs @@ -7,7 +7,7 @@ namespace Neo.Network.RPC.Models public class RpcNep5Balances { public string Address { get; set; } - + public RpcNep5Balance[] Balances { get; set; } public JObject ToJson() @@ -31,9 +31,9 @@ public static RpcNep5Balances FromJson(JObject json) public class RpcNep5Balance { public UInt160 AssetHash { get; set; } - + public BigInteger Amount { get; set; } - + public uint LastUpdatedBlock { get; set; } public JObject ToJson() diff --git a/neo/Network/RPC/Models/RpcPlugin.cs b/neo/Network/RPC/Models/RpcPlugin.cs index 465b3f2d17..ae778ee87a 100644 --- a/neo/Network/RPC/Models/RpcPlugin.cs +++ b/neo/Network/RPC/Models/RpcPlugin.cs @@ -6,9 +6,9 @@ namespace Neo.Network.RPC.Models public class RpcPlugin { public string Name { get; set; } - + public string Version { get; set; } - + public string[] Interfaces { get; set; } public JObject ToJson() diff --git a/neo/Network/RPC/Models/RpcRawMemPool.cs b/neo/Network/RPC/Models/RpcRawMemPool.cs index 1841916ff2..df439bf3a6 100644 --- a/neo/Network/RPC/Models/RpcRawMemPool.cs +++ b/neo/Network/RPC/Models/RpcRawMemPool.cs @@ -6,16 +6,16 @@ namespace Neo.Network.RPC.Models public class RpcRawMemPool { public uint Height { get; set; } - + public string[] Verified { get; set; } - + public string[] UnVerified { get; set; } public JObject ToJson() { JObject json = new JObject(); json["height"] = Height; - json["verified"] = new JArray(Verified.Select(p=>(JObject)p)); + json["verified"] = new JArray(Verified.Select(p => (JObject)p)); json["unverified"] = new JArray(UnVerified.Select(p => (JObject)p)); return json; } diff --git a/neo/Network/RPC/Models/RpcRequest.cs b/neo/Network/RPC/Models/RpcRequest.cs index 9c2d2f05ec..010e3037b8 100644 --- a/neo/Network/RPC/Models/RpcRequest.cs +++ b/neo/Network/RPC/Models/RpcRequest.cs @@ -6,11 +6,11 @@ namespace Neo.Network.RPC.Models public class RpcRequest { public int Id { get; set; } - + public string Jsonrpc { get; set; } - + public string Method { get; set; } - + public JObject[] Params { get; set; } public static RpcRequest FromJson(JObject json) diff --git a/neo/Network/RPC/Models/RpcValidateAddressResult.cs b/neo/Network/RPC/Models/RpcValidateAddressResult.cs index 5e59bf8eaf..f33467356e 100644 --- a/neo/Network/RPC/Models/RpcValidateAddressResult.cs +++ b/neo/Network/RPC/Models/RpcValidateAddressResult.cs @@ -5,7 +5,7 @@ namespace Neo.Network.RPC.Models public class RpcValidateAddressResult { public string Address { get; set; } - + public bool IsValid { get; set; } public JObject ToJson() diff --git a/neo/SmartContract/ApplicationEngine.cs b/neo/SmartContract/ApplicationEngine.cs index 03a098530a..d211d7aac8 100644 --- a/neo/SmartContract/ApplicationEngine.cs +++ b/neo/SmartContract/ApplicationEngine.cs @@ -83,7 +83,7 @@ protected override bool PreExecuteInstruction() return true; return AddGas(OpCodePrices[CurrentContext.CurrentInstruction.OpCode]); } - + private static Block CreateDummyBlock(Snapshot snapshot) { var currentBlock = snapshot.Blocks[snapshot.CurrentBlockHash]; From c13ad46d55bc5da733105c6a1d2738b0c28f74d8 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 5 Aug 2019 00:47:11 +0800 Subject: [PATCH 063/305] Fixes `NeoToken.CalculateBonus()` (#992) --- neo/SmartContract/Native/Tokens/NeoToken.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/SmartContract/Native/Tokens/NeoToken.cs b/neo/SmartContract/Native/Tokens/NeoToken.cs index 6de1ac1970..d9e2dd2d4f 100644 --- a/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -93,7 +93,7 @@ private BigInteger CalculateBonus(Snapshot snapshot, BigInteger value, uint star } amount += (iend - istart) * Blockchain.GenerationAmount[ustart]; } - amount += GAS.GetSysFeeAmount(snapshot, end - 1) - (start == 0 ? 0 : GAS.GetSysFeeAmount(snapshot, start - 1)); + amount += (GAS.GetSysFeeAmount(snapshot, end - 1) - (start == 0 ? 0 : GAS.GetSysFeeAmount(snapshot, start - 1))) / GAS.Factor; return value * amount * GAS.Factor / TotalAmount; } From 1a1dbaf29696d9e67ed7f09714d08c3dd8801e81 Mon Sep 17 00:00:00 2001 From: Shargon Date: Sun, 4 Aug 2019 20:34:42 +0200 Subject: [PATCH 064/305] Small cache changes (#994) * Cache changes * Update UT_Cache.cs --- neo.UnitTests/IO/Caching/UT_Cache.cs | 44 +++++++++++++++++++++++++++- neo/IO/Caching/Cache.cs | 18 ++++-------- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/neo.UnitTests/IO/Caching/UT_Cache.cs b/neo.UnitTests/IO/Caching/UT_Cache.cs index 01484ae65e..1376863e1a 100644 --- a/neo.UnitTests/IO/Caching/UT_Cache.cs +++ b/neo.UnitTests/IO/Caching/UT_Cache.cs @@ -25,6 +25,35 @@ public IEnumerator MyGetEnumerator() } } + class CacheDisposableEntry : IDisposable + { + public int Key { get; set; } + public bool IsDisposed { get; private set; } + + public void Dispose() + { + IsDisposed = true; + } + } + + class MyDisposableCache : Cache + { + public MyDisposableCache(int max_capacity) : base(max_capacity) { } + + protected override int GetKeyForItem(CacheDisposableEntry item) + { + return item.Key; + } + + protected override void OnAccess(CacheItem item) { } + + public IEnumerator MyGetEnumerator() + { + IEnumerable enumerable = this; + return enumerable.GetEnumerator(); + } + } + [TestClass] public class UT_Cache { @@ -133,6 +162,19 @@ public void TestRemoveKey() cache.Contains("hello").Should().BeFalse(); } + [TestMethod] + public void TestRemoveDisposableKey() + { + var entry = new CacheDisposableEntry() { Key = 1 }; + var dcache = new MyDisposableCache(100); + dcache.Add(entry); + + entry.IsDisposed.Should().BeFalse(); + dcache.Remove(entry.Key).Should().BeTrue(); + dcache.Remove(entry.Key).Should().BeFalse(); + entry.IsDisposed.Should().BeTrue(); + } + [TestMethod] public void TestRemoveValue() { @@ -211,4 +253,4 @@ public void TestDispose() action.ShouldThrow(); } } -} \ No newline at end of file +} diff --git a/neo/IO/Caching/Cache.cs b/neo/IO/Caching/Cache.cs index 45556b8ae3..5b97d3a223 100644 --- a/neo/IO/Caching/Cache.cs +++ b/neo/IO/Caching/Cache.cs @@ -10,15 +10,15 @@ internal abstract class Cache : ICollection, IDisposable { protected class CacheItem { - public TKey Key; - public TValue Value; - public DateTime Time; + public readonly TKey Key; + public readonly TValue Value; + public readonly DateTime Time; public CacheItem(TKey key, TValue value) { this.Key = key; this.Value = value; - this.Time = DateTime.Now; + this.Time = TimeProvider.Current.UtcNow; } } @@ -60,13 +60,7 @@ public int Count } } - public bool IsReadOnly - { - get - { - return false; - } - } + public bool IsReadOnly => false; public Cache(int max_capacity) { @@ -97,7 +91,7 @@ private void AddInternal(TKey key, TValue item) { if (InnerDictionary.Count >= max_capacity) { - //TODO: 对PLINQ查询进行性能测试,以便确定此处使用何种算法更优(并行或串行) + //TODO: Perform a performance test on the PLINQ query to determine which algorithm is better here (parallel or not) foreach (CacheItem item_del in InnerDictionary.Values.AsParallel().OrderBy(p => p.Time).Take(InnerDictionary.Count - max_capacity + 1)) { RemoveInternal(item_del); From 343d4dafc329e18963192a4c1f442eae8a981c3d Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 5 Aug 2019 14:02:48 +0800 Subject: [PATCH 065/305] Fixes `GetRegisteredValidators()` (#991) --- .../Native/Tokens/UT_NeoToken.cs | 16 +++++++++++++++- neo/Ledger/StorageKey.cs | 19 +++++++++++++++++++ neo/SmartContract/InteropService.NEO.cs | 18 +----------------- neo/SmartContract/Native/Tokens/NeoToken.cs | 3 ++- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 9a22bab3c4..efa3c2446c 100644 --- a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -128,6 +128,20 @@ public void Check_RegisterValidator() ret.Result.Should().BeTrue(); snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 1); // New validator + + // Check GetRegisteredValidators + + var validators = NativeContract.NEO.GetRegisteredValidators(snapshot).OrderBy(u => u.PublicKey).ToArray(); + var check = Blockchain.StandbyValidators.Select(u => u.EncodePoint(true)).ToList(); + check.Add(point); // Add the new member + + for (int x = 0; x < validators.Length; x++) + { + Assert.AreEqual(1, check.RemoveAll(u => u.SequenceEqual(validators[x].PublicKey.EncodePoint(true)))); + Assert.AreEqual(0, validators[x].Votes); + } + + Assert.AreEqual(0, check.Count); } [TestMethod] @@ -357,4 +371,4 @@ internal static void CheckBalance(byte[] account, DataCache, ISerializable int ISerializable.Size => ScriptHash.Size + (Key.Length / 16 + 1) * 17; + internal static byte[] CreateSearchPrefix(UInt160 hash, byte[] prefix) + { + using (MemoryStream ms = new MemoryStream()) + { + int index = 0; + int remain = prefix.Length; + while (remain >= 16) + { + ms.Write(prefix, index, 16); + ms.WriteByte(16); + index += 16; + remain -= 16; + } + if (remain > 0) + ms.Write(prefix, index, remain); + return hash.ToArray().Concat(ms.ToArray()).ToArray(); + } + } + void ISerializable.Deserialize(BinaryReader reader) { ScriptHash = reader.ReadSerializable(); diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index 669a9b7073..91b941649f 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -10,7 +10,6 @@ using Neo.VM; using Neo.VM.Types; using System; -using System.IO; using System.Linq; using VMArray = Neo.VM.Types.Array; @@ -345,22 +344,7 @@ private static bool Storage_Find(ApplicationEngine engine) StorageContext context = _interface.GetInterface(); if (!CheckStorageContext(engine, context)) return false; byte[] prefix = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - byte[] prefix_key; - using (MemoryStream ms = new MemoryStream()) - { - int index = 0; - int remain = prefix.Length; - while (remain >= 16) - { - ms.Write(prefix, index, 16); - ms.WriteByte(16); - index += 16; - remain -= 16; - } - if (remain > 0) - ms.Write(prefix, index, remain); - prefix_key = context.ScriptHash.ToArray().Concat(ms.ToArray()).ToArray(); - } + byte[] prefix_key = StorageKey.CreateSearchPrefix(context.ScriptHash, prefix); StorageIterator iterator = engine.AddDisposable(new StorageIterator(engine.Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.Take(prefix.Length).SequenceEqual(prefix)).GetEnumerator())); engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator)); return true; diff --git a/neo/SmartContract/Native/Tokens/NeoToken.cs b/neo/SmartContract/Native/Tokens/NeoToken.cs index d9e2dd2d4f..7b28bd0923 100644 --- a/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -201,7 +201,8 @@ private StackItem GetRegisteredValidators(ApplicationEngine engine, VMArray args public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetRegisteredValidators(Snapshot snapshot) { - return snapshot.Storages.Find(new[] { Prefix_Validator }).Select(p => + byte[] prefix_key = StorageKey.CreateSearchPrefix(Hash, new[] { Prefix_Validator }); + return snapshot.Storages.Find(prefix_key).Select(p => ( p.Key.Key.Skip(1).ToArray().AsSerializable(), ValidatorState.FromByteArray(p.Value.Value).Votes From f4dd2348a180c3b87cbd540d259c723c41806ccc Mon Sep 17 00:00:00 2001 From: zhangtao Date: Tue, 6 Aug 2019 15:00:48 +0800 Subject: [PATCH 066/305] Fix/travis parallel jobs (#987) * testDemo * add dll lib * add dbsnapshot dispose * test get blocks in levelDBStore * add levelDBStore test funcs * fix levelDBStore funcs * add DbCache addInternal * differ db path * space * fix delete internal test * add test getInternal tryGetInternal move libleveldb.dll * add dbCache method test * add store test * add cache unit tests * add cache unit tests * up readonly max_capacity * fix leveldbexception * fix comment on UT_Cache * format * fix multithread test problem * up cache * update travis config * update travis.yml * test DbMetaDataCache * fix db directory * format and update travis for maxos * fix mac env travis * 2019/7/12 10:34 * 2019/7/12 11:01 * remove commented line * test BigDecimal * fix format and csproj * rm coverage.opencover.xml * update method name * add UT_P_Helper * modify UT_P_Helper * modify UT_P_helper * Clean ut * test Base58 & BloomFilter * Update UT_Cache.cs * Correct Typo * test JsonArray * update namespace * update namespace * update format * update format * organise folder structure * add UT_JString * test JBoolean JNumber & JObject * 2019/7/16 10:30 add some test case for UInt32Wrapper and SerializableWrapper * fix timestamp * test ECDsa and Crypto * test OrderedDictionary & complete IO.Json tests * 2019/7/16 17:33 add some test case of SQLiteWallet * test FIFOSet * add CloneCache and DataCache unit tests * fix namespace * add UT_Cryptography_Helper * format UT_CloneCache and UT_DataCache * add UT_DataCache.GetAndChange unit test * update namespace * remove comment code * delete Persistence part * 2019/7/19 11:07 add some test case for Helper in VM * Fix Base58 Test * 2019/7/19 11:33 change some format * update IOHelper exception assert * 2019/7/19 14:22 change format * format IOHelper * review IO.Wrapper * review Wallets.SQLite UT * Test ECFieldElement ECPoint * refactor package * format ECDsa * update namespace * Code fix * review cache * modify UT_JString * fomat * using Actin replace with try-catch * add UT_CloneMetaCache and UT_MetaDataCache * update namespace * format UT_DataCache.cs * Code Fix * format * update csproj * Code fix for UT_ECFieldElement and UT_ECPoint * Code fix * format * update travis * delete deleteFiles * fix path and comment * update travis * delete test ToTimeStamp * format UT_*Cache * update format * fomat * use hex extensions in Cryptography_Helper * remove reflection * optimization of UT_DataCache * update namespace * modify TestSha256 * update UT in crypto module * Rename UT_Scrypt.cs to UT_SCrypt.cs * format * update UT_Murmur3 * split travis jobs * add -v n --- .travis.yml | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 516dd5855c..aaf9dd660d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,16 +10,26 @@ osx_image: xcode9.1 mono: none dotnet: 2.2.300 +env: +- TEST_SUITE="without-cultures" +- TEST_SUITE="cultures" + before_install: - cd neo.UnitTests - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ulimit -n 2048; fi +script: | + if [[ "$TEST_SUITE" == cultures ]]; then + dotnet restore + dotnet test + else + dotnet restore + find * -name *.csproj | xargs -I % dotnet add % package coverlet.msbuild + dotnet test -v n --filter FullyQualifiedName!=Neo.UnitTests.UT_Culture.All_Tests_Cultures /p:CollectCoverage=true /p:CoverletOutputFormat=opencover + fi +after_success: | + if [[ "$TEST_SUITE" == "without-cultures" ]]; then + echo "Test Success - Branch($TRAVIS_BRANCH) Pull Request($TRAVIS_PULL_REQUEST) Tag($TRAVIS_TAG)" + bash <(curl -s https://codecov.io/bash) -v + fi -script: - - dotnet restore - - find * -name *.csproj | xargs -I % dotnet add % package coverlet.msbuild - - dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover -after_success: - # After all tests OK, Send CodeDov report - - echo "Test Success - Branch($TRAVIS_BRANCH) Pull Request($TRAVIS_PULL_REQUEST) Tag($TRAVIS_TAG)" - - bash <(curl -s https://codecov.io/bash) -v From c7b12c33250d41fc6e53c36a0d398071b4bbddfd Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 6 Aug 2019 16:25:12 +0800 Subject: [PATCH 067/305] Fixes #999 --- neo/Wallets/Wallet.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index fc490dbea7..310d11032a 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -322,17 +322,17 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at int size = Transaction.HeaderSize + attributes.GetVarSize() + script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); foreach (UInt160 hash in hashes) { - script = GetAccount(hash)?.Contract?.Script ?? snapshot.Contracts.TryGet(hash)?.Script; - if (script is null) continue; - if (script.IsSignatureContract()) + byte[] witness_script = GetAccount(hash)?.Contract?.Script ?? snapshot.Contracts.TryGet(hash)?.Script; + if (witness_script is null) continue; + if (witness_script.IsSignatureContract()) { - size += 66 + script.GetVarSize(); + size += 66 + witness_script.GetVarSize(); tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); } - else if (script.IsMultiSigContract(out int m, out int n)) + else if (witness_script.IsMultiSigContract(out int m, out int n)) { int size_inv = 65 * m; - size += IO.Helper.GetVarSize(size_inv) + size_inv + script.GetVarSize(); + size += IO.Helper.GetVarSize(size_inv) + size_inv + witness_script.GetVarSize(); tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] * m; using (ScriptBuilder sb = new ScriptBuilder()) tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; From 6838d84a80b83fcea3e9e1b8afd05a349dca4a84 Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Wed, 7 Aug 2019 18:20:18 +0800 Subject: [PATCH 068/305] Add and update some Unit tests in IO module (#998) * testDemo * add dll lib * add dbsnapshot dispose * test get blocks in levelDBStore * add levelDBStore test funcs * fix levelDBStore funcs * add DbCache addInternal * differ db path * space * fix delete internal test * add test getInternal tryGetInternal move libleveldb.dll * add dbCache method test * add store test * add cache unit tests * add cache unit tests * up readonly max_capacity * fix leveldbexception * fix comment on UT_Cache * format * fix multithread test problem * up cache * update travis config * update travis.yml * test DbMetaDataCache * fix db directory * format and update travis for maxos * fix mac env travis * 2019/7/12 10:34 * 2019/7/12 11:01 * remove commented line * test BigDecimal * fix format and csproj * rm coverage.opencover.xml * update method name * add UT_P_Helper * modify UT_P_Helper * modify UT_P_helper * Clean ut * test Base58 & BloomFilter * Update UT_Cache.cs * Correct Typo * test JsonArray * update namespace * update namespace * update format * update format * organise folder structure * add UT_JString * test JBoolean JNumber & JObject * 2019/7/16 10:30 add some test case for UInt32Wrapper and SerializableWrapper * fix timestamp * test ECDsa and Crypto * test OrderedDictionary & complete IO.Json tests * 2019/7/16 17:33 add some test case of SQLiteWallet * test FIFOSet * add CloneCache and DataCache unit tests * fix namespace * add UT_Cryptography_Helper * format UT_CloneCache and UT_DataCache * add UT_DataCache.GetAndChange unit test * update namespace * remove comment code * delete Persistence part * 2019/7/19 11:07 add some test case for Helper in VM * Fix Base58 Test * 2019/7/19 11:33 change some format * update IOHelper exception assert * 2019/7/19 14:22 change format * format IOHelper * review IO.Wrapper * review Wallets.SQLite UT * Test ECFieldElement ECPoint * refactor package * format ECDsa * update namespace * Code fix * review cache * modify UT_JString * fomat * using Actin replace with try-catch * add UT_CloneMetaCache and UT_MetaDataCache * update namespace * format UT_DataCache.cs * Code Fix * format * update csproj * Code fix for UT_ECFieldElement and UT_ECPoint * Code fix * format * update travis * delete deleteFiles * fix path and comment * update travis * delete test ToTimeStamp * format UT_*Cache * update format * fomat * use hex extensions in Cryptography_Helper * remove reflection * optimization of UT_DataCache * update namespace * modify TestSha256 * update UT in crypto module * Rename UT_Scrypt.cs to UT_SCrypt.cs * format * update UT_Murmur3 * update IO module test * delete empty line * changename * delete empty line * revert commit --- .../Caching/{UT_FifoSet.cs => UT_FIFOSet.cs} | 43 +- .../IO/Caching/UT_OrderedDictionary.cs | 42 ++ .../IO/Caching/UT_ReflectionCache.cs | 80 ++++ neo.UnitTests/IO/Caching/UT_RelayCache.cs | 41 ++ neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs | 408 ++++++++++++++++++ neo.UnitTests/IO/Json/UT_JArray.cs | 21 +- neo.UnitTests/IO/Json/UT_JObject.cs | 1 - 7 files changed, 619 insertions(+), 17 deletions(-) rename neo.UnitTests/IO/Caching/{UT_FifoSet.cs => UT_FIFOSet.cs} (78%) create mode 100644 neo.UnitTests/IO/Caching/UT_ReflectionCache.cs create mode 100644 neo.UnitTests/IO/Caching/UT_RelayCache.cs create mode 100644 neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs diff --git a/neo.UnitTests/IO/Caching/UT_FifoSet.cs b/neo.UnitTests/IO/Caching/UT_FIFOSet.cs similarity index 78% rename from neo.UnitTests/IO/Caching/UT_FifoSet.cs rename to neo.UnitTests/IO/Caching/UT_FIFOSet.cs index cbbafae402..f36984b89f 100644 --- a/neo.UnitTests/IO/Caching/UT_FifoSet.cs +++ b/neo.UnitTests/IO/Caching/UT_FIFOSet.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; using System; +using System.Collections; using System.Linq; namespace Neo.UnitTests.IO.Caching @@ -81,12 +82,38 @@ public void TestAdd() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 }); - var set = new FIFOSet(1, 1); - set.Add(a); - set.Add(b); + var set = new FIFOSet(1, 1) + { + a, + b + }; CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { b }); } + [TestMethod] + public void TestGetEnumerator() + { + var a = new UInt256(new byte[32] { + 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 + }); + var b = new UInt256(new byte[32] { + 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, 0x02 + }); + var set = new FIFOSet(1, 1) + { + a, + b + }; + IEnumerable ie = set; + ie.GetEnumerator().Should().NotBeNull(); + } + [TestMethod] public void TestExceptWith() { @@ -109,10 +136,12 @@ public void TestExceptWith() 0x01, 0x03 }); - var set = new FIFOSet(10); - set.Add(a); - set.Add(b); - set.Add(c); + var set = new FIFOSet(10) + { + a, + b, + c + }; set.ExceptWith(new UInt256[] { b, c }); CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a }); } diff --git a/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs b/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs index 94d7b70ce1..262528cd88 100644 --- a/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs +++ b/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs @@ -1,6 +1,8 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; +using System.Collections; +using System.Collections.Generic; namespace Neo.UnitTests.IO.Caching { @@ -83,5 +85,45 @@ public void TestTryGetValue() od.TryGetValue("d", out uint j).Should().BeFalse(); j.Should().Be(0); } + + [TestMethod] + public void TestCollectionAddAndContains() + { + var pair = new KeyValuePair("d", 4); + ICollection> collection = od; + collection.Add(pair); + collection.Contains(pair).Should().BeTrue(); + } + + [TestMethod] + public void TestCollectionCopyTo() + { + var arr = new KeyValuePair[3]; + ICollection> collection = od; + collection.CopyTo(arr, 0); + arr[0].Key.Should().Be("a"); + arr[0].Value.Should().Be(1); + arr[1].Key.Should().Be("b"); + arr[1].Value.Should().Be(2); + arr[2].Key.Should().Be("c"); + arr[2].Value.Should().Be(3); + } + + [TestMethod] + public void TestCollectionRemove() + { + ICollection> collection = od; + var pair = new KeyValuePair("a", 1); + collection.Remove(pair); + collection.Contains(pair).Should().BeFalse(); + collection.Count.Should().Be(2); + } + + [TestMethod] + public void TestGetEnumerator() + { + IEnumerable collection = od; + collection.GetEnumerator().MoveNext().Should().BeTrue(); + } } } \ No newline at end of file diff --git a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs new file mode 100644 index 0000000000..e7de68f3c1 --- /dev/null +++ b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs @@ -0,0 +1,80 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Caching; +using System; + +namespace Neo.UnitTests.IO.Caching +{ + public class TestItem { } + + public class TestItem1 : TestItem { } + + public class TestItem2 : TestItem { } + + public enum MyTestEnum : byte + { + [ReflectionCache(typeof(TestItem1))] + Item1 = 0x00, + + [ReflectionCache(typeof(TestItem2))] + Item2 = 0x01, + } + + public enum MyEmptyEnum : byte { } + + [TestClass] + public class UT_ReflectionCache + { + ReflectionCache reflectionCache; + + [TestInitialize] + public void SetUp() + { + reflectionCache = ReflectionCache.CreateFromEnum(); + } + + [TestMethod] + public void TestCreateFromEnum() + { + reflectionCache.Should().NotBeNull(); + } + + [TestMethod] + public void TestCreateFromObjectNotEnum() + { + Action action = () => ReflectionCache.CreateFromEnum(); + action.ShouldThrow(); + } + + [TestMethod] + public void TestCreateFromEmptyEnum() + { + reflectionCache = ReflectionCache.CreateFromEnum(); + reflectionCache.Count.Should().Be(0); + } + + [TestMethod] + public void TestCreateInstance() + { + object item1 = reflectionCache.CreateInstance((byte)MyTestEnum.Item1, null); + (item1 is TestItem1).Should().BeTrue(); + + object item2 = reflectionCache.CreateInstance((byte)MyTestEnum.Item2, null); + (item2 is TestItem2).Should().BeTrue(); + + object item3 = reflectionCache.CreateInstance(0x02, null); + item3.Should().BeNull(); + } + + [TestMethod] + public void TestCreateInstance2() + { + TestItem defaultItem = new TestItem1(); + object item2 = reflectionCache.CreateInstance((byte)MyTestEnum.Item2, defaultItem); + (item2 is TestItem2).Should().BeTrue(); + + object item1 = reflectionCache.CreateInstance(0x02, new TestItem1()); + (item1 is TestItem1).Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Caching/UT_RelayCache.cs b/neo.UnitTests/IO/Caching/UT_RelayCache.cs new file mode 100644 index 0000000000..537305e2ee --- /dev/null +++ b/neo.UnitTests/IO/Caching/UT_RelayCache.cs @@ -0,0 +1,41 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Caching; +using Neo.Network.P2P.Payloads; + +namespace Neo.UnitTests.IO.Caching +{ + [TestClass] + public class UT_RelayCache + { + RelayCache relayCache; + + [TestInitialize] + public void SetUp() + { + relayCache = new RelayCache(10); + } + + [TestMethod] + public void TestGetKeyForItem() + { + Transaction tx = new Transaction() + { + Version = 0, + Nonce = 1, + Sender = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), + SystemFee = 0, + NetworkFee = 0, + ValidUntilBlock = 100, + Attributes = new TransactionAttribute[0], + Script = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }, + Witnesses = new Witness[0] + }; + relayCache.Add(tx); + + relayCache.Contains(tx).Should().BeTrue(); + relayCache.TryGet(tx.Hash, out IInventory tmp).Should().BeTrue(); + (tmp is Transaction).Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs b/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs new file mode 100644 index 0000000000..21dea9f1d1 --- /dev/null +++ b/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs @@ -0,0 +1,408 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.IO.Data.LevelDB; +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Neo.UnitTests.IO.Data.LevelDb +{ + public class Test { } + + [TestClass] + public class UT_Slice + { + private Slice sliceTest; + + [TestMethod] + public void TestConstructor() + { + IntPtr parr = Marshal.AllocHGlobal(1); + Marshal.WriteByte(parr, 0x01); + UIntPtr plength = new UIntPtr(1); + sliceTest = new Slice(parr, plength); + Assert.IsNotNull(sliceTest); + Assert.IsInstanceOfType(sliceTest, typeof(Slice)); + Slice slice = (byte)0x01; + Assert.AreEqual(slice, sliceTest); + Marshal.FreeHGlobal(parr); + } + + [TestMethod] + public void TestCompareTo() + { + Slice slice = new byte[] { 0x01, 0x02 }; + sliceTest = new byte[] { 0x01, 0x02 }; + int result = sliceTest.CompareTo(slice); + Assert.AreEqual(0, result); + sliceTest = new byte[] { 0x01 }; + result = sliceTest.CompareTo(slice); + Assert.AreEqual(-1, result); + sliceTest = new byte[] { 0x01, 0x02, 0x03 }; + result = sliceTest.CompareTo(slice); + Assert.AreEqual(1, result); + sliceTest = new byte[] { 0x01, 0x03 }; + result = sliceTest.CompareTo(slice); + Assert.AreEqual(1, result); + sliceTest = new byte[] { 0x01, 0x01 }; + result = sliceTest.CompareTo(slice); + Assert.AreEqual(-1, result); + } + + [TestMethod] + public void TestEqualsSlice() + { + byte[] arr1 = { 0x01, 0x02 }; + byte[] arr2 = { 0x01, 0x02 }; + Slice slice = arr1; + sliceTest = arr1; + Assert.IsTrue(sliceTest.Equals(slice)); + sliceTest = arr2; + Assert.IsTrue(sliceTest.Equals(slice)); + sliceTest = new byte[] { 0x01, 0x03 }; + Assert.IsFalse(sliceTest.Equals(slice)); + } + + [TestMethod] + public void TestEqualsObj() + { + sliceTest = new byte[] { 0x01 }; + object slice = null; + bool result = sliceTest.Equals(slice); + Assert.AreEqual(false, result); + slice = new Test(); + result = sliceTest.Equals(slice); + Assert.AreEqual(false, result); + slice = sliceTest; + result = sliceTest.Equals(slice); + Assert.AreEqual(true, result); + Slice s = new byte[] { 0x01 }; + result = sliceTest.Equals(s); + Assert.AreEqual(true, result); + s = new byte[] { 0x01, 0x02 }; + result = sliceTest.Equals(s); + Assert.AreEqual(false, result); + } + + [TestMethod] + public void TestGetHashCode() + { + byte[] arr = new byte[] { 0x01, 0x02 }; + sliceTest = arr; + int hash1 = (int)arr.Murmur32(0); + int hash2 = sliceTest.GetHashCode(); + Assert.AreEqual(hash2, hash1); + } + + [TestMethod] + public void TestFromArray() + { + byte[] arr = new byte[]{ + 0x01,0x01,0x01,0x01, + }; + IntPtr parr = Marshal.AllocHGlobal(arr.Length); + for (int i = 0; i < arr.Length; i++) + { + Marshal.WriteByte(parr + i, 0x01); + } + UIntPtr plength = new UIntPtr((uint)arr.Length); + Slice slice = new Slice(parr, plength); + sliceTest = arr; + Assert.AreEqual(slice, sliceTest); + Marshal.FreeHGlobal(parr); + } + + [TestMethod] + public void TestToArray() + { + sliceTest = new Slice(); + byte[] arr = sliceTest.ToArray(); + Assert.AreEqual(0, arr.Length); + arr = new byte[] { 0x01, 0x02 }; + sliceTest = arr; + byte[] parr = sliceTest.ToArray(); + Assert.AreSame(parr, arr); + } + + [TestMethod] + public void TestToBoolean() + { + sliceTest = new byte[] { 0x01, 0x02 }; + Assert.ThrowsException(() => sliceTest.ToBoolean()); + sliceTest = (byte)0x01; + bool result = sliceTest.ToBoolean(); + Assert.AreEqual(true, result); + } + + [TestMethod] + public void TestToByte() + { + sliceTest = new byte[] { 0x01, 0x02 }; + Assert.ThrowsException(() => sliceTest.ToByte()); + sliceTest = (byte)0x01; + byte result = sliceTest.ToByte(); + Assert.AreEqual((byte)0x01, result); + } + + [TestMethod] + public void TestToDouble() + { + sliceTest = new byte[] { 0x01 }; + Assert.ThrowsException(() => sliceTest.ToDouble()); + byte[] arr = new byte[sizeof(double)]; + sliceTest = arr; + double result = sliceTest.ToDouble(); + Assert.AreEqual(0D, result); + sliceTest = 0.5D; + Assert.AreEqual(0.5D, sliceTest.ToDouble()); + } + + [TestMethod] + public void TestToInt16() + { + sliceTest = new byte[] { 0x01 }; + Assert.ThrowsException(() => sliceTest.ToInt16()); + sliceTest = (Int16)(-15); + Assert.AreEqual((Int16)(-15), sliceTest.ToInt16()); + } + + [TestMethod] + public void TestToInt32() + { + sliceTest = new byte[] { 0x01 }; + Assert.ThrowsException(() => sliceTest.ToInt32()); + sliceTest = (Int32)(-15); + Assert.AreEqual((Int32)(-15), sliceTest.ToInt32()); + } + + [TestMethod] + public void TestToInt64() + { + sliceTest = new byte[] { 0x01 }; + Assert.ThrowsException(() => sliceTest.ToInt64()); + sliceTest = Int64.MaxValue; + Assert.AreEqual(Int64.MaxValue, sliceTest.ToInt64()); + } + + [TestMethod] + public void TestToSingle() + { + sliceTest = new byte[] { 0x01 }; + Assert.ThrowsException(() => sliceTest.ToSingle()); + sliceTest = (float)(-15.5); + Assert.AreEqual((float)(-15.5), sliceTest.ToSingle()); + } + + [TestMethod] + public void TestToString() + { + sliceTest = "abc你好"; + Assert.AreEqual("abc你好", sliceTest.ToString()); + } + + [TestMethod] + public void TestToUint16() + { + sliceTest = new byte[] { 0x01 }; + Assert.ThrowsException(() => sliceTest.ToUInt16()); + sliceTest = (UInt16)(25); + Assert.AreEqual((UInt16)25, sliceTest.ToUInt16()); + } + + [TestMethod] + public void TestToUint32() + { + sliceTest = new byte[] { 0x01 }; + Assert.ThrowsException(() => sliceTest.ToUInt32()); + sliceTest = (UInt32)(2525252525); + Assert.AreEqual((UInt32)2525252525, sliceTest.ToUInt32()); + } + + [TestMethod] + public void TestToUint64() + { + sliceTest = new byte[] { 0x01 }; + Assert.ThrowsException(() => sliceTest.ToUInt64()); + sliceTest = (UInt64)(0x2525252525252525); + Assert.AreEqual((UInt64)(0x2525252525252525), sliceTest.ToUInt64()); + } + + [TestMethod] + public void TestFromBool() + { + byte[] arr = { 0x01 }; + Slice slice = arr; + sliceTest = true; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestFromByte() + { + sliceTest = (byte)0x01; + byte[] arr = { 0x01 }; + Slice slice = arr; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestFromDouble() + { + Slice slice = BitConverter.GetBytes(1.23D); + sliceTest = 1.23D; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestFromShort() + { + Slice slice = BitConverter.GetBytes((short)1234); + sliceTest = (short)1234; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestFromInt() + { + Slice slice = BitConverter.GetBytes(-1234); + sliceTest = -1234; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestFromLong() + { + Slice slice = BitConverter.GetBytes(-1234L); + sliceTest = -1234L; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestFromFloat() + { + Slice slice = BitConverter.GetBytes(1.234F); + sliceTest = 1.234F; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestFromString() + { + string str = "abcdefghijklmnopqrstuvwxwz!@#$%^&*&()_+?><你好"; + Slice slice = Encoding.UTF8.GetBytes(str); + sliceTest = str; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestFromUnshort() + { + Slice slice = BitConverter.GetBytes((ushort)12345); + sliceTest = (ushort)12345; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestFromUint() + { + Slice slice = BitConverter.GetBytes((uint)12345); + sliceTest = (uint)12345; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestFromUlong() + { + Slice slice = BitConverter.GetBytes(12345678UL); + sliceTest = 12345678UL; + Assert.AreEqual(slice, sliceTest); + } + + [TestMethod] + public void TestLessThan() + { + sliceTest = new byte[] { 0x01 }; + Slice slice = new byte[] { 0x02 }; + bool result = sliceTest < slice; + Assert.AreEqual(true, result); + slice = new byte[] { 0x01 }; + result = sliceTest < slice; + Assert.AreEqual(false, result); + slice = new byte[] { 0x00 }; + result = sliceTest < slice; + Assert.AreEqual(false, result); + } + + [TestMethod] + public void TestLessThanAndEqual() + { + sliceTest = new byte[] { 0x01 }; + Slice slice = new byte[] { 0x02 }; + bool result = sliceTest <= slice; + Assert.AreEqual(true, result); + slice = new byte[] { 0x01 }; + result = sliceTest <= slice; + Assert.AreEqual(true, result); + slice = new byte[] { 0x00 }; + result = sliceTest <= slice; + Assert.AreEqual(false, result); + } + + [TestMethod] + public void TestGreatThan() + { + sliceTest = new byte[] { 0x01 }; + Slice slice = new byte[] { 0x00 }; + bool result = sliceTest > slice; + Assert.AreEqual(true, result); + slice = new byte[] { 0x01 }; + result = sliceTest > slice; + Assert.AreEqual(false, result); + slice = new byte[] { 0x02 }; + result = sliceTest > slice; + Assert.AreEqual(false, result); + } + + [TestMethod] + public void TestGreatThanAndEqual() + { + sliceTest = new byte[] { 0x01 }; + Slice slice = new byte[] { 0x00 }; + bool result = sliceTest >= slice; + Assert.AreEqual(true, result); + slice = new byte[] { 0x01 }; + result = sliceTest >= slice; + Assert.AreEqual(true, result); + slice = new byte[] { 0x02 }; + result = sliceTest >= slice; + Assert.AreEqual(false, result); + } + + [TestMethod] + public void TestEqual() + { + sliceTest = new byte[] { 0x01 }; + Slice slice = new byte[] { 0x00 }; + bool result = sliceTest == slice; + Assert.AreEqual(false, result); + slice = new byte[] { 0x01 }; + result = sliceTest == slice; + Assert.AreEqual(true, result); + slice = new byte[] { 0x02 }; + result = sliceTest == slice; + Assert.AreEqual(false, result); + } + + [TestMethod] + public void TestUnequal() + { + sliceTest = new byte[] { 0x01 }; + Slice slice = new byte[] { 0x00 }; + bool result = sliceTest != slice; + Assert.AreEqual(true, result); + slice = new byte[] { 0x01 }; + result = sliceTest != slice; + Assert.AreEqual(false, result); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/IO/Json/UT_JArray.cs b/neo.UnitTests/IO/Json/UT_JArray.cs index 7e6e81c8d3..1bb165f224 100644 --- a/neo.UnitTests/IO/Json/UT_JArray.cs +++ b/neo.UnitTests/IO/Json/UT_JArray.cs @@ -1,9 +1,9 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; -using FluentAssertions; using System; -using System.Linq; using System.Collections; +using System.Linq; namespace Neo.UnitTests.IO.Json { @@ -48,9 +48,11 @@ public void SetUp() [TestMethod] public void TestAdd() { - var jArray = new JArray(); - jArray.Add(alice); - jArray.Add(bob); + var jArray = new JArray + { + alice, + bob + }; var jAlice = jArray[0]; var jBob = jArray[1]; jAlice["name"].ToString().Should().Be(alice["name"].ToString()); @@ -104,9 +106,10 @@ public void TestClear() [TestMethod] public void TestContains() { - var jArray = new JArray(); - jArray.Add(alice); - + var jArray = new JArray + { + alice + }; jArray.Contains(alice).Should().BeTrue(); jArray.Contains(bob).Should().BeFalse(); } diff --git a/neo.UnitTests/IO/Json/UT_JObject.cs b/neo.UnitTests/IO/Json/UT_JObject.cs index 38781ed96a..3c8dff0b46 100644 --- a/neo.UnitTests/IO/Json/UT_JObject.cs +++ b/neo.UnitTests/IO/Json/UT_JObject.cs @@ -3,7 +3,6 @@ using Neo.IO.Json; using System; using System.IO; -using System.Reflection; namespace Neo.UnitTests.IO.Json { From 7fba266b86ab1f45319d4e9f404a61df73ed3092 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 8 Aug 2019 11:14:01 +0200 Subject: [PATCH 069/305] Fix (#1008) --- neo/Wallets/Wallet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index 310d11032a..b68733bb71 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -303,7 +303,7 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement, Attributes = attributes }; - using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, tx, testMode: true)) + using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.Clone(), tx, testMode: true)) { if (engine.State.HasFlag(VMState.FAULT)) throw new InvalidOperationException($"Failed execution for '{script.ToHexString()}'"); From 601bb4790143447b1b85e81e1ebb6afa9ff1a4b8 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 8 Aug 2019 13:46:39 +0200 Subject: [PATCH 070/305] Fix for 1005 (#1006) --- neo/SmartContract/Native/Tokens/Nep5AccountState.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/neo/SmartContract/Native/Tokens/Nep5AccountState.cs index 2cc19f4619..c775f044ec 100644 --- a/neo/SmartContract/Native/Tokens/Nep5AccountState.cs +++ b/neo/SmartContract/Native/Tokens/Nep5AccountState.cs @@ -19,7 +19,7 @@ public Nep5AccountState(byte[] data) public void FromByteArray(byte[] data) { - FromStruct((Struct)data.DeserializeStackItem(16, 32)); + FromStruct((Struct)data.DeserializeStackItem(16, 34)); } protected virtual void FromStruct(Struct @struct) From b1f078d0b0dc8d57e2f6e6c57046baf25f1af351 Mon Sep 17 00:00:00 2001 From: belane Date: Sat, 10 Aug 2019 20:38:59 +0200 Subject: [PATCH 071/305] Fix null values during ChangeViewReason (#1016) --- neo/Consensus/ConsensusService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index e4983830a4..8723b2c70b 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -551,7 +551,7 @@ private void OnTimer(Timer timer) { var reason = ChangeViewReason.Timeout; - if (context.Block != null && context.TransactionHashes.Count() > context.Transactions.Count) + if (context.Block != null && context.TransactionHashes?.Count() > context.Transactions?.Count) { reason = ChangeViewReason.TxNotFound; } From 39d4e7031ca5304af5141a2aa5cd5878332f25e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Sun, 11 Aug 2019 10:06:31 -0300 Subject: [PATCH 072/305] Avoid processing own hashes (3x) (#1009) * Avoid processing own hashes * Shargon's suggestion I * Shargon's suggestion II * rename --- neo/IO/Caching/FIFOSet.cs | 5 +++++ neo/Network/P2P/ProtocolHandler.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/neo/IO/Caching/FIFOSet.cs b/neo/IO/Caching/FIFOSet.cs index 7769cb5ad5..aba54463ae 100644 --- a/neo/IO/Caching/FIFOSet.cs +++ b/neo/IO/Caching/FIFOSet.cs @@ -41,6 +41,11 @@ public bool Add(T item) return true; } + public bool Contains(T item) + { + return dictionary.Contains(item); + } + public void ExceptWith(IEnumerable hashes) { foreach (var hash in hashes) diff --git a/neo/Network/P2P/ProtocolHandler.cs b/neo/Network/P2P/ProtocolHandler.cs index 7f9821709b..234c42d9ef 100644 --- a/neo/Network/P2P/ProtocolHandler.cs +++ b/neo/Network/P2P/ProtocolHandler.cs @@ -248,7 +248,7 @@ private void OnInventoryReceived(IInventory inventory) private void OnInvMessageReceived(InvPayload payload) { - UInt256[] hashes = payload.Hashes.Where(p => knownHashes.Add(p)).ToArray(); + UInt256[] hashes = payload.Hashes.Where(p => knownHashes.Add(p) && !sentHashes.Contains(p)).ToArray(); if (hashes.Length == 0) return; switch (payload.Type) { From 02c49ecd7b1635d2b1c144379880bf9d13cc8cca Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 12 Aug 2019 09:34:39 +0300 Subject: [PATCH 073/305] Scoped signatures (#968) * Base * Check Witness * Fix some unit tests * More ut fixes * Clean code * Fix more unit tests * Fake witness for make transfer * Fix ut * Simplifying if * rename to RootAccess * adding scope type, preparing for cosigners * reading scope from stream * attaching CosignerUsage * calculate scope size * Finish cosigner usage * Global constant * Clean * AsSerializable @erikzhang * avoiding empty scope data serialization * fix logic * moved scopes to Transaction * fixed tests * Change CheckWitness * Follow the standard * rename var * Remove FakeWitness * Fix cosigner ussage * Clean * Fix CosignerUsage * fix RootAccess * removed not null * fixed malformed tx generation * no need for attribute on all test tx * testing sizes * renamed RootAccess to EntryOnly, and explained enum compositions * discovering which size is wrong * Remove spaces * doubts on scope sign test * removing scopes everywhere * wrong tx size * fixed tests * remove print * more scope tests * reorder * revert changes on Witness * Modify CheckWitness * Reduce changes * Fix Cosigner usage * Combine CosignerUsage * Indent * filtering groups by pubkey * Organize * Rename ScriptHash to Account * renamed EntryOnly to CalledByEntry * dotnet format * fix file UT_CosignerUsage * tests for double cosigners * assert catch exceptions * clean and format * improvements * Format ut exceptions * Rename ut * Remove changes * Follow comment standard * Rename to Cosigner * Move cosigners outside * Clean comments * Reorder the parameters * Fix unit tests * limit max cosigners by 16 (same as attributes) * testing non-distinct cosigners * respect max cosigners * MaxSubitems * test CalledByEntry together with GAS * Update comment * Clean comments * Global=0 * Fix ut * broken test for combination Global | NEO * back to global 0x80 * global back to 0x00, as default * Update comment --- .../Cryptography/UT_Cryptography_Helper.cs | 1 + .../Nep5NativeContractExtensions.cs | 12 +- neo.UnitTests/IO/Caching/UT_RelayCache.cs | 1 + neo.UnitTests/Ledger/UT_MemoryPool.cs | 1 + neo.UnitTests/Ledger/UT_PoolItem.cs | 11 +- .../Network/P2P/Payloads/UT_Block.cs | 49 +- .../Network/P2P/Payloads/UT_Cosigner.cs | 165 ++++ .../Network/P2P/Payloads/UT_Header.cs | 29 +- .../Network/P2P/Payloads/UT_Transaction.cs | 820 +++++++++++++++++- .../Native/Tokens/UT_NeoToken.cs | 2 +- .../SmartContract/Native/UT_PolicyContract.cs | 8 +- neo.UnitTests/TestUtils.cs | 2 + neo/Ledger/Blockchain.cs | 1 + neo/Network/P2P/Payloads/Cosigner.cs | 76 ++ neo/Network/P2P/Payloads/Transaction.cs | 15 +- .../P2P/Payloads/TransactionAttributeUsage.cs | 1 - neo/Network/P2P/Payloads/WitnessScope.cs | 31 + .../ContractParametersContext.cs | 8 + neo/SmartContract/InteropService.cs | 31 +- neo/Wallets/Wallet.cs | 32 +- 20 files changed, 1204 insertions(+), 92 deletions(-) create mode 100644 neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs create mode 100644 neo/Network/P2P/Payloads/Cosigner.cs create mode 100644 neo/Network/P2P/Payloads/WitnessScope.cs diff --git a/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index 06f672ff77..d7a3e65136 100644 --- a/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -145,6 +145,7 @@ public void TestTest() Sender = UInt160.Zero, SystemFee = 4200000000, Attributes = new TransactionAttribute[0], + Cosigners = new Cosigner[0], Witnesses = new[] { new Witness diff --git a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs index 42c5c87543..a9aa8a7777 100644 --- a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs +++ b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs @@ -12,9 +12,9 @@ namespace Neo.UnitTests.Extensions { public static class Nep5NativeContractExtensions - { + { internal class ManualWitness : IVerifiable - { + { private readonly UInt160[] _hashForVerify; public Witness[] Witnesses @@ -34,14 +34,14 @@ public ManualWitness(params UInt160[] hashForVerify) public void DeserializeUnsigned(BinaryReader reader) { } - public UInt160[] GetScriptHashesForVerifying(Neo.Persistence.Snapshot snapshot) => _hashForVerify; + public UInt160[] GetScriptHashesForVerifying(Persistence.Snapshot snapshot) => _hashForVerify; public void Serialize(BinaryWriter writer) { } public void SerializeUnsigned(BinaryWriter writer) { } } - public static bool Transfer(this NativeContract contract, Neo.Persistence.Snapshot snapshot, byte[] from, byte[] to, BigInteger amount, bool signFrom) + public static bool Transfer(this NativeContract contract, Persistence.Snapshot snapshot, byte[] from, byte[] to, BigInteger amount, bool signFrom) { var engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(signFrom ? new UInt160(from) : null), snapshot, 0, true); @@ -90,7 +90,7 @@ public static string[] SupportedStandards(this NativeContract contract) .ToArray(); } - public static BigInteger TotalSupply(this NativeContract contract, Neo.Persistence.Snapshot snapshot) + public static BigInteger TotalSupply(this NativeContract contract, Persistence.Snapshot snapshot) { var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); @@ -110,7 +110,7 @@ public static BigInteger TotalSupply(this NativeContract contract, Neo.Persisten return (result as VM.Types.Integer).GetBigInteger(); } - public static BigInteger BalanceOf(this NativeContract contract, Neo.Persistence.Snapshot snapshot, byte[] account) + public static BigInteger BalanceOf(this NativeContract contract, Persistence.Snapshot snapshot, byte[] account) { var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); diff --git a/neo.UnitTests/IO/Caching/UT_RelayCache.cs b/neo.UnitTests/IO/Caching/UT_RelayCache.cs index 537305e2ee..ed41c1128a 100644 --- a/neo.UnitTests/IO/Caching/UT_RelayCache.cs +++ b/neo.UnitTests/IO/Caching/UT_RelayCache.cs @@ -27,6 +27,7 @@ public void TestGetKeyForItem() SystemFee = 0, NetworkFee = 0, ValidUntilBlock = 100, + Cosigners = new Cosigner[0], Attributes = new TransactionAttribute[0], Script = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }, Witnesses = new Witness[0] diff --git a/neo.UnitTests/Ledger/UT_MemoryPool.cs b/neo.UnitTests/Ledger/UT_MemoryPool.cs index 1b5af16240..96b0e99a19 100644 --- a/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -54,6 +54,7 @@ private Transaction CreateTransactionWithFee(long fee) mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = fee; mock.Object.Attributes = new TransactionAttribute[0]; + mock.Object.Cosigners = new Cosigner[0]; mock.Object.Witnesses = new[] { new Witness diff --git a/neo.UnitTests/Ledger/UT_PoolItem.cs b/neo.UnitTests/Ledger/UT_PoolItem.cs index d8f4e4f696..e87aad3ffe 100644 --- a/neo.UnitTests/Ledger/UT_PoolItem.cs +++ b/neo.UnitTests/Ledger/UT_PoolItem.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Neo.Ledger; +using Neo.IO; using Neo.Network.P2P.Payloads; using System; @@ -36,10 +37,10 @@ public void TestCleanup() [TestMethod] public void PoolItem_CompareTo_Fee() { - int size1 = 50; + int size1 = 51; int netFeeSatoshi1 = 1; var tx1 = GenerateTx(netFeeSatoshi1, size1); - int size2 = 50; + int size2 = 51; int netFeeSatoshi2 = 2; var tx2 = GenerateTx(netFeeSatoshi2, size2); @@ -56,7 +57,7 @@ public void PoolItem_CompareTo_Fee() [TestMethod] public void PoolItem_CompareTo_Hash() { - int sizeFixed = 50; + int sizeFixed = 51; int netFeeSatoshiFixed = 1; for (int testRuns = 0; testRuns < 30; testRuns++) @@ -122,6 +123,7 @@ public static Transaction GenerateTx(long networkFee, int size, byte[] overrideS Sender = UInt160.Zero, NetworkFee = networkFee, Attributes = new TransactionAttribute[0], + Cosigners = new Cosigner[0], Witnesses = new[] { new Witness @@ -132,6 +134,9 @@ public static Transaction GenerateTx(long networkFee, int size, byte[] overrideS } }; + tx.Attributes.Length.Should().Be(0); + tx.Cosigners.Length.Should().Be(0); + int diff = size - tx.Size; if (diff < 0) throw new ArgumentException(); if (diff > 0) diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index 6736d8f936..f27421dd74 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -1,9 +1,9 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.IO.Json; using Neo.Network.P2P.Payloads; using System.IO; -using System.Text; namespace Neo.UnitTests.Network.P2P.Payloads { @@ -43,7 +43,7 @@ public void Size_Get() { UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 0); - // blockbase 4 + 64 + 32 + 4 + 4 + 20 + 4 + // blockbase 4 + 64 + 1 + 32 + 4 + 4 + 20 + 4 // block 9 + 1 uut.Size.Should().Be(114); } @@ -59,7 +59,7 @@ public void Size_Get_1_Transaction() TestUtils.GetTransaction() }; - uut.Size.Should().Be(165); + uut.Size.Should().Be(166); } [TestMethod] @@ -75,7 +75,7 @@ public void Size_Get_3_Transaction() TestUtils.GetTransaction() }; - uut.Size.Should().Be(267); + uut.Size.Should().Be(270); } [TestMethod] @@ -84,22 +84,8 @@ public void Serialize() UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 1); - byte[] data; - using (MemoryStream stream = new MemoryStream()) - { - using (BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, true)) - { - uut.Serialize(writer); - data = stream.ToArray(); - } - } - - byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 233, 19, 255, 133, 76, 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, 1, 0, 1, 81, 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, 1, 0, 1, 0, 0 }; - data.Length.Should().Be(requiredData.Length); - for (int i = 0; i < data.Length; i++) - { - data[i].Should().Be(requiredData[i]); - } + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000000f29b0d748a9ccf8c5af3cde10db3e36ec9a5f720643a2bcb4add76b3daf41d8e913ff854c000000000000000000000000000000000000000000000000000000010001510200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; + uut.ToArray().ToHexString().Should().Be(hex); } [TestMethod] @@ -108,17 +94,13 @@ 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); - uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - - byte[] data = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 233, 19, 255, 133, 76, 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, 1, 0, 1, 81, 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, 1, 0, 1, 0, 0 }; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c000000000000000000000000000000000000000000000000000000010001510200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; - int index = 0; - using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) + using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) + using (BinaryReader reader = new BinaryReader(ms)) { - using (BinaryReader reader = new BinaryReader(ms)) - { - uut.Deserialize(reader); - } + uut.Deserialize(reader); + merkRoot = uut.MerkleRoot; } assertStandardBlockTestVals(val256, merkRoot, val160, timestampVal, indexVal, scriptVal, transactionsVal); @@ -218,7 +200,7 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); jObj["hash"].AsString().Should().Be("0x4e1d8392e7c44830e7e45c18e5e0e3ef3c36af883868846d3691a436a62494b2"); - jObj["size"].AsNumber().Should().Be(165); + jObj["size"].AsNumber().Should().Be(166); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); jObj["merkleroot"].AsString().Should().Be("0xd841af3d6bd7adb4bca24306725f9aec363edb10de3cafc5f8cca948d7b0290f"); @@ -232,10 +214,11 @@ public void ToJson() jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; - txObj[0]["hash"].AsString().Should().Be("0x64ed4e0d79407c60bde534feb44fbbd19bd065282d27ecd3a1a7a647f66affa6"); - txObj[0]["size"].AsNumber().Should().Be(51); + txObj[0]["hash"].AsString().Should().Be("0xf374032ea013e3d9272f7a25dc1fe497a489ae36e94aa06270b26e60ab693435"); + txObj[0]["size"].AsNumber().Should().Be(52); txObj[0]["version"].AsNumber().Should().Be(0); ((JArray)txObj[0]["attributes"]).Count.Should().Be(0); + ((JArray)txObj[0]["cosigners"]).Count.Should().Be(0); txObj[0]["net_fee"].AsString().Should().Be("0"); } } diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs new file mode 100644 index 0000000000..ef6124b3a8 --- /dev/null +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs @@ -0,0 +1,165 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.IO.Json; +using Neo.Network.P2P.Payloads; + +namespace Neo.UnitTests.Network.P2P.Payloads +{ + [TestClass] + public class UT_Cosigner + { + [TestMethod] + public void Serialize_Deserialize_Global() + { + var attr = new Cosigner() + { + Scopes = WitnessScope.Global, + Account = UInt160.Zero + }; + + var hex = "000000000000000000000000000000000000000000"; + attr.ToArray().ToHexString().Should().Be(hex); + + var copy = hex.HexToBytes().AsSerializable(); + + Assert.AreEqual(attr.Scopes, copy.Scopes); + Assert.AreEqual(attr.Account, copy.Account); + } + + [TestMethod] + public void Serialize_Deserialize_CalledByEntry() + { + var attr = new Cosigner() + { + Scopes = WitnessScope.CalledByEntry, + Account = UInt160.Zero + }; + + var hex = "000000000000000000000000000000000000000001"; + attr.ToArray().ToHexString().Should().Be(hex); + + var copy = hex.HexToBytes().AsSerializable(); + + Assert.AreEqual(attr.Scopes, copy.Scopes); + Assert.AreEqual(attr.Account, copy.Account); + } + + [TestMethod] + public void Serialize_Deserialize_CustomContracts() + { + var attr = new Cosigner() + { + Scopes = WitnessScope.CustomContracts, + AllowedContracts = new[] { UInt160.Zero }, + Account = UInt160.Zero + }; + + var hex = "000000000000000000000000000000000000000010010000000000000000000000000000000000000000"; + attr.ToArray().ToHexString().Should().Be(hex); + + var copy = hex.HexToBytes().AsSerializable(); + + Assert.AreEqual(attr.Scopes, copy.Scopes); + CollectionAssert.AreEqual(attr.AllowedContracts, copy.AllowedContracts); + Assert.AreEqual(attr.Account, copy.Account); + } + + [TestMethod] + public void Serialize_Deserialize_CustomGroups() + { + var attr = new Cosigner() + { + Scopes = WitnessScope.CustomGroups, + AllowedGroups = new[] { ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1) }, + Account = UInt160.Zero + }; + + var hex = "0000000000000000000000000000000000000000200103b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"; + attr.ToArray().ToHexString().Should().Be(hex); + + var copy = hex.HexToBytes().AsSerializable(); + + Assert.AreEqual(attr.Scopes, copy.Scopes); + CollectionAssert.AreEqual(attr.AllowedGroups, copy.AllowedGroups); + Assert.AreEqual(attr.Account, copy.Account); + } + + [TestMethod] + public void Json_Global() + { + var attr = new Cosigner() + { + Scopes = WitnessScope.Global, + Account = UInt160.Zero + }; + + var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"Global\"}"; + attr.ToJson().ToString().Should().Be(json); + + var copy = Cosigner.FromJson(JObject.Parse(json)); + + Assert.AreEqual(attr.Scopes, copy.Scopes); + Assert.AreEqual(attr.Account, copy.Account); + } + + [TestMethod] + public void Json_CalledByEntry() + { + var attr = new Cosigner() + { + Scopes = WitnessScope.CalledByEntry, + Account = UInt160.Zero + }; + + var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CalledByEntry\"}"; + attr.ToJson().ToString().Should().Be(json); + + var copy = Cosigner.FromJson(JObject.Parse(json)); + + Assert.AreEqual(attr.Scopes, copy.Scopes); + Assert.AreEqual(attr.Account, copy.Account); + } + + [TestMethod] + public void Json_CustomContracts() + { + var attr = new Cosigner() + { + Scopes = WitnessScope.CustomContracts, + AllowedContracts = new[] { UInt160.Zero }, + Account = UInt160.Zero + }; + + var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CustomContracts\",\"allowedContracts\":[\"0x0000000000000000000000000000000000000000\"]}"; + attr.ToJson().ToString().Should().Be(json); + + var copy = Cosigner.FromJson(JObject.Parse(json)); + + Assert.AreEqual(attr.Scopes, copy.Scopes); + CollectionAssert.AreEqual(attr.AllowedContracts, copy.AllowedContracts); + Assert.AreEqual(attr.Account, copy.Account); + } + + [TestMethod] + public void Json_CustomGroups() + { + var attr = new Cosigner() + { + Scopes = WitnessScope.CustomGroups, + AllowedGroups = new[] { ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1) }, + Account = UInt160.Zero + }; + + var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CustomGroups\",\"allowedGroups\":[\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\"]}"; + attr.ToJson().ToString().Should().Be(json); + + var copy = Cosigner.FromJson(JObject.Parse(json)); + + Assert.AreEqual(attr.Scopes, copy.Scopes); + CollectionAssert.AreEqual(attr.AllowedGroups, copy.AllowedGroups); + Assert.AreEqual(attr.Account, copy.Account); + } + } +} diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index 8002f4b196..b367a3efbd 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -1,8 +1,8 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.Network.P2P.Payloads; using System.IO; -using System.Text; namespace Neo.UnitTests.Network.P2P.Payloads { @@ -22,7 +22,7 @@ public void Size_Get() { UInt256 val256 = UInt256.Zero; TestUtils.SetupHeaderWithValues(uut, val256, out _, out _, out _, out _, out _); - // blockbase 4 + 64 + 32 + 4 + 4 + 20 + 4 + // blockbase 4 + 64 + 1 + 32 + 4 + 4 + 20 + 4 // header 1 uut.Size.Should().Be(105); } @@ -35,10 +35,9 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 233, 19, 255, 133, 76, 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, 1, 0, 1, 81, 0 }; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000000f29b0d748a9ccf8c5af3cde10db3e36ec9a5f720643a2bcb4add76b3daf41d8e913ff854c0000000000000000000000000000000000000000000000000000000100015100"; - int index = 0; - using (MemoryStream ms = new MemoryStream(requiredData, index, requiredData.Length - index, false)) + using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) { using (BinaryReader reader = new BinaryReader(ms)) { @@ -97,24 +96,8 @@ public void Serialize() UInt256 val256 = UInt256.Zero; TestUtils.SetupHeaderWithValues(uut, val256, out _, out _, out _, out _, out _); - byte[] data; - using (MemoryStream stream = new MemoryStream()) - { - using (BinaryWriter writer = new BinaryWriter(stream, Encoding.ASCII, true)) - { - uut.Serialize(writer); - data = stream.ToArray(); - } - } - - byte[] requiredData = new byte[] { 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, 15, 41, 176, 215, 72, 169, 204, 248, 197, 175, 60, 222, 16, 219, 62, 54, 236, 154, 95, 114, 6, 67, 162, 188, 180, 173, 215, 107, 61, 175, 65, 216, 233, 19, 255, 133, 76, 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, 1, 0, 1, 81, 0 }; - - data.Length.Should().Be(requiredData.Length); - - for (int i = 0; i < data.Length; i++) - { - data[i].Should().Be(requiredData[i]); - } + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000000f29b0d748a9ccf8c5af3cde10db3e36ec9a5f720643a2bcb4add76b3daf41d8e913ff854c0000000000000000000000000000000000000000000000000000000100015100"; + uut.ToArray().ToHexString().Should().Be(hex); } } } diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 2dfa8b4c35..2c81e91537 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -12,6 +12,8 @@ using Neo.VM; using Neo.Wallets; using Neo.Wallets.NEP6; +using System; +using System.Numerics; namespace Neo.UnitTests.Network.P2P.Payloads { @@ -66,6 +68,7 @@ public void Size_Get() uut.Script = TestUtils.GetByteArray(32, 0x42); uut.Sender = UInt160.Zero; uut.Attributes = new TransactionAttribute[0]; + uut.Cosigners = new Cosigner[0]; uut.Witnesses = new[] { new Witness @@ -78,7 +81,7 @@ public void Size_Get() uut.Version.Should().Be(0); uut.Script.Length.Should().Be(32); uut.Script.GetVarSize().Should().Be(33); - uut.Size.Should().Be(82); + uut.Size.Should().Be(83); } private NEP6Wallet GenerateTestWallet() @@ -175,12 +178,15 @@ public void FeeIsMultiSigContract() } var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); - Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); + Assert.AreEqual(verificationGas, 2000540); + Assert.AreEqual(sizeGas, 358000); + Assert.AreEqual(verificationGas + sizeGas, 2358540); + Assert.AreEqual(tx.NetworkFee, 2358540); } } [TestMethod] - public void FeeIsSignatureContract() + public void FeeIsSignatureContractDetailed() { var wallet = GenerateTestWallet(); var snapshot = store.GetSnapshot(); @@ -206,6 +212,7 @@ public void FeeIsSignatureContract() // Make transaction + // self-transfer of 1e-8 GAS var tx = wallet.MakeTransaction(new TransferOutput[] { new TransferOutput() @@ -217,12 +224,26 @@ public void FeeIsSignatureContract() }, acc.ScriptHash); Assert.IsNotNull(tx); + Assert.IsNull(tx.Witnesses); + + // check pre-computed network fee (already guessing signature sizes) + tx.NetworkFee.Should().Be(1257240); + // ---- // Sign + // ---- var data = new ContractParametersContext(tx); - Assert.IsTrue(wallet.Sign(data)); + // 'from' is always required as witness + // if not included on cosigner with a scope, its scope should be considered 'CalledByEntry' + data.ScriptHashes.Count.Should().Be(1); + data.ScriptHashes[0].ShouldBeEquivalentTo(acc.ScriptHash); + // will sign tx + bool signed = wallet.Sign(data); + Assert.IsTrue(signed); + // get witnesses from signed 'data' tx.Witnesses = data.GetWitnesses(); + tx.Witnesses.Length.Should().Be(1); // Fast check @@ -243,8 +264,791 @@ public void FeeIsSignatureContract() verificationGas += engine.GasConsumed; } } + Assert.AreEqual(verificationGas, 1000240); + + // ------------------ + // check tx_size cost + // ------------------ + Assert.AreEqual(tx.Size, 257); + + // will verify tx size, step by step + + // Part I + Assert.AreEqual(Transaction.HeaderSize, 45); + // Part II + Assert.AreEqual(tx.Attributes.GetVarSize(), 1); + Assert.AreEqual(tx.Attributes.Length, 0); + Assert.AreEqual(tx.Cosigners.Length, 1); + Assert.AreEqual(tx.Cosigners.GetVarSize(), 22); + // Note that Data size and Usage size are different (because of first byte on GetVarSize()) + Assert.AreEqual(tx.Cosigners[0].Size, 21); + // Part III + Assert.AreEqual(tx.Script.GetVarSize(), 82); + // Part IV + Assert.AreEqual(tx.Witnesses.GetVarSize(), 107); + // I + II + III + IV + Assert.AreEqual(tx.Size, 45 + 23 + 82 + 107); + + Assert.AreEqual(NativeContract.Policy.GetFeePerByte(snapshot), 1000); + var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); + Assert.AreEqual(sizeGas, 257000); + + // final check on sum: verification_cost + tx_size + Assert.AreEqual(verificationGas + sizeGas, 1257240); + // final assert + Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); + } + } + + [TestMethod] + public void FeeIsSignatureContract_TestScope_Global() + { + var wallet = GenerateTestWallet(); + var snapshot = store.GetSnapshot(); + + // no password on this wallet + using (var unlock = wallet.Unlock("")) + { + var acc = wallet.CreateAccount(); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + // Make transaction + // Manually creating script + + byte[] script; + using (ScriptBuilder sb = new ScriptBuilder()) + { + // 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.Emit(OpCode.THROWIFNOT); + script = sb.ToArray(); + } + + // trying global scope + var cosigners = new Cosigner[]{ new Cosigner + { + Account = acc.ScriptHash, + Scopes = WitnessScope.Global + } }; + + // using this... + + var tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners); + + Assert.IsNotNull(tx); + Assert.IsNull(tx.Witnesses); + + // ---- + // Sign + // ---- + + var data = new ContractParametersContext(tx); + bool signed = wallet.Sign(data); + Assert.IsTrue(signed); + + // get witnesses from signed 'data' + tx.Witnesses = data.GetWitnesses(); + tx.Witnesses.Length.Should().Be(1); + + // Fast check + Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + + // Check + long verificationGas = 0; + foreach (var witness in tx.Witnesses) + { + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + { + engine.LoadScript(witness.VerificationScript); + engine.LoadScript(witness.InvocationScript); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + verificationGas += engine.GasConsumed; + } + } + // get sizeGas + var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); + // final check on sum: verification_cost + tx_size + Assert.AreEqual(verificationGas + sizeGas, 1257240); + // final assert + Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); + } + } + + [TestMethod] + public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() + { + var wallet = GenerateTestWallet(); + var snapshot = store.GetSnapshot(); + + // no password on this wallet + using (var unlock = wallet.Unlock("")) + { + var acc = wallet.CreateAccount(); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + // Make transaction + // Manually creating script + + byte[] script; + using (ScriptBuilder sb = new ScriptBuilder()) + { + // 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.Emit(OpCode.THROWIFNOT); + script = sb.ToArray(); + } + + // trying global scope + var cosigners = new Cosigner[]{ new Cosigner + { + Account = acc.ScriptHash, + Scopes = WitnessScope.CustomContracts, + AllowedContracts = new[] { NativeContract.GAS.Hash } + } }; + + // using this... + + var tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners); + + Assert.IsNotNull(tx); + Assert.IsNull(tx.Witnesses); + + // ---- + // Sign + // ---- + + var data = new ContractParametersContext(tx); + bool signed = wallet.Sign(data); + Assert.IsTrue(signed); + + // get witnesses from signed 'data' + tx.Witnesses = data.GetWitnesses(); + tx.Witnesses.Length.Should().Be(1); + + // Fast check + Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + + // Check + long verificationGas = 0; + foreach (var witness in tx.Witnesses) + { + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + { + engine.LoadScript(witness.VerificationScript); + engine.LoadScript(witness.InvocationScript); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + verificationGas += engine.GasConsumed; + } + } + // get sizeGas + var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); + // final check on sum: verification_cost + tx_size + Assert.AreEqual(verificationGas + sizeGas, 1278240); + // final assert + Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); + } + } + [TestMethod] + public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() + { + var wallet = GenerateTestWallet(); + var snapshot = store.GetSnapshot(); + + // no password on this wallet + using (var unlock = wallet.Unlock("")) + { + var acc = wallet.CreateAccount(); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + // Make transaction + // Manually creating script + + byte[] script; + using (ScriptBuilder sb = new ScriptBuilder()) + { + // 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.Emit(OpCode.THROWIFNOT); + script = sb.ToArray(); + } + + // trying CalledByEntry together with GAS + var cosigners = new Cosigner[]{ new Cosigner + { + Account = acc.ScriptHash, + // This combination is supposed to actually be an OR, + // where it's valid in both Entry and also for Custom hash provided (in any execution level) + // it would be better to test this in the future including situations where a deeper call level uses this custom witness successfully + Scopes = WitnessScope.CustomContracts | WitnessScope.CalledByEntry, + AllowedContracts = new[] { NativeContract.GAS.Hash } + } }; + + // using this... + + var tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners); + + Assert.IsNotNull(tx); + Assert.IsNull(tx.Witnesses); + + // ---- + // Sign + // ---- + + var data = new ContractParametersContext(tx); + bool signed = wallet.Sign(data); + Assert.IsTrue(signed); + + // get witnesses from signed 'data' + tx.Witnesses = data.GetWitnesses(); + tx.Witnesses.Length.Should().Be(1); + + // Fast check + Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + + // Check + long verificationGas = 0; + foreach (var witness in tx.Witnesses) + { + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + { + engine.LoadScript(witness.VerificationScript); + engine.LoadScript(witness.InvocationScript); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + verificationGas += engine.GasConsumed; + } + } + // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); + // final check on sum: verification_cost + tx_size + Assert.AreEqual(verificationGas + sizeGas, 1278240); + // final assert + Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); + } + } + + [TestMethod] + public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() + { + var wallet = GenerateTestWallet(); + var snapshot = store.GetSnapshot(); + + // no password on this wallet + using (var unlock = wallet.Unlock("")) + { + var acc = wallet.CreateAccount(); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + // Make transaction + // Manually creating script + + byte[] script; + using (ScriptBuilder sb = new ScriptBuilder()) + { + // 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.Emit(OpCode.THROWIFNOT); + script = sb.ToArray(); + } + + // trying global scope + var cosigners = new Cosigner[]{ new Cosigner + { + Account = acc.ScriptHash, + Scopes = WitnessScope.CustomContracts, + AllowedContracts = new[] { NativeContract.NEO.Hash } + } }; + + // using this... + + // expects FAULT on execution of 'transfer' Application script + // due to lack of a valid witness validation + Transaction tx = null; + Assert.ThrowsException(() => + tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners)); + Assert.IsNull(tx); + } + } + + [TestMethod] + public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() + { + var wallet = GenerateTestWallet(); + var snapshot = store.GetSnapshot(); + + // no password on this wallet + using (var unlock = wallet.Unlock("")) + { + var acc = wallet.CreateAccount(); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + // Make transaction + // Manually creating script + + byte[] script; + using (ScriptBuilder sb = new ScriptBuilder()) + { + // 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.Emit(OpCode.THROWIFNOT); + script = sb.ToArray(); + } + + // trying two custom hashes, for same target account + var cosigners = new Cosigner[]{ new Cosigner + { + Account = acc.ScriptHash, + Scopes = WitnessScope.CustomContracts, + AllowedContracts = new[] { NativeContract.NEO.Hash, NativeContract.GAS.Hash } + } }; + + // using this... + + var tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners); + + Assert.IsNotNull(tx); + Assert.IsNull(tx.Witnesses); + + // ---- + // Sign + // ---- + + var data = new ContractParametersContext(tx); + bool signed = wallet.Sign(data); + Assert.IsTrue(signed); + + // get witnesses from signed 'data' + tx.Witnesses = data.GetWitnesses(); + // only a single witness should exist + tx.Witnesses.Length.Should().Be(1); + // no attributes must exist + tx.Attributes.Length.Should().Be(0); + // one cosigner must exist + tx.Cosigners.Length.Should().Be(1); + + // Fast check + Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + + // Check + long verificationGas = 0; + foreach (var witness in tx.Witnesses) + { + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + { + engine.LoadScript(witness.VerificationScript); + engine.LoadScript(witness.InvocationScript); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + verificationGas += engine.GasConsumed; + } + } + // get sizeGas + var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); + // final check on sum: verification_cost + tx_size + Assert.AreEqual(verificationGas + sizeGas, 1298240); + // final assert + Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); + } + } + + [TestMethod] + public void FeeIsSignatureContract_TestScope_NoScopeFAULT() + { + var wallet = GenerateTestWallet(); + var snapshot = store.GetSnapshot(); + + // no password on this wallet + using (var unlock = wallet.Unlock("")) + { + var acc = wallet.CreateAccount(); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + // Make transaction + // Manually creating script + + byte[] script; + using (ScriptBuilder sb = new ScriptBuilder()) + { + // 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.Emit(OpCode.THROWIFNOT); + script = sb.ToArray(); + } + + // trying with no scope + var attributes = new TransactionAttribute[] { }; + var cosigners = new Cosigner[] { }; + + // using this... + + // expects FAULT on execution of 'transfer' Application script + // due to lack of a valid witness validation + Transaction tx = null; + Assert.ThrowsException(() => tx = wallet.MakeTransaction(script, acc.ScriptHash, attributes, cosigners)); + Assert.IsNull(tx); + } + } + + [TestMethod] + public void Transaction_Serialize_Deserialize_Simple() + { + // good and simple transaction + Transaction txSimple = new Transaction + { + Version = 0x00, + Nonce = 0x01020304, + Sender = UInt160.Zero, + SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS + NetworkFee = 0x0000000000000001, + ValidUntilBlock = 0x01020304, + Attributes = new TransactionAttribute[0] { }, + Cosigners = new Cosigner[0] { }, + Script = new byte[] { (byte)OpCode.PUSH1 }, + Witnesses = new Witness[0] { } + }; + + byte[] sTx = txSimple.ToArray(); + + // detailed hexstring info (basic checking) + sTx.ToHexString().Should().Be("00" + // version + "04030201" + // nonce + "0000000000000000000000000000000000000000" + // sender + "00e1f50500000000" + // system fee (1 GAS) + "0100000000000000" + // network fee (1 satoshi) + "04030201" + // timelimit + "00" + // no attributes + "00" + // no cosigners + "0151" + // push1 script + "00"); // no witnesses + + // try to deserialize + Transaction tx2 = Neo.IO.Helper.AsSerializable(sTx); + + tx2.Version.Should().Be(0x00); + tx2.Nonce.Should().Be(0x01020304); + tx2.Sender.Should().Be(UInt160.Zero); + tx2.SystemFee.Should().Be(0x0000000005f5e100); // 1 GAS (long)BigInteger.Pow(10, 8) + tx2.NetworkFee.Should().Be(0x0000000000000001); + tx2.ValidUntilBlock.Should().Be(0x01020304); + tx2.Attributes.Should().BeEquivalentTo(new TransactionAttribute[0] { }); + tx2.Cosigners.Should().BeEquivalentTo(new Cosigner[0] { }); + tx2.Script.Should().BeEquivalentTo(new byte[] { (byte)OpCode.PUSH1 }); + tx2.Witnesses.Should().BeEquivalentTo(new Witness[0] { }); + } + + [TestMethod] + public void Transaction_Serialize_Deserialize_DistinctCosigners() + { + // cosigners must be distinct (regarding account) + + Transaction txDoubleCosigners = new Transaction + { + Version = 0x00, + Nonce = 0x01020304, + Sender = UInt160.Zero, + SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS + NetworkFee = 0x0000000000000001, + ValidUntilBlock = 0x01020304, + Attributes = new TransactionAttribute[0] { }, + Cosigners = new Cosigner[] { + new Cosigner + { + Account = UInt160.Parse("0x0001020304050607080900010203040506070809"), + Scopes = WitnessScope.Global + }, + new Cosigner + { + Account = UInt160.Parse("0x0001020304050607080900010203040506070809"), // same account as above + Scopes = WitnessScope.CalledByEntry // different scope, but still, same account (cannot do that) + } + }, + Script = new byte[] { (byte)OpCode.PUSH1 }, + Witnesses = new Witness[0] { } + }; + + byte[] sTx = txDoubleCosigners.ToArray(); + + // no need for detailed hexstring here (see basic tests for it) + sTx.ToHexString().Should().Be("0004030201000000000000000000000000000000000000000000e1f505000000000100000000000000040302010002090807060504030201000908070605040302010000090807060504030201000908070605040302010001015100"); + + // back to transaction (should fail, due to non-distinct cosigners) + Transaction tx2 = null; + Assert.ThrowsException(() => + tx2 = Neo.IO.Helper.AsSerializable(sTx) + ); + Assert.IsNull(tx2); + } + + + [TestMethod] + public void Transaction_Serialize_Deserialize_MaxSizeCosigners() + { + // cosigners must respect count + + int maxCosigners = 16; + + // -------------------------------------- + // this should pass (respecting max size) + + var cosigners1 = new Cosigner[maxCosigners]; + for (int i = 0; i < cosigners1.Length; i++) + { + string hex = i.ToString("X4"); + while (hex.Length < 40) + hex = hex.Insert(0, "0"); + cosigners1[i] = new Cosigner + { + Account = UInt160.Parse(hex) + }; + } + + Transaction txCosigners1 = new Transaction + { + Version = 0x00, + Nonce = 0x01020304, + Sender = UInt160.Zero, + SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS + NetworkFee = 0x0000000000000001, + ValidUntilBlock = 0x01020304, + Attributes = new TransactionAttribute[0] { }, + Cosigners = cosigners1, // max + 1 (should fail) + Script = new byte[] { (byte)OpCode.PUSH1 }, + Witnesses = new Witness[0] { } + }; + + byte[] sTx1 = txCosigners1.ToArray(); + + // back to transaction (should fail, due to non-distinct cosigners) + Transaction tx1 = Neo.IO.Helper.AsSerializable(sTx1); + Assert.IsNotNull(tx1); + + // ---------------------------- + // this should fail (max + 1) + + var cosigners = new Cosigner[maxCosigners + 1]; + for (var i = 0; i < maxCosigners + 1; i++) + { + string hex = i.ToString("X4"); + while (hex.Length < 40) + hex = hex.Insert(0, "0"); + cosigners[i] = new Cosigner + { + Account = UInt160.Parse(hex) + }; + } + + Transaction txCosigners = new Transaction + { + Version = 0x00, + Nonce = 0x01020304, + Sender = UInt160.Zero, + SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS + NetworkFee = 0x0000000000000001, + ValidUntilBlock = 0x01020304, + Attributes = new TransactionAttribute[0] { }, + Cosigners = cosigners, // max + 1 (should fail) + Script = new byte[] { (byte)OpCode.PUSH1 }, + Witnesses = new Witness[0] { } + }; + + byte[] sTx2 = txCosigners.ToArray(); + + // back to transaction (should fail, due to non-distinct cosigners) + Transaction tx2 = null; + Assert.ThrowsException(() => + tx2 = Neo.IO.Helper.AsSerializable(sTx2) + ); + Assert.IsNull(tx2); + } + + [TestMethod] + public void FeeIsSignatureContract_TestScope_Global_Default() + { + // Global is supposed to be default + + Cosigner cosigner = new Cosigner(); + cosigner.Scopes.Should().Be(WitnessScope.Global); + + var wallet = GenerateTestWallet(); + var snapshot = store.GetSnapshot(); + + // no password on this wallet + using (var unlock = wallet.Unlock("")) + { + var acc = wallet.CreateAccount(); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + // Make transaction + // Manually creating script + + byte[] script; + using (ScriptBuilder sb = new ScriptBuilder()) + { + // 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.Emit(OpCode.THROWIFNOT); + script = sb.ToArray(); + } + + // default to global scope + var cosigners = new Cosigner[]{ new Cosigner + { + Account = acc.ScriptHash + } }; + + // using this... + + var tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners); + + Assert.IsNotNull(tx); + Assert.IsNull(tx.Witnesses); + + // ---- + // Sign + // ---- + + var data = new ContractParametersContext(tx); + bool signed = wallet.Sign(data); + Assert.IsTrue(signed); + + // get witnesses from signed 'data' + tx.Witnesses = data.GetWitnesses(); + tx.Witnesses.Length.Should().Be(1); + + // Fast check + Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + + // Check + long verificationGas = 0; + foreach (var witness in tx.Witnesses) + { + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, tx, snapshot, tx.NetworkFee, false)) + { + engine.LoadScript(witness.VerificationScript); + engine.LoadScript(witness.InvocationScript); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + verificationGas += engine.GasConsumed; + } + } + // get sizeGas + var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); + // final check on sum: verification_cost + tx_size + Assert.AreEqual(verificationGas + sizeGas, 1257240); + // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } } @@ -255,7 +1059,8 @@ public void ToJson() uut.Script = TestUtils.GetByteArray(32, 0x42); uut.Sender = UInt160.Zero; uut.SystemFee = 4200000000; - uut.Attributes = new TransactionAttribute[0]; + uut.Attributes = new TransactionAttribute[] { }; + uut.Cosigners = new Cosigner[] { }; uut.Witnesses = new[] { new Witness @@ -267,10 +1072,11 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0xee00d595ccd48a650f62adaccbb9c979e2dc7ef66fb5b1413f0f74d563a2d9c6"); - jObj["size"].AsNumber().Should().Be(82); + jObj["hash"].AsString().Should().Be("0x11e3ee692015f0cd3cb8b6db7a4fc37568540f020cb9ca497a9917c81f20b62f"); + jObj["size"].AsNumber().Should().Be(83); jObj["version"].AsNumber().Should().Be(0); ((JArray)jObj["attributes"]).Count.Should().Be(0); + ((JArray)jObj["cosigners"]).Count.Should().Be(0); jObj["net_fee"].AsString().Should().Be("0"); jObj["script"].AsString().Should().Be("4220202020202020202020202020202020202020202020202020202020202020"); jObj["sys_fee"].AsNumber().Should().Be(42); diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index efa3c2446c..f7b216544c 100644 --- a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -128,7 +128,7 @@ public void Check_RegisterValidator() ret.Result.Should().BeTrue(); snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 1); // New validator - + // Check GetRegisteredValidators var validators = NativeContract.NEO.GetRegisteredValidators(snapshot).OrderBy(u => u.PublicKey).ToArray(); diff --git a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 0f353e37da..b77030d554 100644 --- a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -61,7 +61,7 @@ public void Check_SetMaxTransactionsPerBlock() // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), + var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(), "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -96,7 +96,7 @@ public void Check_SetFeePerByte() // Without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), + var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(), "setFeePerByte", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -131,7 +131,7 @@ public void Check_Block_UnblockAccount() // Block without signature - var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), + var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(), "blockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); @@ -154,7 +154,7 @@ public void Check_Block_UnblockAccount() // Unblock without signature - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(), "unblockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); ret.GetBoolean().Should().BeFalse(); diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index fd06f0ba1b..44cb55047d 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -28,6 +28,7 @@ public static Transaction GetTransaction() Script = new byte[1], Sender = UInt160.Zero, Attributes = new TransactionAttribute[0], + Cosigners = new Cosigner[0], Witnesses = new Witness[]{ new Witness { InvocationScript = new byte[0], @@ -86,6 +87,7 @@ public static Transaction CreateRandomHashTransaction() Script = randomBytes, Sender = UInt160.Zero, Attributes = new TransactionAttribute[0], + Cosigners = new Cosigner[0], Witnesses = new[] { new Witness diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 663d142d62..f5c1d6bc93 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -153,6 +153,7 @@ private static Transaction DeployNativeContracts() Sender = (new[] { (byte)OpCode.PUSHT }).ToScriptHash(), SystemFee = 0, Attributes = new TransactionAttribute[0], + Cosigners = new Cosigner[0], Witnesses = new[] { new Witness diff --git a/neo/Network/P2P/Payloads/Cosigner.cs b/neo/Network/P2P/Payloads/Cosigner.cs new file mode 100644 index 0000000000..2d1f42039c --- /dev/null +++ b/neo/Network/P2P/Payloads/Cosigner.cs @@ -0,0 +1,76 @@ +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.IO.Json; +using System; +using System.IO; +using System.Linq; + +namespace Neo.Network.P2P.Payloads +{ + public class Cosigner : ISerializable + { + public UInt160 Account; + public WitnessScope Scopes; + public UInt160[] AllowedContracts; + public ECPoint[] AllowedGroups; + + public Cosigner() + { + this.Scopes = WitnessScope.Global; + } + + // This limits maximum number of AllowedContracts or AllowedGroups here + private int MaxSubitems = 16; + + public int Size => + /*Account*/ UInt160.Length + + /*Scopes*/ sizeof(WitnessScope) + + /*AllowedContracts*/ (Scopes.HasFlag(WitnessScope.CustomContracts) ? AllowedContracts.GetVarSize() : 0) + + /*AllowedGroups*/ (Scopes.HasFlag(WitnessScope.CustomGroups) ? AllowedGroups.GetVarSize() : 0); + + void ISerializable.Deserialize(BinaryReader reader) + { + Account = reader.ReadSerializable(); + Scopes = (WitnessScope)reader.ReadByte(); + AllowedContracts = Scopes.HasFlag(WitnessScope.CustomContracts) + ? reader.ReadSerializableArray(MaxSubitems) + : new UInt160[0]; + AllowedGroups = Scopes.HasFlag(WitnessScope.CustomGroups) + ? reader.ReadSerializableArray(MaxSubitems) + : new ECPoint[0]; + } + + void ISerializable.Serialize(BinaryWriter writer) + { + writer.Write(Account); + writer.Write((byte)Scopes); + if (Scopes.HasFlag(WitnessScope.CustomContracts)) + writer.Write(AllowedContracts); + if (Scopes.HasFlag(WitnessScope.CustomGroups)) + writer.Write(AllowedGroups); + } + + public JObject ToJson() + { + JObject json = new JObject(); + json["account"] = Account.ToString(); + json["scopes"] = Scopes; + if (Scopes.HasFlag(WitnessScope.CustomContracts)) + json["allowedContracts"] = AllowedContracts.Select(p => (JObject)p.ToString()).ToArray(); + if (Scopes.HasFlag(WitnessScope.CustomGroups)) + json["allowedGroups"] = AllowedGroups.Select(p => (JObject)p.ToString()).ToArray(); + return json; + } + + public static Cosigner FromJson(JObject json) + { + return new Cosigner + { + Account = UInt160.Parse(json["account"].AsString()), + Scopes = (WitnessScope)Enum.Parse(typeof(WitnessScope), json["scopes"].AsString()), + AllowedContracts = ((JArray)json["allowedContracts"])?.Select(p => UInt160.Parse(p.AsString())).ToArray(), + AllowedGroups = ((JArray)json["allowedGroups"])?.Select(p => ECPoint.Parse(p.AsString(), ECCurve.Secp256r1)).ToArray() + }; + } + } +} diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 40efe0e9d3..b83cbd977d 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -21,6 +21,10 @@ public class Transaction : IEquatable, IInventory /// Maximum number of attributes that can be contained within a transaction ///
private const int MaxTransactionAttributes = 16; + /// + /// Maximum number of cosigners that can be contained within a transaction + /// + private const int MaxCosigners = 16; public byte Version; public uint Nonce; @@ -35,6 +39,7 @@ public class Transaction : IEquatable, IInventory public long NetworkFee; public uint ValidUntilBlock; public TransactionAttribute[] Attributes; + public Cosigner[] Cosigners { get; set; } public byte[] Script; public Witness[] Witnesses { get; set; } @@ -69,6 +74,7 @@ public UInt256 Hash public int Size => HeaderSize + Attributes.GetVarSize() + //Attributes + Cosigners.GetVarSize() + //Cosigners Script.GetVarSize() + //Script Witnesses.GetVarSize(); //Witnesses @@ -92,8 +98,8 @@ public void DeserializeUnsigned(BinaryReader reader) if (SystemFee + NetworkFee < SystemFee) throw new FormatException(); ValidUntilBlock = reader.ReadUInt32(); Attributes = reader.ReadSerializableArray(MaxTransactionAttributes); - var cosigners = Attributes.Where(p => p.Usage == TransactionAttributeUsage.Cosigner).Select(p => new UInt160(p.Data)).ToArray(); - if (cosigners.Distinct().Count() != cosigners.Length) throw new FormatException(); + Cosigners = reader.ReadSerializableArray(MaxCosigners); + if (Cosigners.Select(u => u.Account).Distinct().Count() != Cosigners.Length) throw new FormatException(); Script = reader.ReadVarBytes(ushort.MaxValue); if (Script.Length == 0) throw new FormatException(); } @@ -118,7 +124,7 @@ public override int GetHashCode() public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) { var hashes = new HashSet { Sender }; - hashes.UnionWith(Attributes.Where(p => p.Usage == TransactionAttributeUsage.Cosigner).Select(p => new UInt160(p.Data))); + hashes.UnionWith(Cosigners.Select(p => p.Account)); return hashes.OrderBy(p => p).ToArray(); } @@ -157,6 +163,7 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.Write(NetworkFee); writer.Write(ValidUntilBlock); writer.Write(Attributes); + writer.Write(Cosigners); writer.WriteVarBytes(Script); } @@ -172,6 +179,7 @@ public JObject ToJson() json["net_fee"] = new BigDecimal(NetworkFee, NativeContract.GAS.Decimals).ToString(); json["valid_until_block"] = ValidUntilBlock; json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); + json["cosigners"] = Cosigners.Select(p => p.ToJson()).ToArray(); json["script"] = Script.ToHexString(); json["witnesses"] = Witnesses.Select(p => p.ToJson()).ToArray(); return json; @@ -187,6 +195,7 @@ public static Transaction FromJson(JObject json) tx.NetworkFee = long.Parse(json["net_fee"].AsString()); tx.ValidUntilBlock = uint.Parse(json["valid_until_block"].AsString()); tx.Attributes = ((JArray)json["attributes"]).Select(p => TransactionAttribute.FromJson(p)).ToArray(); + tx.Cosigners = ((JArray)json["cosigners"]).Select(p => Cosigner.FromJson(p)).ToArray(); tx.Script = json["script"].AsString().HexToBytes(); tx.Witnesses = ((JArray)json["witnesses"]).Select(p => Witness.FromJson(p)).ToArray(); return tx; diff --git a/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs b/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs index d878348c28..89c1a1cec2 100644 --- a/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs +++ b/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs @@ -2,7 +2,6 @@ { public enum TransactionAttributeUsage : byte { - Cosigner = 0x20, Url = 0x81 } } diff --git a/neo/Network/P2P/Payloads/WitnessScope.cs b/neo/Network/P2P/Payloads/WitnessScope.cs new file mode 100644 index 0000000000..e67706cbbf --- /dev/null +++ b/neo/Network/P2P/Payloads/WitnessScope.cs @@ -0,0 +1,31 @@ +using System; + +namespace Neo.Network.P2P.Payloads +{ + [Flags] + public enum WitnessScope : byte + { + /// + /// Global allows this witness in all contexts (default Neo2 behavior) + /// This cannot be combined with other flags + /// + Global = 0x00, + + /// + /// CalledByEntry means that this condition must hold: EntryScriptHash == CallingScriptHash + /// No params is needed, as the witness/permission/signature given on first invocation will automatically expire if entering deeper internal invokes + /// This can be default safe choice for native NEO/GAS (previously used on Neo 2 as "attach" mode) + /// + CalledByEntry = 0x01, + + /// + /// Custom hash for contract-specific + /// + CustomContracts = 0x10, + + /// + /// Custom pubkey for group members + /// + CustomGroups = 0x20 + } +} diff --git a/neo/SmartContract/ContractParametersContext.cs b/neo/SmartContract/ContractParametersContext.cs index 8fda2c8450..35a2c36898 100644 --- a/neo/SmartContract/ContractParametersContext.cs +++ b/neo/SmartContract/ContractParametersContext.cs @@ -72,7 +72,15 @@ public bool Completed } } + /// + /// Cache for public ScriptHashes field + /// private UInt160[] _ScriptHashes = null; + + /// + /// ScriptHashes are the verifiable ScriptHashes from Verifiable element + /// Equivalent to: Verifiable.GetScriptHashesForVerifying(Blockchain.Singleton.GetSnapshot()) + /// public IReadOnlyList ScriptHashes { get diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index c4daa8a89b..9fbb5c6675 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -143,8 +143,35 @@ private static bool Runtime_GetTrigger(ApplicationEngine engine) internal static bool CheckWitness(ApplicationEngine engine, UInt160 hash) { - var _hashes_for_verifying = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); - return _hashes_for_verifying.Contains(hash); + if (engine.ScriptContainer is Transaction tx) + { + Cosigner usage = tx.Cosigners.FirstOrDefault(p => p.Account.Equals(hash)); + if (usage is null) return false; + if (usage.Scopes == WitnessScope.Global) return true; + if (usage.Scopes.HasFlag(WitnessScope.CalledByEntry)) + { + if (engine.CallingScriptHash == engine.EntryScriptHash) + return true; + } + if (usage.Scopes.HasFlag(WitnessScope.CustomContracts)) + { + if (usage.AllowedContracts.Contains(engine.CurrentScriptHash)) + return true; + } + if (usage.Scopes.HasFlag(WitnessScope.CustomGroups)) + { + var contract = engine.Snapshot.Contracts[engine.CallingScriptHash]; + // check if current group is the required one + if (contract.Manifest.Groups.Select(p => p.PubKey).Intersect(usage.AllowedGroups).Any()) + return true; + } + return false; + } + + // only for non-Transaction types (Block, etc) + + var hashes_for_verifying = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); + return hashes_for_verifying.Contains(hash); } private static bool CheckWitness(ApplicationEngine engine, ECPoint pubkey) diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index b68733bb71..7912ca97b6 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -223,7 +223,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null } using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { - HashSet cosigners = new HashSet(); + HashSet cosignerList = new HashSet(); byte[] script; List<(UInt160 Account, BigInteger Value)> balances_gas = null; using (ScriptBuilder sb = new ScriptBuilder()) @@ -250,7 +250,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null { balances = balances.OrderBy(p => p.Value).ToList(); var balances_used = FindPayingAccounts(balances, output.Value.Value); - cosigners.UnionWith(balances_used.Select(p => p.Account)); + cosignerList.UnionWith(balances_used.Select(p => p.Account)); foreach (var (account, value) in balances_used) { sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); @@ -264,12 +264,20 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null } 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(); - TransactionAttribute[] attributes = cosigners.Select(p => new TransactionAttribute { Usage = TransactionAttributeUsage.Cosigner, Data = p.ToArray() }).ToArray(); - return MakeTransaction(snapshot, attributes, script, balances_gas); + + var cosigners = cosignerList.Select(p => + new Cosigner() + { + // default access for transfers should be valid only for first invocation + Scopes = WitnessScope.CalledByEntry, + Account = new UInt160(p.ToArray()) + }).ToArray(); + + return MakeTransaction(snapshot, script, new TransactionAttribute[0], cosigners, balances_gas); } } - public Transaction MakeTransaction(TransactionAttribute[] attributes, byte[] script, UInt160 sender = null) + public Transaction MakeTransaction(byte[] script, UInt160 sender = null, TransactionAttribute[] attributes = null, Cosigner[] cosigners = null) { UInt160[] accounts; if (sender is null) @@ -285,11 +293,11 @@ public Transaction MakeTransaction(TransactionAttribute[] attributes, byte[] scr using (Snapshot 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, attributes, script, balances_gas); + return MakeTransaction(snapshot, script, attributes ?? new TransactionAttribute[0], cosigners ?? new Cosigner[0], balances_gas); } } - private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] attributes, byte[] script, List<(UInt160 Account, BigInteger Value)> balances_gas) + private Transaction MakeTransaction(Snapshot snapshot, byte[] script, TransactionAttribute[] attributes, Cosigner[] cosigners, List<(UInt160 Account, BigInteger Value)> balances_gas) { Random rand = new Random(); foreach (var (account, value) in balances_gas) @@ -301,8 +309,10 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at Script = script, Sender = account, ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement, - Attributes = attributes + Attributes = attributes, + Cosigners = cosigners }; + // will try to execute 'transfer' script to check if it works using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.Clone(), tx, testMode: true)) { if (engine.State.HasFlag(VMState.FAULT)) @@ -318,8 +328,12 @@ private Transaction MakeTransaction(Snapshot snapshot, TransactionAttribute[] at tx.SystemFee -= remainder; } } + UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); - int size = Transaction.HeaderSize + attributes.GetVarSize() + script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); + + // base size for transaction: includes const_header + attributes + cosigners with scopes + script + hashes + int size = Transaction.HeaderSize + attributes.GetVarSize() + cosigners.GetVarSize() + script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); + foreach (UInt160 hash in hashes) { byte[] witness_script = GetAccount(hash)?.Contract?.Script ?? snapshot.Contracts.TryGet(hash)?.Script; From e22f2eec8637f8a89cffaed3bba774c221ce0dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Mon, 12 Aug 2019 12:00:34 -0300 Subject: [PATCH 074/305] Improving the use of `RelayCache` on `ProtocolHandler` (#1014) * Refactoring and simplifing the use of relaycache * Standarzing name * simplify --- neo/Ledger/Blockchain.cs | 4 ++-- neo/Network/P2P/ProtocolHandler.cs | 19 ++++++++----------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index f5c1d6bc93..889d4b18bb 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -60,7 +60,7 @@ public class FillCompleted { } private uint stored_header_count = 0; private readonly Dictionary block_cache = new Dictionary(); private readonly Dictionary> block_cache_unverified = new Dictionary>(); - internal readonly RelayCache RelayCache = new RelayCache(100); + internal readonly RelayCache ConsensusRelayCache = new RelayCache(100); private Snapshot currentSnapshot; public Store Store { get; } @@ -328,7 +328,7 @@ private RelayResultReason OnNewConsensus(ConsensusPayload payload) { if (!payload.Verify(currentSnapshot)) return RelayResultReason.Invalid; system.Consensus?.Tell(payload); - RelayCache.Add(payload); + ConsensusRelayCache.Add(payload); system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = payload }); return RelayResultReason.Succeed; } diff --git a/neo/Network/P2P/ProtocolHandler.cs b/neo/Network/P2P/ProtocolHandler.cs index 234c42d9ef..63ecbe3318 100644 --- a/neo/Network/P2P/ProtocolHandler.cs +++ b/neo/Network/P2P/ProtocolHandler.cs @@ -180,23 +180,20 @@ private void OnGetDataMessageReceived(InvPayload payload) UInt256[] hashes = payload.Hashes.Where(p => sentHashes.Add(p)).ToArray(); foreach (UInt256 hash in hashes) { - Blockchain.Singleton.RelayCache.TryGet(hash, out IInventory inventory); switch (payload.Type) { case InventoryType.TX: - if (inventory == null) - inventory = Blockchain.Singleton.GetTransaction(hash); - if (inventory is Transaction) - Context.Parent.Tell(Message.Create(MessageCommand.Transaction, inventory)); + Transaction tx = Blockchain.Singleton.GetTransaction(hash); + if (tx != null) + Context.Parent.Tell(Message.Create(MessageCommand.Transaction, tx)); break; case InventoryType.Block: - if (inventory == null) - inventory = Blockchain.Singleton.GetBlock(hash); - if (inventory is Block block) + Block block = Blockchain.Singleton.GetBlock(hash); + if (block != null) { if (bloom_filter == null) { - Context.Parent.Tell(Message.Create(MessageCommand.Block, inventory)); + Context.Parent.Tell(Message.Create(MessageCommand.Block, block)); } else { @@ -206,8 +203,8 @@ private void OnGetDataMessageReceived(InvPayload payload) } break; case InventoryType.Consensus: - if (inventory != null) - Context.Parent.Tell(Message.Create(MessageCommand.Consensus, inventory)); + if (Blockchain.Singleton.ConsensusRelayCache.TryGet(hash, out IInventory inventoryConsensus)) + Context.Parent.Tell(Message.Create(MessageCommand.Consensus, inventoryConsensus)); break; } } From aa15e3723f02b383d18d452566d0386f3acb2918 Mon Sep 17 00:00:00 2001 From: Yongjie Ma <20391402+yongjiema@users.noreply.github.com> Date: Fri, 16 Aug 2019 19:25:06 +0800 Subject: [PATCH 075/305] Fix the potential risk to get null CurrentHeaderHash from Blockchain (#1033) --- neo/Ledger/Blockchain.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 889d4b18bb..7d0b72402d 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -66,9 +66,9 @@ public class FillCompleted { } public Store Store { get; } public MemoryPool MemPool { get; } public uint Height => currentSnapshot.Height; - public uint HeaderHeight => (uint)header_index.Count - 1; + public uint HeaderHeight => currentSnapshot.HeaderHeight; public UInt256 CurrentBlockHash => currentSnapshot.CurrentBlockHash; - public UInt256 CurrentHeaderHash => header_index[header_index.Count - 1]; + public UInt256 CurrentHeaderHash => currentSnapshot.CurrentHeaderHash; private static Blockchain singleton; public static Blockchain Singleton From 4b3c1f2b0f415a938241fa035d3263a4af624e6e Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Fri, 16 Aug 2019 23:08:26 +0800 Subject: [PATCH 076/305] Unit Test for Wallets Module (#1018) * testDemo * add dll lib * add dbsnapshot dispose * test get blocks in levelDBStore * add levelDBStore test funcs * fix levelDBStore funcs * add DbCache addInternal * differ db path * space * fix delete internal test * add test getInternal tryGetInternal move libleveldb.dll * add dbCache method test * add store test * add cache unit tests * add cache unit tests * up readonly max_capacity * fix leveldbexception * fix comment on UT_Cache * format * fix multithread test problem * up cache * update travis config * update travis.yml * test DbMetaDataCache * fix db directory * format and update travis for maxos * fix mac env travis * 2019/7/12 10:34 * 2019/7/12 11:01 * remove commented line * test BigDecimal * fix format and csproj * rm coverage.opencover.xml * update method name * add UT_P_Helper * modify UT_P_Helper * modify UT_P_helper * Clean ut * test Base58 & BloomFilter * Update UT_Cache.cs * Correct Typo * test JsonArray * update namespace * update namespace * update format * update format * organise folder structure * add UT_JString * test JBoolean JNumber & JObject * 2019/7/16 10:30 add some test case for UInt32Wrapper and SerializableWrapper * fix timestamp * test ECDsa and Crypto * test OrderedDictionary & complete IO.Json tests * 2019/7/16 17:33 add some test case of SQLiteWallet * test FIFOSet * add CloneCache and DataCache unit tests * fix namespace * add UT_Cryptography_Helper * format UT_CloneCache and UT_DataCache * add UT_DataCache.GetAndChange unit test * update namespace * remove comment code * delete Persistence part * 2019/7/19 11:07 add some test case for Helper in VM * Fix Base58 Test * 2019/7/19 11:33 change some format * update IOHelper exception assert * 2019/7/19 14:22 change format * format IOHelper * review IO.Wrapper * review Wallets.SQLite UT * Test ECFieldElement ECPoint * refactor package * format ECDsa * update namespace * Code fix * review cache * modify UT_JString * fomat * using Actin replace with try-catch * add UT_CloneMetaCache and UT_MetaDataCache * update namespace * format UT_DataCache.cs * Code Fix * format * update csproj * Code fix for UT_ECFieldElement and UT_ECPoint * Code fix * format * update travis * delete deleteFiles * fix path and comment * update travis * delete test ToTimeStamp * format UT_*Cache * update format * fomat * use hex extensions in Cryptography_Helper * remove reflection * optimization of UT_DataCache * update namespace * modify TestSha256 * update UT in crypto module * Rename UT_Scrypt.cs to UT_SCrypt.cs * format * update UT_Murmur3 * update IO module test * delete empty line * changename * delete empty line * revert commit * add wallet test * update testUtil * delete empty line * Update UT_NEP6Wallet.cs * update ut * Optimize wallet test * fix * add change * fix nep6wallet * Fix * Optimize * fix optimize * fix * Optimize --- .../Network/P2P/Payloads/UT_Transaction.cs | 32 +- neo.UnitTests/TestUtils.cs | 38 +- neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs | 139 ++++++ neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs | 67 +++ neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs | 379 ++++++++++++++- .../Wallets/NEP6/UT_ScryptParameters.cs | 10 + neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs | 210 +++++++++ .../Wallets/SQLite/UT_UserWalletAccount.cs | 31 ++ .../Wallets/SQLite/UT_VerificationContract.cs | 148 ++++++ neo.UnitTests/Wallets/UT_AssetDescriptor.cs | 13 + neo.UnitTests/Wallets/UT_KeyPair.cs | 114 +++++ neo.UnitTests/Wallets/UT_Wallet.cs | 436 ++++++++++++++++++ neo.UnitTests/Wallets/UT_WalletAccount.cs | 48 ++ neo.UnitTests/Wallets/UT_Wallets_Helper.cs | 32 ++ neo/Wallets/NEP6/NEP6Wallet.cs | 4 +- neo/Wallets/SQLite/UserWallet.cs | 4 +- neo/Wallets/Wallet.cs | 4 +- 17 files changed, 1669 insertions(+), 40 deletions(-) create mode 100644 neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs create mode 100644 neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs create mode 100644 neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs create mode 100644 neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs create mode 100644 neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs create mode 100644 neo.UnitTests/Wallets/UT_KeyPair.cs create mode 100644 neo.UnitTests/Wallets/UT_Wallet.cs create mode 100644 neo.UnitTests/Wallets/UT_WalletAccount.cs create mode 100644 neo.UnitTests/Wallets/UT_Wallets_Helper.cs diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 2c81e91537..c357541d7e 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -84,24 +84,12 @@ public void Size_Get() uut.Size.Should().Be(83); } - private NEP6Wallet GenerateTestWallet() - { - JObject wallet = new JObject(); - wallet["name"] = "noname"; - wallet["version"] = new System.Version().ToString(); - wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); - wallet["accounts"] = new JArray(); - wallet["extra"] = null; - wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"0.0\",\"scrypt\":{\"n\":0,\"r\":0,\"p\":0},\"accounts\":[],\"extra\":null}"); - return new NEP6Wallet(wallet); - } - [TestMethod] public void FeeIsMultiSigContract() { var store = TestBlockchain.GetStore(); - var walletA = GenerateTestWallet(); - var walletB = GenerateTestWallet(); + var walletA = TestUtils.GenerateTestWallet(); + var walletB = TestUtils.GenerateTestWallet(); var snapshot = store.GetSnapshot(); using (var unlockA = walletA.Unlock("123")) @@ -188,7 +176,7 @@ public void FeeIsMultiSigContract() [TestMethod] public void FeeIsSignatureContractDetailed() { - var wallet = GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(); var snapshot = store.GetSnapshot(); using (var unlock = wallet.Unlock("123")) @@ -303,7 +291,7 @@ public void FeeIsSignatureContractDetailed() [TestMethod] public void FeeIsSignatureContract_TestScope_Global() { - var wallet = GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(); var snapshot = store.GetSnapshot(); // no password on this wallet @@ -394,7 +382,7 @@ public void FeeIsSignatureContract_TestScope_Global() [TestMethod] public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() { - var wallet = GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(); var snapshot = store.GetSnapshot(); // no password on this wallet @@ -486,7 +474,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() [TestMethod] public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() { - var wallet = GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(); var snapshot = store.GetSnapshot(); // no password on this wallet @@ -581,7 +569,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() [TestMethod] public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() { - var wallet = GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(); var snapshot = store.GetSnapshot(); // no password on this wallet @@ -639,7 +627,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() [TestMethod] public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() { - var wallet = GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(); var snapshot = store.GetSnapshot(); // no password on this wallet @@ -736,7 +724,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() [TestMethod] public void FeeIsSignatureContract_TestScope_NoScopeFAULT() { - var wallet = GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(); var snapshot = store.GetSnapshot(); // no password on this wallet @@ -966,7 +954,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() Cosigner cosigner = new Cosigner(); cosigner.Scopes.Should().Be(WitnessScope.Global); - var wallet = GenerateTestWallet(); + var wallet = TestUtils.GenerateTestWallet(); var snapshot = store.GetSnapshot(); // no password on this wallet diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index 44cb55047d..086098a8ec 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -1,6 +1,11 @@ -using Neo.IO; +using FluentAssertions; +using Neo.IO; +using Neo.IO.Json; +using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.SmartContract.Manifest; using Neo.VM; +using Neo.Wallets.NEP6; using System; using System.IO; @@ -21,6 +26,18 @@ public static byte[] GetByteArray(int length, byte firstByte) return array; } + public static NEP6Wallet GenerateTestWallet() + { + JObject wallet = new JObject(); + wallet["name"] = "noname"; + wallet["version"] = new System.Version().ToString(); + wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); + wallet["accounts"] = new JArray(); + wallet["extra"] = null; + wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"0.0\",\"scrypt\":{\"n\":0,\"r\":0,\"p\":0},\"accounts\":[],\"extra\":null}"); + return new NEP6Wallet(wallet); + } + public static Transaction GetTransaction() { return new Transaction @@ -37,6 +54,15 @@ public static Transaction GetTransaction() }; } + internal static ContractState GetContract() + { + return new ContractState + { + Script = new byte[] { 0x01, 0x01, 0x01, 0x01 }, + Manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")) + }; + } + public static void SetupHeaderWithValues(Header header, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal) { setupBlockBaseWithValues(header, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal); @@ -109,5 +135,13 @@ public static Transaction CreateRandomHashTransaction() return newObj; } + + public static void DeleteFile(string file) + { + if (File.Exists(file)) + { + File.Delete(file); + } + } } -} +} \ No newline at end of file diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs new file mode 100644 index 0000000000..c531c8c138 --- /dev/null +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -0,0 +1,139 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.IO.Json; +using Neo.SmartContract; +using Neo.Wallets; +using Neo.Wallets.NEP6; + +namespace Neo.UnitTests.Wallets.NEP6 +{ + [TestClass] + public class UT_NEP6Account + { + NEP6Account account; + UInt160 hash; + NEP6Wallet wallet; + private static string nep2; + private static KeyPair keyPair; + + [ClassInitialize] + public static void ClassSetup(TestContext context) { + byte[] privateKey = { 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}; + keyPair = new KeyPair(privateKey); + nep2 = keyPair.Export("Satoshi", 0, 0, 0); + } + + [TestInitialize] + public void TestSetup() + { + wallet = TestUtils.GenerateTestWallet(); + byte[] array1 = { 0x01 }; + hash = new UInt160(Crypto.Default.Hash160(array1)); + account = new NEP6Account(wallet, hash); + } + + [TestMethod] + public void TestConstructorWithNep2Key() + { + account.ScriptHash.Should().Be(hash); + account.Decrypted.Should().BeTrue(); + account.HasKey.Should().BeFalse(); + } + + [TestMethod] + public void TestConstructorWithKeyPair() + { + NEP6Wallet wallet = new NEP6Wallet("a"); + byte[] array1 = { 0x01 }; + var hash = new UInt160(Crypto.Default.Hash160(array1)); + string password = "hello world"; + NEP6Account account = new NEP6Account(wallet, hash, keyPair, password); + account.ScriptHash.Should().Be(hash); + account.Decrypted.Should().BeTrue(); + account.HasKey.Should().BeTrue(); + } + + [TestMethod] + public void TestFromJson() + { + JObject json = new JObject(); + json["address"] = "ARxgjcH2K1yeW5f5ryuRQNaBzSa9TZzmVS"; + json["key"] = null; + json["label"] = null; + json["isDefault"] = true; + json["lock"] = false; + json["contract"] = null; + json["extra"] = null; + NEP6Account account = NEP6Account.FromJson(json, wallet); + account.ScriptHash.Should().Be("ARxgjcH2K1yeW5f5ryuRQNaBzSa9TZzmVS".ToScriptHash()); + account.Label.Should().BeNull(); + account.IsDefault.Should().BeTrue(); + account.Lock.Should().BeFalse(); + account.Contract.Should().BeNull(); + account.Extra.Should().BeNull(); + account.GetKey().Should().BeNull(); + + json["key"] = "6PYRjVE1gAbCRyv81FTiFz62cxuPGw91vMjN4yPa68bnoqJtioreTznezn"; + json["label"] = "label"; + account = NEP6Account.FromJson(json, wallet); + account.Label.Should().Be("label"); + account.HasKey.Should().BeTrue(); + } + + [TestMethod] + public void TestGetKey() + { + account.GetKey().Should().BeNull(); + wallet.Unlock("Satoshi"); + account = new NEP6Account(wallet, hash, nep2); + account.GetKey().Should().Be(keyPair); + } + + [TestMethod] + public void TestGetKeyWithString() + { + account.GetKey("Satoshi").Should().BeNull(); + account = new NEP6Account(wallet, hash, nep2); + account.GetKey("Satoshi").Should().Be(keyPair); + } + + [TestMethod] + public void TestToJson() + { + JObject nep6contract = new JObject(); + nep6contract["script"] = "2103603f3880eb7aea0ad4500893925e4a42fea48a44ee6f898a10b3c7ce05d2a267ac"; + JObject parameters = new JObject(); + parameters["type"] = 0x00; + parameters["name"] = "Sig"; + JArray array = new JArray + { + parameters + }; + nep6contract["parameters"] = array; + nep6contract["deployed"] = false; + account.Contract = NEP6Contract.FromJson(nep6contract); + JObject json = account.ToJson(); + json["address"].Should().Equals("AZk5bAanTtD6AvpeesmYgL8CLRYUt5JQsX"); + json["label"].Should().BeNull(); + json["isDefault"].ToString().Should().Be("false"); + json["lock"].ToString().Should().Be("false"); + json["key"].Should().BeNull(); + json["contract"]["script"].ToString().Should().Be("\"2103603f3880eb7aea0ad4500893925e4a42fea48a44ee6f898a10b3c7ce05d2a267ac\""); + json["extra"].Should().BeNull(); + + account.Contract = null; + json = account.ToJson(); + json["contract"].Should().BeNull(); + } + + [TestMethod] + public void TestVerifyPassword() + { + account = new NEP6Account(wallet, hash, nep2); + account.VerifyPassword("Satoshi").Should().BeTrue(); + account.VerifyPassword("b").Should().BeFalse(); + } + } +} diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs new file mode 100644 index 0000000000..6dc7f714d5 --- /dev/null +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs @@ -0,0 +1,67 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.SmartContract; +using Neo.Wallets.NEP6; + +namespace Neo.UnitTests.Wallets.NEP6 +{ + [TestClass] + public class UT_NEP6Contract + { + [TestMethod] + public void TestFromNullJson() + { + NEP6Contract nep6Contract = NEP6Contract.FromJson(null); + nep6Contract.Should().BeNull(); + } + + [TestMethod] + public void TestFromJson() + { + string json = "{\"script\":\"2103ef891df4c0b7eefb937d21ea0fb88cde8e0d82a7ff11872b5e7047969dafb4eb68747476aa\"," + + "\"parameters\":[{\"name\":\"signature\",\"type\":\"Signature\"}],\"deployed\":false}"; + JObject @object = JObject.Parse(json); + + NEP6Contract nep6Contract = NEP6Contract.FromJson(@object); + nep6Contract.Script.Should().BeEquivalentTo("2103ef891df4c0b7eefb937d21ea0fb88cde8e0d82a7ff11872b5e7047969dafb4eb68747476aa".HexToBytes()); + nep6Contract.ParameterList.Length.Should().Be(1); + nep6Contract.ParameterList[0].Should().Be(ContractParameterType.Signature); + nep6Contract.ParameterNames.Length.Should().Be(1); + nep6Contract.ParameterNames[0].Should().Be("signature"); + nep6Contract.Deployed.Should().BeFalse(); + } + + [TestMethod] + public void TestToJson() + { + NEP6Contract nep6Contract = new NEP6Contract() + { + Script = new byte[] { 0x00, 0x01 }, + ParameterList = new ContractParameterType[] { ContractParameterType.Boolean, ContractParameterType.Integer }, + ParameterNames = new string[] { "param1", "param2" }, + Deployed = false + }; + + JObject @object = nep6Contract.ToJson(); + JString jString = (JString)@object["script"]; + jString.Value.Should().Be(nep6Contract.Script.ToHexString()); + + JBoolean jBoolean = (JBoolean)@object["deployed"]; + jBoolean.Value.Should().BeFalse(); + + JArray parameters = (JArray)@object["parameters"]; + parameters.Count.Should().Be(2); + + jString = (JString)(parameters[0]["name"]); + jString.Value.Should().Be("param1"); + jString = (JString)(parameters[0]["type"]); + jString.Value.Should().Be(ContractParameterType.Boolean.ToString()); + + jString = (JString)(parameters[1]["name"]); + jString.Value.Should().Be("param2"); + jString = (JString)(parameters[1]["type"]); + jString.Value.Should().Be(ContractParameterType.Integer.ToString()); + } + } +} diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index 9bac3de4ac..89499e7c57 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -1,41 +1,400 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; +using Neo.Wallets; using Neo.Wallets.NEP6; +using Neo.Wallets.SQLite; using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Threading; namespace Neo.UnitTests.Wallets.NEP6 { [TestClass] public class UT_NEP6Wallet { - NEP6Wallet uut; + private NEP6Wallet uut; + private static string wPath; + private static KeyPair keyPair; + private static string nep2key; + private static UInt160 testScriptHash; + + public static string GetRandomPath() + { + string threadName = Thread.CurrentThread.ManagedThreadId.ToString(); + return Path.GetFullPath(string.Format("Wallet_{0}", new Random().Next(1, 1000000).ToString("X8")) + threadName); + } + + [ClassInitialize] + public static void ClassInit(TestContext context) + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + keyPair = new KeyPair(privateKey); + testScriptHash = Neo.SmartContract.Contract.CreateSignatureContract(keyPair.PublicKey).ScriptHash; + nep2key = keyPair.Export("123", 0, 0, 0); + } + + private NEP6Wallet CreateWallet() + { + return TestUtils.GenerateTestWallet(); + } + + private string CreateWalletFile() + { + string path = GetRandomPath(); + if (!Directory.Exists(path)) Directory.CreateDirectory(path); + path = Path.Combine(path, "wallet.json"); + File.WriteAllText(path, "{\"name\":\"name\",\"version\":\"0.0\",\"scrypt\":{\"n\":0,\"r\":0,\"p\":0},\"accounts\":[],\"extra\":{}}"); + return path; + } [TestInitialize] public void TestSetup() + { + uut = CreateWallet(); + wPath = CreateWalletFile(); + } + + [TestCleanup] + public void TestCleanUp() + { + if (File.Exists(wPath)) File.Delete(wPath); + } + + [TestMethod] + public void TestConstructorWithPathAndName() + { + NEP6Wallet wallet = new NEP6Wallet(wPath); + Assert.AreEqual("name", wallet.Name); + Assert.AreEqual(new ScryptParameters(0, 0, 0).ToJson().ToString(), wallet.Scrypt.ToJson().ToString()); + Assert.AreEqual(new Version().ToString(), wallet.Version.ToString()); + wallet = new NEP6Wallet("", "test"); + Assert.AreEqual("test", wallet.Name); + Assert.AreEqual(ScryptParameters.Default.ToJson().ToString(), wallet.Scrypt.ToJson().ToString()); + Assert.AreEqual(Version.Parse("1.0"), wallet.Version); + } + + [TestMethod] + public void TestConstructorWithJObject() + { + JObject wallet = new JObject(); + wallet["name"] = "test"; + wallet["version"] = Version.Parse("1.0").ToString(); + wallet["scrypt"] = ScryptParameters.Default.ToJson(); + wallet["accounts"] = new JArray(); + wallet["extra"] = new JObject(); + wallet.ToString().Should().Be("{\"name\":\"test\",\"version\":\"1.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":{}}"); + NEP6Wallet w = new NEP6Wallet(wallet); + Assert.AreEqual("test", w.Name); + Assert.AreEqual(Version.Parse("1.0").ToString(), w.Version.ToString()); + } + + [TestMethod] + public void TestGetName() + { + Assert.AreEqual("noname", uut.Name); + } + + [TestMethod] + public void TestGetVersion() + { + Assert.AreEqual(new System.Version().ToString(), uut.Version.ToString()); + } + + [TestMethod] + public void TestContains() + { + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + uut.CreateAccount(testScriptHash); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + } + + [TestMethod] + public void TestAddCount() + { + uut.CreateAccount(testScriptHash); + Assert.IsTrue(uut.Contains(testScriptHash)); + WalletAccount account = uut.GetAccount(testScriptHash); + Assert.IsTrue(account.WatchOnly); + Assert.IsFalse(account.HasKey); + uut.Unlock("123"); + uut.CreateAccount(keyPair.PrivateKey); + account = uut.GetAccount(testScriptHash); + Assert.IsFalse(account.WatchOnly); + Assert.IsTrue(account.HasKey); + uut.CreateAccount(testScriptHash); + account = uut.GetAccount(testScriptHash); + Assert.IsFalse(account.WatchOnly); + Assert.IsFalse(account.HasKey); + uut.CreateAccount(keyPair.PrivateKey); + account = uut.GetAccount(testScriptHash); + Assert.IsFalse(account.WatchOnly); + Assert.IsTrue(account.HasKey); + } + + [TestMethod] + public void TestCreateAccountWithPrivateKey() + { + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + uut.Unlock("123"); + uut.CreateAccount(keyPair.PrivateKey); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + } + + [TestMethod] + public void TestCreateAccountWithKeyPair() + { + Neo.SmartContract.Contract contract = Neo.SmartContract.Contract.CreateSignatureContract(keyPair.PublicKey); + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + uut.CreateAccount(contract); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + uut.DeleteAccount(testScriptHash); + result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + uut.Unlock("123"); + uut.CreateAccount(contract, keyPair); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + } + + [TestMethod] + public void TestCreateAccountWithScriptHash() + { + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + uut.CreateAccount(testScriptHash); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + } + + [TestMethod] + public void TestDecryptKey() + { + string nep2key = keyPair.Export("123", 0, 0, 0); + uut.Unlock("123"); + KeyPair key1 = uut.DecryptKey(nep2key); + bool result = key1.Equals(keyPair); + Assert.AreEqual(true, result); + } + + [TestMethod] + public void TestDeleteAccount() + { + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + uut.CreateAccount(testScriptHash); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + uut.DeleteAccount(testScriptHash); + result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + } + + [TestMethod] + public void TestGetAccount() + { + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + uut.Unlock("123"); + uut.CreateAccount(keyPair.PrivateKey); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + WalletAccount account = uut.GetAccount(testScriptHash); + Assert.AreEqual(Neo.SmartContract.Contract.CreateSignatureContract(keyPair.PublicKey).Address, account.Address); + } + + [TestMethod] + public void TestGetAccounts() + { + Dictionary keys = new Dictionary(); + uut.Unlock("123"); + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + KeyPair key = new KeyPair(privateKey); + Neo.SmartContract.Contract contract = Neo.SmartContract.Contract.CreateSignatureContract(key.PublicKey); + keys.Add(contract.Address, key); + keys.Add(Neo.SmartContract.Contract.CreateSignatureContract(keyPair.PublicKey).Address, keyPair); + uut.CreateAccount(key.PrivateKey); + uut.CreateAccount(keyPair.PrivateKey); + foreach (var account in uut.GetAccounts()) + { + if (!keys.TryGetValue(account.Address, out KeyPair k)) + { + Assert.Fail(); + } + } + } + + public X509Certificate2 NewCertificate() + { + ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP256); + CertificateRequest request = new CertificateRequest( + "CN=Self-Signed ECDSA", + key, + HashAlgorithmName.SHA256); + request.CertificateExtensions.Add( + new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, critical: false)); + request.CertificateExtensions.Add( + new X509BasicConstraintsExtension(false, false, 0, false)); + DateTimeOffset start = DateTimeOffset.UtcNow; + X509Certificate2 cert = request.CreateSelfSigned(notBefore: start, notAfter: start.AddMonths(3)); + return cert; + } + + [TestMethod] + public void TestImportCert() + { + X509Certificate2 cert = NewCertificate(); + Assert.IsNotNull(cert); + Assert.AreEqual(true, cert.HasPrivateKey); + uut.Unlock("123"); + WalletAccount account = uut.Import(cert); + Assert.IsNotNull(account); + } + + [TestMethod] + public void TestImportWif() + { + string wif = keyPair.Export(); + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + uut.Unlock("123"); + uut.Import(wif); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + } + + [TestMethod] + public void TestImportNep2() + { + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + uut.Import(nep2key, "123", 0, 0, 0); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + uut.DeleteAccount(testScriptHash); + result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + JObject wallet = new JObject(); + wallet["name"] = "name"; + wallet["version"] = new Version().ToString(); + wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); + wallet["accounts"] = new JArray(); + wallet["extra"] = new JObject(); + uut = new NEP6Wallet(wallet); + result = uut.Contains(testScriptHash); + Assert.AreEqual(false, result); + uut.Import(nep2key, "123", 0, 0, 0); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + } + + [TestMethod] + public void TestLock() + { + Assert.ThrowsException(() => uut.CreateAccount(keyPair.PrivateKey)); + uut.Unlock("123"); + uut.CreateAccount(keyPair.PrivateKey); + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + uut.DeleteAccount(testScriptHash); + uut.Lock(); + Assert.ThrowsException(() => uut.CreateAccount(keyPair.PrivateKey)); + } + + [TestMethod] + public void TestMigrate() + { + string path = GetRandomPath(); + UserWallet uw = UserWallet.Create(path, "123"); + uw.CreateAccount(keyPair.PrivateKey); + string npath = Path.Combine(path, "w.json"); + NEP6Wallet nw = NEP6Wallet.Migrate(npath, path, "123"); + bool result = nw.Contains(testScriptHash); + Assert.AreEqual(true, result); + } + + [TestMethod] + public void TestSave() { JObject wallet = new JObject(); wallet["name"] = "name"; wallet["version"] = new System.Version().ToString(); - wallet["scrypt"] = ScryptParameters.Default.ToJson(); - // test minimally scryptparameters parsing here - ScryptParameters.FromJson(wallet["scrypt"]).Should().NotBeNull(); - ScryptParameters.FromJson(wallet["scrypt"]).N.Should().Be(ScryptParameters.Default.N); + wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); + wallet["accounts"] = new JArray(); + wallet["extra"] = new JObject(); + File.WriteAllText(wPath, wallet.ToString()); + uut = new NEP6Wallet(wPath); + uut.Unlock("123"); + uut.CreateAccount(keyPair.PrivateKey); + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + uut.Save(); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + } + + [TestMethod] + public void TestUnlock() + { + Assert.ThrowsException(() => uut.CreateAccount(keyPair.PrivateKey)); + uut.Unlock("123"); + uut.CreateAccount(keyPair.PrivateKey); + bool result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + Assert.ThrowsException(() => uut.Unlock("1")); + } + + [TestMethod] + public void TestVerifyPassword() + { + bool result = uut.VerifyPassword("123"); + Assert.AreEqual(true, result); + Assert.ThrowsException(() => uut.CreateAccount(keyPair.PrivateKey)); + uut.Unlock("123"); + uut.CreateAccount(keyPair.PrivateKey); + result = uut.Contains(testScriptHash); + Assert.AreEqual(true, result); + result = uut.VerifyPassword("123"); + Assert.AreEqual(true, result); + uut.DeleteAccount(testScriptHash); + Assert.AreEqual(false, uut.Contains(testScriptHash)); + JObject wallet = new JObject(); + wallet["name"] = "name"; + wallet["version"] = new Version().ToString(); + wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); wallet["accounts"] = new JArray(); - //accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson(p, this)).ToDictionary(p => p.ScriptHash); wallet["extra"] = new JObject(); - // check string json - wallet.ToString().Should().Be("{\"name\":\"name\",\"version\":\"0.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":{}}"); uut = new NEP6Wallet(wallet); + nep2key = keyPair.Export("123", 0, 0, 0); + uut.Import(nep2key, "123", 0, 0, 0); + Assert.IsFalse(uut.VerifyPassword("1")); + Assert.IsTrue(uut.VerifyPassword("123")); } [TestMethod] public void Test_NEP6Wallet_Json() { - uut.Name.Should().Be("name"); + uut.Name.Should().Be("noname"); uut.Version.Should().Be(new Version()); uut.Scrypt.Should().NotBeNull(); - uut.Scrypt.N.Should().Be(ScryptParameters.Default.N); + uut.Scrypt.N.Should().Be(new ScryptParameters(0, 0, 0).N); } } } diff --git a/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs b/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs index 3d4550a48d..adac8a13a2 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs @@ -46,5 +46,15 @@ public void Test_Default_ScryptParameters_FromJson() uut2.R.Should().Be(ScryptParameters.Default.R); uut2.P.Should().Be(ScryptParameters.Default.P); } + + [TestMethod] + public void TestScryptParametersConstructor() + { + int n = 1, r = 2, p = 3; + ScryptParameters parameter = new ScryptParameters(n, r, p); + parameter.N.Should().Be(n); + parameter.R.Should().Be(r); + parameter.P.Should().Be(p); + } } } diff --git a/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs b/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs new file mode 100644 index 0000000000..70f3a649cf --- /dev/null +++ b/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs @@ -0,0 +1,210 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.Wallets; +using Neo.Wallets.SQLite; +using System; +using System.IO; +using System.Security; +using System.Security.Cryptography; +using System.Threading; +using Contract = Neo.SmartContract.Contract; + +namespace Neo.UnitTests.Wallets.SQLite +{ + [TestClass] + public class UT_UserWallet + { + private string path; + private UserWallet wallet; + public static string GetRandomPath() + { + string threadName = Thread.CurrentThread.ManagedThreadId.ToString(); + return Path.GetFullPath(string.Format("Wallet_{0}", new Random().Next(1, 1000000).ToString("X8")) + threadName); + } + + [TestInitialize] + public void Setup() + { + path = GetRandomPath(); + wallet = UserWallet.Create(path, "123456"); + } + + [TestCleanup] + public void Cleanup() + { + TestUtils.DeleteFile(path); + } + + [TestMethod] + public void TestGetName() + { + wallet.Name.Should().Be(Path.GetFileNameWithoutExtension(path)); + } + + [TestMethod] + public void TestGetVersion() + { + Action action = () => wallet.Version.ToString(); + action.ShouldNotThrow(); + } + + [TestMethod] + public void TestCreateAndOpenSecureString() + { + string myPath = GetRandomPath(); + var ss = new SecureString(); + ss.AppendChar('a'); + ss.AppendChar('b'); + ss.AppendChar('c'); + + var w1 = UserWallet.Create(myPath, ss); + w1.Should().NotBeNull(); + + var w2 = UserWallet.Open(myPath, ss); + w2.Should().NotBeNull(); + + ss.AppendChar('d'); + Action action = () => UserWallet.Open(myPath, ss); + action.ShouldThrow(); + + TestUtils.DeleteFile(myPath); + } + + [TestMethod] + public void TestOpen() + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + var account = wallet.CreateAccount(privateKey); + var w1 = UserWallet.Open(path, "123456"); + w1.Should().NotBeNull(); + + Action action = () => UserWallet.Open(path, "123"); + action.ShouldThrow(); + } + + [TestMethod] + public void TestCreateAccountAndGetByPrivateKey() + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + var account = wallet.CreateAccount(privateKey); + var dbAccount = wallet.GetAccount(account.ScriptHash); + account.Should().Be(dbAccount); + + var account1 = wallet.CreateAccount(privateKey); + var dbAccount1 = wallet.GetAccount(account1.ScriptHash); + account1.Should().Be(dbAccount1); + } + + [TestMethod] + public void TestCreateAccountByScriptHash() + { + var account = wallet.CreateAccount(UInt160.Parse("0xa6ee944042f3c7ea900481a95d65e4a887320cf0")); + var dbAccount = wallet.GetAccount(account.ScriptHash); + account.Should().Be(dbAccount); + } + + [TestMethod] + public void TestCreateAccountBySmartContract() + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + KeyPair key = new KeyPair(privateKey); + VerificationContract contract = new VerificationContract + { + Script = Contract.CreateSignatureRedeemScript(key.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } + }; + var account = wallet.CreateAccount(contract, key); + var dbAccount = wallet.GetAccount(account.ScriptHash); + account.Should().Be(dbAccount); + + byte[] privateKey2 = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey2); + } + KeyPair key2 = new KeyPair(privateKey2); + Contract contract2 = new Contract + { + Script = Contract.CreateSignatureRedeemScript(key2.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } + }; + var account2 = wallet.CreateAccount(contract2, key2); + var dbAccount2 = wallet.GetAccount(account2.ScriptHash); + account2.Should().Be(dbAccount2); + } + + [TestMethod] + public void TestDeleteAccount() + { + bool ret = wallet.DeleteAccount(UInt160.Parse("0xa6ee944042f3c7ea900481a95d65e4a887320cf0")); + ret.Should().BeFalse(); + + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + var account = wallet.CreateAccount(privateKey); + bool ret2 = wallet.DeleteAccount(account.ScriptHash); + ret2.Should().BeTrue(); + } + + [TestMethod] + public void TestChangePassword() + { + wallet.ChangePassword("123455", "654321").Should().BeFalse(); + wallet.ChangePassword("123456", "654321").Should().BeTrue(); + } + + [TestMethod] + public void TestContains() + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + var account = wallet.CreateAccount(privateKey); + wallet.Contains(account.ScriptHash).Should().BeTrue(); + } + + [TestMethod] + public void TestGetAccounts() + { + var ret = wallet.GetAccounts(); + ret.Should().BeNullOrEmpty(); + + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + var account = wallet.CreateAccount(privateKey); + ret = wallet.GetAccounts(); + foreach (var dbAccount in ret) + { + dbAccount.Should().Be(account); + } + } + + [TestMethod] + public void TestVerifyPassword() + { + wallet.VerifyPassword("123456").Should().BeTrue(); + wallet.VerifyPassword("123").Should().BeFalse(); + } + } +} diff --git a/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs b/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs new file mode 100644 index 0000000000..2a0dfd3f1d --- /dev/null +++ b/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs @@ -0,0 +1,31 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Wallets.SQLite; +using System.Text; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_UserWalletAccount + { + [TestMethod] + public void TestGenerator() + { + UserWalletAccount account = new UserWalletAccount(UInt160.Zero); + Assert.IsNotNull(account); + } + + [TestMethod] + public void TestGetHasKey() + { + UserWalletAccount account = new UserWalletAccount(UInt160.Zero); + Assert.AreEqual(false, account.HasKey); + } + + [TestMethod] + public void TestGetKey() + { + UserWalletAccount account = new UserWalletAccount(UInt160.Zero); + Assert.AreEqual(null, account.GetKey()); + } + } +} diff --git a/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs b/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs new file mode 100644 index 0000000000..d590640fe5 --- /dev/null +++ b/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs @@ -0,0 +1,148 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.Wallets; +using Neo.Wallets.SQLite; +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_VerificationContract + { + [TestMethod] + public void TestGenerator() + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + KeyPair key = new KeyPair(privateKey); + VerificationContract contract = new VerificationContract + { + Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } + }; + Assert.IsNotNull(contract); + } + + [TestMethod] + public void TestDeserialize() + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + KeyPair key = new KeyPair(privateKey); + VerificationContract contract1 = new VerificationContract + { + Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } + }; + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + BinaryReader reader = new BinaryReader(stream); + contract1.Serialize(writer); + stream.Seek(0, SeekOrigin.Begin); + VerificationContract contract2 = new VerificationContract(); + contract2.Deserialize(reader); + Assert.AreEqual(Encoding.Default.GetString(contract2.Script), Encoding.Default.GetString(contract1.Script)); + Assert.AreEqual(1, contract2.ParameterList.Length); + Assert.AreEqual(ContractParameterType.Signature, contract2.ParameterList[0]); + } + + [TestMethod] + public void TestEquals() + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + KeyPair key = new KeyPair(privateKey); + VerificationContract contract1 = new VerificationContract + { + Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } + }; + object tempObject = contract1; + VerificationContract contract2 = new VerificationContract + { + Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } + }; + Assert.AreEqual(true, contract1.Equals(tempObject)); + Assert.AreEqual(true, contract1.Equals(contract1)); + Assert.AreEqual(false, contract1.Equals(null)); + Assert.AreEqual(true, contract1.Equals(contract2)); + } + + [TestMethod] + public void TestGetHashCode() + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + KeyPair key = new KeyPair(privateKey); + VerificationContract contract1 = new VerificationContract + { + Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } + }; + byte[] script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey); + Assert.AreEqual(script.ToScriptHash().GetHashCode(), contract1.GetHashCode()); + } + + [TestMethod] + public void TestSerialize() + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + KeyPair key = new KeyPair(privateKey); + VerificationContract contract1 = new VerificationContract + { + Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } + }; + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + contract1.Serialize(writer); + stream.Seek(0, SeekOrigin.Begin); + byte[] byteArray = new byte[stream.Length]; + stream.Read(byteArray, 0, (int)stream.Length); + byte[] script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey); + byte[] result = new byte[62]; + result[20] = 0x01; + result[21] = 0x00; + result[22] = 0x27; + Array.Copy(script, 0, result, 23, 39); + Assert.AreEqual(Encoding.Default.GetString(result), Encoding.Default.GetString(byteArray)); + } + + [TestMethod] + public void TestGetSize() + { + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + KeyPair key = new KeyPair(privateKey); + VerificationContract contract1 = new VerificationContract + { + Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } + }; + Assert.AreEqual(62, contract1.Size); + } + } +} diff --git a/neo.UnitTests/Wallets/UT_AssetDescriptor.cs b/neo.UnitTests/Wallets/UT_AssetDescriptor.cs index 6630a8450b..ca80f27b28 100644 --- a/neo.UnitTests/Wallets/UT_AssetDescriptor.cs +++ b/neo.UnitTests/Wallets/UT_AssetDescriptor.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Persistence; using Neo.SmartContract.Native; +using System; namespace Neo.UnitTests.Wallets { @@ -17,12 +18,23 @@ public void TestSetup() Store = TestBlockchain.GetStore(); } + [TestMethod] + public void TestConstructorWithNonexistAssetId() + { + Action action = () => + { + var descriptor = new Neo.Wallets.AssetDescriptor(UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4")); + }; + action.ShouldThrow(); + } + [TestMethod] public void Check_GAS() { var descriptor = new Neo.Wallets.AssetDescriptor(NativeContract.GAS.Hash); descriptor.AssetId.Should().Be(NativeContract.GAS.Hash); descriptor.AssetName.Should().Be("GAS"); + descriptor.ToString().Should().Be("GAS"); descriptor.Decimals.Should().Be(8); } @@ -32,6 +44,7 @@ public void Check_NEO() var descriptor = new Neo.Wallets.AssetDescriptor(NativeContract.NEO.Hash); descriptor.AssetId.Should().Be(NativeContract.NEO.Hash); descriptor.AssetName.Should().Be("NEO"); + descriptor.ToString().Should().Be("NEO"); descriptor.Decimals.Should().Be(0); } } diff --git a/neo.UnitTests/Wallets/UT_KeyPair.cs b/neo.UnitTests/Wallets/UT_KeyPair.cs new file mode 100644 index 0000000000..dc67599111 --- /dev/null +++ b/neo.UnitTests/Wallets/UT_KeyPair.cs @@ -0,0 +1,114 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.Wallets; +using System; +using System.Linq; + +namespace Neo.UnitTests.Wallets +{ + [TestClass] + public class UT_KeyPair + { + [TestMethod] + public void TestConstructor() + { + Random random = new Random(); + byte[] privateKey = new byte[32]; + for (int i = 0; i < privateKey.Length; i++) + privateKey[i] = (byte)random.Next(256); + KeyPair keyPair = new KeyPair(privateKey); + ECPoint publicKey = ECCurve.Secp256r1.G * privateKey; + keyPair.PrivateKey.Should().BeEquivalentTo(privateKey); + keyPair.PublicKey.Should().Be(publicKey); + + byte[] privateKey96 = new byte[96]; + for (int i = 0; i < privateKey96.Length; i++) + privateKey96[i] = (byte)random.Next(256); + keyPair = new KeyPair(privateKey96); + publicKey = ECPoint.DecodePoint(new byte[] { 0x04 }.Concat(privateKey96.Skip(privateKey96.Length - 96).Take(64)).ToArray(), ECCurve.Secp256r1); + keyPair.PrivateKey.Should().BeEquivalentTo(privateKey96.Skip(64).Take(32)); + keyPair.PublicKey.Should().Be(publicKey); + + byte[] privateKey31 = new byte[31]; + for (int i = 0; i < privateKey31.Length; i++) + privateKey31[i] = (byte)random.Next(256); + Action action = () => new KeyPair(privateKey31); + action.ShouldThrow(); + } + + [TestMethod] + public void TestEquals() + { + Random random = new Random(); + byte[] privateKey = new byte[32]; + for (int i = 0; i < privateKey.Length; i++) + privateKey[i] = (byte)random.Next(256); + KeyPair keyPair = new KeyPair(privateKey); + KeyPair keyPair2 = keyPair; + keyPair.Equals(keyPair2).Should().BeTrue(); + + KeyPair keyPair3 = null; + keyPair.Equals(keyPair3).Should().BeFalse(); + + byte[] privateKey1 = { 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}; + byte[] privateKey2 = { 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, 0x02}; + KeyPair keyPair4 = new KeyPair(privateKey1); + KeyPair keyPair5 = new KeyPair(privateKey2); + keyPair4.Equals(keyPair5).Should().BeFalse(); + } + + [TestMethod] + public void TestEqualsWithObj() + { + Random random = new Random(); + byte[] privateKey = new byte[32]; + for (int i = 0; i < privateKey.Length; i++) + privateKey[i] = (byte)random.Next(256); + KeyPair keyPair = new KeyPair(privateKey); + Object keyPair2 = keyPair as Object; + keyPair.Equals(keyPair2).Should().BeTrue(); + } + + [TestMethod] + public void TestExport() + { + byte[] privateKey = { 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}; + byte[] data = { 0x80, 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, 0x01}; + KeyPair keyPair = new KeyPair(privateKey); + keyPair.Export().Should().Be(data.Base58CheckEncode()); + } + + [TestMethod] + public void TestGetPublicKeyHash() + { + byte[] privateKey = { 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}; + KeyPair keyPair = new KeyPair(privateKey); + keyPair.PublicKeyHash.ToString().Should().Be("0x4ab3d6ac3a0609e87af84599c93d57c2d0890406"); + } + + [TestMethod] + public void TestGetHashCode() + { + byte[] privateKey = { 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}; + KeyPair keyPair = new KeyPair(privateKey); + keyPair.GetHashCode().Should().Be(1544360595); + } + + [TestMethod] + public void TestToString() + { + byte[] privateKey = { 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}; + KeyPair keyPair = new KeyPair(privateKey); + keyPair.ToString().Should().Be("026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca16"); + } + } +} diff --git a/neo.UnitTests/Wallets/UT_Wallet.cs b/neo.UnitTests/Wallets/UT_Wallet.cs new file mode 100644 index 0000000000..59289ae548 --- /dev/null +++ b/neo.UnitTests/Wallets/UT_Wallet.cs @@ -0,0 +1,436 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.SmartContract.Native.Tokens; +using Neo.UnitTests.Cryptography; +using Neo.Wallets; +using System; +using System.Collections.Generic; + +namespace Neo.UnitTests.Wallets +{ + internal class MyWallet : Wallet + { + public override string Name => "MyWallet"; + + public override Version Version => Version.Parse("0.0.1"); + + Dictionary accounts = new Dictionary(); + + public override bool Contains(UInt160 scriptHash) + { + return accounts.ContainsKey(scriptHash); + } + + public void AddAccount(WalletAccount account) + { + accounts.Add(account.ScriptHash, account); + } + + public override WalletAccount CreateAccount(byte[] privateKey) + { + KeyPair key = new KeyPair(privateKey); + Neo.Wallets.SQLite.VerificationContract contract = new Neo.Wallets.SQLite.VerificationContract + { + Script = Contract.CreateSignatureRedeemScript(key.PublicKey), + ParameterList = new[] { ContractParameterType.Signature } + }; + MyWalletAccount account = new MyWalletAccount(contract.ScriptHash); + account.SetKey(key); + account.Contract = contract; + AddAccount(account); + return account; + } + + public override WalletAccount CreateAccount(Contract contract, KeyPair key = null) + { + MyWalletAccount account = new MyWalletAccount(contract.ScriptHash) + { + Contract = contract + }; + account.SetKey(key); + AddAccount(account); + return account; + } + + public override WalletAccount CreateAccount(UInt160 scriptHash) + { + MyWalletAccount account = new MyWalletAccount(scriptHash); + AddAccount(account); + return account; + } + + public override bool DeleteAccount(UInt160 scriptHash) + { + return accounts.Remove(scriptHash); + } + + public override WalletAccount GetAccount(UInt160 scriptHash) + { + accounts.TryGetValue(scriptHash, out WalletAccount account); + return account; + } + + public override IEnumerable GetAccounts() + { + return accounts.Values; + } + + public override bool VerifyPassword(string password) + { + return true; + } + } + + [TestClass] + public class UT_Wallet + { + Store store; + private static KeyPair glkey; + private static string nep2Key; + + [ClassInitialize] + public static void ClassInit(TestContext context) + { + glkey = UT_Crypto.generateCertainKey(32); + nep2Key = glkey.Export("pwd", 0, 0, 0); + } + + [TestInitialize] + public void TestSetup() + { + store = TestBlockchain.GetStore(); + } + + [TestMethod] + public void TestContains() + { + MyWallet wallet = new MyWallet(); + Action action = () => wallet.Contains(UInt160.Zero); + action.ShouldNotThrow(); + } + + [TestMethod] + public void TestCreateAccount1() + { + MyWallet wallet = new MyWallet(); + wallet.CreateAccount(new byte[32]).Should().NotBeNull(); + } + + [TestMethod] + public void TestCreateAccount2() + { + MyWallet wallet = new MyWallet(); + Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 }); + WalletAccount account = wallet.CreateAccount(contract, UT_Crypto.generateCertainKey(32).PrivateKey); + account.Should().NotBeNull(); + + wallet = new MyWallet(); + account = wallet.CreateAccount(contract, (byte[])(null)); + account.Should().NotBeNull(); + } + + [TestMethod] + public void TestCreateAccount3() + { + MyWallet wallet = new MyWallet(); + Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 }); + wallet.CreateAccount(contract, glkey).Should().NotBeNull(); + } + + [TestMethod] + public void TestCreateAccount4() + { + MyWallet wallet = new MyWallet(); + wallet.CreateAccount(UInt160.Zero).Should().NotBeNull(); + } + + [TestMethod] + public void TestGetName() + { + MyWallet wallet = new MyWallet(); + wallet.Name.Should().Be("MyWallet"); + } + + [TestMethod] + public void TestGetVersion() + { + MyWallet wallet = new MyWallet(); + wallet.Version.Should().Be(Version.Parse("0.0.1")); + } + + [TestMethod] + public void TestGetAccount1() + { + MyWallet wallet = new MyWallet(); + wallet.CreateAccount(UInt160.Parse("522a2b818c308c7a2c77cfdda11763fe043bfb40")); + WalletAccount account = wallet.GetAccount(ECCurve.Secp256r1.G); + account.ScriptHash.Should().Be(UInt160.Parse("0x522a2b818c308c7a2c77cfdda11763fe043bfb40")); + } + + [TestMethod] + public void TestGetAccount2() + { + MyWallet wallet = new MyWallet(); + Action action = () => wallet.GetAccount(UInt160.Zero); + action.ShouldNotThrow(); + } + + [TestMethod] + public void TestGetAccounts() + { + MyWallet wallet = new MyWallet(); + Action action = () => wallet.GetAccounts(); + action.ShouldNotThrow(); + } + + [TestMethod] + public void TestGetAvailable() + { + MyWallet wallet = new MyWallet(); + Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 }); + WalletAccount account = wallet.CreateAccount(contract, glkey.PrivateKey); + account.Lock = false; + + // Fake balance + var snapshot = store.GetSnapshot(); + var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + wallet.GetAvailable(NativeContract.GAS.Hash).Should().Be(new BigDecimal(1000000000000, 8)); + + entry.Value = new Nep5AccountState() + { + Balance = 0 + } + .ToByteArray(); + } + + [TestMethod] + public void TestGetBalance() + { + MyWallet wallet = new MyWallet(); + Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 }); + WalletAccount account = wallet.CreateAccount(contract, glkey.PrivateKey); + account.Lock = false; + + // Fake balance + var snapshot = store.GetSnapshot(); + var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + 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.Value = new Nep5AccountState() + { + Balance = 0 + } + .ToByteArray(); + } + + [TestMethod] + public void TestGetPrivateKeyFromNEP2() + { + Action action = () => Wallet.GetPrivateKeyFromNEP2(null, null, 0, 0, 0); + action.ShouldThrow(); + + action = () => Wallet.GetPrivateKeyFromNEP2("TestGetPrivateKeyFromNEP2", null, 0, 0, 0); + action.ShouldThrow(); + + action = () => Wallet.GetPrivateKeyFromNEP2("3vQB7B6MrGQZaxCuFg4oh", "TestGetPrivateKeyFromNEP2", 0, 0, 0); + action.ShouldThrow(); + + action = () => Wallet.GetPrivateKeyFromNEP2(nep2Key, "Test", 0, 0, 0); + action.ShouldThrow(); + + Wallet.GetPrivateKeyFromNEP2(nep2Key, "pwd", 0, 0, 0).Should().BeEquivalentTo(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }); + } + + [TestMethod] + public void TestGetPrivateKeyFromWIF() + { + Action action = () => Wallet.GetPrivateKeyFromWIF(null); + action.ShouldThrow(); + + action = () => Wallet.GetPrivateKeyFromWIF("3vQB7B6MrGQZaxCuFg4oh"); + action.ShouldThrow(); + + Wallet.GetPrivateKeyFromWIF("L3tgppXLgdaeqSGSFw1Go3skBiy8vQAM7YMXvTHsKQtE16PBncSU").Should().BeEquivalentTo(new byte[] { 199, 19, 77, 111, 216, 231, 61, 129, 158, 130, 117, 92, 100, 201, 55, 136, 216, 219, 9, 97, 146, 158, 2, 90, 83, 54, 60, 76, 192, 42, 105, 98 }); + } + + [TestMethod] + public void TestImport1() + { + MyWallet wallet = new MyWallet(); + wallet.Import("L3tgppXLgdaeqSGSFw1Go3skBiy8vQAM7YMXvTHsKQtE16PBncSU").Should().NotBeNull(); + } + + [TestMethod] + public void TestImport2() + { + MyWallet wallet = new MyWallet(); + wallet.Import(nep2Key, "pwd", 0, 0, 0).Should().NotBeNull(); + } + + [TestMethod] + public void TestMakeTransaction1() + { + MyWallet wallet = new MyWallet(); + Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 }); + WalletAccount account = wallet.CreateAccount(contract, glkey.PrivateKey); + account.Lock = false; + + Action action = () => wallet.MakeTransaction(new TransferOutput[] + { + new TransferOutput() + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = account.ScriptHash, + Value = new BigDecimal(1,8) + } + }, UInt160.Zero); + action.ShouldThrow(); + + action = () => wallet.MakeTransaction(new TransferOutput[] + { + new TransferOutput() + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = account.ScriptHash, + Value = new BigDecimal(1,8) + } + }, account.ScriptHash); + action.ShouldThrow(); + + action = () => wallet.MakeTransaction(new TransferOutput[] + { + new TransferOutput() + { + AssetId = UInt160.Zero, + ScriptHash = account.ScriptHash, + Value = new BigDecimal(1,8) + } + }, account.ScriptHash); + action.ShouldThrow(); + + // Fake balance + var snapshot = store.GetSnapshot(); + var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + entry.Value = new Nep5AccountState() + { + Balance = 10000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + key = NativeContract.NEO.CreateStorageKey(20, account.ScriptHash); + entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + entry.Value = new NeoToken.AccountState() + { + Balance = 10000 * NativeContract.NEO.Factor + } + .ToByteArray(); + + var tx = wallet.MakeTransaction(new TransferOutput[] + { + new TransferOutput() + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = account.ScriptHash, + Value = new BigDecimal(1,8) + } + }); + tx.Should().NotBeNull(); + + tx = wallet.MakeTransaction(new TransferOutput[] + { + new TransferOutput() + { + AssetId = NativeContract.NEO.Hash, + ScriptHash = account.ScriptHash, + Value = new BigDecimal(1,8) + } + }); + tx.Should().NotBeNull(); + + entry.Value = new NeoToken.AccountState() + { + Balance = 0 + } + .ToByteArray(); + } + + [TestMethod] + public void TestMakeTransaction2() + { + MyWallet wallet = new MyWallet(); + Action action = () => wallet.MakeTransaction(new byte[] { }, UInt160.Zero, new TransactionAttribute[] { }); + action.ShouldThrow(); + + Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 }); + WalletAccount account = wallet.CreateAccount(contract, glkey.PrivateKey); + account.Lock = false; + + // Fake balance + var snapshot = store.GetSnapshot(); + var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + entry.Value = new Nep5AccountState() + { + Balance = 1000000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + var tx = wallet.MakeTransaction(new byte[] { }, account.ScriptHash, new TransactionAttribute[] { }); + tx.Should().NotBeNull(); + + tx = wallet.MakeTransaction(new byte[] { }, null, new TransactionAttribute[] { }); + tx.Should().NotBeNull(); + + entry.Value = new NeoToken.AccountState() + { + Balance = 0 + } + .ToByteArray(); + } + + [TestMethod] + public void TestVerifyPassword() + { + MyWallet wallet = new MyWallet(); + Action action = () => wallet.VerifyPassword("Test"); + action.ShouldNotThrow(); + } + } +} \ No newline at end of file diff --git a/neo.UnitTests/Wallets/UT_WalletAccount.cs b/neo.UnitTests/Wallets/UT_WalletAccount.cs new file mode 100644 index 0000000000..15e2bd1974 --- /dev/null +++ b/neo.UnitTests/Wallets/UT_WalletAccount.cs @@ -0,0 +1,48 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.Wallets; + +namespace Neo.UnitTests.Wallets +{ + public class MyWalletAccount : WalletAccount + { + private KeyPair key = null; + public override bool HasKey => key != null; + + public MyWalletAccount(UInt160 scriptHash) + : base(scriptHash) + { + } + + public override KeyPair GetKey() + { + return key; + } + + public void SetKey(KeyPair inputKey) + { + key = inputKey; + } + } + + [TestClass] + public class UT_WalletAccount + { + [TestMethod] + public void TestGetAddress() + { + MyWalletAccount walletAccount = new MyWalletAccount(UInt160.Zero); + walletAccount.Address.Should().Be("AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM"); + } + + [TestMethod] + public void TestGetWatchOnly() + { + MyWalletAccount walletAccount = new MyWalletAccount(UInt160.Zero); + walletAccount.WatchOnly.Should().BeTrue(); + walletAccount.Contract = new Contract(); + walletAccount.WatchOnly.Should().BeFalse(); + } + } +} diff --git a/neo.UnitTests/Wallets/UT_Wallets_Helper.cs b/neo.UnitTests/Wallets/UT_Wallets_Helper.cs new file mode 100644 index 0000000000..01f47fea16 --- /dev/null +++ b/neo.UnitTests/Wallets/UT_Wallets_Helper.cs @@ -0,0 +1,32 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.Wallets; +using System; + +namespace Neo.UnitTests.Wallets +{ + [TestClass] + public class UT_Wallets_Helper + { + [TestMethod] + public void TestToScriptHash() + { + byte[] array = { 0x01 }; + UInt160 scriptHash = new UInt160(Crypto.Default.Hash160(array)); + "AZk5bAanTtD6AvpeesmYgL8CLRYUt5JQsX".ToScriptHash().Should().Be(scriptHash); + + Action action = () => "3vQB7B6MrGQZaxCuFg4oh".ToScriptHash(); + action.ShouldThrow(); + + var address = scriptHash.ToAddress(); + byte[] data = new byte[21]; + // NEO version is 0x17 + data[0] = 0x01; + Buffer.BlockCopy(scriptHash.ToArray(), 0, data, 1, 20); + address = data.Base58CheckEncode(); + action = () => address.ToScriptHash(); + action.ShouldThrow(); + } + } +} diff --git a/neo/Wallets/NEP6/NEP6Wallet.cs b/neo/Wallets/NEP6/NEP6Wallet.cs index 9fe133d9ce..9f0d0a1315 100644 --- a/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/neo/Wallets/NEP6/NEP6Wallet.cs @@ -216,9 +216,9 @@ public override WalletAccount Import(string wif) return account; } - public override WalletAccount Import(string nep2, string passphrase) + public override WalletAccount Import(string nep2, string passphrase, int N = 16384, int r = 8, int p = 8) { - KeyPair key = new KeyPair(GetPrivateKeyFromNEP2(nep2, passphrase)); + KeyPair key = new KeyPair(GetPrivateKeyFromNEP2(nep2, passphrase, N, r, p)); NEP6Contract contract = new NEP6Contract { Script = Contract.CreateSignatureRedeemScript(key.PublicKey), diff --git a/neo/Wallets/SQLite/UserWallet.cs b/neo/Wallets/SQLite/UserWallet.cs index c66734f172..df4cccfe08 100644 --- a/neo/Wallets/SQLite/UserWallet.cs +++ b/neo/Wallets/SQLite/UserWallet.cs @@ -123,12 +123,12 @@ private void AddAccount(UserWalletAccount account, bool is_import) } //add address { - Address db_address = ctx.Addresses.FirstOrDefault(p => p.ScriptHash == account.Contract.ScriptHash.ToArray()); + Address db_address = ctx.Addresses.FirstOrDefault(p => p.ScriptHash == account.ScriptHash.ToArray()); if (db_address == null) { ctx.Addresses.Add(new Address { - ScriptHash = account.Contract.ScriptHash.ToArray() + ScriptHash = account.ScriptHash.ToArray() }); } } diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index 7912ca97b6..d22c8ff128 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -200,9 +200,9 @@ public virtual WalletAccount Import(string wif) return account; } - public virtual WalletAccount Import(string nep2, string passphrase) + public virtual WalletAccount Import(string nep2, string passphrase, int N = 16384, int r = 8, int p = 8) { - byte[] privateKey = GetPrivateKeyFromNEP2(nep2, passphrase); + byte[] privateKey = GetPrivateKeyFromNEP2(nep2, passphrase, N, r, p); WalletAccount account = CreateAccount(privateKey); Array.Clear(privateKey, 0, privateKey.Length); return account; From 37ae8ddbf6116895e7d69c5a39e9ae7cc9f524c5 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 19 Aug 2019 10:52:20 +0200 Subject: [PATCH 077/305] Revert Console.WriteLine (#986) --- neo/Consensus/ConsensusService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 8723b2c70b..0bc77f0fff 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -175,7 +175,6 @@ private void InitializeConsensus(byte viewNumber) private void Log(string message, LogLevel level = LogLevel.Info) { - Console.WriteLine($"[{DateTime.Now.TimeOfDay:hh\\:mm\\:ss\\.fff}] {message}"); Plugin.Log(nameof(ConsensusService), level, message); } From b37109a445a01de367035cc93f074f90eee86ecf Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 20 Aug 2019 09:08:18 +0200 Subject: [PATCH 078/305] Set max block size (#953) * Draft * Take into account p2p max payload * Move max allowed to policy * Check block size on prepResponse * Change reason * Prevent overflow * Optimization * Reduce the length of the array * Organizing consensus code * Revert "Organizing consensus code" This reverts commit 94a29dc9dc18f9a3836db773d652e72c5e688222. * Remove Policy UT * Resolve Policy conflicts * prepare unit test * Small unit test * More ut * Add one check * Clean using * Organizing consensus and comments * Split unit test * UT check block size * Clean * Expected witness size * optimize * Remove fakeWitness * Format comments * rename var * Add (..) * Remove SetKey method * Centralize expected block size * Optimize * Fix * Add one test * Optimize `EnsureMaxBlockSize()` * Fix unit tests * Rename * Indent * Vitor suggestion * Merge with Scoped signatures * Remove extra line * Revert "Remove extra line" This reverts commit e1348813a488e3be736f177e20df79e1efa01c6c. * Remove extra line --- .../Consensus/UT_ConsensusContext.cs | 163 ++++++++++++++++++ .../SmartContract/Native/UT_PolicyContract.cs | 52 +++++- neo/Consensus/ChangeViewReason.cs | 1 + neo/Consensus/ConsensusContext.cs | 98 ++++++++++- neo/Consensus/ConsensusService.cs | 8 + neo/Network/P2P/Payloads/Block.cs | 1 - neo/Network/P2P/Payloads/BlockBase.cs | 6 +- neo/SmartContract/Native/PolicyContract.cs | 27 +++ 8 files changed, 344 insertions(+), 12 deletions(-) create mode 100644 neo.UnitTests/Consensus/UT_ConsensusContext.cs diff --git a/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/neo.UnitTests/Consensus/UT_ConsensusContext.cs new file mode 100644 index 0000000000..696d6aaa0a --- /dev/null +++ b/neo.UnitTests/Consensus/UT_ConsensusContext.cs @@ -0,0 +1,163 @@ +using Akka.TestKit.Xunit2; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.Consensus; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +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, TestBlockchain.GetStore()) + { + 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.EnsureMaxBlockSize(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.EnsureMaxBlockSize(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.EnsureMaxBlockSize(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.EnsureMaxBlockSize(txs); + EnsureContext(_context, txs.Take(max).ToArray()); + } + + private Transaction CreateTransactionWithSize(int v) + { + var r = new Random(); + var tx = new Transaction() + { + Cosigners = new Cosigner[0], + Attributes = new TransactionAttribute[0], + NetworkFee = 0, + Nonce = (uint)Environment.TickCount, + Script = new byte[0], + Sender = UInt160.Zero, + 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 Block SignBlock(ConsensusContext context) + { + context.Block.MerkleRoot = null; + + for (int x = 0; x < _validatorKeys.Length; x++) + { + _context.MyIndex = x; + + var com = _context.MakeCommit(); + _context.CommitPayloads[_context.MyIndex] = com; + } + + // Manual block sign + + Contract contract = Contract.CreateMultiSigContract(context.M, context.Validators); + ContractParametersContext sc = new ContractParametersContext(context.Block); + for (int i = 0, j = 0; i < context.Validators.Length && j < context.M; i++) + { + if (context.CommitPayloads[i]?.ConsensusMessage.ViewNumber != context.ViewNumber) continue; + sc.AddSignature(contract, context.Validators[i], context.CommitPayloads[i].GetDeserializedMessage().Signature); + j++; + } + context.Block.Witness = sc.GetWitnesses()[0]; + context.Block.Transactions = context.TransactionHashes.Select(p => context.Transactions[p]).ToArray(); + return context.Block; + } + + 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/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index b77030d554..b53932dbf8 100644 --- a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -32,12 +32,16 @@ public void Check_Initialize() NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); - (keyCount + 3).Should().Be(snapshot.Storages.GetChangeSet().Count()); + (keyCount + 4).Should().Be(snapshot.Storages.GetChangeSet().Count()); var ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); ret.Should().BeOfType(); ret.GetBigInteger().Should().Be(512); + ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); + ret.Should().BeOfType(); + ret.GetBigInteger().Should().Be(1024 * 256); + ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); ret.GetBigInteger().Should().Be(1000); @@ -47,6 +51,52 @@ public void Check_Initialize() ((VM.Types.Array)ret).Count.Should().Be(0); } + [TestMethod] + public void Check_SetMaxBlockSize() + { + var snapshot = Store.GetSnapshot().Clone(); + + // Fake blockchain + + snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); + + NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); + + // Without signature + + var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), + "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); + ret.Should().BeOfType(); + ret.GetBoolean().Should().BeFalse(); + + ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); + ret.Should().BeOfType(); + ret.GetBigInteger().Should().Be(1024 * 256); + + // More than expected + + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), + "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = Neo.Network.P2P.Message.PayloadMaxSize }); + ret.Should().BeOfType(); + ret.GetBoolean().Should().BeFalse(); + + ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); + ret.Should().BeOfType(); + ret.GetBigInteger().Should().Be(1024 * 256); + + // With signature + + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), + "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); + ret.Should().BeOfType(); + ret.GetBoolean().Should().BeTrue(); + + ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); + ret.Should().BeOfType(); + ret.GetBigInteger().Should().Be(1024); + } + [TestMethod] public void Check_SetMaxTransactionsPerBlock() { diff --git a/neo/Consensus/ChangeViewReason.cs b/neo/Consensus/ChangeViewReason.cs index 64a2a6053e..eb06b7494a 100644 --- a/neo/Consensus/ChangeViewReason.cs +++ b/neo/Consensus/ChangeViewReason.cs @@ -7,5 +7,6 @@ public enum ChangeViewReason : byte TxNotFound = 0x2, TxRejectedByPolicy = 0x3, TxInvalid = 0x4, + BlockRejectedByPolicy = 0x5 } } \ No newline at end of file diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index ba7edf66c5..a06c6c965b 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -6,6 +6,7 @@ using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; +using Neo.VM; using Neo.Wallets; using System; using System.Collections.Generic; @@ -38,10 +39,11 @@ internal class ConsensusContext : IDisposable, ISerializable public Snapshot Snapshot { get; private set; } private KeyPair keyPair; + private int _witnessSize; private readonly Wallet wallet; private readonly Store store; private readonly Random random = new Random(); - + public int F => (Validators.Length - 1) / 3; public int M => Validators.Length - F; public bool IsPrimary => MyIndex == Block.ConsensusData.PrimaryIndex; @@ -203,19 +205,84 @@ private void SignPayload(ConsensusPayload payload) 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 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 EnsureMaxBlockSize(IEnumerable txs) + { + uint maxBlockSize = NativeContract.Policy.GetMaxBlockSize(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(); + Block.Transactions = new Transaction[0]; + + // We need to know the expected block size + + var blockSize = GetExpectedBlockSizeWithoutTransactions(txs.Count()); + + // Iterate transaction until reach the size + + 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; + + hashes.Add(tx.Hash); + Transactions.Add(tx.Hash, tx); + } + + TransactionHashes = hashes.ToArray(); } public ConsensusPayload MakePrepareRequest() { byte[] buffer = new byte[sizeof(ulong)]; random.NextBytes(buffer); - List transactions = Blockchain.Singleton.MemPool.GetSortedVerifiedTransactions() - .Take((int)NativeContract.Policy.GetMaxTransactionsPerBlock(Snapshot)) - .ToList(); - TransactionHashes = transactions.Select(p => p.Hash).ToArray(); - Transactions = transactions.ToDictionary(p => p.Hash); - Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1); Block.ConsensusData.Nonce = BitConverter.ToUInt64(buffer, 0); + EnsureMaxBlockSize(Blockchain.Singleton.MemPool.GetSortedVerifiedTransactions()); + Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1); + return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareRequest { Timestamp = Block.Timestamp, @@ -279,7 +346,24 @@ public void Reset(byte viewNumber) NextConsensus = Blockchain.GetConsensusAddress(NativeContract.NEO.GetValidators(Snapshot).ToArray()), ConsensusData = new ConsensusData() }; + 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 ConsensusPayload[Validators.Length]; LastChangeViewPayloads = new ConsensusPayload[Validators.Length]; diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 0bc77f0fff..ee5c91d635 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -80,6 +80,14 @@ private bool AddTransaction(Transaction tx, bool verify) // 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}{Environment.NewLine} The size 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); diff --git a/neo/Network/P2P/Payloads/Block.cs b/neo/Network/P2P/Payloads/Block.cs index 3f29bb9428..75e5d7c424 100644 --- a/neo/Network/P2P/Payloads/Block.cs +++ b/neo/Network/P2P/Payloads/Block.cs @@ -2,7 +2,6 @@ using Neo.IO; using Neo.IO.Json; using Neo.Ledger; -using Neo.Wallets; using System; using System.Collections.Generic; using System.IO; diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 8820c95538..20a0c98211 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -35,11 +35,11 @@ public UInt256 Hash public virtual int Size => sizeof(uint) + //Version - PrevHash.Size + //PrevHash - MerkleRoot.Size + //MerkleRoot + UInt256.Length + //PrevHash + UInt256.Length + //MerkleRoot sizeof(ulong) + //Timestamp sizeof(uint) + //Index - NextConsensus.Size + //NextConsensus + UInt160.Length + //NextConsensus 1 + // Witness.Size; //Witness diff --git a/neo/SmartContract/Native/PolicyContract.cs b/neo/SmartContract/Native/PolicyContract.cs index 239cd92200..c5820cfd15 100644 --- a/neo/SmartContract/Native/PolicyContract.cs +++ b/neo/SmartContract/Native/PolicyContract.cs @@ -21,6 +21,7 @@ public sealed class PolicyContract : NativeContract private const byte Prefix_MaxTransactionsPerBlock = 23; private const byte Prefix_FeePerByte = 10; private const byte Prefix_BlockedAccounts = 15; + private const byte Prefix_MaxBlockSize = 16; public PolicyContract() { @@ -45,6 +46,10 @@ private bool CheckValidators(ApplicationEngine engine) internal override bool Initialize(ApplicationEngine engine) { if (!base.Initialize(engine)) return false; + engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MaxBlockSize), new StorageItem + { + Value = BitConverter.GetBytes(1024u * 256u) + }); engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MaxTransactionsPerBlock), new StorageItem { Value = BitConverter.GetBytes(512u) @@ -71,6 +76,17 @@ public uint GetMaxTransactionsPerBlock(Snapshot snapshot) return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxTransactionsPerBlock)].Value, 0); } + [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] + private StackItem GetMaxBlockSize(ApplicationEngine engine, VMArray args) + { + return GetMaxBlockSize(engine.Snapshot); + } + + public uint GetMaxBlockSize(Snapshot snapshot) + { + return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxBlockSize)].Value, 0); + } + [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] private StackItem GetFeePerByte(ApplicationEngine engine, VMArray args) { @@ -93,6 +109,17 @@ public UInt160[] GetBlockedAccounts(Snapshot snapshot) return snapshot.Storages[CreateStorageKey(Prefix_BlockedAccounts)].Value.AsSerializableArray(); } + [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] + private StackItem SetMaxBlockSize(ApplicationEngine engine, VMArray args) + { + if (!CheckValidators(engine)) return false; + uint value = (uint)args[0].GetBigInteger(); + if (Network.P2P.Message.PayloadMaxSize <= value) return false; + StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize)); + storage.Value = BitConverter.GetBytes(value); + return true; + } + [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray args) { From afcb9fc4e42c1ab83a1b8a2aec17d1a31bfe7fe0 Mon Sep 17 00:00:00 2001 From: Ricardo Prado <38396062+lock9@users.noreply.github.com> Date: Tue, 20 Aug 2019 16:13:00 -0300 Subject: [PATCH 079/305] GitHub Contribution Guide (#1028) Adding GitHub Contribution Guide --- .github/images/compiler.png | Bin 0 -> 1605 bytes .github/images/consensus.png | Bin 0 -> 1644 bytes .github/images/cosmetic.png | Bin 0 -> 1614 bytes .github/images/discussion.png | Bin 0 -> 1673 bytes .github/images/enhancement.png | Bin 0 -> 1711 bytes .github/images/house-keeping.png | Bin 0 -> 2267 bytes .github/images/ledger.png | Bin 0 -> 1270 bytes .github/images/migration.png | Bin 0 -> 1685 bytes .github/images/network-policy.png | Bin 0 -> 2383 bytes .github/images/new-feature.png | Bin 0 -> 1771 bytes .github/images/p2p.png | Bin 0 -> 816 bytes .github/images/ready-to-implement.png | Bin 0 -> 2350 bytes .github/images/rpc.png | Bin 0 -> 799 bytes .github/images/sdk.png | Bin 0 -> 917 bytes .github/images/solution-design.png | Bin 0 -> 1420 bytes .github/images/to-review.png | Bin 0 -> 1629 bytes .github/images/vm.png | Bin 0 -> 653 bytes .github/images/wallet.png | Bin 0 -> 1252 bytes CONTRIBUTING.md | 82 ++++++++++++++++++++++++++ 19 files changed, 82 insertions(+) create mode 100644 .github/images/compiler.png create mode 100644 .github/images/consensus.png create mode 100644 .github/images/cosmetic.png create mode 100644 .github/images/discussion.png create mode 100644 .github/images/enhancement.png create mode 100644 .github/images/house-keeping.png create mode 100644 .github/images/ledger.png create mode 100644 .github/images/migration.png create mode 100644 .github/images/network-policy.png create mode 100644 .github/images/new-feature.png create mode 100644 .github/images/p2p.png create mode 100644 .github/images/ready-to-implement.png create mode 100644 .github/images/rpc.png create mode 100644 .github/images/sdk.png create mode 100644 .github/images/solution-design.png create mode 100644 .github/images/to-review.png create mode 100644 .github/images/vm.png create mode 100644 .github/images/wallet.png create mode 100644 CONTRIBUTING.md diff --git a/.github/images/compiler.png b/.github/images/compiler.png new file mode 100644 index 0000000000000000000000000000000000000000..affa7ca8ac3ad4e2c35e6975f9fdab81e7f1d58d GIT binary patch literal 1605 zcmV-L2D>$?_JREUP37yU++E`8B}eDP(PcBunyplDWO=j0C-(1z^D_6D>8j zSEb6(>luRH{lr3>ni|r-j{|uH6_l0N;MUuo{7aQwE387R)jfKQWS(4Tw>}O;s(grv z%i;LRLcUz-!JY$oe7!MUIN_1klMHQUW{CTM*5u@>X<^ljjq82?TWFVVHmqIcNuOSJ z)YfS^aq2qr7abwLpaQ4PEm;@riCb?6^z|Vtr<~dI4wIfyOjjps!b1j=dr{2`-EC>- z*j)T3G$M_+r*`KBS6goWS;zZx6VcV{Sn}xr&R-~_UtfFLIa*S5;}$dLC35U|Asstd zvVP5Aw#Q_V{d+m8)n0d@WmzU9*b8ruwip@dlaW~>Y{>e(oDS_R`R1!vNIzH1fPSs0 z(P)`IJAvFh^_}EsEYcR{MjVJ8g^RNd$-i8|!On~>om�kWbK>lSHU|aP4NpfkXM| z>q~eHbQB=tChf!A+(@Kk#|~Cpx_lEoJ+QYk!^luyq-0)x1PXAK#5O ztt~j6T0~`KElNsjZ|m?M7l%^Qjiq-*T5&$R6m_|V7kk=Krq(cF%6<$C z^oWY^!>LmXZfkYy*qKG(7bm%1RMj-}f)D#LV_HvC;i+tn&ZMHUmLGPF7RH@PE5dVN zTU6m^*|P1t*b536$mG|Y`Cxt$W$GGK!Cn-XRx{(nL<;_>B>CV3TwQGN9vUOOAG>Y1 z$dah&Ova6L5{{U&AW1wMuy6oxP3g{(<-c;Zu!=Q79t~<*t=2H>-Ci6%a+$y{P6(L5 zfPqYz=*&Aaf26dmhR85ql;Dk>w3o~arO5I<3eZ@jBSU={{%R-u$Hs9s{lpP^=i9IS z@bPL-RCESoM>w$|>fBxDDmA=*?O+Zb%IDb0Ypht}-T=B?8w>n=pBHB<*`cJmt*t4o z?9Evgbd0of#e@WVl5+Ga!6Bza$=tUZgWk4jC|)nOWyt7VA~N?bWzS=fBSXi;-Q62C ztg`^!zB8M%X+nJq6>3BO%~o)jD8sCJ^S-m7k-xF zl4_POeu+0Gxw3d!N|Whr+1yx^#p=-0MDMsj<;_~5@`_M~Z%9X5FaNvJHwN9ew*yLw zTUi>jI^?tvrE#O2NI8B@r0v3w+{AD&=i_9QQc)mnZA=+HJ`NL;JL2;&&^Fd4EDZPs zV`BqmPVdEYwx;+D+li5}expK`m~@HIFTKTc!D~;U)5*-3-V^)h%!LPK?{yK^C5bJ%dU@SnN+D|`>gDWQ00000NkvXXu0mjf D*IE&+ literal 0 HcmV?d00001 diff --git a/.github/images/consensus.png b/.github/images/consensus.png new file mode 100644 index 0000000000000000000000000000000000000000..b1c6a009c5ba2b33fbe8c8d20cafe1c953f54ce7 GIT binary patch literal 1644 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 M07*qoM6N<$f=>bzwg3PC literal 0 HcmV?d00001 diff --git a/.github/images/discussion.png b/.github/images/discussion.png new file mode 100644 index 0000000000000000000000000000000000000000..c9b09dcfe57fe5d14093cb2d0f6c8e9189cf56cb GIT binary patch literal 1673 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 FV1mKWT-*Qv literal 0 HcmV?d00001 diff --git a/.github/images/house-keeping.png b/.github/images/house-keeping.png new file mode 100644 index 0000000000000000000000000000000000000000..84db93866199b32c6a71dc2f4ef1fd5ce31ed104 GIT binary patch literal 2267 zcmV<12qgE3P)BS}idM??cn)Q+iR5?@tG6&sD|jAxN+dQ0pJ<{y1bM#}*oB1!_Ra2ooqH)1bl0M0NT zJLh-K`If1enAYgn9%(X4BY1_d%UH`M5 zBDRX+B4Mg+K~G0Fz_P<)D@VwO{}?4Knl{+m3_qNY+yUYruSE14OQ8F*04=Al!qIIW zmPkJSmq!7pg;51uNwdXUm7nH(f>w!qj0MBx!c?t6<*s8e*J<6-(ft|X7aIZ9>IHC^tn{42X!aYNkMUn_yjYKKo`2-NE{LF^;<>@KUL`zm%Gr9SCj56cju)TV2Pqst8MFgYA%EAGDa z0W2+gB>sLad{hdwUo8g1;UFM622PU|n$N$X&eop0-cNUgQlw9hL&?_tu(cX6_WjlH z67pfZ*8sn`2ngg7lx{l!!|h5W{B9LQV^q}0o-Q*)vElSDd2g<3c_5qz0%F&9B5GX* zcmaO6yX^psWpzl{vBtf;w{xLhpW#s#xtnsudjDh#*oq5j}`5C^MPr9(b@3d(mJ zhT*$%uny-SQ2GGrC@6(=S^~U$gcJ-CtwGNvBjS~LZsqCo3n7^tkB%FqZgp|}eG$2Q zF075MsN8cL)@D5-UzvxH=cl3e?_a>CGazbx#vnjE`AhOtJQNu-(QxQIba}TZYb5T< zf`42D?!3Nd=zz3eDek9|UdK#E$(#G3$Vh=~Mk0zg??d;!dg`FS>4}J1vyeuM|2P$z zPF!*e2mzNwi(gC_dOD5hEUHHHseBsEk*_`vdD6%;SbgmgOb*I)Xb zAG2vOq%)IH{KkKt(F+W{!@r2j2>SCCRNka&w_V275=hTU{Ih58+GGQ2)_cwB;Al zJ><@$zIEIzL+9-Z8bAXnYhYKZd~H60=k@{;wm22yiKA%%_t8@?qfDX!K4i)oq+kFb z!E>e}=qD3UzViqS1!ZvcI4Bq-BDCkOL6(w?)~~-q(}{0r79{>*9sFX$QIwqnAEkm8 zNzEsDu(q_rZ*(}NLwkD{NE0EM9EXOZdC;A|(aYP3YADjDQ-BHo-aixdQI_V|MXHO~ zL7I-T_YOk#OcFvDKJ8W>nG%mJ3r& z6D^w5QPO~3N_DzW^T8?TzAB(y-2JP+%R2lO2#0(53NiM*l^_vbn4JUCM0k=H^lY-n zcall*6cDbjH#kBb?Ic|`J8BP{hVH^m_(p{E0+M%&28wd^fN;f024b z0TuhvI}utYfD~=Y`R^Gr)KQ7&?@Q^&X|zzINblXZ2vC9`39On{x6`waG6cS=P;}j` zr3IMGBYlf}4)GtW0-=?zlG-Q!Qt&W&p5DoxO{Rd<<(`JAstI1+0@`*DyyH;)L}nPj zxJYysR?z@;S!{HMA(UDAb}kLlht45GRrLS7Zy+H0%SE&OVMZZI>Q?{&002ovPDHLkV1mBvJR$%9 literal 0 HcmV?d00001 diff --git a/.github/images/ledger.png b/.github/images/ledger.png new file mode 100644 index 0000000000000000000000000000000000000000..babf6a3ca797c7d49080abbc2c354b93fb26434f GIT binary patch literal 1270 zcmV+Q)k9P^FrF7$;#UlB{Ydv z3TbUdHw|qIYF;vLAx^v?AUduGjtAkKx4pkvQ|TN%HaG40_j#Z1`+c6@^Zh-~PYRft zVW(B8-j=Q86iLPtBrBd`lc3HBdknqVB)Mu<gdOGKa#`pzyEZz786j9&U%HB0|8Sd*&)%6A(?fT(pZ%Z#pA}MPZ@1{&5 z$lHa})wOs!JK|*D4eezFt3{k^UaUt;oaQ-I>hfSd-Wa)}}=o)HZ!c%`W8pm3rlRyw}WE z-qU#iv7?63*wRj`PA}TTslOT*M|*NU|ADc?hY%I$!_0NNIZ;(3{5k2W1MCBFxaA6`-qJo*8RY2nNAFPS+U-{}Jl4HWq>N*)?vQiIL+z}ICU6C(n#v+XMc+pg=RAY-;kY@;kcY~J*&ywWP$ z{~1!{qDPl%#*02zOi_MTVJXE&en+d*^G@;vK~f>CSk295K`3E$KI2CCiDVU>D3xq% z>%iW&FA1xZHmenJrmz*g-OHm%+hbbyB>!(yPEmY#fHx{tFOF4SqqSXkpYoN&f_+`N zP;*nP6b6G4FK0(Whk8?u_4?gAm)f?hh55qPybsvqqg=1TM z9_Zyl-m;nK^mq8=yI*m2>QCClD3M^sjssk6XnGvj2d~N}w-PU(5yR}5aM4D}9etsu zfrTIK71Q~@nplb5UsZZ&%ie8-WedLNZcX8i%g|Q4#P10mT7UK|sV*zz=rm1M`sPMx}x)sGzIjln6?jpeWiY2rUd&Q>+vb z6vK7Z1wXTuZ4JxgCx(P31E?qj@-+i9Ffe=GheD5QdOV<~bGZMRKi+xH{odE}yMOod zJ~C(LznYnMtaz@HgO(D z_kYiqDLK?ut2!M*#2SAp?$nUI_cV4xtU5nmMsNt*Gn#d=G=*=prX3A()cuLD(G6k1Qa2q=m zji#BH@D%!*o3L=%YmD-60GW(ZWjP)VXT)k1~M6!&nrnwK2GrB$?f?p z2lgfE!$3y5+hbs8K+)AQvG&KyrPw=Ji#qbo6*10h7&b$!Xi#fd7oEX3$1kC2Y-&^O z!yx=BaY=f&N-HrikYO>PFGfa&)ZA5xmvnHJQ&myR&Mn6XdV6wvJLsBqBxgo2)J0A~ zUMX^03;J2KP2SkZbml0080;_*ovuX`vhDAOiQBvoCs$j}{I>vUy~m%N$vbNO;gjOiX)<){*K^ zUMoYZ(J^@FKpIpU76&GAt>EV4A>6I31w z!Yh6Y!pitrc)m28Mg9pwF;Ymo<2%EPq}Y7|!ins2?Ai8ju~X8{ZpsM9(Zz=HvTBNd zy2D_{ffyV2;H{aTJQBhfuVFZkuw{|oy<9%8c@VzZkL-V(=E}ulmd5yVe;rF!PGf4| zQ-+{;$CoM7J&6h5PHs*<%}qKIQ$t0su3MGye-IRZ^Tj`S0x6r0a4_Rr>gyWVk{N-M zi_K#pNCx~zZ&zZ%?&Vs#%8xe!yog`9o9fDXqT**gdk9W0a-u()gETt#q@2WduqED; zMq^@T+-{8bD`D!a7YK>)<#~ zaCqPM>`gsMZMB+tp|1)@@7LcQ5rM$Pu5!_0o`6@JYpnXrGbH+}k^YZPh*sM~gIde#kAuY6iWS!O563C;qtcYHJMEpx?a0Y fE2&ytYvTU^2ouTVSSEnf00000NkvXXu0mjf7G69Y literal 0 HcmV?d00001 diff --git a/.github/images/network-policy.png b/.github/images/network-policy.png new file mode 100644 index 0000000000000000000000000000000000000000..84452a613eb763b3172d790aeb6f98e21c8037c7 GIT binary patch literal 2383 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 N002ovPDHLkV1mMLk2wGU literal 0 HcmV?d00001 diff --git a/.github/images/p2p.png b/.github/images/p2p.png new file mode 100644 index 0000000000000000000000000000000000000000..d638bd234240661a6a39c7d7bc01eadaf9c1c3d6 GIT binary patch literal 816 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 U?0+PWl>h($07*qoM6N<$f*tL9$^ZZW literal 0 HcmV?d00001 diff --git a/.github/images/rpc.png b/.github/images/rpc.png new file mode 100644 index 0000000000000000000000000000000000000000..24b6c3537510ec5f5cbf776f2ad24f2bdccb0024 GIT binary patch literal 799 zcmV+)1K|9LP)&d9s%k=D8?%86{c$k=81wh4^TLf(V>)0uq_v+g?QctLV6P9s4Z~R4!aKo_c#nr z|A#rl<&HLbsnl#v&!MWaf-%7paC4I&ll7&%>>8tjLK!?n#mA5D*qyePM-Pm&wzQ!3 zA4&YCT?7OM)7)(3X+=5vGLxx${p$Bec7*wk7>#aw9;)Gfw6(TU_3Rm!qs zv}QiuYJZll*74@G34KN)b+u-etV$*_dLemxQyHrAVP@2PjvqP5h0`T8Ha4&)_Y|Ik zz0f5r#QgI4-(e5$-lC*X&sgmwV&hUcbG+zRu^{av5_MUW87|@Nqha*8P}VJvrt0Z~ zu6oiQ%kG%B0+Q9e>Rj)P5sLh>fDU@{x03 zhHK|IR(ycZpFXfOPRFd-3&_jfN>E4`Vc`*Erp0sn<`tToEu?3a(0{-nwr^QUjrqmj zZEUZspNEn|`V=IRUPQ;l;-T~;ZgHd|ts7JKqfoeW^l%pTG*63|L&g0va`Y*@tM4ix z{-e%G#Sgbw-q#b1!_!EW8S z=up+o<49wdBW7}V1gjF#C^(RT(QpZ?)zUqdeZ>6J;b~tB+~mRr7hLRX!O$L93+hFL dV!YQKrkRG#rD#Y8iCjdiZnsI@M9IlEX<`PzW@s!859t3 zNSHq?X+`kDqXYwW;|9l0kHwgngw5VgFesB^GSTaF;_-P=6lE+u;|H%;ww#>orBv;$ zAzGthbAA!WPMzS_^S|-e@bLcxoVjo@%d%cUpD>f4EOVyWO2e5(ygn}meG(g9&nMZC zjQ_SD_n@1E_$S#}Qx&H8DWi#k*VZxQ_w&^^hq?LZANL#xw_4Kac<+sOnDoRX99^A+ zHqV$Ii)G(#9Nk^KyUk3p!ANhP6P;FvPOC)-!AGB${|oH)cqq(WkED~-z4p%Q$g(^( z_~p!(Se>&1Ys*=h&RP)yHslp>>1rE^GxaQ4l!3LSnN!x&_-^_5;H?s7nNs+;>a!8> z<=?L`Km9p08jT9=aM(vfL-26Ln#mg8-@Zdt<@Ncn*=;m6Td_MFY$@Ety!2%U`Q(0zZ%9-L}@f}U5G=8!&TE|ONKTKVPv zTCy{sIHIwK$VqwU!aZQ*?KmU{q8TU+k^nTDy&)92^Y~2Zw?c zvhG;p5A?hGF&UHDT2ze3>rw488BH8K@--LRE)%#DV8=Eyb5hgz#8NQ=KGAd%?=2ts zYw`#L0_-`okG`8X{u`X};v(`^t;N>S&hf?u42enPtz66bwoA0zJIGzR21SuMaea(J_zANQ4ZW078QR3bH$6Yl&~Lk00000NkvXXu0mjfa5K!B literal 0 HcmV?d00001 diff --git a/.github/images/solution-design.png b/.github/images/solution-design.png new file mode 100644 index 0000000000000000000000000000000000000000..552c083d0b6598ccb4ccf942d25845a46de2e92a GIT binary patch literal 1420 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 O0000 Date: Wed, 21 Aug 2019 05:05:41 +0800 Subject: [PATCH 080/305] Optimize `GetHeader()` and `GetBlock()` (#1039) --- neo/Persistence/Helper.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/neo/Persistence/Helper.cs b/neo/Persistence/Helper.cs index 3cc1b92ca9..d884e0da6b 100644 --- a/neo/Persistence/Helper.cs +++ b/neo/Persistence/Helper.cs @@ -20,6 +20,7 @@ public static bool ContainsTransaction(this IPersistence persistence, UInt256 ha public static Block GetBlock(this IPersistence persistence, uint index) { + if (index == 0) return Blockchain.GenesisBlock; UInt256 hash = Blockchain.Singleton.GetBlockHash(index); if (hash == null) return null; return persistence.GetBlock(hash); @@ -35,6 +36,7 @@ public static Block GetBlock(this IPersistence persistence, UInt256 hash) public static Header GetHeader(this IPersistence persistence, uint index) { + if (index == 0) return Blockchain.GenesisBlock.Header; UInt256 hash = Blockchain.Singleton.GetBlockHash(index); if (hash == null) return null; return persistence.GetHeader(hash); From 7228fc5a8163e53df79556a0e439d4d234e552a1 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 21 Aug 2019 20:38:04 +0200 Subject: [PATCH 081/305] Update policy - Add maximum witness size (in bytes) (#1020) * Max witness size * Add error test * Rename class * allow 10/10 * rounding up to 1024 bytes * Error with 11 * Fix ut --- .../Network/P2P/Payloads/UT_Witness.cs | 122 +++++++++++++++++- neo/Network/P2P/Payloads/Witness.cs | 7 +- 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index ec3525931e..01a76eeee4 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -1,13 +1,62 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.IO.Json; using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.Wallets; +using Neo.Wallets.NEP6; +using System; +using System.IO; +using System.Linq; namespace Neo.UnitTests.Network.P2P.Payloads { [TestClass] public class UT_Witness { + class DummyVerificable : IVerifiable + { + private UInt160 _hash; + + public Witness[] Witnesses { get; set; } + + public int Size => 1; + + public DummyVerificable(UInt160 hash) + { + _hash = hash; + } + + public void Deserialize(BinaryReader reader) + { + DeserializeUnsigned(reader); + Witnesses = reader.ReadSerializableArray(16); + } + + public void DeserializeUnsigned(BinaryReader reader) + { + reader.ReadByte(); + } + + public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) + { + return new UInt160[] { _hash }; + } + + public void Serialize(BinaryWriter writer) + { + SerializeUnsigned(writer); + writer.Write(Witnesses); + } + + public void SerializeUnsigned(BinaryWriter writer) + { + writer.Write((byte)1); + } + } + Witness uut; [TestInitialize] @@ -22,6 +71,77 @@ public void InvocationScript_Get() uut.InvocationScript.Should().BeNull(); } + private Witness PrepareDummyWitness(int maxAccounts) + { + var store = TestBlockchain.GetStore(); + var wallet = TestUtils.GenerateTestWallet(); + var snapshot = store.GetSnapshot(); + + // Prepare + + var address = new WalletAccount[maxAccounts]; + var wallets = new NEP6Wallet[maxAccounts]; + var walletsUnlocks = new IDisposable[maxAccounts]; + + for (int x = 0; x < maxAccounts; x++) + { + wallets[x] = TestUtils.GenerateTestWallet(); + walletsUnlocks[x] = wallets[x].Unlock("123"); + address[x] = wallets[x].CreateAccount(); + } + + // Generate multisignature + + var multiSignContract = Contract.CreateMultiSigContract(maxAccounts, address.Select(a => a.GetKey().PublicKey).ToArray()); + + for (int x = 0; x < maxAccounts; x++) + { + wallets[x].CreateAccount(multiSignContract, address[x].GetKey()); + } + + // Sign + + var data = new ContractParametersContext(new DummyVerificable(multiSignContract.ScriptHash)); + + for (int x = 0; x < maxAccounts; x++) + { + Assert.IsTrue(wallets[x].Sign(data)); + } + + Assert.IsTrue(data.Completed); + return data.GetWitnesses()[0]; + } + + [TestMethod] + public void MaxSize_OK() + { + var witness = PrepareDummyWitness(10); + + // Check max size + + witness.Size.Should().Be(1003); + witness.InvocationScript.GetVarSize().Should().Be(653); + witness.VerificationScript.GetVarSize().Should().Be(350); + + Assert.IsTrue(witness.Size <= 1024); + + var copy = witness.ToArray().AsSerializable(); + + CollectionAssert.AreEqual(witness.InvocationScript, copy.InvocationScript); + CollectionAssert.AreEqual(witness.VerificationScript, copy.VerificationScript); + } + + [TestMethod] + public void MaxSize_Error() + { + var witness = PrepareDummyWitness(11); + + // Check max size + + Assert.IsTrue(witness.Size > 1024); + Assert.ThrowsException(() => witness.ToArray().AsSerializable()); + } + [TestMethod] public void InvocationScript_Set() { @@ -73,4 +193,4 @@ public void ToJson() Assert.AreEqual(json["verification"].AsString(), "202020"); } } -} \ No newline at end of file +} diff --git a/neo/Network/P2P/Payloads/Witness.cs b/neo/Network/P2P/Payloads/Witness.cs index d352882e17..52a2186ee8 100644 --- a/neo/Network/P2P/Payloads/Witness.cs +++ b/neo/Network/P2P/Payloads/Witness.cs @@ -28,8 +28,11 @@ public virtual UInt160 ScriptHash void ISerializable.Deserialize(BinaryReader reader) { - InvocationScript = reader.ReadVarBytes(65536); - VerificationScript = reader.ReadVarBytes(65536); + // This is designed to allow a MultiSig 10/10 (around 1003 bytes) ~1024 bytes + // Invocation = 10 * 64 + 10 = 650 ~ 664 (exact is 653) + InvocationScript = reader.ReadVarBytes(664); + // Verification = 10 * 33 + 10 = 340 ~ 360 (exact is 350) + VerificationScript = reader.ReadVarBytes(360); } void ISerializable.Serialize(BinaryWriter writer) From cd4340f7b6529b78008f54dad4eaf6d2f476c041 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 23 Aug 2019 15:05:48 +0200 Subject: [PATCH 082/305] Update VM to last changes (#1048) * Update VM to last changes * Remove hashes --- neo/SmartContract/ApplicationEngine.cs | 27 +++++++++++----------- neo/SmartContract/ExecutionContextState.cs | 10 ++++++++ neo/neo.csproj | 2 +- 3 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 neo/SmartContract/ExecutionContextState.cs diff --git a/neo/SmartContract/ApplicationEngine.cs b/neo/SmartContract/ApplicationEngine.cs index d211d7aac8..45763ed30a 100644 --- a/neo/SmartContract/ApplicationEngine.cs +++ b/neo/SmartContract/ApplicationEngine.cs @@ -15,7 +15,6 @@ public partial class ApplicationEngine : ExecutionEngine public const long GasFree = 0; private readonly long gas_amount; private readonly bool testMode; - private readonly RandomAccessStack hashes = new RandomAccessStack(); private readonly List notifications = new List(); private readonly List disposables = new List(); @@ -23,9 +22,9 @@ public partial class ApplicationEngine : ExecutionEngine public IVerifiable ScriptContainer { get; } public Snapshot Snapshot { get; } public long GasConsumed { get; private set; } = 0; - public UInt160 CurrentScriptHash => hashes.Count > 0 ? hashes.Peek() : null; - public UInt160 CallingScriptHash => hashes.Count > 1 ? hashes.Peek(1) : null; - public UInt160 EntryScriptHash => hashes.Count > 0 ? hashes.Peek(hashes.Count - 1) : null; + public UInt160 CurrentScriptHash => CurrentContext?.GetState().ScriptHash; + public UInt160 CallingScriptHash => InvocationStack.Count > 1 ? InvocationStack.Peek(1).GetState().ScriptHash : null; + public UInt160 EntryScriptHash => EntryContext?.GetState().ScriptHash; public IReadOnlyList Notifications => notifications; internal Dictionary InvocationCounter { get; } = new Dictionary(); @@ -36,8 +35,6 @@ public ApplicationEngine(TriggerType trigger, IVerifiable container, Snapshot sn this.Trigger = trigger; this.ScriptContainer = container; this.Snapshot = snapshot; - ContextLoaded += ApplicationEngine_ContextLoaded; - ContextUnloaded += ApplicationEngine_ContextUnloaded; } internal T AddDisposable(T disposable) where T : IDisposable @@ -52,14 +49,16 @@ private bool AddGas(long gas) return testMode || GasConsumed <= gas_amount; } - private void ApplicationEngine_ContextLoaded(object sender, ExecutionContext e) - { - hashes.Push(((byte[])e.Script).ToScriptHash()); - } - - private void ApplicationEngine_ContextUnloaded(object sender, ExecutionContext e) - { - hashes.Pop(); + protected override void LoadContext(ExecutionContext context) + { + // Set default execution context state + + context.SetState(new ExecutionContextState() + { + ScriptHash = ((byte[])context.Script).ToScriptHash() + }); + + base.LoadContext(context); } public override void Dispose() diff --git a/neo/SmartContract/ExecutionContextState.cs b/neo/SmartContract/ExecutionContextState.cs new file mode 100644 index 0000000000..7895d943a2 --- /dev/null +++ b/neo/SmartContract/ExecutionContextState.cs @@ -0,0 +1,10 @@ +namespace Neo.SmartContract +{ + public class ExecutionContextState + { + /// + /// Script hash + /// + public UInt160 ScriptHash { get; set; } + } +} \ No newline at end of file diff --git a/neo/neo.csproj b/neo/neo.csproj index df29d088f2..66e85016bc 100644 --- a/neo/neo.csproj +++ b/neo/neo.csproj @@ -29,7 +29,7 @@ - + From 09d1063ef0401dc17b23cf5d7015246837f1fac5 Mon Sep 17 00:00:00 2001 From: Igor Machado Coelho Date: Sat, 24 Aug 2019 16:33:14 -0300 Subject: [PATCH 083/305] dotnet format (#1054) * dotnet format * fix CRLF --- .../Network/P2P/Payloads/UT_Witness.cs | 2 +- neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs | 3 +- neo/BigDecimal.cs | 2 +- neo/Consensus/ChangeView.cs | 2 +- neo/Consensus/ChangeViewReason.cs | 2 +- neo/Consensus/Commit.cs | 2 +- neo/Consensus/ConsensusContext.cs | 116 ++++---- neo/Consensus/ConsensusMessage.cs | 2 +- neo/Consensus/ConsensusMessageType.cs | 2 +- neo/Consensus/ConsensusService.cs | 2 +- neo/Consensus/PrepareRequest.cs | 2 +- neo/Consensus/PrepareResponse.cs | 2 +- ...ecoveryMessage.ChangeViewPayloadCompact.cs | 2 +- .../RecoveryMessage.CommitPayloadCompact.cs | 2 +- ...coveryMessage.PreparationPayloadCompact.cs | 2 +- neo/Consensus/RecoveryMessage.cs | 2 +- neo/Consensus/RecoveryRequest.cs | 2 +- neo/Cryptography/Base58.cs | 2 +- neo/Cryptography/BloomFilter.cs | 2 +- neo/Cryptography/Crypto.cs | 2 +- neo/Cryptography/ECC/ECCurve.cs | 2 +- neo/Cryptography/ECC/ECDsa.cs | 2 +- neo/Cryptography/ECC/ECFieldElement.cs | 2 +- neo/Cryptography/ECC/ECPoint.cs | 2 +- neo/Cryptography/Helper.cs | 2 +- neo/Cryptography/MerkleTree.cs | 2 +- neo/Cryptography/MerkleTreeNode.cs | 2 +- neo/Cryptography/Murmur3.cs | 2 +- neo/Cryptography/RIPEMD160Managed.cs | 2 +- neo/Cryptography/SCrypt.cs | 2 +- neo/Helper.cs | 2 +- neo/IO/Actors/Idle.cs | 2 +- neo/IO/Actors/PriorityMailbox.cs | 2 +- neo/IO/Actors/PriorityMessageQueue.cs | 2 +- neo/IO/ByteArrayComparer.cs | 2 +- neo/IO/Caching/Cache.cs | 2 +- neo/IO/Caching/CloneCache.cs | 2 +- neo/IO/Caching/CloneMetaCache.cs | 2 +- neo/IO/Caching/DataCache.cs | 2 +- neo/IO/Caching/FIFOCache.cs | 2 +- neo/IO/Caching/FIFOSet.cs | 130 ++++----- neo/IO/Caching/MetaDataCache.cs | 2 +- neo/IO/Caching/OrderedDictionary.cs | 2 +- neo/IO/Caching/ReflectionCache.cs | 2 +- neo/IO/Caching/ReflectionCacheAttribute.cs | 2 +- neo/IO/Caching/RelayCache.cs | 2 +- neo/IO/Caching/TrackState.cs | 2 +- neo/IO/Data/LevelDB/DB.cs | 2 +- neo/IO/Data/LevelDB/Helper.cs | 2 +- neo/IO/Data/LevelDB/Iterator.cs | 2 +- neo/IO/Data/LevelDB/LevelDBException.cs | 2 +- neo/IO/Data/LevelDB/Native.cs | 2 +- neo/IO/Data/LevelDB/Options.cs | 2 +- neo/IO/Data/LevelDB/ReadOptions.cs | 2 +- neo/IO/Data/LevelDB/Slice.cs | 2 +- neo/IO/Data/LevelDB/SliceBuilder.cs | 2 +- neo/IO/Data/LevelDB/Snapshot.cs | 2 +- neo/IO/Data/LevelDB/WriteBatch.cs | 2 +- neo/IO/Data/LevelDB/WriteOptions.cs | 2 +- neo/IO/Helper.cs | 2 +- neo/IO/ICloneable.cs | 2 +- neo/IO/ISerializable.cs | 2 +- neo/IO/Json/JArray.cs | 2 +- neo/IO/Json/JBoolean.cs | 2 +- neo/IO/Json/JNumber.cs | 2 +- neo/IO/Json/JObject.cs | 2 +- neo/IO/Json/JString.cs | 2 +- neo/IO/Wrappers/SerializableWrapper.cs | 2 +- neo/IO/Wrappers/UInt32Wrapper.cs | 2 +- neo/Ledger/Blockchain.ApplicationExecuted.cs | 2 +- neo/Ledger/Blockchain.cs | 2 +- neo/Ledger/ContractState.cs | 2 +- neo/Ledger/HashIndexState.cs | 2 +- neo/Ledger/HeaderHashList.cs | 2 +- neo/Ledger/RelayResultReason.cs | 2 +- neo/Ledger/StorageFlags.cs | 2 +- neo/Ledger/StorageItem.cs | 2 +- neo/Ledger/StorageKey.cs | 2 +- neo/Ledger/TransactionState.cs | 2 +- neo/Ledger/TrimmedBlock.cs | 2 +- neo/NeoSystem.cs | 2 +- .../P2P/Capabilities/FullNodeCapability.cs | 2 +- .../P2P/Capabilities/NodeCapability.cs | 2 +- .../P2P/Capabilities/NodeCapabilityType.cs | 2 +- .../P2P/Capabilities/ServerCapability.cs | 2 +- neo/Network/P2P/ChannelsConfig.cs | 2 +- neo/Network/P2P/Connection.cs | 2 +- neo/Network/P2P/Helper.cs | 2 +- neo/Network/P2P/LocalNode.cs | 2 +- neo/Network/P2P/Message.cs | 2 +- neo/Network/P2P/MessageCommand.cs | 2 +- neo/Network/P2P/MessageFlags.cs | 2 +- neo/Network/P2P/Payloads/AddrPayload.cs | 2 +- neo/Network/P2P/Payloads/Block.cs | 2 +- neo/Network/P2P/Payloads/BlockBase.cs | 2 +- neo/Network/P2P/Payloads/ConsensusData.cs | 2 +- neo/Network/P2P/Payloads/ConsensusPayload.cs | 2 +- neo/Network/P2P/Payloads/Cosigner.cs | 2 +- neo/Network/P2P/Payloads/FilterAddPayload.cs | 2 +- neo/Network/P2P/Payloads/FilterLoadPayload.cs | 2 +- neo/Network/P2P/Payloads/GetBlocksPayload.cs | 2 +- neo/Network/P2P/Payloads/Header.cs | 2 +- neo/Network/P2P/Payloads/HeadersPayload.cs | 2 +- neo/Network/P2P/Payloads/IInventory.cs | 2 +- neo/Network/P2P/Payloads/IVerifiable.cs | 2 +- neo/Network/P2P/Payloads/InvPayload.cs | 2 +- neo/Network/P2P/Payloads/InventoryType.cs | 2 +- .../P2P/Payloads/MerkleBlockPayload.cs | 2 +- .../P2P/Payloads/NetworkAddressWithTime.cs | 2 +- neo/Network/P2P/Payloads/PingPayload.cs | 2 +- .../P2P/Payloads/TransactionAttribute.cs | 2 +- .../P2P/Payloads/TransactionAttributeUsage.cs | 2 +- neo/Network/P2P/Payloads/VersionPayload.cs | 2 +- neo/Network/P2P/Payloads/Witness.cs | 2 +- neo/Network/P2P/Payloads/WitnessScope.cs | 2 +- neo/Network/P2P/Peer.cs | 2 +- neo/Network/P2P/ProtocolHandler.cs | 2 +- neo/Network/P2P/RemoteNode.cs | 2 +- neo/Network/P2P/TaskManager.cs | 2 +- neo/Network/P2P/TaskSession.cs | 2 +- neo/Network/RPC/Models/RpcBlock.cs | 2 +- neo/Network/RPC/Models/RpcBlockHeader.cs | 2 +- neo/Network/RPC/Models/RpcInvokeResult.cs | 2 +- neo/Network/RPC/Models/RpcNep5Balances.cs | 2 +- neo/Network/RPC/Models/RpcPeers.cs | 2 +- neo/Network/RPC/Models/RpcPlugin.cs | 2 +- neo/Network/RPC/Models/RpcRawMemPool.cs | 2 +- neo/Network/RPC/Models/RpcRequest.cs | 2 +- neo/Network/RPC/Models/RpcResponse.cs | 2 +- neo/Network/RPC/Models/RpcTransaction.cs | 2 +- .../RPC/Models/RpcValidateAddressResult.cs | 2 +- neo/Network/RPC/Models/RpcValidator.cs | 2 +- neo/Network/RPC/Models/RpcVersion.cs | 2 +- neo/Network/RPC/RpcClient.cs | 2 +- neo/Network/RPC/RpcException.cs | 2 +- neo/Persistence/CloneSnapshot.cs | 2 +- neo/Persistence/Helper.cs | 2 +- neo/Persistence/IPersistence.cs | 2 +- neo/Persistence/LevelDB/DbCache.cs | 2 +- neo/Persistence/LevelDB/DbMetaDataCache.cs | 2 +- neo/Persistence/LevelDB/DbSnapshot.cs | 2 +- neo/Persistence/LevelDB/LevelDBStore.cs | 2 +- neo/Persistence/LevelDB/Prefixes.cs | 2 +- neo/Persistence/Snapshot.cs | 2 +- neo/Persistence/Store.cs | 2 +- neo/Plugins/ILogPlugin.cs | 2 +- neo/Plugins/IMemoryPoolTxObserverPlugin.cs | 2 +- neo/Plugins/IP2PPlugin.cs | 2 +- neo/Plugins/IPersistencePlugin.cs | 2 +- neo/Plugins/IRpcPlugin.cs | 2 +- neo/Plugins/LogLevel.cs | 2 +- neo/Plugins/MemoryPoolTxRemovalReason.cs | 2 +- neo/Plugins/Plugin.cs | 2 +- neo/ProtocolSettings.cs | 2 +- .../ApplicationEngine.OpCodePrices.cs | 2 +- neo/SmartContract/ApplicationEngine.cs | 20 +- neo/SmartContract/ContainerPlaceholder.cs | 2 +- neo/SmartContract/Contract.cs | 2 +- neo/SmartContract/ContractParameter.cs | 2 +- neo/SmartContract/ContractParameterType.cs | 2 +- .../ContractParametersContext.cs | 12 +- .../Enumerators/ConcatenatedEnumerator.cs | 2 +- neo/SmartContract/Enumerators/IEnumerator.cs | 2 +- .../Enumerators/IteratorKeysWrapper.cs | 2 +- .../Enumerators/IteratorValuesWrapper.cs | 2 +- neo/SmartContract/ExecutionContextState.cs | 18 +- neo/SmartContract/Helper.cs | 2 +- neo/SmartContract/InteropDescriptor.cs | 2 +- neo/SmartContract/InteropService.NEO.cs | 2 +- neo/SmartContract/InteropService.cs | 2 +- neo/SmartContract/Iterators/ArrayWrapper.cs | 2 +- neo/SmartContract/Iterators/IIterator.cs | 2 +- neo/SmartContract/Iterators/MapWrapper.cs | 2 +- .../Iterators/StorageIterator.cs | 2 +- neo/SmartContract/JsonSerializer.cs | 2 +- neo/SmartContract/LogEventArgs.cs | 2 +- neo/SmartContract/Manifest/ContractAbi.cs | 2 +- .../Manifest/ContractEventDescriptor.cs | 2 +- .../Manifest/ContractFeatures.cs | 2 +- neo/SmartContract/Manifest/ContractGroup.cs | 2 +- .../Manifest/ContractManifest.cs | 2 +- .../Manifest/ContractMethodDescriptor.cs | 2 +- .../Manifest/ContractParameterDefinition.cs | 2 +- .../Manifest/ContractPermission.cs | 2 +- .../Manifest/ContractPermissionDescriptor.cs | 2 +- .../Manifest/WildCardContainer.cs | 2 +- .../Native/ContractMethodAttribute.cs | 2 +- .../Native/ContractMethodMetadata.cs | 2 +- neo/SmartContract/Native/NativeContract.cs | 2 +- neo/SmartContract/Native/PolicyContract.cs | 2 +- neo/SmartContract/Native/Tokens/GasToken.cs | 2 +- .../Native/Tokens/Nep5AccountState.cs | 2 +- neo/SmartContract/Native/Tokens/Nep5Token.cs | 2 +- neo/SmartContract/NefFile.cs | 258 +++++++++--------- neo/SmartContract/NotifyEventArgs.cs | 2 +- neo/SmartContract/StackItemType.cs | 2 +- neo/SmartContract/StorageContext.cs | 2 +- neo/SmartContract/WitnessWrapper.cs | 2 +- neo/UInt160.cs | 2 +- neo/UInt256.cs | 2 +- neo/UIntBase.cs | 2 +- neo/VM/Helper.cs | 2 +- neo/Wallets/AssetDescriptor.cs | 2 +- neo/Wallets/Helper.cs | 2 +- neo/Wallets/KeyPair.cs | 2 +- neo/Wallets/NEP6/NEP6Account.cs | 2 +- neo/Wallets/NEP6/NEP6Contract.cs | 2 +- neo/Wallets/NEP6/NEP6Wallet.cs | 2 +- neo/Wallets/NEP6/ScryptParameters.cs | 2 +- neo/Wallets/NEP6/WalletLocker.cs | 2 +- neo/Wallets/SQLite/Account.cs | 2 +- neo/Wallets/SQLite/Address.cs | 2 +- neo/Wallets/SQLite/Contract.cs | 2 +- neo/Wallets/SQLite/Key.cs | 2 +- neo/Wallets/SQLite/UserWallet.cs | 2 +- neo/Wallets/SQLite/UserWalletAccount.cs | 2 +- neo/Wallets/SQLite/VerificationContract.cs | 2 +- neo/Wallets/SQLite/WalletDataContext.cs | 2 +- neo/Wallets/TransferOutput.cs | 2 +- neo/Wallets/Wallet.cs | 2 +- neo/Wallets/WalletAccount.cs | 2 +- neo/neo.csproj | 2 +- 222 files changed, 494 insertions(+), 493 deletions(-) diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index 01a76eeee4..efb06d93be 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -122,7 +122,7 @@ public void MaxSize_OK() witness.Size.Should().Be(1003); witness.InvocationScript.GetVarSize().Should().Be(653); witness.VerificationScript.GetVarSize().Should().Be(350); - + Assert.IsTrue(witness.Size <= 1024); var copy = witness.ToArray().AsSerializable(); diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index c531c8c138..ade2472ba8 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -18,7 +18,8 @@ public class UT_NEP6Account private static KeyPair keyPair; [ClassInitialize] - public static void ClassSetup(TestContext context) { + public static void ClassSetup(TestContext context) + { byte[] privateKey = { 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}; keyPair = new KeyPair(privateKey); diff --git a/neo/BigDecimal.cs b/neo/BigDecimal.cs index 1d65859b0f..aaf541caa9 100644 --- a/neo/BigDecimal.cs +++ b/neo/BigDecimal.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Numerics; namespace Neo diff --git a/neo/Consensus/ChangeView.cs b/neo/Consensus/ChangeView.cs index 4c18b3fb3f..607e44cfd5 100644 --- a/neo/Consensus/ChangeView.cs +++ b/neo/Consensus/ChangeView.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; namespace Neo.Consensus { diff --git a/neo/Consensus/ChangeViewReason.cs b/neo/Consensus/ChangeViewReason.cs index eb06b7494a..066ecc1085 100644 --- a/neo/Consensus/ChangeViewReason.cs +++ b/neo/Consensus/ChangeViewReason.cs @@ -1,4 +1,4 @@ -namespace Neo.Consensus +namespace Neo.Consensus { public enum ChangeViewReason : byte { diff --git a/neo/Consensus/Commit.cs b/neo/Consensus/Commit.cs index f7c17e559a..84585e1ee4 100644 --- a/neo/Consensus/Commit.cs +++ b/neo/Consensus/Commit.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; namespace Neo.Consensus { diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index a06c6c965b..fbb21d8cd7 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -6,7 +6,7 @@ using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; -using Neo.VM; +using Neo.VM; using Neo.Wallets; using System; using System.Collections.Generic; @@ -39,11 +39,11 @@ internal class ConsensusContext : IDisposable, ISerializable public Snapshot Snapshot { get; private set; } private KeyPair keyPair; - private int _witnessSize; + private int _witnessSize; private readonly Wallet wallet; private readonly Store store; private readonly Random random = new Random(); - + public int F => (Validators.Length - 1) / 3; public int M => Validators.Length - F; public bool IsPrimary => MyIndex == Block.ConsensusData.PrimaryIndex; @@ -205,40 +205,40 @@ private void SignPayload(ConsensusPayload payload) 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 + internal int GetExpectedBlockSize() + { + return GetExpectedBlockSizeWithoutTransactions(Transactions.Count) + // Base size + Transactions.Values.Sum(u => u.Size); // 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; + /// + /// 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; } /// @@ -247,18 +247,18 @@ internal int GetExpectedBlockSizeWithoutTransactions(int expectedTransactions) /// Ordered transactions internal void EnsureMaxBlockSize(IEnumerable txs) { - uint maxBlockSize = NativeContract.Policy.GetMaxBlockSize(Snapshot); - uint maxTransactionsPerBlock = NativeContract.Policy.GetMaxTransactionsPerBlock(Snapshot); + uint maxBlockSize = NativeContract.Policy.GetMaxBlockSize(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(); - Block.Transactions = new Transaction[0]; - - // We need to know the expected block size - - var blockSize = GetExpectedBlockSizeWithoutTransactions(txs.Count()); + Block.Transactions = new Transaction[0]; + + // We need to know the expected block size + + var blockSize = GetExpectedBlockSizeWithoutTransactions(txs.Count()); // Iterate transaction until reach the size @@ -271,8 +271,8 @@ internal void EnsureMaxBlockSize(IEnumerable txs) hashes.Add(tx.Hash); Transactions.Add(tx.Hash, tx); } - - TransactionHashes = hashes.ToArray(); + + TransactionHashes = hashes.ToArray(); } public ConsensusPayload MakePrepareRequest() @@ -281,8 +281,8 @@ public ConsensusPayload MakePrepareRequest() random.NextBytes(buffer); Block.ConsensusData.Nonce = BitConverter.ToUInt64(buffer, 0); EnsureMaxBlockSize(Blockchain.Singleton.MemPool.GetSortedVerifiedTransactions()); - Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1); - + Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1); + return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareRequest { Timestamp = Block.Timestamp, @@ -348,21 +348,21 @@ public void Reset(byte viewNumber) }; 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; - } + 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 ConsensusPayload[Validators.Length]; diff --git a/neo/Consensus/ConsensusMessage.cs b/neo/Consensus/ConsensusMessage.cs index 85bf3174e5..af16db03e3 100644 --- a/neo/Consensus/ConsensusMessage.cs +++ b/neo/Consensus/ConsensusMessage.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.IO.Caching; using System; using System.IO; diff --git a/neo/Consensus/ConsensusMessageType.cs b/neo/Consensus/ConsensusMessageType.cs index 0aff263595..fe13207db1 100644 --- a/neo/Consensus/ConsensusMessageType.cs +++ b/neo/Consensus/ConsensusMessageType.cs @@ -1,4 +1,4 @@ -using Neo.IO.Caching; +using Neo.IO.Caching; namespace Neo.Consensus { diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index ee5c91d635..cac657c95f 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Akka.Configuration; using Neo.Cryptography; using Neo.IO; diff --git a/neo/Consensus/PrepareRequest.cs b/neo/Consensus/PrepareRequest.cs index 85823fe957..2369b4f9f7 100644 --- a/neo/Consensus/PrepareRequest.cs +++ b/neo/Consensus/PrepareRequest.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.Network.P2P.Payloads; using System; using System.IO; diff --git a/neo/Consensus/PrepareResponse.cs b/neo/Consensus/PrepareResponse.cs index 93eb95833a..7c2956ccc2 100644 --- a/neo/Consensus/PrepareResponse.cs +++ b/neo/Consensus/PrepareResponse.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System.IO; namespace Neo.Consensus diff --git a/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs b/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs index 6d4d6a7ae2..dfc46385c0 100644 --- a/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs +++ b/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.Network.P2P.Payloads; using System.IO; diff --git a/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs b/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs index d9347b71bc..72962f92b3 100644 --- a/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs +++ b/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.Network.P2P.Payloads; using System.IO; diff --git a/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs b/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs index 8c0e52f59f..962ae69185 100644 --- a/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs +++ b/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.Network.P2P.Payloads; using System.IO; diff --git a/neo/Consensus/RecoveryMessage.cs b/neo/Consensus/RecoveryMessage.cs index afae45470b..a5e1c09ac6 100644 --- a/neo/Consensus/RecoveryMessage.cs +++ b/neo/Consensus/RecoveryMessage.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; diff --git a/neo/Consensus/RecoveryRequest.cs b/neo/Consensus/RecoveryRequest.cs index c02ceb176c..6ea0d7c2f7 100644 --- a/neo/Consensus/RecoveryRequest.cs +++ b/neo/Consensus/RecoveryRequest.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; namespace Neo.Consensus { diff --git a/neo/Cryptography/Base58.cs b/neo/Cryptography/Base58.cs index fd5bf4d432..b64f12216b 100644 --- a/neo/Cryptography/Base58.cs +++ b/neo/Cryptography/Base58.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Numerics; using System.Text; diff --git a/neo/Cryptography/BloomFilter.cs b/neo/Cryptography/BloomFilter.cs index 637cd9fac5..c4b79d01f2 100644 --- a/neo/Cryptography/BloomFilter.cs +++ b/neo/Cryptography/BloomFilter.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using System.Linq; namespace Neo.Cryptography diff --git a/neo/Cryptography/Crypto.cs b/neo/Cryptography/Crypto.cs index ae1c8cd168..c835e3eebb 100644 --- a/neo/Cryptography/Crypto.cs +++ b/neo/Cryptography/Crypto.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Security.Cryptography; diff --git a/neo/Cryptography/ECC/ECCurve.cs b/neo/Cryptography/ECC/ECCurve.cs index 9d43bcd25f..45d70d31f4 100644 --- a/neo/Cryptography/ECC/ECCurve.cs +++ b/neo/Cryptography/ECC/ECCurve.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using System.Numerics; namespace Neo.Cryptography.ECC diff --git a/neo/Cryptography/ECC/ECDsa.cs b/neo/Cryptography/ECC/ECDsa.cs index a254b4248e..07373a7844 100644 --- a/neo/Cryptography/ECC/ECDsa.cs +++ b/neo/Cryptography/ECC/ECDsa.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Numerics; using System.Security.Cryptography; diff --git a/neo/Cryptography/ECC/ECFieldElement.cs b/neo/Cryptography/ECC/ECFieldElement.cs index e33cab4206..331e1e4408 100644 --- a/neo/Cryptography/ECC/ECFieldElement.cs +++ b/neo/Cryptography/ECC/ECFieldElement.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using System.Numerics; diff --git a/neo/Cryptography/ECC/ECPoint.cs b/neo/Cryptography/ECC/ECPoint.cs index dbd556eb50..803aab9987 100644 --- a/neo/Cryptography/ECC/ECPoint.cs +++ b/neo/Cryptography/ECC/ECPoint.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System; using System.IO; using System.Linq; diff --git a/neo/Cryptography/Helper.cs b/neo/Cryptography/Helper.cs index ff1bb8d3d7..07c9ce74e9 100644 --- a/neo/Cryptography/Helper.cs +++ b/neo/Cryptography/Helper.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.Network.P2P.Payloads; using System; using System.Collections.Generic; diff --git a/neo/Cryptography/MerkleTree.cs b/neo/Cryptography/MerkleTree.cs index 8d73f56a23..0d8e617f3f 100644 --- a/neo/Cryptography/MerkleTree.cs +++ b/neo/Cryptography/MerkleTree.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; diff --git a/neo/Cryptography/MerkleTreeNode.cs b/neo/Cryptography/MerkleTreeNode.cs index 6bfc75c8d1..73b84b43e9 100644 --- a/neo/Cryptography/MerkleTreeNode.cs +++ b/neo/Cryptography/MerkleTreeNode.cs @@ -1,4 +1,4 @@ -namespace Neo.Cryptography +namespace Neo.Cryptography { internal class MerkleTreeNode { diff --git a/neo/Cryptography/Murmur3.cs b/neo/Cryptography/Murmur3.cs index e54c19ebc1..6296bb20be 100644 --- a/neo/Cryptography/Murmur3.cs +++ b/neo/Cryptography/Murmur3.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; using System.Security.Cryptography; diff --git a/neo/Cryptography/RIPEMD160Managed.cs b/neo/Cryptography/RIPEMD160Managed.cs index 898780dc47..22ffafb835 100644 --- a/neo/Cryptography/RIPEMD160Managed.cs +++ b/neo/Cryptography/RIPEMD160Managed.cs @@ -1,4 +1,4 @@ -#if !NET47 +#if !NET47 using System; using System.Runtime.InteropServices; using System.Security; diff --git a/neo/Cryptography/SCrypt.cs b/neo/Cryptography/SCrypt.cs index 3022af296c..e40c2c83c5 100644 --- a/neo/Cryptography/SCrypt.cs +++ b/neo/Cryptography/SCrypt.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Security.Cryptography; namespace Neo.Cryptography diff --git a/neo/Helper.cs b/neo/Helper.cs index d8409b64bd..1a513ec79e 100644 --- a/neo/Helper.cs +++ b/neo/Helper.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using Neo.Plugins; using System; using System.Collections.Generic; diff --git a/neo/IO/Actors/Idle.cs b/neo/IO/Actors/Idle.cs index 4bb8655bd8..bd4a2d05fa 100644 --- a/neo/IO/Actors/Idle.cs +++ b/neo/IO/Actors/Idle.cs @@ -1,4 +1,4 @@ -namespace Neo.IO.Actors +namespace Neo.IO.Actors { internal sealed class Idle { diff --git a/neo/IO/Actors/PriorityMailbox.cs b/neo/IO/Actors/PriorityMailbox.cs index c6c8a8fbe2..31a50c9a89 100644 --- a/neo/IO/Actors/PriorityMailbox.cs +++ b/neo/IO/Actors/PriorityMailbox.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Akka.Configuration; using Akka.Dispatch; using Akka.Dispatch.MessageQueues; diff --git a/neo/IO/Actors/PriorityMessageQueue.cs b/neo/IO/Actors/PriorityMessageQueue.cs index d22f7626fe..b7820b065a 100644 --- a/neo/IO/Actors/PriorityMessageQueue.cs +++ b/neo/IO/Actors/PriorityMessageQueue.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Akka.Dispatch; using Akka.Dispatch.MessageQueues; using System; diff --git a/neo/IO/ByteArrayComparer.cs b/neo/IO/ByteArrayComparer.cs index 956ab758e6..478c6e3c7d 100644 --- a/neo/IO/ByteArrayComparer.cs +++ b/neo/IO/ByteArrayComparer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Neo.IO diff --git a/neo/IO/Caching/Cache.cs b/neo/IO/Caching/Cache.cs index 5b97d3a223..b5095231f3 100644 --- a/neo/IO/Caching/Cache.cs +++ b/neo/IO/Caching/Cache.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; diff --git a/neo/IO/Caching/CloneCache.cs b/neo/IO/Caching/CloneCache.cs index f13865a41b..0dd030fcb6 100644 --- a/neo/IO/Caching/CloneCache.cs +++ b/neo/IO/Caching/CloneCache.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; namespace Neo.IO.Caching diff --git a/neo/IO/Caching/CloneMetaCache.cs b/neo/IO/Caching/CloneMetaCache.cs index 6b354e256e..3cb0d83201 100644 --- a/neo/IO/Caching/CloneMetaCache.cs +++ b/neo/IO/Caching/CloneMetaCache.cs @@ -1,4 +1,4 @@ -namespace Neo.IO.Caching +namespace Neo.IO.Caching { internal class CloneMetaCache : MetaDataCache where T : class, ICloneable, ISerializable, new() diff --git a/neo/IO/Caching/DataCache.cs b/neo/IO/Caching/DataCache.cs index 278c01d650..dc97031d10 100644 --- a/neo/IO/Caching/DataCache.cs +++ b/neo/IO/Caching/DataCache.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; diff --git a/neo/IO/Caching/FIFOCache.cs b/neo/IO/Caching/FIFOCache.cs index 4aa7198b50..c96b11bf6e 100644 --- a/neo/IO/Caching/FIFOCache.cs +++ b/neo/IO/Caching/FIFOCache.cs @@ -1,4 +1,4 @@ -namespace Neo.IO.Caching +namespace Neo.IO.Caching { internal abstract class FIFOCache : Cache { diff --git a/neo/IO/Caching/FIFOSet.cs b/neo/IO/Caching/FIFOSet.cs index aba54463ae..98733ce444 100644 --- a/neo/IO/Caching/FIFOSet.cs +++ b/neo/IO/Caching/FIFOSet.cs @@ -1,65 +1,65 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; - -namespace Neo.IO.Caching -{ - internal class FIFOSet : IEnumerable where T : IEquatable - { - private readonly int maxCapacity; - private readonly int removeCount; - private readonly OrderedDictionary dictionary; - - public FIFOSet(int maxCapacity, decimal batchSize = 0.1m) - { - if (maxCapacity <= 0) throw new ArgumentOutOfRangeException(nameof(maxCapacity)); - if (batchSize <= 0 || batchSize > 1) throw new ArgumentOutOfRangeException(nameof(batchSize)); - - this.maxCapacity = maxCapacity; - this.removeCount = Math.Max((int)(maxCapacity * batchSize), 1); - this.dictionary = new OrderedDictionary(maxCapacity); - } - - public bool Add(T item) - { - if (dictionary.Contains(item)) return false; - if (dictionary.Count >= maxCapacity) - { - if (removeCount == maxCapacity) - { - dictionary.Clear(); - } - else - { - for (int i = 0; i < removeCount; i++) - dictionary.RemoveAt(0); - } - } - dictionary.Add(item, null); - return true; - } - - public bool Contains(T item) - { - return dictionary.Contains(item); - } - - public void ExceptWith(IEnumerable hashes) - { - foreach (var hash in hashes) - { - dictionary.Remove(hash); - } - } - - public IEnumerator GetEnumerator() - { - var entries = dictionary.Keys.Cast().ToArray(); - foreach (var entry in entries) yield return entry; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; + +namespace Neo.IO.Caching +{ + internal class FIFOSet : IEnumerable where T : IEquatable + { + private readonly int maxCapacity; + private readonly int removeCount; + private readonly OrderedDictionary dictionary; + + public FIFOSet(int maxCapacity, decimal batchSize = 0.1m) + { + if (maxCapacity <= 0) throw new ArgumentOutOfRangeException(nameof(maxCapacity)); + if (batchSize <= 0 || batchSize > 1) throw new ArgumentOutOfRangeException(nameof(batchSize)); + + this.maxCapacity = maxCapacity; + this.removeCount = Math.Max((int)(maxCapacity * batchSize), 1); + this.dictionary = new OrderedDictionary(maxCapacity); + } + + public bool Add(T item) + { + if (dictionary.Contains(item)) return false; + if (dictionary.Count >= maxCapacity) + { + if (removeCount == maxCapacity) + { + dictionary.Clear(); + } + else + { + for (int i = 0; i < removeCount; i++) + dictionary.RemoveAt(0); + } + } + dictionary.Add(item, null); + return true; + } + + public bool Contains(T item) + { + return dictionary.Contains(item); + } + + public void ExceptWith(IEnumerable hashes) + { + foreach (var hash in hashes) + { + dictionary.Remove(hash); + } + } + + public IEnumerator GetEnumerator() + { + var entries = dictionary.Keys.Cast().ToArray(); + foreach (var entry in entries) yield return entry; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/neo/IO/Caching/MetaDataCache.cs b/neo/IO/Caching/MetaDataCache.cs index fa3fed6215..f0c40cc59a 100644 --- a/neo/IO/Caching/MetaDataCache.cs +++ b/neo/IO/Caching/MetaDataCache.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.IO.Caching { diff --git a/neo/IO/Caching/OrderedDictionary.cs b/neo/IO/Caching/OrderedDictionary.cs index bf12785406..efc85b6305 100644 --- a/neo/IO/Caching/OrderedDictionary.cs +++ b/neo/IO/Caching/OrderedDictionary.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; diff --git a/neo/IO/Caching/ReflectionCache.cs b/neo/IO/Caching/ReflectionCache.cs index 800561c6d9..705366e2fb 100644 --- a/neo/IO/Caching/ReflectionCache.cs +++ b/neo/IO/Caching/ReflectionCache.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; diff --git a/neo/IO/Caching/ReflectionCacheAttribute.cs b/neo/IO/Caching/ReflectionCacheAttribute.cs index e1c1200278..ba5fd4e1bc 100644 --- a/neo/IO/Caching/ReflectionCacheAttribute.cs +++ b/neo/IO/Caching/ReflectionCacheAttribute.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.IO.Caching { diff --git a/neo/IO/Caching/RelayCache.cs b/neo/IO/Caching/RelayCache.cs index ec6e35bdce..0bc2884eda 100644 --- a/neo/IO/Caching/RelayCache.cs +++ b/neo/IO/Caching/RelayCache.cs @@ -1,4 +1,4 @@ -using Neo.Network.P2P.Payloads; +using Neo.Network.P2P.Payloads; namespace Neo.IO.Caching { diff --git a/neo/IO/Caching/TrackState.cs b/neo/IO/Caching/TrackState.cs index aa144aab34..cba7daec0c 100644 --- a/neo/IO/Caching/TrackState.cs +++ b/neo/IO/Caching/TrackState.cs @@ -1,4 +1,4 @@ -namespace Neo.IO.Caching +namespace Neo.IO.Caching { public enum TrackState : byte { diff --git a/neo/IO/Data/LevelDB/DB.cs b/neo/IO/Data/LevelDB/DB.cs index 559a109594..04e8f50e8f 100644 --- a/neo/IO/Data/LevelDB/DB.cs +++ b/neo/IO/Data/LevelDB/DB.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.IO.Data.LevelDB { diff --git a/neo/IO/Data/LevelDB/Helper.cs b/neo/IO/Data/LevelDB/Helper.cs index 2b58690db0..d4c451f574 100644 --- a/neo/IO/Data/LevelDB/Helper.cs +++ b/neo/IO/Data/LevelDB/Helper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; diff --git a/neo/IO/Data/LevelDB/Iterator.cs b/neo/IO/Data/LevelDB/Iterator.cs index 11e72faf63..b3a6a0bfe0 100644 --- a/neo/IO/Data/LevelDB/Iterator.cs +++ b/neo/IO/Data/LevelDB/Iterator.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.IO.Data.LevelDB { diff --git a/neo/IO/Data/LevelDB/LevelDBException.cs b/neo/IO/Data/LevelDB/LevelDBException.cs index 0fa0578c56..8804f1f7f2 100644 --- a/neo/IO/Data/LevelDB/LevelDBException.cs +++ b/neo/IO/Data/LevelDB/LevelDBException.cs @@ -1,4 +1,4 @@ -using System.Data.Common; +using System.Data.Common; namespace Neo.IO.Data.LevelDB { diff --git a/neo/IO/Data/LevelDB/Native.cs b/neo/IO/Data/LevelDB/Native.cs index fc3ccd6387..6a19ef4cfe 100644 --- a/neo/IO/Data/LevelDB/Native.cs +++ b/neo/IO/Data/LevelDB/Native.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Runtime.InteropServices; diff --git a/neo/IO/Data/LevelDB/Options.cs b/neo/IO/Data/LevelDB/Options.cs index 676627cb08..53dd6e488b 100644 --- a/neo/IO/Data/LevelDB/Options.cs +++ b/neo/IO/Data/LevelDB/Options.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.IO.Data.LevelDB { diff --git a/neo/IO/Data/LevelDB/ReadOptions.cs b/neo/IO/Data/LevelDB/ReadOptions.cs index d27f0d7b59..9c198cfba1 100644 --- a/neo/IO/Data/LevelDB/ReadOptions.cs +++ b/neo/IO/Data/LevelDB/ReadOptions.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.IO.Data.LevelDB { diff --git a/neo/IO/Data/LevelDB/Slice.cs b/neo/IO/Data/LevelDB/Slice.cs index 95da8c6a44..1f9d927783 100644 --- a/neo/IO/Data/LevelDB/Slice.cs +++ b/neo/IO/Data/LevelDB/Slice.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using System; using System.Linq; using System.Runtime.InteropServices; diff --git a/neo/IO/Data/LevelDB/SliceBuilder.cs b/neo/IO/Data/LevelDB/SliceBuilder.cs index f00ce20454..d5888c6b5e 100644 --- a/neo/IO/Data/LevelDB/SliceBuilder.cs +++ b/neo/IO/Data/LevelDB/SliceBuilder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Text; diff --git a/neo/IO/Data/LevelDB/Snapshot.cs b/neo/IO/Data/LevelDB/Snapshot.cs index 89a89cb55a..d651098388 100644 --- a/neo/IO/Data/LevelDB/Snapshot.cs +++ b/neo/IO/Data/LevelDB/Snapshot.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.IO.Data.LevelDB { diff --git a/neo/IO/Data/LevelDB/WriteBatch.cs b/neo/IO/Data/LevelDB/WriteBatch.cs index eaa3e08bd6..b3a9782108 100644 --- a/neo/IO/Data/LevelDB/WriteBatch.cs +++ b/neo/IO/Data/LevelDB/WriteBatch.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.IO.Data.LevelDB { diff --git a/neo/IO/Data/LevelDB/WriteOptions.cs b/neo/IO/Data/LevelDB/WriteOptions.cs index 8a74fb5340..8d120c3997 100644 --- a/neo/IO/Data/LevelDB/WriteOptions.cs +++ b/neo/IO/Data/LevelDB/WriteOptions.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.IO.Data.LevelDB { diff --git a/neo/IO/Helper.cs b/neo/IO/Helper.cs index a306e297d5..c985e676b2 100644 --- a/neo/IO/Helper.cs +++ b/neo/IO/Helper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/neo/IO/ICloneable.cs b/neo/IO/ICloneable.cs index 4df61c84b8..83b4d77725 100644 --- a/neo/IO/ICloneable.cs +++ b/neo/IO/ICloneable.cs @@ -1,4 +1,4 @@ -namespace Neo.IO +namespace Neo.IO { public interface ICloneable { diff --git a/neo/IO/ISerializable.cs b/neo/IO/ISerializable.cs index 28f1ec3e01..d03540deb2 100644 --- a/neo/IO/ISerializable.cs +++ b/neo/IO/ISerializable.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; namespace Neo.IO { diff --git a/neo/IO/Json/JArray.cs b/neo/IO/Json/JArray.cs index 3109b3b61c..6de623d29a 100644 --- a/neo/IO/Json/JArray.cs +++ b/neo/IO/Json/JArray.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.IO; diff --git a/neo/IO/Json/JBoolean.cs b/neo/IO/Json/JBoolean.cs index 83a9111422..6b7c7a34c4 100644 --- a/neo/IO/Json/JBoolean.cs +++ b/neo/IO/Json/JBoolean.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace Neo.IO.Json diff --git a/neo/IO/Json/JNumber.cs b/neo/IO/Json/JNumber.cs index 2a23dc3157..daac440094 100644 --- a/neo/IO/Json/JNumber.cs +++ b/neo/IO/Json/JNumber.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.IO; using System.Text; diff --git a/neo/IO/Json/JObject.cs b/neo/IO/Json/JObject.cs index 40aa18f5da..cf6f68cbc7 100644 --- a/neo/IO/Json/JObject.cs +++ b/neo/IO/Json/JObject.cs @@ -1,4 +1,4 @@ -using Neo.IO.Caching; +using Neo.IO.Caching; using System; using System.Collections.Generic; using System.IO; diff --git a/neo/IO/Json/JString.cs b/neo/IO/Json/JString.cs index 065e7b59f9..032470cf4e 100644 --- a/neo/IO/Json/JString.cs +++ b/neo/IO/Json/JString.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.IO; using System.Text; diff --git a/neo/IO/Wrappers/SerializableWrapper.cs b/neo/IO/Wrappers/SerializableWrapper.cs index 868c9bcba2..098e4b9c7a 100644 --- a/neo/IO/Wrappers/SerializableWrapper.cs +++ b/neo/IO/Wrappers/SerializableWrapper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace Neo.IO.Wrappers diff --git a/neo/IO/Wrappers/UInt32Wrapper.cs b/neo/IO/Wrappers/UInt32Wrapper.cs index fa8406d50a..6e1fb71da1 100644 --- a/neo/IO/Wrappers/UInt32Wrapper.cs +++ b/neo/IO/Wrappers/UInt32Wrapper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace Neo.IO.Wrappers diff --git a/neo/Ledger/Blockchain.ApplicationExecuted.cs b/neo/Ledger/Blockchain.ApplicationExecuted.cs index 2f5d74fcb8..1bcc65563f 100644 --- a/neo/Ledger/Blockchain.ApplicationExecuted.cs +++ b/neo/Ledger/Blockchain.ApplicationExecuted.cs @@ -1,4 +1,4 @@ -using Neo.Network.P2P.Payloads; +using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.VM; using System.Linq; diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 7d0b72402d..db879c0732 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Akka.Configuration; using Neo.Cryptography.ECC; using Neo.IO; diff --git a/neo/Ledger/ContractState.cs b/neo/Ledger/ContractState.cs index da02db912f..f6c641d7d5 100644 --- a/neo/Ledger/ContractState.cs +++ b/neo/Ledger/ContractState.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.IO.Json; using Neo.SmartContract; using Neo.SmartContract.Manifest; diff --git a/neo/Ledger/HashIndexState.cs b/neo/Ledger/HashIndexState.cs index f83aefd34f..04f7b0bd4a 100644 --- a/neo/Ledger/HashIndexState.cs +++ b/neo/Ledger/HashIndexState.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System.IO; namespace Neo.Ledger diff --git a/neo/Ledger/HeaderHashList.cs b/neo/Ledger/HeaderHashList.cs index c9e9878235..928a26e5d2 100644 --- a/neo/Ledger/HeaderHashList.cs +++ b/neo/Ledger/HeaderHashList.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System.IO; namespace Neo.Ledger diff --git a/neo/Ledger/RelayResultReason.cs b/neo/Ledger/RelayResultReason.cs index e698d0ea34..7bf92afac6 100644 --- a/neo/Ledger/RelayResultReason.cs +++ b/neo/Ledger/RelayResultReason.cs @@ -1,4 +1,4 @@ -namespace Neo.Ledger +namespace Neo.Ledger { public enum RelayResultReason : byte { diff --git a/neo/Ledger/StorageFlags.cs b/neo/Ledger/StorageFlags.cs index a0d7034c1d..cace3c97ef 100644 --- a/neo/Ledger/StorageFlags.cs +++ b/neo/Ledger/StorageFlags.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.Ledger { diff --git a/neo/Ledger/StorageItem.cs b/neo/Ledger/StorageItem.cs index 37a69dba2d..49ea93d8a1 100644 --- a/neo/Ledger/StorageItem.cs +++ b/neo/Ledger/StorageItem.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System.IO; namespace Neo.Ledger diff --git a/neo/Ledger/StorageKey.cs b/neo/Ledger/StorageKey.cs index 187a895aa3..c214260c21 100644 --- a/neo/Ledger/StorageKey.cs +++ b/neo/Ledger/StorageKey.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.IO; using System; using System.IO; diff --git a/neo/Ledger/TransactionState.cs b/neo/Ledger/TransactionState.cs index c493bd631e..c8a479424a 100644 --- a/neo/Ledger/TransactionState.cs +++ b/neo/Ledger/TransactionState.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.VM; using System.IO; diff --git a/neo/Ledger/TrimmedBlock.cs b/neo/Ledger/TrimmedBlock.cs index 614ee89ac1..2a6f84672b 100644 --- a/neo/Ledger/TrimmedBlock.cs +++ b/neo/Ledger/TrimmedBlock.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.IO.Caching; using Neo.IO.Json; using Neo.Network.P2P.Payloads; diff --git a/neo/NeoSystem.cs b/neo/NeoSystem.cs index f53d6efa76..867d4e4201 100644 --- a/neo/NeoSystem.cs +++ b/neo/NeoSystem.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Neo.Consensus; using Neo.Ledger; using Neo.Network.P2P; diff --git a/neo/Network/P2P/Capabilities/FullNodeCapability.cs b/neo/Network/P2P/Capabilities/FullNodeCapability.cs index 7c2ae75845..ccaabbdb5a 100644 --- a/neo/Network/P2P/Capabilities/FullNodeCapability.cs +++ b/neo/Network/P2P/Capabilities/FullNodeCapability.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; namespace Neo.Network.P2P.Capabilities { diff --git a/neo/Network/P2P/Capabilities/NodeCapability.cs b/neo/Network/P2P/Capabilities/NodeCapability.cs index 1160dad912..034afb6be8 100644 --- a/neo/Network/P2P/Capabilities/NodeCapability.cs +++ b/neo/Network/P2P/Capabilities/NodeCapability.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System; using System.IO; diff --git a/neo/Network/P2P/Capabilities/NodeCapabilityType.cs b/neo/Network/P2P/Capabilities/NodeCapabilityType.cs index e8eb71850c..5ea0594d7b 100644 --- a/neo/Network/P2P/Capabilities/NodeCapabilityType.cs +++ b/neo/Network/P2P/Capabilities/NodeCapabilityType.cs @@ -1,4 +1,4 @@ -namespace Neo.Network.P2P.Capabilities +namespace Neo.Network.P2P.Capabilities { public enum NodeCapabilityType : byte { diff --git a/neo/Network/P2P/Capabilities/ServerCapability.cs b/neo/Network/P2P/Capabilities/ServerCapability.cs index a27573d438..0ce31e130e 100644 --- a/neo/Network/P2P/Capabilities/ServerCapability.cs +++ b/neo/Network/P2P/Capabilities/ServerCapability.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace Neo.Network.P2P.Capabilities diff --git a/neo/Network/P2P/ChannelsConfig.cs b/neo/Network/P2P/ChannelsConfig.cs index fe09c54996..2882876bd2 100644 --- a/neo/Network/P2P/ChannelsConfig.cs +++ b/neo/Network/P2P/ChannelsConfig.cs @@ -1,4 +1,4 @@ -using System.Net; +using System.Net; namespace Neo.Network.P2P { diff --git a/neo/Network/P2P/Connection.cs b/neo/Network/P2P/Connection.cs index d741f1d0ee..5a00a588ef 100644 --- a/neo/Network/P2P/Connection.cs +++ b/neo/Network/P2P/Connection.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Akka.IO; using System; using System.Net; diff --git a/neo/Network/P2P/Helper.cs b/neo/Network/P2P/Helper.cs index 433725c45b..e3e7cece1b 100644 --- a/neo/Network/P2P/Helper.cs +++ b/neo/Network/P2P/Helper.cs @@ -1,4 +1,4 @@ -using K4os.Compression.LZ4; +using K4os.Compression.LZ4; using Neo.Network.P2P.Payloads; using System; using System.Buffers; diff --git a/neo/Network/P2P/LocalNode.cs b/neo/Network/P2P/LocalNode.cs index 6f30ed50cb..79ad7fb096 100644 --- a/neo/Network/P2P/LocalNode.cs +++ b/neo/Network/P2P/LocalNode.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; diff --git a/neo/Network/P2P/Message.cs b/neo/Network/P2P/Message.cs index 5be16b2be0..61ee4493b4 100644 --- a/neo/Network/P2P/Message.cs +++ b/neo/Network/P2P/Message.cs @@ -1,4 +1,4 @@ -using Akka.IO; +using Akka.IO; using Neo.Cryptography; using Neo.IO; using Neo.Network.P2P.Payloads; diff --git a/neo/Network/P2P/MessageCommand.cs b/neo/Network/P2P/MessageCommand.cs index ce0527e136..ed8dc6b96b 100644 --- a/neo/Network/P2P/MessageCommand.cs +++ b/neo/Network/P2P/MessageCommand.cs @@ -1,4 +1,4 @@ -namespace Neo.Network.P2P +namespace Neo.Network.P2P { public enum MessageCommand : byte { diff --git a/neo/Network/P2P/MessageFlags.cs b/neo/Network/P2P/MessageFlags.cs index 9af64e5fbc..4e8a34c1a7 100644 --- a/neo/Network/P2P/MessageFlags.cs +++ b/neo/Network/P2P/MessageFlags.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.Network.P2P { diff --git a/neo/Network/P2P/Payloads/AddrPayload.cs b/neo/Network/P2P/Payloads/AddrPayload.cs index 65337f0869..0a8f09c71e 100644 --- a/neo/Network/P2P/Payloads/AddrPayload.cs +++ b/neo/Network/P2P/Payloads/AddrPayload.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System; using System.IO; diff --git a/neo/Network/P2P/Payloads/Block.cs b/neo/Network/P2P/Payloads/Block.cs index 75e5d7c424..d339b536a0 100644 --- a/neo/Network/P2P/Payloads/Block.cs +++ b/neo/Network/P2P/Payloads/Block.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; using Neo.Ledger; diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 20a0c98211..90b1531cd0 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; using Neo.Persistence; diff --git a/neo/Network/P2P/Payloads/ConsensusData.cs b/neo/Network/P2P/Payloads/ConsensusData.cs index 622c42e464..48d3d14ac8 100644 --- a/neo/Network/P2P/Payloads/ConsensusData.cs +++ b/neo/Network/P2P/Payloads/ConsensusData.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; using Neo.Ledger; diff --git a/neo/Network/P2P/Payloads/ConsensusPayload.cs b/neo/Network/P2P/Payloads/ConsensusPayload.cs index a4c6e86ee6..062e5d9830 100644 --- a/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ b/neo/Network/P2P/Payloads/ConsensusPayload.cs @@ -1,4 +1,4 @@ -using Neo.Consensus; +using Neo.Consensus; using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; diff --git a/neo/Network/P2P/Payloads/Cosigner.cs b/neo/Network/P2P/Payloads/Cosigner.cs index 2d1f42039c..99672ecf4d 100644 --- a/neo/Network/P2P/Payloads/Cosigner.cs +++ b/neo/Network/P2P/Payloads/Cosigner.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography.ECC; +using Neo.Cryptography.ECC; using Neo.IO; using Neo.IO.Json; using System; diff --git a/neo/Network/P2P/Payloads/FilterAddPayload.cs b/neo/Network/P2P/Payloads/FilterAddPayload.cs index 71bddea5dc..3a7a4a9e6f 100644 --- a/neo/Network/P2P/Payloads/FilterAddPayload.cs +++ b/neo/Network/P2P/Payloads/FilterAddPayload.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System.IO; namespace Neo.Network.P2P.Payloads diff --git a/neo/Network/P2P/Payloads/FilterLoadPayload.cs b/neo/Network/P2P/Payloads/FilterLoadPayload.cs index 5d337c8dac..dfba728fc0 100644 --- a/neo/Network/P2P/Payloads/FilterLoadPayload.cs +++ b/neo/Network/P2P/Payloads/FilterLoadPayload.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.IO; using System; using System.IO; diff --git a/neo/Network/P2P/Payloads/GetBlocksPayload.cs b/neo/Network/P2P/Payloads/GetBlocksPayload.cs index f7e39927f1..eccd66ed19 100644 --- a/neo/Network/P2P/Payloads/GetBlocksPayload.cs +++ b/neo/Network/P2P/Payloads/GetBlocksPayload.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System; using System.IO; diff --git a/neo/Network/P2P/Payloads/Header.cs b/neo/Network/P2P/Payloads/Header.cs index abd7fbd370..eaef0dc160 100644 --- a/neo/Network/P2P/Payloads/Header.cs +++ b/neo/Network/P2P/Payloads/Header.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using Neo.Ledger; using Neo.Wallets; using System; diff --git a/neo/Network/P2P/Payloads/HeadersPayload.cs b/neo/Network/P2P/Payloads/HeadersPayload.cs index 54948a142d..3b13405d24 100644 --- a/neo/Network/P2P/Payloads/HeadersPayload.cs +++ b/neo/Network/P2P/Payloads/HeadersPayload.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/neo/Network/P2P/Payloads/IInventory.cs b/neo/Network/P2P/Payloads/IInventory.cs index dcf3114a8b..26b62346d9 100644 --- a/neo/Network/P2P/Payloads/IInventory.cs +++ b/neo/Network/P2P/Payloads/IInventory.cs @@ -1,4 +1,4 @@ -using Neo.Persistence; +using Neo.Persistence; namespace Neo.Network.P2P.Payloads { diff --git a/neo/Network/P2P/Payloads/IVerifiable.cs b/neo/Network/P2P/Payloads/IVerifiable.cs index 50651ad9ff..8540d8a626 100644 --- a/neo/Network/P2P/Payloads/IVerifiable.cs +++ b/neo/Network/P2P/Payloads/IVerifiable.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.Persistence; using System.IO; diff --git a/neo/Network/P2P/Payloads/InvPayload.cs b/neo/Network/P2P/Payloads/InvPayload.cs index a3bc82a4d9..613d280234 100644 --- a/neo/Network/P2P/Payloads/InvPayload.cs +++ b/neo/Network/P2P/Payloads/InvPayload.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System; using System.Collections.Generic; using System.IO; diff --git a/neo/Network/P2P/Payloads/InventoryType.cs b/neo/Network/P2P/Payloads/InventoryType.cs index 58ad12471b..775eed958f 100644 --- a/neo/Network/P2P/Payloads/InventoryType.cs +++ b/neo/Network/P2P/Payloads/InventoryType.cs @@ -1,4 +1,4 @@ -namespace Neo.Network.P2P.Payloads +namespace Neo.Network.P2P.Payloads { public enum InventoryType : byte { diff --git a/neo/Network/P2P/Payloads/MerkleBlockPayload.cs b/neo/Network/P2P/Payloads/MerkleBlockPayload.cs index 11b9fb3a6e..0d00434cd4 100644 --- a/neo/Network/P2P/Payloads/MerkleBlockPayload.cs +++ b/neo/Network/P2P/Payloads/MerkleBlockPayload.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.IO; using System.Collections; using System.IO; diff --git a/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs b/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs index 01508524a5..6fcda2f809 100644 --- a/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs +++ b/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.Network.P2P.Capabilities; using System; using System.IO; diff --git a/neo/Network/P2P/Payloads/PingPayload.cs b/neo/Network/P2P/Payloads/PingPayload.cs index 107dd501d5..680de9dd0a 100644 --- a/neo/Network/P2P/Payloads/PingPayload.cs +++ b/neo/Network/P2P/Payloads/PingPayload.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System; using System.IO; diff --git a/neo/Network/P2P/Payloads/TransactionAttribute.cs b/neo/Network/P2P/Payloads/TransactionAttribute.cs index 99d1671ebd..5adc34db8e 100644 --- a/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.IO.Json; using System; using System.IO; diff --git a/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs b/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs index 89c1a1cec2..9bf5cc204c 100644 --- a/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs +++ b/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs @@ -1,4 +1,4 @@ -namespace Neo.Network.P2P.Payloads +namespace Neo.Network.P2P.Payloads { public enum TransactionAttributeUsage : byte { diff --git a/neo/Network/P2P/Payloads/VersionPayload.cs b/neo/Network/P2P/Payloads/VersionPayload.cs index 754000e062..16c401b7db 100644 --- a/neo/Network/P2P/Payloads/VersionPayload.cs +++ b/neo/Network/P2P/Payloads/VersionPayload.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.Network.P2P.Capabilities; using System; using System.IO; diff --git a/neo/Network/P2P/Payloads/Witness.cs b/neo/Network/P2P/Payloads/Witness.cs index 52a2186ee8..227d810a39 100644 --- a/neo/Network/P2P/Payloads/Witness.cs +++ b/neo/Network/P2P/Payloads/Witness.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.IO.Json; using Neo.SmartContract; using Neo.VM; diff --git a/neo/Network/P2P/Payloads/WitnessScope.cs b/neo/Network/P2P/Payloads/WitnessScope.cs index e67706cbbf..f35e550a34 100644 --- a/neo/Network/P2P/Payloads/WitnessScope.cs +++ b/neo/Network/P2P/Payloads/WitnessScope.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.Network.P2P.Payloads { diff --git a/neo/Network/P2P/Peer.cs b/neo/Network/P2P/Peer.cs index eb21095685..0cbd6cbaff 100644 --- a/neo/Network/P2P/Peer.cs +++ b/neo/Network/P2P/Peer.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Akka.IO; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; diff --git a/neo/Network/P2P/ProtocolHandler.cs b/neo/Network/P2P/ProtocolHandler.cs index 63ecbe3318..aa7f0e06f2 100644 --- a/neo/Network/P2P/ProtocolHandler.cs +++ b/neo/Network/P2P/ProtocolHandler.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Akka.Configuration; using Neo.Cryptography; using Neo.IO; diff --git a/neo/Network/P2P/RemoteNode.cs b/neo/Network/P2P/RemoteNode.cs index 7700c896b2..ec2a16e047 100644 --- a/neo/Network/P2P/RemoteNode.cs +++ b/neo/Network/P2P/RemoteNode.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Akka.Configuration; using Akka.IO; using Neo.Cryptography; diff --git a/neo/Network/P2P/TaskManager.cs b/neo/Network/P2P/TaskManager.cs index cd96547df8..2c0f7ae07b 100644 --- a/neo/Network/P2P/TaskManager.cs +++ b/neo/Network/P2P/TaskManager.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Akka.Configuration; using Neo.IO.Actors; using Neo.IO.Caching; diff --git a/neo/Network/P2P/TaskSession.cs b/neo/Network/P2P/TaskSession.cs index 5617e0efa1..a1cf8a0ffc 100644 --- a/neo/Network/P2P/TaskSession.cs +++ b/neo/Network/P2P/TaskSession.cs @@ -1,4 +1,4 @@ -using Akka.Actor; +using Akka.Actor; using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; using System; diff --git a/neo/Network/RPC/Models/RpcBlock.cs b/neo/Network/RPC/Models/RpcBlock.cs index 0d49b3b53a..f71af51168 100644 --- a/neo/Network/RPC/Models/RpcBlock.cs +++ b/neo/Network/RPC/Models/RpcBlock.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using Neo.Network.P2P.Payloads; namespace Neo.Network.RPC.Models diff --git a/neo/Network/RPC/Models/RpcBlockHeader.cs b/neo/Network/RPC/Models/RpcBlockHeader.cs index 250acfd818..2b9293ecaf 100644 --- a/neo/Network/RPC/Models/RpcBlockHeader.cs +++ b/neo/Network/RPC/Models/RpcBlockHeader.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using Neo.Network.P2P.Payloads; namespace Neo.Network.RPC.Models diff --git a/neo/Network/RPC/Models/RpcInvokeResult.cs b/neo/Network/RPC/Models/RpcInvokeResult.cs index 9df9492e2b..73f2c6a2af 100644 --- a/neo/Network/RPC/Models/RpcInvokeResult.cs +++ b/neo/Network/RPC/Models/RpcInvokeResult.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using Newtonsoft.Json; using System.Linq; diff --git a/neo/Network/RPC/Models/RpcNep5Balances.cs b/neo/Network/RPC/Models/RpcNep5Balances.cs index f29f0c5564..b471ab4545 100644 --- a/neo/Network/RPC/Models/RpcNep5Balances.cs +++ b/neo/Network/RPC/Models/RpcNep5Balances.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System.Linq; using System.Numerics; diff --git a/neo/Network/RPC/Models/RpcPeers.cs b/neo/Network/RPC/Models/RpcPeers.cs index 5caa99a66a..fac73842de 100644 --- a/neo/Network/RPC/Models/RpcPeers.cs +++ b/neo/Network/RPC/Models/RpcPeers.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System.Linq; namespace Neo.Network.RPC.Models diff --git a/neo/Network/RPC/Models/RpcPlugin.cs b/neo/Network/RPC/Models/RpcPlugin.cs index ae778ee87a..db03f70eb3 100644 --- a/neo/Network/RPC/Models/RpcPlugin.cs +++ b/neo/Network/RPC/Models/RpcPlugin.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System.Linq; namespace Neo.Network.RPC.Models diff --git a/neo/Network/RPC/Models/RpcRawMemPool.cs b/neo/Network/RPC/Models/RpcRawMemPool.cs index df439bf3a6..c5ebd63419 100644 --- a/neo/Network/RPC/Models/RpcRawMemPool.cs +++ b/neo/Network/RPC/Models/RpcRawMemPool.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System.Linq; namespace Neo.Network.RPC.Models diff --git a/neo/Network/RPC/Models/RpcRequest.cs b/neo/Network/RPC/Models/RpcRequest.cs index 010e3037b8..1970adedbf 100644 --- a/neo/Network/RPC/Models/RpcRequest.cs +++ b/neo/Network/RPC/Models/RpcRequest.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System.Linq; namespace Neo.Network.RPC.Models diff --git a/neo/Network/RPC/Models/RpcResponse.cs b/neo/Network/RPC/Models/RpcResponse.cs index ff20200519..e4ebcaed1b 100644 --- a/neo/Network/RPC/Models/RpcResponse.cs +++ b/neo/Network/RPC/Models/RpcResponse.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; namespace Neo.Network.RPC.Models { diff --git a/neo/Network/RPC/Models/RpcTransaction.cs b/neo/Network/RPC/Models/RpcTransaction.cs index 3e45830ae6..f41c04710a 100644 --- a/neo/Network/RPC/Models/RpcTransaction.cs +++ b/neo/Network/RPC/Models/RpcTransaction.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using Neo.Network.P2P.Payloads; namespace Neo.Network.RPC.Models diff --git a/neo/Network/RPC/Models/RpcValidateAddressResult.cs b/neo/Network/RPC/Models/RpcValidateAddressResult.cs index f33467356e..5e0a704797 100644 --- a/neo/Network/RPC/Models/RpcValidateAddressResult.cs +++ b/neo/Network/RPC/Models/RpcValidateAddressResult.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; namespace Neo.Network.RPC.Models { diff --git a/neo/Network/RPC/Models/RpcValidator.cs b/neo/Network/RPC/Models/RpcValidator.cs index b694a6dfcd..f3116ed2e4 100644 --- a/neo/Network/RPC/Models/RpcValidator.cs +++ b/neo/Network/RPC/Models/RpcValidator.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System.Numerics; namespace Neo.Network.RPC.Models diff --git a/neo/Network/RPC/Models/RpcVersion.cs b/neo/Network/RPC/Models/RpcVersion.cs index 02f4dc3407..8163875b64 100644 --- a/neo/Network/RPC/Models/RpcVersion.cs +++ b/neo/Network/RPC/Models/RpcVersion.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; namespace Neo.Network.RPC.Models { diff --git a/neo/Network/RPC/RpcClient.cs b/neo/Network/RPC/RpcClient.cs index 4b968163c7..69352b8cfd 100644 --- a/neo/Network/RPC/RpcClient.cs +++ b/neo/Network/RPC/RpcClient.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using Neo.Ledger; using Neo.Network.RPC.Models; using System; diff --git a/neo/Network/RPC/RpcException.cs b/neo/Network/RPC/RpcException.cs index 5a2120f083..b5030750ca 100644 --- a/neo/Network/RPC/RpcException.cs +++ b/neo/Network/RPC/RpcException.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.Network.RPC { diff --git a/neo/Persistence/CloneSnapshot.cs b/neo/Persistence/CloneSnapshot.cs index 9f86f2a1bf..0b79bd2739 100644 --- a/neo/Persistence/CloneSnapshot.cs +++ b/neo/Persistence/CloneSnapshot.cs @@ -1,4 +1,4 @@ -using Neo.IO.Caching; +using Neo.IO.Caching; using Neo.IO.Wrappers; using Neo.Ledger; diff --git a/neo/Persistence/Helper.cs b/neo/Persistence/Helper.cs index d884e0da6b..7f412e47d3 100644 --- a/neo/Persistence/Helper.cs +++ b/neo/Persistence/Helper.cs @@ -1,4 +1,4 @@ -using Neo.Ledger; +using Neo.Ledger; using Neo.Network.P2P.Payloads; namespace Neo.Persistence diff --git a/neo/Persistence/IPersistence.cs b/neo/Persistence/IPersistence.cs index 34f1169f9b..0632b5d0fa 100644 --- a/neo/Persistence/IPersistence.cs +++ b/neo/Persistence/IPersistence.cs @@ -1,4 +1,4 @@ -using Neo.IO.Caching; +using Neo.IO.Caching; using Neo.IO.Wrappers; using Neo.Ledger; diff --git a/neo/Persistence/LevelDB/DbCache.cs b/neo/Persistence/LevelDB/DbCache.cs index 86ff7a39ea..ae55b31326 100644 --- a/neo/Persistence/LevelDB/DbCache.cs +++ b/neo/Persistence/LevelDB/DbCache.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.IO.Caching; using Neo.IO.Data.LevelDB; using System; diff --git a/neo/Persistence/LevelDB/DbMetaDataCache.cs b/neo/Persistence/LevelDB/DbMetaDataCache.cs index e2c03ca2fd..0163d84e39 100644 --- a/neo/Persistence/LevelDB/DbMetaDataCache.cs +++ b/neo/Persistence/LevelDB/DbMetaDataCache.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.IO.Caching; using Neo.IO.Data.LevelDB; using System; diff --git a/neo/Persistence/LevelDB/DbSnapshot.cs b/neo/Persistence/LevelDB/DbSnapshot.cs index f7ade52ad8..365584fa7c 100644 --- a/neo/Persistence/LevelDB/DbSnapshot.cs +++ b/neo/Persistence/LevelDB/DbSnapshot.cs @@ -1,4 +1,4 @@ -using Neo.IO.Caching; +using Neo.IO.Caching; using Neo.IO.Data.LevelDB; using Neo.IO.Wrappers; using Neo.Ledger; diff --git a/neo/Persistence/LevelDB/LevelDBStore.cs b/neo/Persistence/LevelDB/LevelDBStore.cs index 189f0cfbb5..bb6ac26698 100644 --- a/neo/Persistence/LevelDB/LevelDBStore.cs +++ b/neo/Persistence/LevelDB/LevelDBStore.cs @@ -1,4 +1,4 @@ -using Neo.IO.Caching; +using Neo.IO.Caching; using Neo.IO.Data.LevelDB; using Neo.IO.Wrappers; using Neo.Ledger; diff --git a/neo/Persistence/LevelDB/Prefixes.cs b/neo/Persistence/LevelDB/Prefixes.cs index 605e9f1451..f40359a55a 100644 --- a/neo/Persistence/LevelDB/Prefixes.cs +++ b/neo/Persistence/LevelDB/Prefixes.cs @@ -1,4 +1,4 @@ -namespace Neo.Persistence.LevelDB +namespace Neo.Persistence.LevelDB { internal static class Prefixes { diff --git a/neo/Persistence/Snapshot.cs b/neo/Persistence/Snapshot.cs index f058562200..1e900800b0 100644 --- a/neo/Persistence/Snapshot.cs +++ b/neo/Persistence/Snapshot.cs @@ -1,4 +1,4 @@ -using Neo.IO.Caching; +using Neo.IO.Caching; using Neo.IO.Wrappers; using Neo.Ledger; using Neo.Network.P2P.Payloads; diff --git a/neo/Persistence/Store.cs b/neo/Persistence/Store.cs index 7b5b3b6435..3dc7127f3a 100644 --- a/neo/Persistence/Store.cs +++ b/neo/Persistence/Store.cs @@ -1,4 +1,4 @@ -using Neo.IO.Caching; +using Neo.IO.Caching; using Neo.IO.Wrappers; using Neo.Ledger; diff --git a/neo/Plugins/ILogPlugin.cs b/neo/Plugins/ILogPlugin.cs index ff1052be2e..c0733f9143 100644 --- a/neo/Plugins/ILogPlugin.cs +++ b/neo/Plugins/ILogPlugin.cs @@ -1,4 +1,4 @@ -namespace Neo.Plugins +namespace Neo.Plugins { public interface ILogPlugin { diff --git a/neo/Plugins/IMemoryPoolTxObserverPlugin.cs b/neo/Plugins/IMemoryPoolTxObserverPlugin.cs index e596b9a7b5..3dd1cb3ef2 100644 --- a/neo/Plugins/IMemoryPoolTxObserverPlugin.cs +++ b/neo/Plugins/IMemoryPoolTxObserverPlugin.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Neo.Network.P2P.Payloads; namespace Neo.Plugins diff --git a/neo/Plugins/IP2PPlugin.cs b/neo/Plugins/IP2PPlugin.cs index 3029aaf849..e2043104fc 100644 --- a/neo/Plugins/IP2PPlugin.cs +++ b/neo/Plugins/IP2PPlugin.cs @@ -1,4 +1,4 @@ -using Neo.Network.P2P; +using Neo.Network.P2P; using Neo.Network.P2P.Payloads; namespace Neo.Plugins diff --git a/neo/Plugins/IPersistencePlugin.cs b/neo/Plugins/IPersistencePlugin.cs index af3cbe05b8..0f06ae51eb 100644 --- a/neo/Plugins/IPersistencePlugin.cs +++ b/neo/Plugins/IPersistencePlugin.cs @@ -1,4 +1,4 @@ -using System; +using System; using Neo.Persistence; using System.Collections.Generic; using static Neo.Ledger.Blockchain; diff --git a/neo/Plugins/IRpcPlugin.cs b/neo/Plugins/IRpcPlugin.cs index 19e5b71808..92e7a0fa48 100644 --- a/neo/Plugins/IRpcPlugin.cs +++ b/neo/Plugins/IRpcPlugin.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; using Neo.IO.Json; namespace Neo.Plugins diff --git a/neo/Plugins/LogLevel.cs b/neo/Plugins/LogLevel.cs index 72158c8aad..8f6e0b9df7 100644 --- a/neo/Plugins/LogLevel.cs +++ b/neo/Plugins/LogLevel.cs @@ -1,4 +1,4 @@ -namespace Neo.Plugins +namespace Neo.Plugins { public enum LogLevel : byte { diff --git a/neo/Plugins/MemoryPoolTxRemovalReason.cs b/neo/Plugins/MemoryPoolTxRemovalReason.cs index f7320dc60e..d08a37eba2 100644 --- a/neo/Plugins/MemoryPoolTxRemovalReason.cs +++ b/neo/Plugins/MemoryPoolTxRemovalReason.cs @@ -1,4 +1,4 @@ -namespace Neo.Plugins +namespace Neo.Plugins { public enum MemoryPoolTxRemovalReason : byte { diff --git a/neo/Plugins/Plugin.cs b/neo/Plugins/Plugin.cs index 72c7ce3b14..aa04b0778d 100644 --- a/neo/Plugins/Plugin.cs +++ b/neo/Plugins/Plugin.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using System; using System.Collections.Generic; using System.IO; diff --git a/neo/ProtocolSettings.cs b/neo/ProtocolSettings.cs index 381e56c8a7..18c915d614 100644 --- a/neo/ProtocolSettings.cs +++ b/neo/ProtocolSettings.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Configuration; using System; using System.Linq; using System.Threading; diff --git a/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index 9da0f6c75d..271a76f935 100644 --- a/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM; using System.Collections.Generic; namespace Neo.SmartContract diff --git a/neo/SmartContract/ApplicationEngine.cs b/neo/SmartContract/ApplicationEngine.cs index 45763ed30a..2eeeef52e0 100644 --- a/neo/SmartContract/ApplicationEngine.cs +++ b/neo/SmartContract/ApplicationEngine.cs @@ -49,16 +49,16 @@ private bool AddGas(long gas) return testMode || GasConsumed <= gas_amount; } - protected override void LoadContext(ExecutionContext context) - { - // Set default execution context state - - context.SetState(new ExecutionContextState() - { - ScriptHash = ((byte[])context.Script).ToScriptHash() - }); - - base.LoadContext(context); + protected override void LoadContext(ExecutionContext context) + { + // Set default execution context state + + context.SetState(new ExecutionContextState() + { + ScriptHash = ((byte[])context.Script).ToScriptHash() + }); + + base.LoadContext(context); } public override void Dispose() diff --git a/neo/SmartContract/ContainerPlaceholder.cs b/neo/SmartContract/ContainerPlaceholder.cs index 14e97ae848..a379ddd9d3 100644 --- a/neo/SmartContract/ContainerPlaceholder.cs +++ b/neo/SmartContract/ContainerPlaceholder.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM; using System; namespace Neo.SmartContract diff --git a/neo/SmartContract/Contract.cs b/neo/SmartContract/Contract.cs index 8d23997006..19fcceb7ca 100644 --- a/neo/SmartContract/Contract.cs +++ b/neo/SmartContract/Contract.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography.ECC; +using Neo.Cryptography.ECC; using Neo.VM; using Neo.Wallets; using System; diff --git a/neo/SmartContract/ContractParameter.cs b/neo/SmartContract/ContractParameter.cs index 5e78005e13..cae59229b8 100644 --- a/neo/SmartContract/ContractParameter.cs +++ b/neo/SmartContract/ContractParameter.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography.ECC; +using Neo.Cryptography.ECC; using Neo.IO.Json; using System; using System.Collections.Generic; diff --git a/neo/SmartContract/ContractParameterType.cs b/neo/SmartContract/ContractParameterType.cs index 67191852fa..748ff86654 100644 --- a/neo/SmartContract/ContractParameterType.cs +++ b/neo/SmartContract/ContractParameterType.cs @@ -1,4 +1,4 @@ -namespace Neo.SmartContract +namespace Neo.SmartContract { public enum ContractParameterType : byte { diff --git a/neo/SmartContract/ContractParametersContext.cs b/neo/SmartContract/ContractParametersContext.cs index 35a2c36898..8a91c936b8 100644 --- a/neo/SmartContract/ContractParametersContext.cs +++ b/neo/SmartContract/ContractParametersContext.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography.ECC; +using Neo.Cryptography.ECC; using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; @@ -72,14 +72,14 @@ public bool Completed } } - /// - /// Cache for public ScriptHashes field + /// + /// Cache for public ScriptHashes field /// private UInt160[] _ScriptHashes = null; - /// - /// ScriptHashes are the verifiable ScriptHashes from Verifiable element - /// Equivalent to: Verifiable.GetScriptHashesForVerifying(Blockchain.Singleton.GetSnapshot()) + /// + /// ScriptHashes are the verifiable ScriptHashes from Verifiable element + /// Equivalent to: Verifiable.GetScriptHashesForVerifying(Blockchain.Singleton.GetSnapshot()) /// public IReadOnlyList ScriptHashes { diff --git a/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs b/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs index 36e8c4fe97..2b75b1d46b 100644 --- a/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs +++ b/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM; namespace Neo.SmartContract.Enumerators { diff --git a/neo/SmartContract/Enumerators/IEnumerator.cs b/neo/SmartContract/Enumerators/IEnumerator.cs index 27e30b5e18..4d1f11fe65 100644 --- a/neo/SmartContract/Enumerators/IEnumerator.cs +++ b/neo/SmartContract/Enumerators/IEnumerator.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM; using System; namespace Neo.SmartContract.Enumerators diff --git a/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs b/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs index ef2e49d49f..d134eb884c 100644 --- a/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs +++ b/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs @@ -1,4 +1,4 @@ -using Neo.SmartContract.Iterators; +using Neo.SmartContract.Iterators; using Neo.VM; namespace Neo.SmartContract.Enumerators diff --git a/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs b/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs index fb69070d40..15e06f2223 100644 --- a/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs +++ b/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs @@ -1,4 +1,4 @@ -using Neo.SmartContract.Iterators; +using Neo.SmartContract.Iterators; using Neo.VM; namespace Neo.SmartContract.Enumerators diff --git a/neo/SmartContract/ExecutionContextState.cs b/neo/SmartContract/ExecutionContextState.cs index 7895d943a2..11e6f88a00 100644 --- a/neo/SmartContract/ExecutionContextState.cs +++ b/neo/SmartContract/ExecutionContextState.cs @@ -1,10 +1,10 @@ -namespace Neo.SmartContract -{ - public class ExecutionContextState - { - /// - /// Script hash - /// - public UInt160 ScriptHash { get; set; } - } +namespace Neo.SmartContract +{ + public class ExecutionContextState + { + /// + /// Script hash + /// + public UInt160 ScriptHash { get; set; } + } } \ No newline at end of file diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index 259e997b09..6a22249cd7 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/neo/SmartContract/InteropDescriptor.cs b/neo/SmartContract/InteropDescriptor.cs index c283e474de..6db599ea64 100644 --- a/neo/SmartContract/InteropDescriptor.cs +++ b/neo/SmartContract/InteropDescriptor.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM; using System; namespace Neo.SmartContract diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index 91b941649f..7f0d857fe9 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P; diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 9fbb5c6675..b4121d7a24 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; using Neo.Ledger; diff --git a/neo/SmartContract/Iterators/ArrayWrapper.cs b/neo/SmartContract/Iterators/ArrayWrapper.cs index 3284f1317f..883327bee0 100644 --- a/neo/SmartContract/Iterators/ArrayWrapper.cs +++ b/neo/SmartContract/Iterators/ArrayWrapper.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM; using System; using System.Collections.Generic; diff --git a/neo/SmartContract/Iterators/IIterator.cs b/neo/SmartContract/Iterators/IIterator.cs index ca67ae0331..3438286615 100644 --- a/neo/SmartContract/Iterators/IIterator.cs +++ b/neo/SmartContract/Iterators/IIterator.cs @@ -1,4 +1,4 @@ -using Neo.SmartContract.Enumerators; +using Neo.SmartContract.Enumerators; using Neo.VM; namespace Neo.SmartContract.Iterators diff --git a/neo/SmartContract/Iterators/MapWrapper.cs b/neo/SmartContract/Iterators/MapWrapper.cs index d42f940fac..343b946982 100644 --- a/neo/SmartContract/Iterators/MapWrapper.cs +++ b/neo/SmartContract/Iterators/MapWrapper.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM; using System.Collections.Generic; namespace Neo.SmartContract.Iterators diff --git a/neo/SmartContract/Iterators/StorageIterator.cs b/neo/SmartContract/Iterators/StorageIterator.cs index 2dd604d5d8..45c9d21ebd 100644 --- a/neo/SmartContract/Iterators/StorageIterator.cs +++ b/neo/SmartContract/Iterators/StorageIterator.cs @@ -1,4 +1,4 @@ -using Neo.Ledger; +using Neo.Ledger; using Neo.VM; using System.Collections.Generic; diff --git a/neo/SmartContract/JsonSerializer.cs b/neo/SmartContract/JsonSerializer.cs index d635c6c7b8..a4853fc39a 100644 --- a/neo/SmartContract/JsonSerializer.cs +++ b/neo/SmartContract/JsonSerializer.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using Neo.VM; using Neo.VM.Types; using System; diff --git a/neo/SmartContract/LogEventArgs.cs b/neo/SmartContract/LogEventArgs.cs index d124b6f9ab..8c007fe101 100644 --- a/neo/SmartContract/LogEventArgs.cs +++ b/neo/SmartContract/LogEventArgs.cs @@ -1,4 +1,4 @@ -using Neo.Network.P2P.Payloads; +using Neo.Network.P2P.Payloads; using System; namespace Neo.SmartContract diff --git a/neo/SmartContract/Manifest/ContractAbi.cs b/neo/SmartContract/Manifest/ContractAbi.cs index 033306f8e6..1d27ae4f1b 100644 --- a/neo/SmartContract/Manifest/ContractAbi.cs +++ b/neo/SmartContract/Manifest/ContractAbi.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System.Linq; namespace Neo.SmartContract.Manifest diff --git a/neo/SmartContract/Manifest/ContractEventDescriptor.cs b/neo/SmartContract/Manifest/ContractEventDescriptor.cs index b06b64f341..00748f9b09 100644 --- a/neo/SmartContract/Manifest/ContractEventDescriptor.cs +++ b/neo/SmartContract/Manifest/ContractEventDescriptor.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System.Linq; namespace Neo.SmartContract.Manifest diff --git a/neo/SmartContract/Manifest/ContractFeatures.cs b/neo/SmartContract/Manifest/ContractFeatures.cs index dd8336f695..9f94a9d3f1 100644 --- a/neo/SmartContract/Manifest/ContractFeatures.cs +++ b/neo/SmartContract/Manifest/ContractFeatures.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.SmartContract.Manifest { diff --git a/neo/SmartContract/Manifest/ContractGroup.cs b/neo/SmartContract/Manifest/ContractGroup.cs index fd73787a09..75359e782a 100644 --- a/neo/SmartContract/Manifest/ContractGroup.cs +++ b/neo/SmartContract/Manifest/ContractGroup.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO.Json; diff --git a/neo/SmartContract/Manifest/ContractManifest.cs b/neo/SmartContract/Manifest/ContractManifest.cs index 967a10e856..ebbcd61f2c 100644 --- a/neo/SmartContract/Manifest/ContractManifest.cs +++ b/neo/SmartContract/Manifest/ContractManifest.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.IO.Json; using System.IO; using System.Linq; diff --git a/neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/neo/SmartContract/Manifest/ContractMethodDescriptor.cs index c631aa0450..476ec546a6 100644 --- a/neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System; using System.Linq; diff --git a/neo/SmartContract/Manifest/ContractParameterDefinition.cs b/neo/SmartContract/Manifest/ContractParameterDefinition.cs index af07315a3a..3f184d6ba8 100644 --- a/neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System; namespace Neo.SmartContract.Manifest diff --git a/neo/SmartContract/Manifest/ContractPermission.cs b/neo/SmartContract/Manifest/ContractPermission.cs index b7d1d54bb9..890de9d200 100644 --- a/neo/SmartContract/Manifest/ContractPermission.cs +++ b/neo/SmartContract/Manifest/ContractPermission.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System; using System.Linq; diff --git a/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs b/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs index 7b64a66935..909ae27e42 100644 --- a/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs +++ b/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography.ECC; +using Neo.Cryptography.ECC; using Neo.IO.Json; using System; diff --git a/neo/SmartContract/Manifest/WildCardContainer.cs b/neo/SmartContract/Manifest/WildCardContainer.cs index aab6ba838e..12dbac60fe 100644 --- a/neo/SmartContract/Manifest/WildCardContainer.cs +++ b/neo/SmartContract/Manifest/WildCardContainer.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System; using System.Collections; using System.Collections.Generic; diff --git a/neo/SmartContract/Native/ContractMethodAttribute.cs b/neo/SmartContract/Native/ContractMethodAttribute.cs index 393737d92e..42b414eed6 100644 --- a/neo/SmartContract/Native/ContractMethodAttribute.cs +++ b/neo/SmartContract/Native/ContractMethodAttribute.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.SmartContract.Native { diff --git a/neo/SmartContract/Native/ContractMethodMetadata.cs b/neo/SmartContract/Native/ContractMethodMetadata.cs index f63344eec9..9267bb7db9 100644 --- a/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM; using System; using VMArray = Neo.VM.Types.Array; diff --git a/neo/SmartContract/Native/NativeContract.cs b/neo/SmartContract/Native/NativeContract.cs index 08971dcf6b..546743fc49 100644 --- a/neo/SmartContract/Native/NativeContract.cs +++ b/neo/SmartContract/Native/NativeContract.cs @@ -1,4 +1,4 @@ -#pragma warning disable IDE0060 +#pragma warning disable IDE0060 using Neo.IO; using Neo.Ledger; diff --git a/neo/SmartContract/Native/PolicyContract.cs b/neo/SmartContract/Native/PolicyContract.cs index c5820cfd15..3ffe8199e5 100644 --- a/neo/SmartContract/Native/PolicyContract.cs +++ b/neo/SmartContract/Native/PolicyContract.cs @@ -1,4 +1,4 @@ -#pragma warning disable IDE0051 +#pragma warning disable IDE0051 #pragma warning disable IDE0060 using Neo.IO; diff --git a/neo/SmartContract/Native/Tokens/GasToken.cs b/neo/SmartContract/Native/Tokens/GasToken.cs index fd39979670..7ef522dab7 100644 --- a/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/neo/SmartContract/Native/Tokens/GasToken.cs @@ -1,4 +1,4 @@ -#pragma warning disable IDE0051 +#pragma warning disable IDE0051 using Neo.Cryptography.ECC; using Neo.Ledger; diff --git a/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/neo/SmartContract/Native/Tokens/Nep5AccountState.cs index c775f044ec..aa62cf4bec 100644 --- a/neo/SmartContract/Native/Tokens/Nep5AccountState.cs +++ b/neo/SmartContract/Native/Tokens/Nep5AccountState.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM; using Neo.VM.Types; using System.Numerics; diff --git a/neo/SmartContract/Native/Tokens/Nep5Token.cs b/neo/SmartContract/Native/Tokens/Nep5Token.cs index 6a2e1c78b3..a95885a665 100644 --- a/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -1,4 +1,4 @@ -#pragma warning disable IDE0060 +#pragma warning disable IDE0060 using Neo.Ledger; using Neo.Persistence; diff --git a/neo/SmartContract/NefFile.cs b/neo/SmartContract/NefFile.cs index 52507eda08..e0068b7213 100644 --- a/neo/SmartContract/NefFile.cs +++ b/neo/SmartContract/NefFile.cs @@ -1,129 +1,129 @@ -using Neo.Cryptography; -using Neo.IO; -using System; -using System.IO; - -namespace Neo.SmartContract -{ - /// - /// +------------+-----------+------------------------------------------------------------+ - /// | Field | Length | Comment | - /// +------------+-----------+------------------------------------------------------------+ - /// | Magic | 4 bytes | Magic header | - /// | Compiler | 32 bytes | Compiler used | - /// | Version | 16 bytes | Compiler version (Mayor, Minor, Build, Version) | - /// | ScriptHash | 20 bytes | ScriptHash for the script | - /// +------------+-----------+------------------------------------------------------------+ - /// | Checksum | 4 bytes | Sha256 of the header (CRC) | - /// +------------+-----------+------------------------------------------------------------+ - /// | Script | Var bytes | Var bytes for the payload | - /// +------------+-----------+------------------------------------------------------------+ - /// - public class NefFile : ISerializable - { - /// - /// NEO Executable Format 3 (NEF3) - /// - private const uint Magic = 0x3346454E; - - /// - /// Compiler - /// - public string Compiler { get; set; } - - /// - /// Version - /// - public Version Version { get; set; } - - /// - /// Script Hash - /// - public UInt160 ScriptHash { get; set; } - - /// - /// Checksum - /// - public uint CheckSum { get; set; } - - /// - /// Script - /// - public byte[] Script { get; set; } - - private const int HeaderSize = - sizeof(uint) + // Magic - 32 + // Compiler - (sizeof(int) * 4) + // Version - UInt160.Length + // ScriptHash - sizeof(uint); // Checksum - - public int Size => - HeaderSize + // Header - Script.GetVarSize(); // Script - - public void Serialize(BinaryWriter writer) - { - writer.Write(Magic); - writer.WriteFixedString(Compiler, 32); - - // Version - writer.Write(Version.Major); - writer.Write(Version.Minor); - writer.Write(Version.Build); - writer.Write(Version.Revision); - - writer.Write(ScriptHash); - writer.Write(CheckSum); - writer.WriteVarBytes(Script ?? new byte[0]); - } - - public void Deserialize(BinaryReader reader) - { - if (reader.ReadUInt32() != Magic) - { - throw new FormatException("Wrong magic"); - } - - Compiler = reader.ReadFixedString(32); - Version = new Version(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); - ScriptHash = reader.ReadSerializable(); - CheckSum = reader.ReadUInt32(); - - if (CheckSum != ComputeChecksum(this)) - { - throw new FormatException("CRC verification fail"); - } - - Script = reader.ReadVarBytes(1024 * 1024); - - if (Script.ToScriptHash() != ScriptHash) - { - throw new FormatException("ScriptHash is different"); - } - } - - /// - /// Compute checksum for a file - /// - /// File - /// Return checksum - public static uint ComputeChecksum(NefFile file) - { - using (var ms = new MemoryStream()) - using (var wr = new BinaryWriter(ms)) - { - file.Serialize(wr); - wr.Flush(); - - // Read header without CRC - - var buffer = new byte[HeaderSize - sizeof(uint)]; - ms.Seek(0, SeekOrigin.Begin); - ms.Read(buffer, 0, buffer.Length); - - return BitConverter.ToUInt32(buffer.Sha256(), 0); - } - } - } -} +using Neo.Cryptography; +using Neo.IO; +using System; +using System.IO; + +namespace Neo.SmartContract +{ + /// + /// +------------+-----------+------------------------------------------------------------+ + /// | Field | Length | Comment | + /// +------------+-----------+------------------------------------------------------------+ + /// | Magic | 4 bytes | Magic header | + /// | Compiler | 32 bytes | Compiler used | + /// | Version | 16 bytes | Compiler version (Mayor, Minor, Build, Version) | + /// | ScriptHash | 20 bytes | ScriptHash for the script | + /// +------------+-----------+------------------------------------------------------------+ + /// | Checksum | 4 bytes | Sha256 of the header (CRC) | + /// +------------+-----------+------------------------------------------------------------+ + /// | Script | Var bytes | Var bytes for the payload | + /// +------------+-----------+------------------------------------------------------------+ + /// + public class NefFile : ISerializable + { + /// + /// NEO Executable Format 3 (NEF3) + /// + private const uint Magic = 0x3346454E; + + /// + /// Compiler + /// + public string Compiler { get; set; } + + /// + /// Version + /// + public Version Version { get; set; } + + /// + /// Script Hash + /// + public UInt160 ScriptHash { get; set; } + + /// + /// Checksum + /// + public uint CheckSum { get; set; } + + /// + /// Script + /// + public byte[] Script { get; set; } + + private const int HeaderSize = + sizeof(uint) + // Magic + 32 + // Compiler + (sizeof(int) * 4) + // Version + UInt160.Length + // ScriptHash + sizeof(uint); // Checksum + + public int Size => + HeaderSize + // Header + Script.GetVarSize(); // Script + + public void Serialize(BinaryWriter writer) + { + writer.Write(Magic); + writer.WriteFixedString(Compiler, 32); + + // Version + writer.Write(Version.Major); + writer.Write(Version.Minor); + writer.Write(Version.Build); + writer.Write(Version.Revision); + + writer.Write(ScriptHash); + writer.Write(CheckSum); + writer.WriteVarBytes(Script ?? new byte[0]); + } + + public void Deserialize(BinaryReader reader) + { + if (reader.ReadUInt32() != Magic) + { + throw new FormatException("Wrong magic"); + } + + Compiler = reader.ReadFixedString(32); + Version = new Version(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32()); + ScriptHash = reader.ReadSerializable(); + CheckSum = reader.ReadUInt32(); + + if (CheckSum != ComputeChecksum(this)) + { + throw new FormatException("CRC verification fail"); + } + + Script = reader.ReadVarBytes(1024 * 1024); + + if (Script.ToScriptHash() != ScriptHash) + { + throw new FormatException("ScriptHash is different"); + } + } + + /// + /// Compute checksum for a file + /// + /// File + /// Return checksum + public static uint ComputeChecksum(NefFile file) + { + using (var ms = new MemoryStream()) + using (var wr = new BinaryWriter(ms)) + { + file.Serialize(wr); + wr.Flush(); + + // Read header without CRC + + var buffer = new byte[HeaderSize - sizeof(uint)]; + ms.Seek(0, SeekOrigin.Begin); + ms.Read(buffer, 0, buffer.Length); + + return BitConverter.ToUInt32(buffer.Sha256(), 0); + } + } + } +} diff --git a/neo/SmartContract/NotifyEventArgs.cs b/neo/SmartContract/NotifyEventArgs.cs index f4e4e169b8..8fab961408 100644 --- a/neo/SmartContract/NotifyEventArgs.cs +++ b/neo/SmartContract/NotifyEventArgs.cs @@ -1,4 +1,4 @@ -using Neo.Network.P2P.Payloads; +using Neo.Network.P2P.Payloads; using Neo.VM; using System; diff --git a/neo/SmartContract/StackItemType.cs b/neo/SmartContract/StackItemType.cs index 94ee4f23ba..b47999e157 100644 --- a/neo/SmartContract/StackItemType.cs +++ b/neo/SmartContract/StackItemType.cs @@ -1,4 +1,4 @@ -namespace Neo.SmartContract +namespace Neo.SmartContract { internal enum StackItemType : byte { diff --git a/neo/SmartContract/StorageContext.cs b/neo/SmartContract/StorageContext.cs index 1c3336c9ac..abea2e343d 100644 --- a/neo/SmartContract/StorageContext.cs +++ b/neo/SmartContract/StorageContext.cs @@ -1,4 +1,4 @@ -namespace Neo.SmartContract +namespace Neo.SmartContract { internal class StorageContext { diff --git a/neo/SmartContract/WitnessWrapper.cs b/neo/SmartContract/WitnessWrapper.cs index f67690cad5..28019be0c2 100644 --- a/neo/SmartContract/WitnessWrapper.cs +++ b/neo/SmartContract/WitnessWrapper.cs @@ -1,4 +1,4 @@ -using Neo.Network.P2P.Payloads; +using Neo.Network.P2P.Payloads; using Neo.Persistence; using System.Linq; diff --git a/neo/UInt160.cs b/neo/UInt160.cs index 0c6bea4b5a..0052d45569 100644 --- a/neo/UInt160.cs +++ b/neo/UInt160.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Linq; diff --git a/neo/UInt256.cs b/neo/UInt256.cs index ec79ae21ea..72719f0e90 100644 --- a/neo/UInt256.cs +++ b/neo/UInt256.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Linq; diff --git a/neo/UIntBase.cs b/neo/UIntBase.cs index 22431c4cae..af26e36d0a 100644 --- a/neo/UIntBase.cs +++ b/neo/UIntBase.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using System; using System.IO; using System.Linq; diff --git a/neo/VM/Helper.cs b/neo/VM/Helper.cs index 6a7a37d7d5..e996463e3f 100644 --- a/neo/VM/Helper.cs +++ b/neo/VM/Helper.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography.ECC; +using Neo.Cryptography.ECC; using Neo.IO; using Neo.SmartContract; using Neo.VM.Types; diff --git a/neo/Wallets/AssetDescriptor.cs b/neo/Wallets/AssetDescriptor.cs index 49144834bb..dca0b10b25 100644 --- a/neo/Wallets/AssetDescriptor.cs +++ b/neo/Wallets/AssetDescriptor.cs @@ -1,4 +1,4 @@ -using Neo.SmartContract; +using Neo.SmartContract; using Neo.VM; using System; diff --git a/neo/Wallets/Helper.cs b/neo/Wallets/Helper.cs index 4b26982db1..5960876fe2 100644 --- a/neo/Wallets/Helper.cs +++ b/neo/Wallets/Helper.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using System; diff --git a/neo/Wallets/KeyPair.cs b/neo/Wallets/KeyPair.cs index 0125d10f99..afea8e822a 100644 --- a/neo/Wallets/KeyPair.cs +++ b/neo/Wallets/KeyPair.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.SmartContract; using System; using System.Linq; diff --git a/neo/Wallets/NEP6/NEP6Account.cs b/neo/Wallets/NEP6/NEP6Account.cs index ddeace2523..990612d542 100644 --- a/neo/Wallets/NEP6/NEP6Account.cs +++ b/neo/Wallets/NEP6/NEP6Account.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using System; namespace Neo.Wallets.NEP6 diff --git a/neo/Wallets/NEP6/NEP6Contract.cs b/neo/Wallets/NEP6/NEP6Contract.cs index a3dd36f414..45f0b5e157 100644 --- a/neo/Wallets/NEP6/NEP6Contract.cs +++ b/neo/Wallets/NEP6/NEP6Contract.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using Neo.SmartContract; using System.Linq; diff --git a/neo/Wallets/NEP6/NEP6Wallet.cs b/neo/Wallets/NEP6/NEP6Wallet.cs index 9f0d0a1315..f76640e886 100644 --- a/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/neo/Wallets/NEP6/NEP6Wallet.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; using Neo.SmartContract; using System; using System.Collections.Generic; diff --git a/neo/Wallets/NEP6/ScryptParameters.cs b/neo/Wallets/NEP6/ScryptParameters.cs index 018f0bfd46..a987159ad6 100644 --- a/neo/Wallets/NEP6/ScryptParameters.cs +++ b/neo/Wallets/NEP6/ScryptParameters.cs @@ -1,4 +1,4 @@ -using Neo.IO.Json; +using Neo.IO.Json; namespace Neo.Wallets.NEP6 { diff --git a/neo/Wallets/NEP6/WalletLocker.cs b/neo/Wallets/NEP6/WalletLocker.cs index 6f28779361..45574b5cfe 100644 --- a/neo/Wallets/NEP6/WalletLocker.cs +++ b/neo/Wallets/NEP6/WalletLocker.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Neo.Wallets.NEP6 { diff --git a/neo/Wallets/SQLite/Account.cs b/neo/Wallets/SQLite/Account.cs index 5c91556fb2..ce8ed1e01e 100644 --- a/neo/Wallets/SQLite/Account.cs +++ b/neo/Wallets/SQLite/Account.cs @@ -1,4 +1,4 @@ -namespace Neo.Wallets.SQLite +namespace Neo.Wallets.SQLite { internal class Account { diff --git a/neo/Wallets/SQLite/Address.cs b/neo/Wallets/SQLite/Address.cs index 79ef9e1031..a5abe4dcfc 100644 --- a/neo/Wallets/SQLite/Address.cs +++ b/neo/Wallets/SQLite/Address.cs @@ -1,4 +1,4 @@ -namespace Neo.Wallets.SQLite +namespace Neo.Wallets.SQLite { internal class Address { diff --git a/neo/Wallets/SQLite/Contract.cs b/neo/Wallets/SQLite/Contract.cs index a69c0f0968..96ba3c51a6 100644 --- a/neo/Wallets/SQLite/Contract.cs +++ b/neo/Wallets/SQLite/Contract.cs @@ -1,4 +1,4 @@ -namespace Neo.Wallets.SQLite +namespace Neo.Wallets.SQLite { internal class Contract { diff --git a/neo/Wallets/SQLite/Key.cs b/neo/Wallets/SQLite/Key.cs index 97c063a3f2..f493b7310d 100644 --- a/neo/Wallets/SQLite/Key.cs +++ b/neo/Wallets/SQLite/Key.cs @@ -1,4 +1,4 @@ -namespace Neo.Wallets.SQLite +namespace Neo.Wallets.SQLite { internal class Key { diff --git a/neo/Wallets/SQLite/UserWallet.cs b/neo/Wallets/SQLite/UserWallet.cs index df4cccfe08..efb477a61a 100644 --- a/neo/Wallets/SQLite/UserWallet.cs +++ b/neo/Wallets/SQLite/UserWallet.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Neo.Cryptography; using Neo.IO; using Neo.SmartContract; diff --git a/neo/Wallets/SQLite/UserWalletAccount.cs b/neo/Wallets/SQLite/UserWalletAccount.cs index 2d3ac74836..d586f59d36 100644 --- a/neo/Wallets/SQLite/UserWalletAccount.cs +++ b/neo/Wallets/SQLite/UserWalletAccount.cs @@ -1,4 +1,4 @@ -namespace Neo.Wallets.SQLite +namespace Neo.Wallets.SQLite { internal class UserWalletAccount : WalletAccount { diff --git a/neo/Wallets/SQLite/VerificationContract.cs b/neo/Wallets/SQLite/VerificationContract.cs index 41f8d44055..587a0fe468 100644 --- a/neo/Wallets/SQLite/VerificationContract.cs +++ b/neo/Wallets/SQLite/VerificationContract.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.SmartContract; using Neo.VM; using System; diff --git a/neo/Wallets/SQLite/WalletDataContext.cs b/neo/Wallets/SQLite/WalletDataContext.cs index cba60cb00d..6d1ec957a8 100644 --- a/neo/Wallets/SQLite/WalletDataContext.cs +++ b/neo/Wallets/SQLite/WalletDataContext.cs @@ -1,4 +1,4 @@ -using Microsoft.Data.Sqlite; +using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; namespace Neo.Wallets.SQLite diff --git a/neo/Wallets/TransferOutput.cs b/neo/Wallets/TransferOutput.cs index 6e45baa04b..9431f5616d 100644 --- a/neo/Wallets/TransferOutput.cs +++ b/neo/Wallets/TransferOutput.cs @@ -1,4 +1,4 @@ -namespace Neo.Wallets +namespace Neo.Wallets { public class TransferOutput { diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index d22c8ff128..eb816c7a0a 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography; +using Neo.Cryptography; using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; diff --git a/neo/Wallets/WalletAccount.cs b/neo/Wallets/WalletAccount.cs index a427a9e50b..5e2408a1d0 100644 --- a/neo/Wallets/WalletAccount.cs +++ b/neo/Wallets/WalletAccount.cs @@ -1,4 +1,4 @@ -using Neo.SmartContract; +using Neo.SmartContract; namespace Neo.Wallets { diff --git a/neo/neo.csproj b/neo/neo.csproj index 66e85016bc..54ba724da9 100644 --- a/neo/neo.csproj +++ b/neo/neo.csproj @@ -1,4 +1,4 @@ - + 2015-2019 The Neo Project From dca0e50b9e3693a6fd0f6ee984d1940194d31211 Mon Sep 17 00:00:00 2001 From: Shargon Date: Sun, 25 Aug 2019 11:40:46 +0200 Subject: [PATCH 084/305] Fix consensus problem for proposed blocks with zero transactions (#1053) * Fix consensus (CRLF) * Removing unnecessary lines changes - Cleaning PR * Reverting all changes on CC * Modifying ConsensusContext with proposed changes --- neo/Consensus/ConsensusContext.cs | 5 +---- neo/Consensus/ConsensusService.cs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index fbb21d8cd7..4574d70f7e 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -254,14 +254,11 @@ internal void EnsureMaxBlockSize(IEnumerable txs) txs = txs.Take((int)maxTransactionsPerBlock); List hashes = new List(); Transactions = new Dictionary(); - Block.Transactions = new Transaction[0]; - - // We need to know the expected block size + // Expected block size var blockSize = GetExpectedBlockSizeWithoutTransactions(txs.Count()); // Iterate transaction until reach the size - foreach (Transaction tx in txs) { // Check if maximum block size has been already exceeded with the current selected set diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index cac657c95f..4d8b70a333 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -74,6 +74,11 @@ private bool AddTransaction(Transaction tx, bool verify) return false; } context.Transactions[tx.Hash] = 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 @@ -428,6 +433,14 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m if (context.CommitPayloads[i]?.ConsensusMessage.ViewNumber == context.ViewNumber) if (!Crypto.Default.VerifySignature(hashData, context.CommitPayloads[i].GetDeserializedMessage().Signature, context.Validators[i].EncodePoint(false))) 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) From db6db5eb32b37c514f2ebb3d9dded950c49e506e Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 26 Aug 2019 22:12:34 +0200 Subject: [PATCH 085/305] Small OnPersist optimization (#1058) * Small update * Cache native persist script --- neo/Ledger/Blockchain.cs | 24 +++++++++++++----------- neo/Ledger/HashIndexState.cs | 7 +++++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index db879c0732..a74bfa981c 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -53,6 +53,7 @@ public class FillCompleted { } Transactions = new[] { DeployNativeContracts() } }; + private readonly static byte[] onPersistNativeContractScript; private const int MaxTxToReverifyPerIdle = 10; private static readonly object lockObj = new object(); private readonly NeoSystem system; @@ -83,6 +84,15 @@ public static Blockchain Singleton static Blockchain() { GenesisBlock.RebuildMerkleRoot(); + + NativeContract[] contracts = { NativeContract.GAS, NativeContract.NEO }; + using (ScriptBuilder sb = new ScriptBuilder()) + { + foreach (NativeContract contract in contracts) + sb.EmitAppCall(contract.Hash, "onPersist"); + + onPersistNativeContractScript = sb.ToArray(); + } } public Blockchain(NeoSystem system, Store store) @@ -416,15 +426,9 @@ private void Persist(Block block) snapshot.PersistingBlock = block; if (block.Index > 0) { - NativeContract[] contracts = { NativeContract.GAS, NativeContract.NEO }; using (ApplicationEngine engine = new ApplicationEngine(TriggerType.System, null, snapshot, 0, true)) { - using (ScriptBuilder sb = new ScriptBuilder()) - { - foreach (NativeContract contract in contracts) - sb.EmitAppCall(contract.Hash, "onPersist"); - engine.LoadScript(sb.ToArray()); - } + engine.LoadScript(onPersistNativeContractScript); if (engine.Execute() != VMState.HALT) throw new InvalidOperationException(); ApplicationExecuted application_executed = new ApplicationExecuted(engine); Context.System.EventStream.Publish(application_executed); @@ -455,13 +459,11 @@ private void Persist(Block block) all_application_executed.Add(application_executed); } } - snapshot.BlockHashIndex.GetAndChange().Hash = block.Hash; - snapshot.BlockHashIndex.GetAndChange().Index = block.Index; + snapshot.BlockHashIndex.GetAndChange().Set(block); if (block.Index == header_index.Count) { header_index.Add(block.Hash); - snapshot.HeaderHashIndex.GetAndChange().Hash = block.Hash; - snapshot.HeaderHashIndex.GetAndChange().Index = block.Index; + snapshot.HeaderHashIndex.GetAndChange().Set(block); } foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) plugin.OnPersist(snapshot, all_application_executed); diff --git a/neo/Ledger/HashIndexState.cs b/neo/Ledger/HashIndexState.cs index 04f7b0bd4a..fb7439a252 100644 --- a/neo/Ledger/HashIndexState.cs +++ b/neo/Ledger/HashIndexState.cs @@ -1,4 +1,5 @@ using Neo.IO; +using Neo.Network.P2P.Payloads; using System.IO; namespace Neo.Ledger @@ -36,5 +37,11 @@ void ISerializable.Serialize(BinaryWriter writer) writer.Write(Hash); writer.Write(Index); } + + internal void Set(BlockBase block) + { + Hash = block.Hash; + Index = block.Index; + } } } From cf751784b94c978d201c34845a2943e7687d5a06 Mon Sep 17 00:00:00 2001 From: Igor Machado Coelho Date: Tue, 27 Aug 2019 14:47:55 +0800 Subject: [PATCH 086/305] fix crlf on neo.UnitTests and set all files to not use BOM (byte order mark) (#1057) * Check dotnet format in travis * Try to find the path * Update .travis.yml * Update .travis.yml * Update .travis.yml * Format travis * test and source * test and source * Move to before the script * testing if CRLF format is respected * fix crlf on unit tests * testing for crlf on unit tests too * all utf-8 * neo/ on utf8 too * looks like sudo is needed by travis * download dos2unix 7.4.0 * temporaly remove | test * back to | test * explicit test zero * no bom * more non-boom to boom * ensuring no_bom at all files * Printing files with bom prefix * Check both folders in one command * Add verbosity * Create .editorconfig * Ensure the path of .editorconfig file * Update .editorconfig * Update .editorconfig * Update .editorconfig * use only dotnet-format Is confirmed that .editorconfig works, so we should configure only this file * basic formatting (just 5 files affected) * dotnet version * new lines on end * update travis * advice on auto crlf * Update .travis.yml * Update .travis.yml * enforcing lf * Fix dotnet format version * removed dos2unix --- .editorconfig | 18 ++ .travis.yml | 16 +- NuGet.Config | 2 +- .../Consensus/UT_ConsensusContext.cs | 306 +++++++++--------- .../Consensus/UT_ConsensusServiceMailbox.cs | 2 +- neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs | 4 +- .../Cryptography/ECC/UT_ECFieldElement.cs | 2 +- neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs | 4 +- neo.UnitTests/Cryptography/UT_Base58.cs | 4 +- neo.UnitTests/Cryptography/UT_BloomFilter.cs | 4 +- neo.UnitTests/Cryptography/UT_Crypto.cs | 4 +- .../Cryptography/UT_Cryptography_Helper.cs | 4 +- neo.UnitTests/Cryptography/UT_MerkleTree.cs | 4 +- .../Cryptography/UT_MerkleTreeNode.cs | 4 +- neo.UnitTests/Cryptography/UT_Murmur3.cs | 4 +- neo.UnitTests/Cryptography/UT_SCrypt.cs | 4 +- .../Extensions/NativeContractExtensions.cs | 4 +- .../Nep5NativeContractExtensions.cs | 8 +- neo.UnitTests/IO/Caching/UT_Cache.cs | 2 +- neo.UnitTests/IO/Caching/UT_CloneCache.cs | 4 +- neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs | 4 +- neo.UnitTests/IO/Caching/UT_DataCache.cs | 4 +- neo.UnitTests/IO/Caching/UT_FIFOSet.cs | 4 +- neo.UnitTests/IO/Caching/UT_MetaDataCache.cs | 4 +- .../IO/Caching/UT_OrderedDictionary.cs | 4 +- .../IO/Caching/UT_ReflectionCache.cs | 4 +- neo.UnitTests/IO/Caching/UT_RelayCache.cs | 4 +- neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs | 2 +- neo.UnitTests/IO/Json/UT_JArray.cs | 4 +- neo.UnitTests/IO/Json/UT_JBoolean.cs | 4 +- neo.UnitTests/IO/Json/UT_JNumber.cs | 4 +- neo.UnitTests/IO/Json/UT_JObject.cs | 4 +- neo.UnitTests/IO/Json/UT_JString.cs | 4 +- neo.UnitTests/IO/UT_IOHelper.cs | 2 +- .../IO/Wrappers/UT_SerializableWrapper.cs | 4 +- neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs | 4 +- neo.UnitTests/Ledger/UT_MemoryPool.cs | 2 +- neo.UnitTests/Ledger/UT_PoolItem.cs | 2 +- neo.UnitTests/Ledger/UT_StorageKey.cs | 2 +- .../Network/P2P/Payloads/UT_Cosigner.cs | 2 +- .../Network/P2P/Payloads/UT_Header.cs | 2 +- .../Network/P2P/Payloads/UT_Transaction.cs | 2 +- .../Network/P2P/Payloads/UT_Witness.cs | 2 +- neo.UnitTests/Network/P2P/UT_Message.cs | 2 +- .../Network/P2P/UT_ProtocolHandler.cs | 2 +- .../Network/P2P/UT_ProtocolHandlerMailbox.cs | 2 +- neo.UnitTests/Network/P2P/UT_RemoteNode.cs | 2 +- .../Network/P2P/UT_RemoteNodeMailbox.cs | 2 +- .../Network/P2P/UT_TaskManagerMailbox.cs | 2 +- neo.UnitTests/Network/RPC/UT_RpcClient.cs | 2 +- .../Iterators/UT_ConcatenatedIterator.cs | 2 +- .../Manifest/UT_ContractManifest.cs | 2 +- .../Native/Tokens/UT_GasToken.cs | 4 +- .../Native/Tokens/UT_NeoToken.cs | 2 +- .../SmartContract/Native/UT_PolicyContract.cs | 2 +- .../SmartContract/UT_InteropService.cs | 2 +- .../SmartContract/UT_JsonSerializer.cs | 2 +- .../SmartContract/UT_OpCodePrices.cs | 2 +- neo.UnitTests/SmartContract/UT_Syscalls.cs | 4 +- neo.UnitTests/TestBlockchain.cs | 4 +- neo.UnitTests/TestDataCache.cs | 2 +- neo.UnitTests/TestMetaDataCache.cs | 2 +- neo.UnitTests/TestUtils.cs | 4 +- neo.UnitTests/TestVerifiable.cs | 2 +- neo.UnitTests/TestWalletAccount.cs | 2 +- neo.UnitTests/UT_BigDecimal.cs | 4 +- neo.UnitTests/UT_Culture.cs | 2 +- neo.UnitTests/UT_DataCache.cs | 2 +- neo.UnitTests/UT_Helper.cs | 2 +- neo.UnitTests/UT_NefFile.cs | 150 ++++----- neo.UnitTests/UT_ProtocolSettings.cs | 4 +- neo.UnitTests/VM/UT_Helper.cs | 4 +- neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs | 2 +- neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs | 2 +- neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs | 2 +- .../Wallets/NEP6/UT_ScryptParameters.cs | 2 +- neo.UnitTests/Wallets/SQLite/UT_Account.cs | 4 +- neo.UnitTests/Wallets/SQLite/UT_Address.cs | 4 +- neo.UnitTests/Wallets/SQLite/UT_Contract.cs | 4 +- neo.UnitTests/Wallets/SQLite/UT_Key.cs | 4 +- neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs | 2 +- .../Wallets/SQLite/UT_UserWalletAccount.cs | 2 +- .../Wallets/SQLite/UT_VerificationContract.cs | 2 +- neo.UnitTests/Wallets/UT_AssetDescriptor.cs | 4 +- neo.UnitTests/Wallets/UT_KeyPair.cs | 2 +- neo.UnitTests/Wallets/UT_Wallet.cs | 4 +- neo.UnitTests/Wallets/UT_WalletAccount.cs | 2 +- neo.UnitTests/Wallets/UT_Wallets_Helper.cs | 2 +- neo.UnitTests/neo.UnitTests.csproj | 2 +- neo.sln | 2 +- neo/Consensus/ChangeViewReason.cs | 2 +- neo/IO/Caching/ReflectionCache.cs | 2 +- neo/IO/Caching/ReflectionCacheAttribute.cs | 2 +- neo/Plugins/MemoryPoolTxRemovalReason.cs | 2 +- neo/SmartContract/ExecutionContextState.cs | 2 +- 95 files changed, 390 insertions(+), 366 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..2cbe224983 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +############################### +# Core EditorConfig Options # +############################### + +# dotnet-format requires version 3.1.37601 +# dotnet tool update -g dotnet-format +# remember to have: git config --global core.autocrlf false #(which is usually default) + +root = true + +# Every file + +[*] +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 +end_of_line = lf +#indent_style = tab # TODO diff --git a/.travis.yml b/.travis.yml index aaf9dd660d..f08f8110bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,18 +11,24 @@ mono: none dotnet: 2.2.300 env: -- TEST_SUITE="without-cultures" -- TEST_SUITE="cultures" + - TEST_SUITE="without-cultures" + - TEST_SUITE="cultures" before_install: - - cd neo.UnitTests - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ulimit -n 2048; fi +install: + - dotnet tool install -g dotnet-format --version 4.0.40103 --add-source https://dotnet.myget.org/F/format/api/v3/index.json + - export PATH="$PATH:$HOME/.dotnet/tools" + - dotnet-format --version +before_script: + - echo "Checking format..." + - dotnet format --check --dry-run -w . -v diagnostic # check C# formatting for neo.sln + - cd neo.UnitTests script: | + dotnet restore if [[ "$TEST_SUITE" == cultures ]]; then - dotnet restore dotnet test else - dotnet restore find * -name *.csproj | xargs -I % dotnet add % package coverlet.msbuild dotnet test -v n --filter FullyQualifiedName!=Neo.UnitTests.UT_Culture.All_Tests_Cultures /p:CollectCoverage=true /p:CoverletOutputFormat=opencover fi diff --git a/NuGet.Config b/NuGet.Config index 640fd0fe31..c06788942f 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -1,4 +1,4 @@ - + diff --git a/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/neo.UnitTests/Consensus/UT_ConsensusContext.cs index 696d6aaa0a..9a0dd39ba7 100644 --- a/neo.UnitTests/Consensus/UT_ConsensusContext.cs +++ b/neo.UnitTests/Consensus/UT_ConsensusContext.cs @@ -1,134 +1,134 @@ -using Akka.TestKit.Xunit2; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Neo.Consensus; -using Neo.IO; -using Neo.Network.P2P.Payloads; -using Neo.SmartContract; -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, TestBlockchain.GetStore()) - { - 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.EnsureMaxBlockSize(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.EnsureMaxBlockSize(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.EnsureMaxBlockSize(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.EnsureMaxBlockSize(txs); - EnsureContext(_context, txs.Take(max).ToArray()); - } - - private Transaction CreateTransactionWithSize(int v) - { - var r = new Random(); - var tx = new Transaction() - { - Cosigners = new Cosigner[0], - Attributes = new TransactionAttribute[0], - NetworkFee = 0, - Nonce = (uint)Environment.TickCount, - Script = new byte[0], - Sender = UInt160.Zero, - 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 Block SignBlock(ConsensusContext context) - { - context.Block.MerkleRoot = null; - - for (int x = 0; x < _validatorKeys.Length; x++) - { - _context.MyIndex = x; - - var com = _context.MakeCommit(); - _context.CommitPayloads[_context.MyIndex] = com; - } - - // Manual block sign - +using Akka.TestKit.Xunit2; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.Consensus; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +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, TestBlockchain.GetStore()) + { + 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.EnsureMaxBlockSize(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.EnsureMaxBlockSize(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.EnsureMaxBlockSize(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.EnsureMaxBlockSize(txs); + EnsureContext(_context, txs.Take(max).ToArray()); + } + + private Transaction CreateTransactionWithSize(int v) + { + var r = new Random(); + var tx = new Transaction() + { + Cosigners = new Cosigner[0], + Attributes = new TransactionAttribute[0], + NetworkFee = 0, + Nonce = (uint)Environment.TickCount, + Script = new byte[0], + Sender = UInt160.Zero, + 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 Block SignBlock(ConsensusContext context) + { + context.Block.MerkleRoot = null; + + for (int x = 0; x < _validatorKeys.Length; x++) + { + _context.MyIndex = x; + + var com = _context.MakeCommit(); + _context.CommitPayloads[_context.MyIndex] = com; + } + + // Manual block sign + Contract contract = Contract.CreateMultiSigContract(context.M, context.Validators); ContractParametersContext sc = new ContractParametersContext(context.Block); for (int i = 0, j = 0; i < context.Validators.Length && j < context.M; i++) @@ -139,25 +139,25 @@ private Block SignBlock(ConsensusContext context) } context.Block.Witness = sc.GetWitnesses()[0]; context.Block.Transactions = context.TransactionHashes.Select(p => context.Transactions[p]).ToArray(); - return context.Block; - } - - 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)); - } - } -} + return context.Block; + } + + 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/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs b/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs index fec1ee6bc9..3c58c53473 100644 --- a/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs +++ b/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs @@ -1,4 +1,4 @@ -using Akka.TestKit; +using Akka.TestKit; using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs b/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs index dcd394fa4f..6c24fcb922 100644 --- a/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs +++ b/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Wallets; using System; @@ -52,4 +52,4 @@ public void TestVerifySignature() sa.VerifySignature(message, new BigInteger(-100), result[1]).Should().BeFalse(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs b/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs index 035e4dc469..90a31b495d 100644 --- a/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs +++ b/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs @@ -66,4 +66,4 @@ public void TestToByteArray() new ECFieldElement(BigInteger.Pow(new BigInteger(10), 77), ECCurve.Secp256k1).ToByteArray().Should().BeEquivalentTo(result3); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs b/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs index cc6fa87d5a..26d71cc3e6 100644 --- a/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs +++ b/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.IO; @@ -349,4 +349,4 @@ public void TestTwice() ECCurve.Secp256k1)); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Cryptography/UT_Base58.cs b/neo.UnitTests/Cryptography/UT_Base58.cs index 21cc63dda2..1a539cc42c 100644 --- a/neo.UnitTests/Cryptography/UT_Base58.cs +++ b/neo.UnitTests/Cryptography/UT_Base58.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using System; @@ -28,4 +28,4 @@ public void TestDecode() action.ShouldThrow(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Cryptography/UT_BloomFilter.cs b/neo.UnitTests/Cryptography/UT_BloomFilter.cs index d611e0965d..5deabe1b8e 100644 --- a/neo.UnitTests/Cryptography/UT_BloomFilter.cs +++ b/neo.UnitTests/Cryptography/UT_BloomFilter.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using System; @@ -66,4 +66,4 @@ public void TestGetBits() value.Should().Be(0); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Cryptography/UT_Crypto.cs b/neo.UnitTests/Cryptography/UT_Crypto.cs index ec0d95add6..c46f25aa69 100644 --- a/neo.UnitTests/Cryptography/UT_Crypto.cs +++ b/neo.UnitTests/Cryptography/UT_Crypto.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.Wallets; @@ -61,4 +61,4 @@ public void TestVerifySignature() action.ShouldThrow(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index d7a3e65136..67ed2a920d 100644 --- a/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.Network.P2P.Payloads; @@ -223,4 +223,4 @@ public void TestToArray() result.Should().Equal(byteArray); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Cryptography/UT_MerkleTree.cs b/neo.UnitTests/Cryptography/UT_MerkleTree.cs index 75ab2330b2..4b56380957 100644 --- a/neo.UnitTests/Cryptography/UT_MerkleTree.cs +++ b/neo.UnitTests/Cryptography/UT_MerkleTree.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using System; @@ -77,4 +77,4 @@ public void TestTrim() hashArray[0].Should().Be(result); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs b/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs index 48fd05745d..78274c2d18 100644 --- a/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs +++ b/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using System.Text; @@ -48,4 +48,4 @@ public void TestGetIsRoot() node.IsRoot.Should().BeTrue(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Cryptography/UT_Murmur3.cs b/neo.UnitTests/Cryptography/UT_Murmur3.cs index c59a53645a..3ed3ab22fa 100644 --- a/neo.UnitTests/Cryptography/UT_Murmur3.cs +++ b/neo.UnitTests/Cryptography/UT_Murmur3.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; @@ -21,4 +21,4 @@ public void TestHashCore() array.Murmur32(10u).Should().Be(378574820u); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Cryptography/UT_SCrypt.cs b/neo.UnitTests/Cryptography/UT_SCrypt.cs index afe41bf475..612a8307c6 100644 --- a/neo.UnitTests/Cryptography/UT_SCrypt.cs +++ b/neo.UnitTests/Cryptography/UT_SCrypt.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; namespace Neo.UnitTests.Cryptography @@ -15,4 +15,4 @@ public void DeriveKeyTest() Assert.AreEqual("b6274d3a81892c24335ab46a08ec16d040ac00c5943b212099a44b76a9b8102631ab988fa07fb35357cee7b0e3910098c0774c0e97399997676d890b2bf2bb25", derivedkey); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Extensions/NativeContractExtensions.cs b/neo.UnitTests/Extensions/NativeContractExtensions.cs index 6a641c2387..4d3f7bcb95 100644 --- a/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -1,4 +1,4 @@ -using Neo.Network.P2P.Payloads; +using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -37,4 +37,4 @@ public static StackItem Call(this NativeContract contract, Neo.Persistence.Snaps return engine.ResultStack.Pop(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs index a9aa8a7777..a531591d34 100644 --- a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs +++ b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -12,9 +12,9 @@ namespace Neo.UnitTests.Extensions { public static class Nep5NativeContractExtensions - { + { internal class ManualWitness : IVerifiable - { + { private readonly UInt160[] _hashForVerify; public Witness[] Witnesses @@ -191,4 +191,4 @@ public static string Name(this NativeContract contract) return Encoding.UTF8.GetString((result as VM.Types.ByteArray).GetByteArray()); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Caching/UT_Cache.cs b/neo.UnitTests/IO/Caching/UT_Cache.cs index 1376863e1a..930547f314 100644 --- a/neo.UnitTests/IO/Caching/UT_Cache.cs +++ b/neo.UnitTests/IO/Caching/UT_Cache.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; using System; diff --git a/neo.UnitTests/IO/Caching/UT_CloneCache.cs b/neo.UnitTests/IO/Caching/UT_CloneCache.cs index 52c665a95d..afa550e45c 100644 --- a/neo.UnitTests/IO/Caching/UT_CloneCache.cs +++ b/neo.UnitTests/IO/Caching/UT_CloneCache.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.IO.Caching; @@ -124,4 +124,4 @@ public void TestUpdateInternal() myDataCache[new MyKey("key2")].Should().Be(new MyValue("value_new_2")); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs b/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs index 812cbc26ba..591ea8ea05 100644 --- a/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs +++ b/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; @@ -45,4 +45,4 @@ public void TestUpdateInternal() value.Value.Should().Be("value2"); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Caching/UT_DataCache.cs b/neo.UnitTests/IO/Caching/UT_DataCache.cs index d93316022d..f21624814d 100644 --- a/neo.UnitTests/IO/Caching/UT_DataCache.cs +++ b/neo.UnitTests/IO/Caching/UT_DataCache.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.IO.Caching; @@ -344,4 +344,4 @@ public void TestTryGet() myDataCache.TryGet(new MyKey("key3")).Should().BeNull(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Caching/UT_FIFOSet.cs b/neo.UnitTests/IO/Caching/UT_FIFOSet.cs index f36984b89f..9b781858ee 100644 --- a/neo.UnitTests/IO/Caching/UT_FIFOSet.cs +++ b/neo.UnitTests/IO/Caching/UT_FIFOSet.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; using System; @@ -146,4 +146,4 @@ public void TestExceptWith() CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a }); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs b/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs index 368e866f54..1ed84a5a0e 100644 --- a/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs +++ b/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.IO.Caching; @@ -73,4 +73,4 @@ public void TestCreateSnapshot() myMetaCache.CreateSnapshot().Should().NotBeNull(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs b/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs index 262528cd88..66b1dd0e7c 100644 --- a/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs +++ b/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; using System.Collections; @@ -126,4 +126,4 @@ public void TestGetEnumerator() collection.GetEnumerator().MoveNext().Should().BeTrue(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs index e7de68f3c1..e51f6f81d6 100644 --- a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs +++ b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; using System; @@ -77,4 +77,4 @@ public void TestCreateInstance2() (item1 is TestItem1).Should().BeTrue(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Caching/UT_RelayCache.cs b/neo.UnitTests/IO/Caching/UT_RelayCache.cs index ed41c1128a..4608dd0e14 100644 --- a/neo.UnitTests/IO/Caching/UT_RelayCache.cs +++ b/neo.UnitTests/IO/Caching/UT_RelayCache.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; using Neo.Network.P2P.Payloads; @@ -39,4 +39,4 @@ public void TestGetKeyForItem() (tmp is Transaction).Should().BeTrue(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs b/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs index 21dea9f1d1..d9ad5af951 100644 --- a/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs +++ b/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs @@ -405,4 +405,4 @@ public void TestUnequal() Assert.AreEqual(false, result); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Json/UT_JArray.cs b/neo.UnitTests/IO/Json/UT_JArray.cs index 1bb165f224..5b7a30c9ae 100644 --- a/neo.UnitTests/IO/Json/UT_JArray.cs +++ b/neo.UnitTests/IO/Json/UT_JArray.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using System; @@ -249,4 +249,4 @@ public void TestAsString() Assert.AreEqual(s, "{\"name\":\"alice\",\"age\":30,\"score\":100.001,\"gender\":\"female\",\"isMarried\":true,\"pet\":{\"name\":\"Tom\",\"type\":\"cat\"}},{\"name\":\"bob\",\"age\":100000,\"score\":0.001,\"gender\":\"male\",\"isMarried\":false,\"pet\":{\"name\":\"Paul\",\"type\":\"dog\"}}"); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Json/UT_JBoolean.cs b/neo.UnitTests/IO/Json/UT_JBoolean.cs index cb44570e45..8e5f4acdd6 100644 --- a/neo.UnitTests/IO/Json/UT_JBoolean.cs +++ b/neo.UnitTests/IO/Json/UT_JBoolean.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using System; @@ -74,4 +74,4 @@ public void TestParseTrue() ret3.AsBoolean().Should().BeTrue(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Json/UT_JNumber.cs b/neo.UnitTests/IO/Json/UT_JNumber.cs index 7232e8b969..16adbe9d80 100644 --- a/neo.UnitTests/IO/Json/UT_JNumber.cs +++ b/neo.UnitTests/IO/Json/UT_JNumber.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using System; @@ -64,4 +64,4 @@ public void TestParse() action2.ShouldThrow(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Json/UT_JObject.cs b/neo.UnitTests/IO/Json/UT_JObject.cs index 3c8dff0b46..990363bcb1 100644 --- a/neo.UnitTests/IO/Json/UT_JObject.cs +++ b/neo.UnitTests/IO/Json/UT_JObject.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using System; @@ -115,4 +115,4 @@ public void TestGetNull() JObject.Null.Should().BeNull(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Json/UT_JString.cs b/neo.UnitTests/IO/Json/UT_JString.cs index 07f231674f..5d20b30fa0 100644 --- a/neo.UnitTests/IO/Json/UT_JString.cs +++ b/neo.UnitTests/IO/Json/UT_JString.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using Neo.SmartContract; using System; @@ -70,4 +70,4 @@ public void TestTryGetEnum() Assert.AreEqual(ContractParameterType.Void, cp); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/UT_IOHelper.cs b/neo.UnitTests/IO/UT_IOHelper.cs index b0593c02ec..350cc31425 100644 --- a/neo.UnitTests/IO/UT_IOHelper.cs +++ b/neo.UnitTests/IO/UT_IOHelper.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using System; diff --git a/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs b/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs index 3f4f0c6d06..aa6c0cfbc8 100644 --- a/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs +++ b/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Wrappers; using System.IO; @@ -47,4 +47,4 @@ public void TestEqualsOtherSerializableWrapper() Assert.AreEqual(true, temp.Equals(temp2)); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs b/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs index 7762c56b7c..766ed8e694 100644 --- a/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs +++ b/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Wrappers; using System.IO; using System.Text; @@ -94,4 +94,4 @@ public void TestOperatorUInt32Wrapper() Assert.AreEqual(true, temp.Equals(1)); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Ledger/UT_MemoryPool.cs b/neo.UnitTests/Ledger/UT_MemoryPool.cs index 96b0e99a19..ea797d7c03 100644 --- a/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Neo.Ledger; diff --git a/neo.UnitTests/Ledger/UT_PoolItem.cs b/neo.UnitTests/Ledger/UT_PoolItem.cs index e87aad3ffe..10a57ecc90 100644 --- a/neo.UnitTests/Ledger/UT_PoolItem.cs +++ b/neo.UnitTests/Ledger/UT_PoolItem.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Neo.Ledger; diff --git a/neo.UnitTests/Ledger/UT_StorageKey.cs b/neo.UnitTests/Ledger/UT_StorageKey.cs index 7e2ec9bdb2..6e2223cdcb 100644 --- a/neo.UnitTests/Ledger/UT_StorageKey.cs +++ b/neo.UnitTests/Ledger/UT_StorageKey.cs @@ -128,4 +128,4 @@ public void GetHashCode_Get() uut.GetHashCode().Should().Be(806209853); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs index ef6124b3a8..e21f468575 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.IO; diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index b367a3efbd..3eabd054b6 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Network.P2P.Payloads; diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index c357541d7e..91510e0ec9 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.IO; diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index efb06d93be..5114d524b2 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.IO.Json; diff --git a/neo.UnitTests/Network/P2P/UT_Message.cs b/neo.UnitTests/Network/P2P/UT_Message.cs index 4bb37a8166..6f194ea862 100644 --- a/neo.UnitTests/Network/P2P/UT_Message.cs +++ b/neo.UnitTests/Network/P2P/UT_Message.cs @@ -112,4 +112,4 @@ public void Compression() ((ServerCapability)payloadCopy.Capabilities[0]).Port.Should().Be(25); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs b/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs index 4ea998ef40..f8b932c4f2 100644 --- a/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs +++ b/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs @@ -1,4 +1,4 @@ -using Akka.TestKit.Xunit2; +using Akka.TestKit.Xunit2; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P; using Neo.Network.P2P.Capabilities; diff --git a/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs b/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs index 91caf65297..cb9c4586a8 100644 --- a/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs +++ b/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs @@ -1,4 +1,4 @@ -using Akka.TestKit.Xunit2; +using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; diff --git a/neo.UnitTests/Network/P2P/UT_RemoteNode.cs b/neo.UnitTests/Network/P2P/UT_RemoteNode.cs index 03da2a3db8..570cc904da 100644 --- a/neo.UnitTests/Network/P2P/UT_RemoteNode.cs +++ b/neo.UnitTests/Network/P2P/UT_RemoteNode.cs @@ -8,7 +8,7 @@ namespace Neo.UnitTests.Network.P2P { - [TestClass] + [TestClass] [NotReRunnable] public class UT_RemoteNode : TestKit { diff --git a/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs b/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs index 860012a31e..b57e9e93a7 100644 --- a/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs +++ b/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs @@ -1,4 +1,4 @@ -using Akka.IO; +using Akka.IO; using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs b/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs index 6d014cd9c7..fca280d1eb 100644 --- a/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs +++ b/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs @@ -1,4 +1,4 @@ -using Akka.TestKit.Xunit2; +using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P; diff --git a/neo.UnitTests/Network/RPC/UT_RpcClient.cs b/neo.UnitTests/Network/RPC/UT_RpcClient.cs index 955c481248..76cf7556bf 100644 --- a/neo.UnitTests/Network/RPC/UT_RpcClient.cs +++ b/neo.UnitTests/Network/RPC/UT_RpcClient.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Moq.Protected; using Neo.IO; diff --git a/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs b/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs index 51db50ea31..5fad01c94b 100644 --- a/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs +++ b/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs @@ -66,4 +66,4 @@ private Integer MakeIntegerStackItem(int val) return new Integer(new BigInteger(val)); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index bbb613c2be..a96c3ff4c5 100644 --- a/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.SmartContract.Manifest; diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index 0ad69a7198..0202e9d6d0 100644 --- a/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; using Neo.Network.P2P.Payloads; @@ -141,4 +141,4 @@ public void Check_BadScript() NativeContract.GAS.Invoke(engine).Should().BeFalse(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index f7b216544c..38befbe790 100644 --- a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.Cryptography.ECC; diff --git a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index b53932dbf8..8442904f5b 100644 --- a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P.Payloads; using Neo.Persistence; diff --git a/neo.UnitTests/SmartContract/UT_InteropService.cs b/neo.UnitTests/SmartContract/UT_InteropService.cs index 023ba4c8e5..39687fd720 100644 --- a/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -189,4 +189,4 @@ private void AssertNotification(StackItem stackItem, UInt160 scriptHash, int not Assert.AreEqual(notification, array[1].GetBigInteger()); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs index adfbea8760..e7fdf30667 100644 --- a/neo.UnitTests/SmartContract/UT_JsonSerializer.cs +++ b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using Neo.SmartContract; using Neo.VM; diff --git a/neo.UnitTests/SmartContract/UT_OpCodePrices.cs b/neo.UnitTests/SmartContract/UT_OpCodePrices.cs index 3174d8494b..cb04990e31 100644 --- a/neo.UnitTests/SmartContract/UT_OpCodePrices.cs +++ b/neo.UnitTests/SmartContract/UT_OpCodePrices.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; using Neo.VM; using System; diff --git a/neo.UnitTests/SmartContract/UT_Syscalls.cs b/neo.UnitTests/SmartContract/UT_Syscalls.cs index c80899b8f8..65066b9079 100644 --- a/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; using Neo.SmartContract; using Neo.VM; @@ -63,4 +63,4 @@ public void System_Runtime_GetInvocationCounter() ); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/TestBlockchain.cs b/neo.UnitTests/TestBlockchain.cs index 3037e9e3dc..5276dc2e4c 100644 --- a/neo.UnitTests/TestBlockchain.cs +++ b/neo.UnitTests/TestBlockchain.cs @@ -1,4 +1,4 @@ -using Moq; +using Moq; using Neo.IO.Wrappers; using Neo.Ledger; using Neo.Persistence; @@ -59,4 +59,4 @@ public static NeoSystem InitializeMockNeoSystem() return TheNeoSystem; } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/TestDataCache.cs b/neo.UnitTests/TestDataCache.cs index 68f3b07da7..44d86141b5 100644 --- a/neo.UnitTests/TestDataCache.cs +++ b/neo.UnitTests/TestDataCache.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.IO.Caching; using System; using System.Collections.Generic; diff --git a/neo.UnitTests/TestMetaDataCache.cs b/neo.UnitTests/TestMetaDataCache.cs index 55dca734f4..0cb5646928 100644 --- a/neo.UnitTests/TestMetaDataCache.cs +++ b/neo.UnitTests/TestMetaDataCache.cs @@ -1,4 +1,4 @@ -using Neo.IO; +using Neo.IO; using Neo.IO.Caching; namespace Neo.UnitTests diff --git a/neo.UnitTests/TestUtils.cs b/neo.UnitTests/TestUtils.cs index 086098a8ec..554b492d4b 100644 --- a/neo.UnitTests/TestUtils.cs +++ b/neo.UnitTests/TestUtils.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Neo.IO; using Neo.IO.Json; using Neo.Ledger; @@ -144,4 +144,4 @@ public static void DeleteFile(string file) } } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/TestVerifiable.cs b/neo.UnitTests/TestVerifiable.cs index dfe21c2d0f..c0920df480 100644 --- a/neo.UnitTests/TestVerifiable.cs +++ b/neo.UnitTests/TestVerifiable.cs @@ -42,4 +42,4 @@ public void SerializeUnsigned(BinaryWriter writer) writer.Write((string)testStr); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/TestWalletAccount.cs b/neo.UnitTests/TestWalletAccount.cs index 5b07045d1e..b8a242c578 100644 --- a/neo.UnitTests/TestWalletAccount.cs +++ b/neo.UnitTests/TestWalletAccount.cs @@ -1,4 +1,4 @@ -using Moq; +using Moq; using Neo.SmartContract; using Neo.Wallets; using System; diff --git a/neo.UnitTests/UT_BigDecimal.cs b/neo.UnitTests/UT_BigDecimal.cs index 3975d5503e..2d359da891 100644 --- a/neo.UnitTests/UT_BigDecimal.cs +++ b/neo.UnitTests/UT_BigDecimal.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Numerics; @@ -188,4 +188,4 @@ public void TestTryParse() result.Should().Be(default(BigDecimal)); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/UT_Culture.cs b/neo.UnitTests/UT_Culture.cs index 807e67990c..37a44b24ef 100644 --- a/neo.UnitTests/UT_Culture.cs +++ b/neo.UnitTests/UT_Culture.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; diff --git a/neo.UnitTests/UT_DataCache.cs b/neo.UnitTests/UT_DataCache.cs index 85df272db5..a428e72d8d 100644 --- a/neo.UnitTests/UT_DataCache.cs +++ b/neo.UnitTests/UT_DataCache.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; using Neo.Ledger; using System.Linq; diff --git a/neo.UnitTests/UT_Helper.cs b/neo.UnitTests/UT_Helper.cs index d64d9c9173..aaad993bb1 100644 --- a/neo.UnitTests/UT_Helper.cs +++ b/neo.UnitTests/UT_Helper.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P; using Neo.SmartContract; diff --git a/neo.UnitTests/UT_NefFile.cs b/neo.UnitTests/UT_NefFile.cs index ba14496a27..056333163d 100644 --- a/neo.UnitTests/UT_NefFile.cs +++ b/neo.UnitTests/UT_NefFile.cs @@ -1,75 +1,75 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Cryptography; -using Neo.IO; -using Neo.SmartContract; -using System; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_NefFile - { - [TestMethod] - public void ParseTest() - { - var file = new NefFile() - { - Compiler = "".PadLeft(32, ' '), - Version = new Version(1, 2, 3, 4), - Script = new byte[] { 0x01, 0x02, 0x03 } - }; - - file.ScriptHash = file.Script.ToScriptHash(); - file.CheckSum = NefFile.ComputeChecksum(file); - - var data = file.ToArray(); - file = data.AsSerializable(); - - Assert.AreEqual("".PadLeft(32, ' '), file.Compiler); - Assert.AreEqual(new Version(1, 2, 3, 4), file.Version); - Assert.AreEqual(file.Script.ToScriptHash(), file.ScriptHash); - CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, file.Script); - } - - [TestMethod] - public void LimitTest() - { - var file = new NefFile() - { - Compiler = "".PadLeft(byte.MaxValue, ' '), - Version = new Version(1, 2, 3, 4), - Script = new byte[1024 * 1024], - ScriptHash = new byte[1024 * 1024].ToScriptHash(), - CheckSum = 0 - }; - - // Wrong compiler - - Assert.ThrowsException(() => file.ToArray()); - - // Wrong script - - file.Compiler = ""; - file.Script = new byte[(1024 * 1024) + 1]; - file.ScriptHash = file.Script.ToScriptHash(); - var data = file.ToArray(); - - Assert.ThrowsException(() => data.AsSerializable()); - - // Wrong script hash - - file.Script = new byte[1024 * 1024]; - data = file.ToArray(); - - Assert.ThrowsException(() => data.AsSerializable()); - - // Wrong checksum - - file.Script = new byte[1024]; - data = file.ToArray(); - file.CheckSum = NefFile.ComputeChecksum(file) + 1; - - Assert.ThrowsException(() => data.AsSerializable()); - } - } -} \ No newline at end of file +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.IO; +using Neo.SmartContract; +using System; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_NefFile + { + [TestMethod] + public void ParseTest() + { + var file = new NefFile() + { + Compiler = "".PadLeft(32, ' '), + Version = new Version(1, 2, 3, 4), + Script = new byte[] { 0x01, 0x02, 0x03 } + }; + + file.ScriptHash = file.Script.ToScriptHash(); + file.CheckSum = NefFile.ComputeChecksum(file); + + var data = file.ToArray(); + file = data.AsSerializable(); + + Assert.AreEqual("".PadLeft(32, ' '), file.Compiler); + Assert.AreEqual(new Version(1, 2, 3, 4), file.Version); + Assert.AreEqual(file.Script.ToScriptHash(), file.ScriptHash); + CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, file.Script); + } + + [TestMethod] + public void LimitTest() + { + var file = new NefFile() + { + Compiler = "".PadLeft(byte.MaxValue, ' '), + Version = new Version(1, 2, 3, 4), + Script = new byte[1024 * 1024], + ScriptHash = new byte[1024 * 1024].ToScriptHash(), + CheckSum = 0 + }; + + // Wrong compiler + + Assert.ThrowsException(() => file.ToArray()); + + // Wrong script + + file.Compiler = ""; + file.Script = new byte[(1024 * 1024) + 1]; + file.ScriptHash = file.Script.ToScriptHash(); + var data = file.ToArray(); + + Assert.ThrowsException(() => data.AsSerializable()); + + // Wrong script hash + + file.Script = new byte[1024 * 1024]; + data = file.ToArray(); + + Assert.ThrowsException(() => data.AsSerializable()); + + // Wrong checksum + + file.Script = new byte[1024]; + data = file.ToArray(); + file.CheckSum = NefFile.ComputeChecksum(file) + 1; + + Assert.ThrowsException(() => data.AsSerializable()); + } + } +} diff --git a/neo.UnitTests/UT_ProtocolSettings.cs b/neo.UnitTests/UT_ProtocolSettings.cs index 053c5d7e37..a5fa189231 100644 --- a/neo.UnitTests/UT_ProtocolSettings.cs +++ b/neo.UnitTests/UT_ProtocolSettings.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.Extensions.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; @@ -93,4 +93,4 @@ public void Cant_initialize_ProtocolSettings_twice() ProtocolSettings.Default.Magic.Should().Be(expectedMagic); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/VM/UT_Helper.cs b/neo.UnitTests/VM/UT_Helper.cs index b57ca02a7c..e9ccc539bb 100644 --- a/neo.UnitTests/VM/UT_Helper.cs +++ b/neo.UnitTests/VM/UT_Helper.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; using Neo.VM; using System; @@ -79,4 +79,4 @@ public void TestEmitAppCall3() Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(resultArray)); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index ade2472ba8..e2f4ca13f4 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.IO.Json; diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs index 6dc7f714d5..3f4e7b3a63 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using Neo.SmartContract; diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index 89499e7c57..294afd91cd 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using Neo.Wallets; diff --git a/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs b/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs index adac8a13a2..65d29e9fce 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using Neo.Wallets.NEP6; diff --git a/neo.UnitTests/Wallets/SQLite/UT_Account.cs b/neo.UnitTests/Wallets/SQLite/UT_Account.cs index c60e90fd10..0fabb80b1e 100644 --- a/neo.UnitTests/Wallets/SQLite/UT_Account.cs +++ b/neo.UnitTests/Wallets/SQLite/UT_Account.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Wallets.SQLite; using System.Text; @@ -34,4 +34,4 @@ public void TestSetAndGetPublicKeyHash() Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(account.PublicKeyHash)); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Wallets/SQLite/UT_Address.cs b/neo.UnitTests/Wallets/SQLite/UT_Address.cs index 4d3e2d6717..6157d26fe7 100644 --- a/neo.UnitTests/Wallets/SQLite/UT_Address.cs +++ b/neo.UnitTests/Wallets/SQLite/UT_Address.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Wallets.SQLite; using System.Text; @@ -24,4 +24,4 @@ public void TestSetAndGetScriptHash() Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(address.ScriptHash)); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Wallets/SQLite/UT_Contract.cs b/neo.UnitTests/Wallets/SQLite/UT_Contract.cs index 52c197a778..262c74ce08 100644 --- a/neo.UnitTests/Wallets/SQLite/UT_Contract.cs +++ b/neo.UnitTests/Wallets/SQLite/UT_Contract.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Wallets.SQLite; using System.Text; @@ -62,4 +62,4 @@ public void TestSetAndGetAddress() Assert.AreEqual(address, contract.Address); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Wallets/SQLite/UT_Key.cs b/neo.UnitTests/Wallets/SQLite/UT_Key.cs index e09896b72c..f4876cee11 100644 --- a/neo.UnitTests/Wallets/SQLite/UT_Key.cs +++ b/neo.UnitTests/Wallets/SQLite/UT_Key.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Wallets.SQLite; using System.Text; @@ -34,4 +34,4 @@ public void TestSetAndGetValue() Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(key.Value)); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs b/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs index 70f3a649cf..dcba52bc11 100644 --- a/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs +++ b/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; using Neo.Wallets; diff --git a/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs b/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs index 2a0dfd3f1d..0a1fde1321 100644 --- a/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs +++ b/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Wallets.SQLite; using System.Text; diff --git a/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs b/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs index d590640fe5..8f111f2112 100644 --- a/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs +++ b/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; using Neo.Wallets; using Neo.Wallets.SQLite; diff --git a/neo.UnitTests/Wallets/UT_AssetDescriptor.cs b/neo.UnitTests/Wallets/UT_AssetDescriptor.cs index ca80f27b28..14eb6766b1 100644 --- a/neo.UnitTests/Wallets/UT_AssetDescriptor.cs +++ b/neo.UnitTests/Wallets/UT_AssetDescriptor.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Persistence; using Neo.SmartContract.Native; @@ -48,4 +48,4 @@ public void Check_NEO() descriptor.Decimals.Should().Be(0); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Wallets/UT_KeyPair.cs b/neo.UnitTests/Wallets/UT_KeyPair.cs index dc67599111..57f06bb5a9 100644 --- a/neo.UnitTests/Wallets/UT_KeyPair.cs +++ b/neo.UnitTests/Wallets/UT_KeyPair.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.Cryptography.ECC; diff --git a/neo.UnitTests/Wallets/UT_Wallet.cs b/neo.UnitTests/Wallets/UT_Wallet.cs index 59289ae548..c09f8e26c2 100644 --- a/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/neo.UnitTests/Wallets/UT_Wallet.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.Ledger; @@ -433,4 +433,4 @@ public void TestVerifyPassword() action.ShouldNotThrow(); } } -} \ No newline at end of file +} diff --git a/neo.UnitTests/Wallets/UT_WalletAccount.cs b/neo.UnitTests/Wallets/UT_WalletAccount.cs index 15e2bd1974..413caf6880 100644 --- a/neo.UnitTests/Wallets/UT_WalletAccount.cs +++ b/neo.UnitTests/Wallets/UT_WalletAccount.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; using Neo.Wallets; diff --git a/neo.UnitTests/Wallets/UT_Wallets_Helper.cs b/neo.UnitTests/Wallets/UT_Wallets_Helper.cs index 01f47fea16..4abf10d9ed 100644 --- a/neo.UnitTests/Wallets/UT_Wallets_Helper.cs +++ b/neo.UnitTests/Wallets/UT_Wallets_Helper.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.Wallets; diff --git a/neo.UnitTests/neo.UnitTests.csproj b/neo.UnitTests/neo.UnitTests.csproj index 5d967c0845..4cbc7f4164 100644 --- a/neo.UnitTests/neo.UnitTests.csproj +++ b/neo.UnitTests/neo.UnitTests.csproj @@ -1,4 +1,4 @@ - + Exe diff --git a/neo.sln b/neo.sln index 9d4c436c0e..fd72762fc2 100644 --- a/neo.sln +++ b/neo.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26430.15 diff --git a/neo/Consensus/ChangeViewReason.cs b/neo/Consensus/ChangeViewReason.cs index 066ecc1085..87c88c74c2 100644 --- a/neo/Consensus/ChangeViewReason.cs +++ b/neo/Consensus/ChangeViewReason.cs @@ -9,4 +9,4 @@ public enum ChangeViewReason : byte TxInvalid = 0x4, BlockRejectedByPolicy = 0x5 } -} \ No newline at end of file +} diff --git a/neo/IO/Caching/ReflectionCache.cs b/neo/IO/Caching/ReflectionCache.cs index 705366e2fb..452856698c 100644 --- a/neo/IO/Caching/ReflectionCache.cs +++ b/neo/IO/Caching/ReflectionCache.cs @@ -77,4 +77,4 @@ public K CreateInstance(T key, K def = default(K)) return def; } } -} \ No newline at end of file +} diff --git a/neo/IO/Caching/ReflectionCacheAttribute.cs b/neo/IO/Caching/ReflectionCacheAttribute.cs index ba5fd4e1bc..b2ac3c3054 100644 --- a/neo/IO/Caching/ReflectionCacheAttribute.cs +++ b/neo/IO/Caching/ReflectionCacheAttribute.cs @@ -18,4 +18,4 @@ public ReflectionCacheAttribute(Type type) Type = type; } } -} \ No newline at end of file +} diff --git a/neo/Plugins/MemoryPoolTxRemovalReason.cs b/neo/Plugins/MemoryPoolTxRemovalReason.cs index d08a37eba2..ddc7e10981 100644 --- a/neo/Plugins/MemoryPoolTxRemovalReason.cs +++ b/neo/Plugins/MemoryPoolTxRemovalReason.cs @@ -11,4 +11,4 @@ public enum MemoryPoolTxRemovalReason : byte /// NoLongerValid, } -} \ No newline at end of file +} diff --git a/neo/SmartContract/ExecutionContextState.cs b/neo/SmartContract/ExecutionContextState.cs index 11e6f88a00..d26641f7dd 100644 --- a/neo/SmartContract/ExecutionContextState.cs +++ b/neo/SmartContract/ExecutionContextState.cs @@ -7,4 +7,4 @@ public class ExecutionContextState /// public UInt160 ScriptHash { get; set; } } -} \ No newline at end of file +} From 16e41783c02b8cd1b0db8fe4cb5f860032fe090f Mon Sep 17 00:00:00 2001 From: Ricardo Prado <38396062+lock9@users.noreply.github.com> Date: Tue, 27 Aug 2019 04:58:24 -0300 Subject: [PATCH 087/305] Exposing supported methods used by InteropService (#1060) * Supported methods names * dotnet format * Simplify --- neo/SmartContract/InteropService.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index b4121d7a24..8651dfbc3d 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -77,6 +77,11 @@ public static long GetPrice(uint hash, RandomAccessStack stack) return methods[hash].GetPrice(stack); } + public static Dictionary SupportedMethods() + { + return methods.ToDictionary(p => p.Key, p => p.Value.Method); + } + private static long GetStoragePrice(RandomAccessStack stack) { return (stack.Peek(1).GetByteLength() + stack.Peek(2).GetByteLength()) * GasPerByte; From edb08fa66481086dea1df810ba41c37ef5e4a368 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 27 Aug 2019 14:25:39 +0200 Subject: [PATCH 088/305] Remove lines of code (#1062) It's not working --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 77e2a2e566..ac251a7a77 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,6 @@ Current Coverage Status. - - Current total lines. - License. From cffda40f5c6509e19eb2648fbcd1588533899bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Wed, 28 Aug 2019 17:30:43 +0800 Subject: [PATCH 089/305] Cleaning .editorconfig file (#1067) --- .editorconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 2cbe224983..5acd074d26 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,4 +15,3 @@ insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 end_of_line = lf -#indent_style = tab # TODO From f06f26c98c34f62273a83e60a16b8c0cdefa6724 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 28 Aug 2019 21:05:24 +0800 Subject: [PATCH 090/305] Hide the prefix in the store (#1070) --- neo/Consensus/ConsensusContext.cs | 8 ++++---- neo/Persistence/LevelDB/LevelDBStore.cs | 12 ++++++------ neo/Persistence/Store.cs | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 4574d70f7e..f96409318a 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -19,9 +19,9 @@ namespace Neo.Consensus internal class ConsensusContext : IDisposable, ISerializable { /// - /// Prefix for saving consensus state. + /// Key for saving consensus state. /// - public const byte CN_Context = 0xf4; + private static readonly byte[] ConsensusStateKey = { 0xf4 }; public Block Block; public byte ViewNumber; @@ -143,7 +143,7 @@ public uint GetPrimaryIndex(byte viewNumber) public bool Load() { - byte[] data = store.Get(CN_Context, new byte[0]); + byte[] data = store.Get(ConsensusStateKey); if (data is null || data.Length == 0) return false; using (MemoryStream ms = new MemoryStream(data, false)) using (BinaryReader reader = new BinaryReader(ms)) @@ -401,7 +401,7 @@ public void Reset(byte viewNumber) public void Save() { - store.PutSync(CN_Context, new byte[0], this.ToArray()); + store.PutSync(ConsensusStateKey, this.ToArray()); } public void Serialize(BinaryWriter writer) diff --git a/neo/Persistence/LevelDB/LevelDBStore.cs b/neo/Persistence/LevelDB/LevelDBStore.cs index bb6ac26698..805c1fe915 100644 --- a/neo/Persistence/LevelDB/LevelDBStore.cs +++ b/neo/Persistence/LevelDB/LevelDBStore.cs @@ -34,9 +34,9 @@ public void Dispose() db.Dispose(); } - public override byte[] Get(byte prefix, byte[] key) + public override byte[] Get(byte[] key) { - if (!db.TryGet(ReadOptions.Default, SliceBuilder.Begin(prefix).Add(key), out Slice slice)) + if (!db.TryGet(ReadOptions.Default, key, out Slice slice)) return null; return slice.ToArray(); } @@ -81,14 +81,14 @@ public override MetaDataCache GetHeaderHashIndex() return new DbMetaDataCache(db, null, null, Prefixes.IX_CurrentHeader); } - public override void Put(byte prefix, byte[] key, byte[] value) + public override void Put(byte[] key, byte[] value) { - db.Put(WriteOptions.Default, SliceBuilder.Begin(prefix).Add(key), value); + db.Put(WriteOptions.Default, key, value); } - public override void PutSync(byte prefix, byte[] key, byte[] value) + public override void PutSync(byte[] key, byte[] value) { - db.Put(new WriteOptions { Sync = true }, SliceBuilder.Begin(prefix).Add(key), value); + db.Put(new WriteOptions { Sync = true }, key, value); } } } diff --git a/neo/Persistence/Store.cs b/neo/Persistence/Store.cs index 3dc7127f3a..eab5c182b9 100644 --- a/neo/Persistence/Store.cs +++ b/neo/Persistence/Store.cs @@ -14,7 +14,7 @@ public abstract class Store : IPersistence MetaDataCache IPersistence.BlockHashIndex => GetBlockHashIndex(); MetaDataCache IPersistence.HeaderHashIndex => GetHeaderHashIndex(); - public abstract byte[] Get(byte prefix, byte[] key); + public abstract byte[] Get(byte[] key); public abstract DataCache GetBlocks(); public abstract DataCache GetTransactions(); public abstract DataCache GetContracts(); @@ -22,8 +22,8 @@ public abstract class Store : IPersistence public abstract DataCache GetHeaderHashList(); public abstract MetaDataCache GetBlockHashIndex(); public abstract MetaDataCache GetHeaderHashIndex(); - public abstract void Put(byte prefix, byte[] key, byte[] value); - public abstract void PutSync(byte prefix, byte[] key, byte[] value); + public abstract void Put(byte[] key, byte[] value); + public abstract void PutSync(byte[] key, byte[] value); public abstract Snapshot GetSnapshot(); } From bc25b7b06823d0a4d3b9fce370ee4332ad5afa89 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 29 Aug 2019 04:34:30 +0800 Subject: [PATCH 091/305] Fixes `Transaction.ToJson()` (#1071) * Fixes `Transaction.ToJson()` * Fix tests --- neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs | 2 +- neo/Network/P2P/Payloads/Transaction.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 91510e0ec9..fc32744b96 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -1067,7 +1067,7 @@ public void ToJson() ((JArray)jObj["cosigners"]).Count.Should().Be(0); jObj["net_fee"].AsString().Should().Be("0"); jObj["script"].AsString().Should().Be("4220202020202020202020202020202020202020202020202020202020202020"); - jObj["sys_fee"].AsNumber().Should().Be(42); + jObj["sys_fee"].AsString().Should().Be("4200000000"); } } } diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index b83cbd977d..a2586e6da2 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -175,8 +175,8 @@ public JObject ToJson() json["version"] = Version; json["nonce"] = Nonce; json["sender"] = Sender.ToAddress(); - json["sys_fee"] = new BigDecimal(SystemFee, NativeContract.GAS.Decimals).ToString(); - json["net_fee"] = new BigDecimal(NetworkFee, NativeContract.GAS.Decimals).ToString(); + json["sys_fee"] = SystemFee.ToString(); + json["net_fee"] = NetworkFee.ToString(); json["valid_until_block"] = ValidUntilBlock; json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); json["cosigners"] = Cosigners.Select(p => p.ToJson()).ToArray(); From 1e07dd5be37a55ff03c0548e0231556636953ece Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 29 Aug 2019 06:19:28 +0200 Subject: [PATCH 092/305] Optimize mempool message (#1068) * Optimization * Prevent relay * Change comment * Optimizations --- neo/Ledger/Blockchain.cs | 14 ++++++++++---- neo/Ledger/MemoryPool.cs | 7 ++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index a74bfa981c..50393d4c8e 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -364,7 +364,7 @@ private void OnNewHeaders(Header[] headers) system.TaskManager.Tell(new TaskManager.HeaderTaskCompleted(), Sender); } - private RelayResultReason OnNewTransaction(Transaction transaction) + private RelayResultReason OnNewTransaction(Transaction transaction, bool relay) { if (ContainsTransaction(transaction.Hash)) return RelayResultReason.AlreadyExists; @@ -377,8 +377,8 @@ private RelayResultReason OnNewTransaction(Transaction transaction) if (!MemPool.TryAdd(transaction.Hash, transaction)) return RelayResultReason.OutOfMemory; - - system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = transaction }); + if (relay) + system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = transaction }); return RelayResultReason.Succeed; } @@ -405,8 +405,14 @@ protected override void OnReceive(object message) case Block block: Sender.Tell(OnNewBlock(block)); break; + case Transaction[] transactions: + { + // This message comes from a mempool's revalidation, already relayed + foreach (var tx in transactions) OnNewTransaction(tx, false); + break; + } case Transaction transaction: - Sender.Tell(OnNewTransaction(transaction)); + Sender.Tell(OnNewTransaction(transaction, true)); break; case ConsensusPayload payload: Sender.Tell(OnNewConsensus(payload)); diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 739085f5b6..a6af9fc721 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -360,9 +360,14 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) if (policyChanged) { + var tx = new List(); foreach (PoolItem item in _unverifiedSortedTransactions.Reverse()) if (item.Tx.FeePerByte >= _feePerByte) - _system.Blockchain.Tell(item.Tx, ActorRefs.NoSender); + tx.Add(item.Tx); + + if (tx.Count > 0) + _system.Blockchain.Tell(tx.ToArray(), ActorRefs.NoSender); + _unverifiedTransactions.Clear(); _unverifiedSortedTransactions.Clear(); } From 9e899073f78ad7cd095e65259205ec47b28a5c7e Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 29 Aug 2019 09:00:21 +0200 Subject: [PATCH 093/305] Fix Runtime_GetNotifications (#1073) --- neo/SmartContract/InteropService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 8651dfbc3d..6868da1886 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -238,13 +238,13 @@ private static bool Runtime_GetNotifications(ApplicationEngine engine) { var data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); if (data.Length != UInt160.Length) return false; - if (!engine.CheckArraySize(engine.Notifications.Count)) return false; var hash = new UInt160(data); IEnumerable notifications = engine.Notifications; if (!hash.Equals(UInt160.Zero)) notifications = notifications.Where(p => p.ScriptHash == hash); + if (!engine.CheckArraySize(notifications.Count())) return false; engine.CurrentContext.EvaluationStack.Push(notifications.Select(u => new VM.Types.Array(new StackItem[] { u.ScriptHash.ToArray(), u.State })).ToArray()); return true; } From ee478c97014eb10c1e4a4d8300a5af31026e97e1 Mon Sep 17 00:00:00 2001 From: Igor Machado Coelho Date: Fri, 30 Aug 2019 22:27:16 +0800 Subject: [PATCH 094/305] empty scripthash, all notifications (#1074) * empty scripthash, all notifications * || to && * Fix ut --- neo.UnitTests/SmartContract/UT_InteropService.cs | 2 +- neo/SmartContract/InteropService.cs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/neo.UnitTests/SmartContract/UT_InteropService.cs b/neo.UnitTests/SmartContract/UT_InteropService.cs index 39687fd720..6a33a32abc 100644 --- a/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -85,7 +85,7 @@ public void Runtime_GetNotifications_Test() // Receive all notifications - script.EmitPush(UInt160.Zero.ToArray()); + script.EmitPush(new byte[0]); script.EmitSysCall(InteropService.System_Runtime_GetNotifications); // Execute diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 6868da1886..7aa7fd7284 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -236,13 +236,15 @@ private static bool Runtime_Serialize(ApplicationEngine engine) private static bool Runtime_GetNotifications(ApplicationEngine engine) { - var data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - if (data.Length != UInt160.Length) return false; + byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + if ((data.Length != 0) && (data.Length != UInt160.Length)) return false; - var hash = new UInt160(data); IEnumerable notifications = engine.Notifications; - if (!hash.Equals(UInt160.Zero)) + if (data.Length == UInt160.Length) // must filter by scriptHash + { + var hash = new UInt160(data); notifications = notifications.Where(p => p.ScriptHash == hash); + } if (!engine.CheckArraySize(notifications.Count())) return false; engine.CurrentContext.EvaluationStack.Push(notifications.Select(u => new VM.Types.Array(new StackItem[] { u.ScriptHash.ToArray(), u.State })).ToArray()); From 36d8bc03624a6edf6b016184fb970d76897934fe Mon Sep 17 00:00:00 2001 From: Ricardo Prado <38396062+lock9@users.noreply.github.com> Date: Sun, 1 Sep 2019 00:45:31 -0300 Subject: [PATCH 095/305] Issues templates for Bug report and Feature Request (#1034) --- .github/ISSUE_TEMPLATE/bug_report.md | 31 +++++++++++++++++++ .../feature-or-enhancement-request.md | 26 ++++++++++++++++ .github/ISSUE_TEMPLATE/questions.md | 9 ++++++ 3 files changed, 66 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature-or-enhancement-request.md create mode 100644 .github/ISSUE_TEMPLATE/questions.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..f69eccca73 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to detail an error or unexpected behavior +title: '' +labels: '' +assignees: '' +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Open the project, run '...' +2. Type '...' or do '...' +3. ... + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Platform:** + - OS: [e.g. Windows 10 x64] + - Version [e.g. neo-cli 2.10.2] + +**(Optional) Additional context** +Add any other context about the problem here. + +However, if your issue does not fit these aforementioned criteria, or it can be understood in another manner, feel free to open it in a different format. diff --git a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md new file mode 100644 index 0000000000..30077088b9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md @@ -0,0 +1,26 @@ +--- +name: Feature or enhancement request +about: Suggest an idea for Neo +title: '' +labels: discussion +assignees: '' +--- + +**Summary** +A summary of the problem you want to solve or metric you want to improve + +**Do you have any solution you want to propose?** +A clear and concise description of what you expect with this change. + +**Where in software does this update applies to?** +- [ ] Compiler +- [ ] Consensus +- [ ] CLI +- [ ] Plugins +- [ ] Ledger +- [ ] Network Policy +- [ ] P2P (TCP) +- [ ] RPC (HTTP) +- [ ] SDK +- [ ] VM +- [ ] Other: diff --git a/.github/ISSUE_TEMPLATE/questions.md b/.github/ISSUE_TEMPLATE/questions.md new file mode 100644 index 0000000000..4d97925f89 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/questions.md @@ -0,0 +1,9 @@ +--- +name: Questions +about: Questions about Neo Platform +title: '' +labels: question +assignees: '' +--- + +**Delete this: We would like to use GitHub for bug reports and feature requests only however if you are unable to get support from our team in: our [Discord](https://discord.io/neo) server or in our [offical documentation](https://docs.neo.org/docs/en-us/index.html), feel encouraged to create an issue here on GitHub.** From c212d6e969122bb436e7e818ae4afe49c58291de Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 2 Sep 2019 23:45:39 +0800 Subject: [PATCH 096/305] Update NuGets (#1080) * Update NuGets * Clean BOM * Update Neo.VM --- neo/neo.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/neo/neo.csproj b/neo/neo.csproj index 54ba724da9..d6d8f7cb4d 100644 --- a/neo/neo.csproj +++ b/neo/neo.csproj @@ -21,15 +21,15 @@ - - + + - - + + - + From 44b81048939f02699bf67bcdd36455edc6efbb80 Mon Sep 17 00:00:00 2001 From: cloud8little <34291844+cloud8little@users.noreply.github.com> Date: Wed, 11 Sep 2019 02:19:18 +0800 Subject: [PATCH 097/305] Downgrade Sqlite to 2.1.4 (#1086) --- neo/neo.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/neo/neo.csproj b/neo/neo.csproj index d6d8f7cb4d..556487c280 100644 --- a/neo/neo.csproj +++ b/neo/neo.csproj @@ -27,7 +27,8 @@ - + + From 4c54f47d40fc7155eec53cc3b336ac6f9609e2b5 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 12 Sep 2019 15:56:27 +0800 Subject: [PATCH 098/305] Fix consensus (#1083) --- neo/Consensus/ConsensusContext.cs | 14 ++++++++------ neo/Consensus/ConsensusService.cs | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index f96409318a..0d9edbb3dc 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -103,10 +103,7 @@ public void Deserialize(BinaryReader reader) Block.ConsensusData = reader.ReadSerializable(); ViewNumber = reader.ReadByte(); TransactionHashes = reader.ReadSerializableArray(); - if (TransactionHashes.Length == 0) - TransactionHashes = null; Transaction[] transactions = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); - Transactions = transactions.Length == 0 ? null : transactions.ToDictionary(p => p.Hash); PreparationPayloads = new ConsensusPayload[reader.ReadVarInt(Blockchain.MaxValidators)]; for (int i = 0; i < PreparationPayloads.Length; i++) PreparationPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; @@ -119,6 +116,9 @@ public void Deserialize(BinaryReader reader) LastChangeViewPayloads = new ConsensusPayload[reader.ReadVarInt(Blockchain.MaxValidators)]; for (int i = 0; i < LastChangeViewPayloads.Length; i++) LastChangeViewPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; + if (TransactionHashes.Length == 0 && !RequestSentOrReceived) + TransactionHashes = null; + Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash); } public void Dispose() @@ -340,8 +340,7 @@ public void Reset(byte viewNumber) { PrevHash = Snapshot.CurrentBlockHash, Index = Snapshot.Height + 1, - NextConsensus = Blockchain.GetConsensusAddress(NativeContract.NEO.GetValidators(Snapshot).ToArray()), - ConsensusData = new ConsensusData() + NextConsensus = Blockchain.GetConsensusAddress(NativeContract.NEO.GetValidators(Snapshot).ToArray()) }; var pv = Validators; Validators = NativeContract.NEO.GetNextBlockValidators(Snapshot); @@ -390,7 +389,10 @@ public void Reset(byte viewNumber) LastChangeViewPayloads[i] = null; } ViewNumber = viewNumber; - Block.ConsensusData.PrimaryIndex = GetPrimaryIndex(viewNumber); + Block.ConsensusData = new ConsensusData + { + PrimaryIndex = GetPrimaryIndex(viewNumber) + }; Block.MerkleRoot = null; Block.Timestamp = 0; Block.Transactions = null; diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 4d8b70a333..a65916b5aa 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -202,7 +202,7 @@ private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) if (message.NewViewNumber <= expectedView) return; - Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber}"); + Log($"{nameof(OnChangeViewReceived)}: height={payload.BlockIndex} view={message.ViewNumber} index={payload.ValidatorIndex} nv={message.NewViewNumber} reason={message.Reason}"); context.ChangeViewPayloads[payload.ValidatorIndex] = payload; CheckExpectedView(message.NewViewNumber); } From f76c0375ba2262132d72ab6dc0cbb73b8dc75a4e Mon Sep 17 00:00:00 2001 From: erikzhang Date: Mon, 16 Sep 2019 18:02:40 +0800 Subject: [PATCH 099/305] v3.0.0-preview1 --- neo/neo.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/neo.csproj b/neo/neo.csproj index 556487c280..724090c004 100644 --- a/neo/neo.csproj +++ b/neo/neo.csproj @@ -3,7 +3,7 @@ 2015-2019 The Neo Project Neo - 2.10.1 + 3.0.0-preview1 The Neo Project netstandard2.0;net47 true @@ -30,7 +30,7 @@ - + From 9c054321cedfaf118764886994a97dbe370665a1 Mon Sep 17 00:00:00 2001 From: Krain Chen Date: Fri, 20 Sep 2019 20:54:42 +0800 Subject: [PATCH 100/305] C# SDK Add Transaction Manager and Smart Contract APIs (#1026) * Add NEO SDK based on RPC client * add rpc interface methods for neo3 * update unit test * add unit test * Update TransactionHelper.cs Changed for neo 3.0, not final yet. * implement sdk rpc client methods * backup files * change class name * remove uncompleted modules for pull request * change json deserialize method with Neo JObject * modified JSON implementation, added FromJson() * more RPC change * PR correction * RPC module fix, remove newton.json * fix * fix getblock issue * PR correction * PR Correction * PR Correction: rename RPC models * PR Correction * resolve conflicts * Clean code * Clean code * Clean code * Clean code * Update RpcValidateAddressResult.cs * Clean code * PR correction * Move test file to the right place * Added SDK Transaction module. * Add SDK SmartContract module * height = count - 1 * Add sign function for TxManager * Add Deploy Contract * Add unit tests * Add Network Fee calculate for TxManager, add unit tests * adjust cosigners change * PR Correction * Remove empty line * Rename TxManager to TransactionManager * PR correction * PR correction * change namespace * Reorder methods * Remove TransactionContext * fix unit test * Remove `virtual` * Remove virtuals * Remove ScriptHash from KeyPair * Add comments * Add comments * Adjust to Neo_Contract_Create parameter * use default mainfest * fix unit test * Fix typo * use manifest as parameter * add cosigner for nep5 transfer * code clean * Update neo.UnitTests/Network/RPC/UT_RpcClient.cs Co-Authored-By: Shargon * move MakeScript to VM.Helper * Add unit test for InteropInterface * PR Correction * Add unit test --- .../Network/RPC/UT_ContractClient.cs | 68 ++++++ neo.UnitTests/Network/RPC/UT_Nep5API.cs | 89 +++++++ neo.UnitTests/Network/RPC/UT_PolicyAPI.cs | 69 ++++++ neo.UnitTests/Network/RPC/UT_RpcClient.cs | 24 +- .../Network/RPC/UT_TransactionManager.cs | 194 +++++++++++++++ neo.UnitTests/VM/UT_Helper.cs | 60 +++++ neo/Network/RPC/ContractClient.cs | 65 ++++++ neo/Network/RPC/Models/RpcInvokeResult.cs | 11 +- neo/Network/RPC/Nep5API.cs | 97 ++++++++ neo/Network/RPC/PolicyAPI.cs | 57 +++++ neo/Network/RPC/RpcClient.cs | 43 ++-- neo/Network/RPC/TransactionManager.cs | 220 ++++++++++++++++++ neo/SmartContract/Contract.cs | 14 ++ .../ContractParametersContext.cs | 27 +++ neo/VM/Helper.cs | 81 +++++++ neo/Wallets/Wallet.cs | 51 ++-- 16 files changed, 1112 insertions(+), 58 deletions(-) create mode 100644 neo.UnitTests/Network/RPC/UT_ContractClient.cs create mode 100644 neo.UnitTests/Network/RPC/UT_Nep5API.cs create mode 100644 neo.UnitTests/Network/RPC/UT_PolicyAPI.cs create mode 100644 neo.UnitTests/Network/RPC/UT_TransactionManager.cs create mode 100644 neo/Network/RPC/ContractClient.cs create mode 100644 neo/Network/RPC/Nep5API.cs create mode 100644 neo/Network/RPC/PolicyAPI.cs create mode 100644 neo/Network/RPC/TransactionManager.cs diff --git a/neo.UnitTests/Network/RPC/UT_ContractClient.cs b/neo.UnitTests/Network/RPC/UT_ContractClient.cs new file mode 100644 index 0000000000..fde8a7747d --- /dev/null +++ b/neo.UnitTests/Network/RPC/UT_ContractClient.cs @@ -0,0 +1,68 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.Network.RPC; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; + +namespace Neo.UnitTests.Network.RPC +{ + [TestClass] + public class UT_ContractClient + { + Mock rpcClientMock; + KeyPair keyPair1; + UInt160 sender; + + [TestInitialize] + public void TestSetup() + { + keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); + sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); + rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]); + } + + [TestMethod] + public void TestMakeScript() + { + byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); + + Assert.AreEqual("14000000000000000000000000000000000000000051c10962616c616e63654f66142582d1b275e86c8f0e93a9b2facd5fdb760976a168627d5b52", + testScript.ToHexString()); + } + + [TestMethod] + public void TestInvoke() + { + byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.ByteArray, Value = "00e057eb481b".HexToBytes() }); + + ContractClient contractClient = new ContractClient(rpcClientMock.Object); + var result = contractClient.TestInvoke(NativeContract.GAS.Hash, "balanceOf", UInt160.Zero); + + Assert.AreEqual(30000000000000L, (long)result.Stack[0].ToStackItem().GetBigInteger()); + } + + [TestMethod] + public void TestDeployContract() + { + byte[] script; + var manifest = ContractManifest.CreateDefault(new byte[1].ToScriptHash()); + manifest.Features = ContractFeatures.HasStorage | ContractFeatures.Payable; + using (ScriptBuilder sb = new ScriptBuilder()) + { + sb.EmitSysCall(InteropService.Neo_Contract_Create, new byte[1], manifest.ToString()); + script = sb.ToArray(); + } + + UT_TransactionManager.MockInvokeScript(rpcClientMock, script, new ContractParameter()); + + ContractClient contractClient = new ContractClient(rpcClientMock.Object); + var result = contractClient.DeployContract(new byte[1], manifest, keyPair1); + + Assert.IsNotNull(result); + } + } +} diff --git a/neo.UnitTests/Network/RPC/UT_Nep5API.cs b/neo.UnitTests/Network/RPC/UT_Nep5API.cs new file mode 100644 index 0000000000..72e0c29487 --- /dev/null +++ b/neo.UnitTests/Network/RPC/UT_Nep5API.cs @@ -0,0 +1,89 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.Network.RPC; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using System.Numerics; + +namespace Neo.UnitTests.Network.RPC +{ + [TestClass] + public class UT_Nep5API + { + Mock rpcClientMock; + KeyPair keyPair1; + UInt160 sender; + Nep5API nep5API; + + [TestInitialize] + public void TestSetup() + { + keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); + sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); + rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]); + nep5API = new Nep5API(rpcClientMock.Object); + } + + [TestMethod] + public void TestBalanceOf() + { + byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(10000) }); + + var balance = nep5API.BalanceOf(NativeContract.GAS.Hash, UInt160.Zero); + Assert.AreEqual(10000, (int)balance); + } + + [TestMethod] + public void TestGetName() + { + byte[] testScript = NativeContract.GAS.Hash.MakeScript("name"); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.String, Value = NativeContract.GAS.Name }); + + var result = nep5API.Name(NativeContract.GAS.Hash); + Assert.AreEqual(NativeContract.GAS.Name, result); + } + + [TestMethod] + public void TestGetSymbol() + { + byte[] testScript = NativeContract.GAS.Hash.MakeScript("symbol"); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.String, Value = NativeContract.GAS.Symbol }); + + var result = nep5API.Symbol(NativeContract.GAS.Hash); + Assert.AreEqual(NativeContract.GAS.Symbol, result); + } + + [TestMethod] + public void TestGetDecimals() + { + byte[] testScript = NativeContract.GAS.Hash.MakeScript("decimals"); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(NativeContract.GAS.Decimals) }); + + var result = nep5API.Decimals(NativeContract.GAS.Hash); + Assert.AreEqual(NativeContract.GAS.Decimals, (byte)result); + } + + [TestMethod] + public void TestGetTotalSupply() + { + byte[] testScript = NativeContract.GAS.Hash.MakeScript("totalSupply"); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_00000000) }); + + var result = nep5API.TotalSupply(NativeContract.GAS.Hash); + Assert.AreEqual(1_00000000, (int)result); + } + + [TestMethod] + public void TestTransfer() + { + byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, new BigInteger(1_00000000)); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter()); + + var result = nep5API.Transfer(NativeContract.GAS.Hash, keyPair1, UInt160.Zero, new BigInteger(1_00000000)); + Assert.IsNotNull(result); + } + } +} diff --git a/neo.UnitTests/Network/RPC/UT_PolicyAPI.cs b/neo.UnitTests/Network/RPC/UT_PolicyAPI.cs new file mode 100644 index 0000000000..6b6c449111 --- /dev/null +++ b/neo.UnitTests/Network/RPC/UT_PolicyAPI.cs @@ -0,0 +1,69 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.Network.RPC; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using System.Numerics; + +namespace Neo.UnitTests.Network.RPC +{ + [TestClass] + public class UT_PolicyAPI + { + Mock rpcClientMock; + KeyPair keyPair1; + UInt160 sender; + PolicyAPI policyAPI; + + [TestInitialize] + public void TestSetup() + { + keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); + sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); + rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]); + policyAPI = new PolicyAPI(rpcClientMock.Object); + } + + [TestMethod] + public void TestGetMaxTransactionsPerBlock() + { + byte[] testScript = NativeContract.Policy.Hash.MakeScript("getMaxTransactionsPerBlock"); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(512) }); + + var result = policyAPI.GetMaxTransactionsPerBlock(); + Assert.AreEqual(512u, result); + } + + [TestMethod] + public void TestGetMaxBlockSize() + { + byte[] testScript = NativeContract.Policy.Hash.MakeScript("getMaxBlockSize"); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1024u * 256u) }); + + var result = policyAPI.GetMaxBlockSize(); + Assert.AreEqual(1024u * 256u, result); + } + + [TestMethod] + public void TestGetFeePerByte() + { + byte[] testScript = NativeContract.Policy.Hash.MakeScript("getFeePerByte"); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1000) }); + + var result = policyAPI.GetFeePerByte(); + Assert.AreEqual(1000L, result); + } + + [TestMethod] + public void TestGetBlockedAccounts() + { + byte[] testScript = NativeContract.Policy.Hash.MakeScript("getBlockedAccounts"); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Array, Value = new[] { new ContractParameter { Type = ContractParameterType.Hash160, Value = UInt160.Zero } } }); + + var result = policyAPI.GetBlockedAccounts(); + Assert.AreEqual(UInt160.Zero, result[0]); + } + } +} diff --git a/neo.UnitTests/Network/RPC/UT_RpcClient.cs b/neo.UnitTests/Network/RPC/UT_RpcClient.cs index 76cf7556bf..0875a5a793 100644 --- a/neo.UnitTests/Network/RPC/UT_RpcClient.cs +++ b/neo.UnitTests/Network/RPC/UT_RpcClient.cs @@ -119,7 +119,7 @@ public void TestGetBlock() { // create block var block = new Block(); - TestUtils.SetupBlockWithValues(block, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, 0); + TestUtils.SetupBlockWithValues(block, UInt256.Zero, out _, out UInt160 _, out ulong _, out uint _, out Witness _, out Transaction[] _, 0); block.Transactions = new[] { @@ -158,7 +158,7 @@ public void TestGetBlockCount() MockResponse(response.ToString()); var result = rpc.GetBlockCount(); - Assert.AreEqual(100, result); + Assert.AreEqual(100u, result); } [TestMethod] @@ -187,7 +187,7 @@ public void TestGetBlockHeaderHex() public void TestGetBlockHeader() { Header header = new Header(); - TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal); + TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 _, out UInt160 _, out ulong _, out uint _, out Witness _); JObject json = header.ToJson(); JObject response = CreateResponse(1); @@ -231,12 +231,16 @@ public void TestGetConnectionCount() [TestMethod] public void TestGetContractState() { - var sb = new ScriptBuilder(); - sb.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); + byte[] script; + using (var sb = new ScriptBuilder()) + { + sb.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); + script = sb.ToArray(); + } ContractState state = new ContractState { - Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(sb.ToArray()).ToArray(), + Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script).ToArray(), Manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")) }; @@ -316,7 +320,7 @@ public void TestGetRawMempoolBoth() MockResponse(response.ToString()); var result = rpc.GetRawMempoolBoth(); - Assert.AreEqual((uint)65535, result.Height); + Assert.AreEqual(65535u, result.Height); Assert.AreEqual("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e", result.Verified[0]); Assert.AreEqual("0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6", result.UnVerified[1]); } @@ -476,7 +480,7 @@ public void TestInvokeScript() response["result"] = json; MockResponse(response.ToString()); - var result = rpc.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191"); + var result = rpc.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191".HexToBytes()); Assert.AreEqual(json.ToString(), result.ToJson().ToString()); } @@ -507,7 +511,7 @@ public void TestSendRawTransaction() response["result"] = json; MockResponse(response.ToString()); - var result = rpc.SendRawTransaction("80000001195876cb34364dc38b730077156c6bc3a7fc570044a66fbfeeea56f71327e8ab0000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500c65eaf440000000f9a23e06f74cf86b8827a9108ec2e0f89ad956c9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50092e14b5e00000030aab52ad93f6ce17ca07fa88fc191828c58cb71014140915467ecd359684b2dc358024ca750609591aa731a0b309c7fb3cab5cd0836ad3992aa0a24da431f43b68883ea5651d548feb6bd3c8e16376e6e426f91f84c58232103322f35c7819267e721335948d385fae5be66e7ba8c748ac15467dcca0693692dac"); + var result = rpc.SendRawTransaction("80000001195876cb34364dc38b730077156c6bc3a7fc570044a66fbfeeea56f71327e8ab0000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500c65eaf440000000f9a23e06f74cf86b8827a9108ec2e0f89ad956c9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50092e14b5e00000030aab52ad93f6ce17ca07fa88fc191828c58cb71014140915467ecd359684b2dc358024ca750609591aa731a0b309c7fb3cab5cd0836ad3992aa0a24da431f43b68883ea5651d548feb6bd3c8e16376e6e426f91f84c58232103322f35c7819267e721335948d385fae5be66e7ba8c748ac15467dcca0693692dac".HexToBytes()); Assert.AreEqual(json.ToString(), ((JObject)result).ToString()); } @@ -519,7 +523,7 @@ public void TestSubmitBlock() response["result"] = json; MockResponse(response.ToString()); - var result = rpc.SubmitBlock("03febccf81ac85e3d795bc5cbd4e84e907812aa3"); + var result = rpc.SubmitBlock("03febccf81ac85e3d795bc5cbd4e84e907812aa3".HexToBytes()); Assert.AreEqual(json.ToString(), ((JObject)result).ToString()); } diff --git a/neo.UnitTests/Network/RPC/UT_TransactionManager.cs b/neo.UnitTests/Network/RPC/UT_TransactionManager.cs new file mode 100644 index 0000000000..ee55f81bb9 --- /dev/null +++ b/neo.UnitTests/Network/RPC/UT_TransactionManager.cs @@ -0,0 +1,194 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Json; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.Network.RPC; +using Neo.Network.RPC.Models; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using System; +using System.Linq; +using System.Numerics; + +namespace Neo.UnitTests.Network.RPC +{ + [TestClass] + public class UT_TransactionManager + { + TransactionManager txManager; + Mock rpcClientMock; + KeyPair keyPair1; + KeyPair keyPair2; + UInt160 sender; + + [TestInitialize] + public void TestSetup() + { + keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); + keyPair2 = new KeyPair(Wallet.GetPrivateKeyFromWIF("L2LGkrwiNmUAnWYb1XGd5mv7v2eDf6P4F3gHyXSrNJJR4ArmBp7Q")); + sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); + rpcClientMock = MockRpcClient(sender, new byte[1]); + } + + public static Mock MockRpcClient(UInt160 sender, byte[] script) + { + var mockRpc = new Mock(MockBehavior.Strict, "http://seed1.neo.org:10331"); + + // MockHeight + mockRpc.Setup(p => p.RpcSend("getblockcount")).Returns(100).Verifiable(); + + // MockGasBalance + byte[] balanceScript = NativeContract.GAS.Hash.MakeScript("balanceOf", sender); + var balanceResult = new ContractParameter() { Type = ContractParameterType.Integer, Value = BigInteger.Parse("10000000000000000") }; + + MockInvokeScript(mockRpc, balanceScript, balanceResult); + + // MockFeePerByte + byte[] policyScript = NativeContract.Policy.Hash.MakeScript("getFeePerByte"); + var policyResult = new ContractParameter() { Type = ContractParameterType.Integer, Value = BigInteger.Parse("1000") }; + + MockInvokeScript(mockRpc, policyScript, policyResult); + + // MockGasConsumed + var result = new ContractParameter(); + MockInvokeScript(mockRpc, script, result); + + return mockRpc; + } + + public static void MockInvokeScript(Mock mockClient, byte[] script, params ContractParameter[] parameters) + { + var result = new RpcInvokeResult() + { + Stack = parameters, + GasConsumed = "100", + Script = script.ToHexString(), + State = "", + Tx = "" + }; + + mockClient.Setup(p => p.RpcSend("invokescript", It.Is(j => j.AsString() == script.ToHexString()))) + .Returns(result.ToJson()) + .Verifiable(); + } + + [TestMethod] + public void TestMakeTransaction() + { + txManager = new TransactionManager(rpcClientMock.Object, sender); + + TransactionAttribute[] attributes = new TransactionAttribute[1] + { + new TransactionAttribute + { + Usage = TransactionAttributeUsage.Url, + Data = "53616d706c6555726c".HexToBytes() // "SampleUrl" + } + }; + + byte[] script = new byte[1]; + txManager.MakeTransaction(script, attributes, null, 60000); + + var tx = txManager.Tx; + Assert.AreEqual("53616d706c6555726c", tx.Attributes[0].Data.ToHexString()); + Assert.AreEqual(0, tx.SystemFee % (long)NativeContract.GAS.Factor); + Assert.AreEqual(60000, tx.NetworkFee); + } + + [TestMethod] + public void TestSign() + { + txManager = new TransactionManager(rpcClientMock.Object, sender); + + TransactionAttribute[] attributes = new TransactionAttribute[1] + { + new TransactionAttribute + { + Usage = TransactionAttributeUsage.Url, + Data = "53616d706c6555726c".HexToBytes() // "SampleUrl" + } + }; + + byte[] script = new byte[1]; + txManager.MakeTransaction(script, attributes) + .AddSignature(keyPair1) + .Sign(); + + // get signature from Witnesses + var tx = txManager.Tx; + byte[] signature = tx.Witnesses[0].InvocationScript.Skip(1).ToArray(); + + Assert.IsTrue(Crypto.Default.VerifySignature(tx.GetHashData(), signature, keyPair1.PublicKey.EncodePoint(false).Skip(1).ToArray())); + + // duplicate sign should not add new witness + txManager.AddSignature(keyPair1).Sign(); + Assert.AreEqual(1, txManager.Tx.Witnesses.Length); + + // throw exception when the KeyPair is wrong + Assert.ThrowsException(() => txManager.AddSignature(keyPair2)); + } + + [TestMethod] + public void TestSignMulti() + { + txManager = new TransactionManager(rpcClientMock.Object, sender); + + var multiContract = Contract.CreateMultiSigContract(2, keyPair1.PublicKey, keyPair2.PublicKey); + + // Cosigner needs multi signature + Cosigner[] cosigners = new Cosigner[1] + { + new Cosigner + { + Account = multiContract.ScriptHash, + Scopes = WitnessScope.Global + } + }; + + byte[] script = new byte[1]; + txManager.MakeTransaction(script, null, cosigners, 0_10000000) + .AddMultiSig(keyPair1, 2, keyPair1.PublicKey, keyPair2.PublicKey) + .AddMultiSig(keyPair2, 2, keyPair1.PublicKey, keyPair2.PublicKey) + .AddSignature(keyPair1) + .Sign(); + + var store = TestBlockchain.GetStore(); + var snapshot = store.GetSnapshot(); + + var tx = txManager.Tx; + Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + } + + [TestMethod] + public void TestAddWitness() + { + txManager = new TransactionManager(rpcClientMock.Object, sender); + + // Cosigner as contract scripthash + Cosigner[] cosigners = new Cosigner[1] + { + new Cosigner + { + Account = UInt160.Zero, + Scopes = WitnessScope.Global + } + }; + + byte[] script = new byte[1]; + txManager.MakeTransaction(script, null, cosigners, 0_10000000); + txManager.AddWitness(UInt160.Zero); + txManager.AddSignature(keyPair1); + txManager.Sign(); + + var tx = txManager.Tx; + Assert.AreEqual(2, tx.Witnesses.Length); + Assert.AreEqual(0, tx.Witnesses[0].VerificationScript.Length); + Assert.AreEqual(0, tx.Witnesses[0].InvocationScript.Length); + } + } +} diff --git a/neo.UnitTests/VM/UT_Helper.cs b/neo.UnitTests/VM/UT_Helper.cs index e9ccc539bb..cf5ad0bff5 100644 --- a/neo.UnitTests/VM/UT_Helper.cs +++ b/neo.UnitTests/VM/UT_Helper.cs @@ -1,7 +1,11 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; using Neo.SmartContract; using Neo.VM; using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; using System.Text; namespace Neo.UnitTests.IO @@ -78,5 +82,61 @@ public void TestEmitAppCall3() byte[] resultArray = sb.ToArray(); Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(resultArray)); } + + [TestMethod] + public void TestToParameter() + { + StackItem byteItem = "00e057eb481b".HexToBytes(); + Assert.AreEqual(30000000000000L, (long)new BigInteger(byteItem.ToParameter().Value as byte[])); + + StackItem boolItem = false; + Assert.AreEqual(false, (bool)boolItem.ToParameter().Value); + + StackItem intItem = new BigInteger(1000); + Assert.AreEqual(1000, (BigInteger)intItem.ToParameter().Value); + + StackItem interopItem = new VM.Types.InteropInterface("test"); + Assert.AreEqual(null, interopItem.ToParameter().Value); + + StackItem arrayItem = new VM.Types.Array(new[] { byteItem, boolItem, intItem, interopItem }); + Assert.AreEqual(1000, (BigInteger)(arrayItem.ToParameter().Value as List)[2].Value); + + StackItem mapItem = new VM.Types.Map(new Dictionary(new[] { new KeyValuePair(byteItem, intItem) })); + Assert.AreEqual(1000, (BigInteger)(mapItem.ToParameter().Value as List>)[0].Value.Value); + } + + [TestMethod] + public void TestToStackItem() + { + ContractParameter byteParameter = new ContractParameter { Type = ContractParameterType.ByteArray, Value = "00e057eb481b".HexToBytes() }; + Assert.AreEqual(30000000000000L, (long)byteParameter.ToStackItem().GetBigInteger()); + + ContractParameter boolParameter = new ContractParameter { Type = ContractParameterType.Boolean, Value = false }; + Assert.AreEqual(false, boolParameter.ToStackItem().GetBoolean()); + + ContractParameter intParameter = new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1000) }; + Assert.AreEqual(1000, intParameter.ToStackItem().GetBigInteger()); + + ContractParameter h160Parameter = new ContractParameter { Type = ContractParameterType.Hash160, Value = UInt160.Zero }; + Assert.AreEqual(0, h160Parameter.ToStackItem().GetBigInteger()); + + ContractParameter h256Parameter = new ContractParameter { Type = ContractParameterType.Hash256, Value = UInt256.Zero }; + Assert.AreEqual(0, h256Parameter.ToStackItem().GetBigInteger()); + + ContractParameter pkParameter = new ContractParameter { Type = ContractParameterType.PublicKey, Value = ECPoint.Parse("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575", ECCurve.Secp256r1) }; + Assert.AreEqual("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575", pkParameter.ToStackItem().GetByteArray().ToHexString()); + + ContractParameter strParameter = new ContractParameter { Type = ContractParameterType.String, Value = "test😂👍" }; + Assert.AreEqual("test😂👍", Encoding.UTF8.GetString(strParameter.ToStackItem().GetByteArray())); + + ContractParameter interopParameter = new ContractParameter { Type = ContractParameterType.InteropInterface }; + Assert.AreEqual(null, interopParameter.ToStackItem()); + + ContractParameter arrayParameter = new ContractParameter { Type = ContractParameterType.Array, Value = new[] { byteParameter, boolParameter, intParameter, h160Parameter, h256Parameter, pkParameter, strParameter, interopParameter }.ToList() }; + Assert.AreEqual(1000, ((VM.Types.Array)arrayParameter.ToStackItem())[2].GetBigInteger()); + + ContractParameter mapParameter = new ContractParameter { Type = ContractParameterType.Map, Value = new[] { new KeyValuePair(byteParameter, pkParameter) } }; + Assert.AreEqual(30000000000000L, (long)((VM.Types.Map)mapParameter.ToStackItem()).Keys.First().GetBigInteger()); + } } } diff --git a/neo/Network/RPC/ContractClient.cs b/neo/Network/RPC/ContractClient.cs new file mode 100644 index 0000000000..a329e07674 --- /dev/null +++ b/neo/Network/RPC/ContractClient.cs @@ -0,0 +1,65 @@ +using Neo.Network.P2P.Payloads; +using Neo.Network.RPC.Models; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; +using Neo.VM; +using Neo.Wallets; + +namespace Neo.Network.RPC +{ + /// + /// Contract related operations through RPC API + /// + public class ContractClient + { + protected readonly RpcClient rpcClient; + + /// + /// ContractClient Constructor + /// + /// the RPC client to call NEO RPC methods + public ContractClient(RpcClient rpc) + { + rpcClient = rpc; + } + + /// + /// Use RPC method to test invoke operation. + /// + /// contract script hash + /// contract operation + /// operation arguments + /// + public RpcInvokeResult TestInvoke(UInt160 scriptHash, string operation, params object[] args) + { + byte[] script = scriptHash.MakeScript(operation, args); + return rpcClient.InvokeScript(script); + } + + /// + /// Deploy Contract, return signed transaction + /// + /// contract script + /// contract manifest + /// sender KeyPair + /// transaction NetworkFee, set to be 0 if you don't need higher priority + /// + public Transaction DeployContract(byte[] contractScript, ContractManifest manifest, KeyPair key, long networkFee = 0) + { + byte[] script; + using (ScriptBuilder sb = new ScriptBuilder()) + { + sb.EmitSysCall(InteropService.Neo_Contract_Create, contractScript, manifest.ToString()); + script = sb.ToArray(); + } + + Transaction tx = new TransactionManager(rpcClient, Contract.CreateSignatureRedeemScript(key.PublicKey).ToScriptHash()) + .MakeTransaction(script, null, null, networkFee) + .AddSignature(key) + .Sign() + .Tx; + + return tx; + } + } +} diff --git a/neo/Network/RPC/Models/RpcInvokeResult.cs b/neo/Network/RPC/Models/RpcInvokeResult.cs index 73f2c6a2af..c56307950a 100644 --- a/neo/Network/RPC/Models/RpcInvokeResult.cs +++ b/neo/Network/RPC/Models/RpcInvokeResult.cs @@ -1,24 +1,19 @@ using Neo.IO.Json; -using Newtonsoft.Json; +using Neo.SmartContract; using System.Linq; namespace Neo.Network.RPC.Models { public class RpcInvokeResult { - [JsonProperty(PropertyName = "script")] public string Script { get; set; } - [JsonProperty(PropertyName = "state")] public string State { get; set; } - [JsonProperty(PropertyName = "gas_consumed")] public string GasConsumed { get; set; } - [JsonProperty(PropertyName = "stack")] - public RpcStack[] Stack { get; set; } + public ContractParameter[] Stack { get; set; } - [JsonProperty(PropertyName = "tx")] public string Tx { get; set; } public JObject ToJson() @@ -39,7 +34,7 @@ public static RpcInvokeResult FromJson(JObject json) invokeScriptResult.State = json["state"].AsString(); invokeScriptResult.GasConsumed = json["gas_consumed"].AsString(); invokeScriptResult.Tx = json["tx"].AsString(); - invokeScriptResult.Stack = ((JArray)json["stack"]).Select(p => RpcStack.FromJson(p)).ToArray(); + invokeScriptResult.Stack = ((JArray)json["stack"]).Select(p => ContractParameter.FromJson(p)).ToArray(); return invokeScriptResult; } } diff --git a/neo/Network/RPC/Nep5API.cs b/neo/Network/RPC/Nep5API.cs new file mode 100644 index 0000000000..abfad6c9ed --- /dev/null +++ b/neo/Network/RPC/Nep5API.cs @@ -0,0 +1,97 @@ +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.VM; +using Neo.Wallets; +using System.Linq; +using System.Numerics; + +namespace Neo.Network.RPC +{ + /// + /// Call NEP5 methods with RPC API + /// + public class Nep5API : ContractClient + { + /// + /// Nep5API Constructor + /// + /// the RPC client to call NEO RPC methods + public Nep5API(RpcClient rpcClient) : base(rpcClient) { } + + /// + /// Get balance of NEP5 token + /// + /// contract script hash + /// account script hash + /// + public BigInteger BalanceOf(UInt160 scriptHash, UInt160 account) + { + BigInteger balance = TestInvoke(scriptHash, "balanceOf", account).Stack.Single().ToStackItem().GetBigInteger(); + return balance; + } + + /// + /// Get name of NEP5 token + /// + /// contract script hash + /// + public string Name(UInt160 scriptHash) + { + return TestInvoke(scriptHash, "name").Stack.Single().ToStackItem().GetString(); + } + + /// + /// Get symbol of NEP5 token + /// + /// contract script hash + /// + public string Symbol(UInt160 scriptHash) + { + return TestInvoke(scriptHash, "symbol").Stack.Single().ToStackItem().GetString(); + } + + /// + /// Get decimals of NEP5 token + /// + /// contract script hash + /// + public uint Decimals(UInt160 scriptHash) + { + return (uint)TestInvoke(scriptHash, "decimals").Stack.Single().ToStackItem().GetBigInteger(); + } + + /// + /// Get total supply of NEP5 token + /// + /// contract script hash + /// + public BigInteger TotalSupply(UInt160 scriptHash) + { + return TestInvoke(scriptHash, "totalSupply").Stack.Single().ToStackItem().GetBigInteger(); + } + + /// + /// Get name of NEP5 token + /// + /// contract script hash + /// from KeyPair + /// to account script hash + /// transfer amount + /// netwotk fee, set to be 0 if you don't need higher priority + /// + public Transaction Transfer(UInt160 scriptHash, KeyPair fromKey, UInt160 to, BigInteger amount, long networkFee = 0) + { + var sender = Contract.CreateSignatureRedeemScript(fromKey.PublicKey).ToScriptHash(); + Cosigner[] cosigners = new[] { new Cosigner { Scopes = WitnessScope.CalledByEntry, Account = sender } }; + + byte[] script = scriptHash.MakeScript("transfer", sender, to, amount); + Transaction tx = new TransactionManager(rpcClient, sender) + .MakeTransaction(script, null, cosigners, networkFee) + .AddSignature(fromKey) + .Sign() + .Tx; + + return tx; + } + } +} diff --git a/neo/Network/RPC/PolicyAPI.cs b/neo/Network/RPC/PolicyAPI.cs new file mode 100644 index 0000000000..969c1c22f1 --- /dev/null +++ b/neo/Network/RPC/PolicyAPI.cs @@ -0,0 +1,57 @@ +using Neo.SmartContract.Native; +using Neo.VM; +using System.Linq; + +namespace Neo.Network.RPC +{ + /// + /// Get Policy info by RPC API + /// + public class PolicyAPI : ContractClient + { + readonly UInt160 scriptHash = NativeContract.Policy.Hash; + + /// + /// PolicyAPI Constructor + /// + /// the RPC client to call NEO RPC methods + public PolicyAPI(RpcClient rpcClient) : base(rpcClient) { } + + /// + /// Get Max Transactions Count Per Block + /// + /// + public uint GetMaxTransactionsPerBlock() + { + return (uint)TestInvoke(scriptHash, "getMaxTransactionsPerBlock").Stack.Single().ToStackItem().GetBigInteger(); + } + + /// + /// Get Max Block Size + /// + /// + public uint GetMaxBlockSize() + { + return (uint)TestInvoke(scriptHash, "getMaxBlockSize").Stack.Single().ToStackItem().GetBigInteger(); + } + + /// + /// Get Network Fee Per Byte + /// + /// + public long GetFeePerByte() + { + return (long)TestInvoke(scriptHash, "getFeePerByte").Stack.Single().ToStackItem().GetBigInteger(); + } + + /// + /// Get Ploicy Blocked Accounts + /// + /// + public UInt160[] GetBlockedAccounts() + { + var result = (VM.Types.Array)TestInvoke(scriptHash, "getBlockedAccounts").Stack.Single().ToStackItem(); + return result.Select(p => new UInt160(p.GetByteArray())).ToArray(); + } + } +} diff --git a/neo/Network/RPC/RpcClient.cs b/neo/Network/RPC/RpcClient.cs index 69352b8cfd..cd0578953e 100644 --- a/neo/Network/RPC/RpcClient.cs +++ b/neo/Network/RPC/RpcClient.cs @@ -9,6 +9,9 @@ namespace Neo.Network.RPC { + /// + /// The RPC client to call NEO RPC methods + /// public class RpcClient : IDisposable { private readonly HttpClient httpClient; @@ -31,17 +34,19 @@ public void Dispose() public async Task SendAsync(RpcRequest request) { var requestJson = request.ToJson().ToString(); - var result = await httpClient.PostAsync(httpClient.BaseAddress, new StringContent(requestJson, Encoding.UTF8)); - var content = await result.Content.ReadAsStringAsync(); - var response = RpcResponse.FromJson(JObject.Parse(content)); - response.RawResponse = content; - - if (response.Error != null) + using (var result = await httpClient.PostAsync(httpClient.BaseAddress, new StringContent(requestJson, Encoding.UTF8))) { - throw new RpcException(response.Error.Code, response.Error.Message); - } + var content = await result.Content.ReadAsStringAsync(); + var response = RpcResponse.FromJson(JObject.Parse(content)); + response.RawResponse = content; - return response; + if (response.Error != null) + { + throw new RpcException(response.Error.Code, response.Error.Message); + } + + return response; + } } public RpcResponse Send(RpcRequest request) @@ -56,7 +61,7 @@ public RpcResponse Send(RpcRequest request) } } - private JObject RpcSend(string method, params JObject[] paraArgs) + public virtual JObject RpcSend(string method, params JObject[] paraArgs) { var request = new RpcRequest { @@ -104,9 +109,9 @@ public RpcBlock GetBlock(string hashOrIndex) /// /// Gets the number of blocks in the main chain. /// - public int GetBlockCount() + public uint GetBlockCount() { - return (int)RpcSend("getblockcount").AsNumber(); + return (uint)RpcSend("getblockcount").AsNumber(); } /// @@ -187,7 +192,7 @@ public string[] GetRawMempool() /// public RpcRawMemPool GetRawMempoolBoth() { - return RpcRawMemPool.FromJson(RpcSend("getrawmempool")); + return RpcRawMemPool.FromJson(RpcSend("getrawmempool", true)); } /// @@ -252,9 +257,9 @@ public RpcInvokeResult InvokeFunction(string address, string function, RpcStack[ /// Returns the result after passing a script through the VM. /// This RPC call does not affect the blockchain in any way. /// - public RpcInvokeResult InvokeScript(string script) + public RpcInvokeResult InvokeScript(byte[] script) { - return RpcInvokeResult.FromJson(RpcSend("invokescript", script)); + return RpcInvokeResult.FromJson(RpcSend("invokescript", script.ToHexString())); } /// @@ -268,17 +273,17 @@ public RpcPlugin[] ListPlugins() /// /// Broadcasts a transaction over the NEO network. /// - public bool SendRawTransaction(string rawTransaction) + public bool SendRawTransaction(byte[] rawTransaction) { - return RpcSend("sendrawtransaction", rawTransaction).AsBoolean(); + return RpcSend("sendrawtransaction", rawTransaction.ToHexString()).AsBoolean(); } /// /// Broadcasts a raw block over the NEO network. /// - public bool SubmitBlock(string block) + public bool SubmitBlock(byte[] block) { - return RpcSend("submitblock", block).AsBoolean(); + return RpcSend("submitblock", block.ToHexString()).AsBoolean(); } /// diff --git a/neo/Network/RPC/TransactionManager.cs b/neo/Network/RPC/TransactionManager.cs new file mode 100644 index 0000000000..7b4db697ab --- /dev/null +++ b/neo/Network/RPC/TransactionManager.cs @@ -0,0 +1,220 @@ +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.Network.RPC.Models; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using System; + +namespace Neo.Network.RPC +{ + /// + /// This class helps to create transaction with RPC API. + /// + public class TransactionManager + { + private static readonly Random rand = new Random(); + private readonly RpcClient rpcClient; + private readonly UInt160 sender; + + /// + /// The Transaction context to manage the witnesses + /// + private ContractParametersContext context; + + /// + /// The Transaction managed by this class + /// + public Transaction Tx { get; private set; } + + /// + /// TransactionManager Constructor + /// + /// the RPC client to call NEO RPC API + /// the account script hash of sender + public TransactionManager(RpcClient rpc, UInt160 sender) + { + rpcClient = rpc; + this.sender = sender; + } + + /// + /// Create an unsigned Transaction object with given parameters. + /// + /// Transaction Script + /// Transaction Attributes + /// Transaction Cosigners + /// Transaction NetworkFee, will set to estimate value(with only basic signatures) when networkFee is 0 + /// + public TransactionManager MakeTransaction(byte[] script, TransactionAttribute[] attributes = null, Cosigner[] cosigners = null, long networkFee = 0) + { + uint height = rpcClient.GetBlockCount() - 1; + Tx = new Transaction + { + Version = 0, + Nonce = (uint)rand.Next(), + Script = script, + Sender = sender, + ValidUntilBlock = height + Transaction.MaxValidUntilBlockIncrement, + Attributes = attributes ?? new TransactionAttribute[0], + Cosigners = cosigners ?? new Cosigner[0], + Witnesses = new Witness[0] + }; + + RpcInvokeResult result = rpcClient.InvokeScript(script); + Tx.SystemFee = Math.Max(long.Parse(result.GasConsumed) - ApplicationEngine.GasFree, 0); + if (Tx.SystemFee > 0) + { + long d = (long)NativeContract.GAS.Factor; + long remainder = Tx.SystemFee % d; + if (remainder > 0) + Tx.SystemFee += d - remainder; + else if (remainder < 0) + Tx.SystemFee -= remainder; + } + + context = new ContractParametersContext(Tx); + + // set networkfee to estimate value when networkFee is 0 + Tx.NetworkFee = networkFee == 0 ? EstimateNetworkFee() : networkFee; + + var gasBalance = new Nep5API(rpcClient).BalanceOf(NativeContract.GAS.Hash, sender); + if (gasBalance >= Tx.SystemFee + Tx.NetworkFee) return this; + throw new InvalidOperationException($"Insufficient GAS in address: {sender.ToAddress()}"); + } + + /// + /// Estimate NetworkFee, assuming the witnesses are basic Signature Contract + /// + private long EstimateNetworkFee() + { + long networkFee = 0; + UInt160[] hashes = Tx.GetScriptHashesForVerifying(null); + int size = Transaction.HeaderSize + Tx.Attributes.GetVarSize() + Tx.Script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); + + // assume the hashes are single Signature + foreach (var hash in hashes) + { + size += 166; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); + } + + networkFee += size * new PolicyAPI(rpcClient).GetFeePerByte(); + return networkFee; + } + + /// + /// Calculate NetworkFee with context items + /// + private long CalculateNetworkFee() + { + long networkFee = 0; + UInt160[] hashes = Tx.GetScriptHashesForVerifying(null); + int size = Transaction.HeaderSize + Tx.Attributes.GetVarSize() + Tx.Script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); + foreach (UInt160 hash in hashes) + { + byte[] witness_script = context.GetScript(hash); + if (witness_script is null || witness_script.Length == 0) + { + try + { + witness_script = rpcClient.GetContractState(hash.ToString())?.Script; + } + catch { } + } + + if (witness_script is null) continue; + + networkFee += Wallet.CalculateNetWorkFee(witness_script, ref size); + } + networkFee += size * new PolicyAPI(rpcClient).GetFeePerByte(); + return networkFee; + } + + /// + /// Add Signature + /// + /// The KeyPair to sign transction + /// + public TransactionManager AddSignature(KeyPair key) + { + var contract = Contract.CreateSignatureContract(key.PublicKey); + + byte[] signature = Tx.Sign(key); + if (!context.AddSignature(contract, key.PublicKey, signature)) + { + throw new Exception("AddSignature failed!"); + } + + return this; + } + + /// + /// Add Multi-Signature + /// + /// The KeyPair to sign transction + /// The least count of signatures needed for multiple signature contract + /// The Public Keys construct the multiple signature contract + public TransactionManager AddMultiSig(KeyPair key, int m, params ECPoint[] publicKeys) + { + Contract contract = Contract.CreateMultiSigContract(m, publicKeys); + + byte[] signature = Tx.Sign(key); + if (!context.AddSignature(contract, key.PublicKey, signature)) + { + throw new Exception("AddMultiSig failed!"); + } + + return this; + } + + /// + /// Add Witness with contract + /// + /// The witness verification contract + /// The witness invocation parameters + public TransactionManager AddWitness(Contract contract, params object[] parameters) + { + if (!context.Add(contract, parameters)) + { + throw new Exception("AddWitness failed!"); + }; + return this; + } + + /// + /// Add Witness with scriptHash + /// + /// The witness verification contract hash + /// The witness invocation parameters + public TransactionManager AddWitness(UInt160 scriptHash, params object[] parameters) + { + var contract = Contract.Create(scriptHash); + return AddWitness(contract, parameters); + } + + /// + /// Verify Witness count and add witnesses + /// + public TransactionManager Sign() + { + // Verify witness count + if (!context.Completed) + { + throw new Exception($"Please add signature or witness first!"); + } + + // Calculate NetworkFee + long leastNetworkFee = CalculateNetworkFee(); + if (Tx.NetworkFee < leastNetworkFee) + { + throw new InvalidOperationException("Insufficient NetworkFee"); + } + + Tx.Witnesses = context.GetWitnesses(); + return this; + } + } +} diff --git a/neo/SmartContract/Contract.cs b/neo/SmartContract/Contract.cs index 19fcceb7ca..58e32768d7 100644 --- a/neo/SmartContract/Contract.cs +++ b/neo/SmartContract/Contract.cs @@ -46,6 +46,20 @@ public static Contract Create(ContractParameterType[] parameterList, byte[] rede }; } + /// + /// Construct special Contract with empty Script, will get the Script with scriptHash from blockchain when doing the Verify + /// verification = snapshot.Contracts.TryGet(hashes[i])?.Script; + /// + public static Contract Create(UInt160 scriptHash, params ContractParameterType[] parameterList) + { + return new Contract + { + Script = new byte[0], + _scriptHash = scriptHash, + ParameterList = parameterList + }; + } + public static Contract CreateMultiSigContract(int m, params ECPoint[] publicKeys) { return new Contract diff --git a/neo/SmartContract/ContractParametersContext.cs b/neo/SmartContract/ContractParametersContext.cs index 8a91c936b8..0388df342c 100644 --- a/neo/SmartContract/ContractParametersContext.cs +++ b/neo/SmartContract/ContractParametersContext.cs @@ -86,10 +86,19 @@ public IReadOnlyList ScriptHashes get { if (_ScriptHashes == null) + { + // snapshot is not necessary for Transaction + if (Verifiable is Transaction) + { + _ScriptHashes = Verifiable.GetScriptHashesForVerifying(null); + return _ScriptHashes; + } + using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) { _ScriptHashes = Verifiable.GetScriptHashesForVerifying(snapshot); } + } return _ScriptHashes; } } @@ -108,6 +117,17 @@ public bool Add(Contract contract, int index, object parameter) return true; } + public bool Add(Contract contract, params object[] parameters) + { + ContextItem item = CreateItem(contract); + if (item == null) return false; + for (int index = 0; index < parameters.Length; index++) + { + item.Parameters[index].Value = parameters[index]; + } + return true; + } + public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) { if (contract.Script.IsMultiSigContract(out _, out _)) @@ -220,6 +240,13 @@ public IReadOnlyList GetParameters(UInt160 scriptHash) return item.Parameters; } + public byte[] GetScript(UInt160 scriptHash) + { + if (!ContextItems.TryGetValue(scriptHash, out ContextItem item)) + return null; + return item.Script; + } + public Witness[] GetWitnesses() { if (!Completed) throw new InvalidOperationException(); diff --git a/neo/VM/Helper.cs b/neo/VM/Helper.cs index e996463e3f..d63bebb89a 100644 --- a/neo/VM/Helper.cs +++ b/neo/VM/Helper.cs @@ -162,6 +162,25 @@ public static ScriptBuilder EmitSysCall(this ScriptBuilder sb, uint method, para return sb.EmitSysCall(method); } + /// + /// Generate scripts to call a specific method from a specific contract. + /// + /// contract script hash + /// contract operation + /// operation arguments + /// + public static byte[] MakeScript(this UInt160 scriptHash, string operation, 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(); + } + } + public static ContractParameter ToParameter(this StackItem item) { return ToParameter(item, null); @@ -228,5 +247,67 @@ private static ContractParameter ToParameter(StackItem item, List> context) + { + StackItem stackItem = null; + switch (parameter.Type) + { + case ContractParameterType.Array: + if (context is null) + context = new List>(); + else + stackItem = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter))?.Item1; + if (stackItem is null) + { + stackItem = ((IList)parameter.Value).Select(p => ToStackItem(p, context)).ToList(); + context.Add(new Tuple(stackItem, parameter)); + } + break; + case ContractParameterType.Map: + if (context is null) + context = new List>(); + else + stackItem = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter))?.Item1; + if (stackItem is null) + { + stackItem = new Map(((IList>)parameter.Value).ToDictionary(p => ToStackItem(p.Key, context), p => ToStackItem(p.Value, context))); + context.Add(new Tuple(stackItem, parameter)); + } + break; + case ContractParameterType.Boolean: + stackItem = (bool)parameter.Value; + break; + case ContractParameterType.ByteArray: + case ContractParameterType.Signature: + stackItem = (byte[])parameter.Value; + break; + case ContractParameterType.Integer: + stackItem = (BigInteger)parameter.Value; + break; + case ContractParameterType.Hash160: + stackItem = ((UInt160)parameter.Value).ToArray(); + break; + case ContractParameterType.Hash256: + stackItem = ((UInt256)parameter.Value).ToArray(); + break; + case ContractParameterType.PublicKey: + stackItem = ((ECPoint)parameter.Value).EncodePoint(true); + break; + case ContractParameterType.String: + stackItem = (string)parameter.Value; + break; + case ContractParameterType.InteropInterface: + break; + default: + throw new ArgumentException($"ContractParameterType({parameter.Type}) is not supported to StackItem."); + } + return stackItem; + } } } diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index eb816c7a0a..5149c2b2fe 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -338,27 +338,7 @@ private Transaction MakeTransaction(Snapshot snapshot, byte[] script, Transactio { byte[] witness_script = GetAccount(hash)?.Contract?.Script ?? snapshot.Contracts.TryGet(hash)?.Script; if (witness_script is null) continue; - if (witness_script.IsSignatureContract()) - { - size += 66 + witness_script.GetVarSize(); - tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); - } - else if (witness_script.IsMultiSigContract(out int m, out int n)) - { - int size_inv = 65 * m; - size += IO.Helper.GetVarSize(size_inv) + size_inv + witness_script.GetVarSize(); - tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] * m; - using (ScriptBuilder sb = new ScriptBuilder()) - tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; - tx.NetworkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] * n; - using (ScriptBuilder sb = new ScriptBuilder()) - tx.NetworkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; - tx.NetworkFee += InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null) * n; - } - else - { - //We can support more contract types in the future. - } + tx.NetworkFee += CalculateNetWorkFee(witness_script, ref size); } tx.NetworkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); if (value >= tx.SystemFee + tx.NetworkFee) return tx; @@ -366,6 +346,35 @@ private Transaction MakeTransaction(Snapshot snapshot, byte[] script, Transactio throw new InvalidOperationException("Insufficient GAS"); } + public static long CalculateNetWorkFee(byte[] witness_script, ref int size) + { + long networkFee = 0; + + if (witness_script.IsSignatureContract()) + { + size += 66 + witness_script.GetVarSize(); + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); + } + else if (witness_script.IsMultiSigContract(out int m, out int n)) + { + int size_inv = 65 * m; + size += IO.Helper.GetVarSize(size_inv) + size_inv + witness_script.GetVarSize(); + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] * m; + using (ScriptBuilder sb = new ScriptBuilder()) + networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] * n; + using (ScriptBuilder sb = new ScriptBuilder()) + networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; + networkFee += InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null) * n; + } + else + { + //We can support more contract types in the future. + } + + return networkFee; + } + public bool Sign(ContractParametersContext context) { bool fSuccess = false; From 65005fb33586ed2167d8b1d37f2cb0c7aee10418 Mon Sep 17 00:00:00 2001 From: Ricardo Prado <38396062+lock9@users.noreply.github.com> Date: Sat, 21 Sep 2019 23:53:17 -0300 Subject: [PATCH 101/305] Template fix (#1107) * Fixing template * Fix --- .../feature-or-enhancement-request.md | 25 +++++++------- CONTRIBUTING.md | 34 +++++++++---------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md index 30077088b9..a2ddf9047c 100644 --- a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md +++ b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md @@ -12,15 +12,16 @@ A summary of the problem you want to solve or metric you want to improve **Do you have any solution you want to propose?** A clear and concise description of what you expect with this change. -**Where in software does this update applies to?** -- [ ] Compiler -- [ ] Consensus -- [ ] CLI -- [ ] Plugins -- [ ] Ledger -- [ ] Network Policy -- [ ] P2P (TCP) -- [ ] RPC (HTTP) -- [ ] SDK -- [ ] VM -- [ ] Other: +**Where in the software does this update applies to?** +- Compiler +- Consensus +- CLI +- Plugins +- Ledger +- Network Policy +- P2P (TCP) +- RPC (HTTP) +- SDK +- VM +- Other: + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 655dcda27a..5fd0592924 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,45 +37,45 @@ If you are looking to start contributing to NEO, we suggest you start working on ### Tags for Issues States -![](./.github/images/discussion.png) Whenever someone posts a new feature request, the tag discussion is added. This means that there is no consensus if the feature should be implemented or not. Avoid creating PR to solve issues in this state since it may be completely discarded. +![Discussion](./.github/images/discussion.png) Whenever someone posts a new feature request, the tag discussion is added. This means that there is no consensus if the feature should be implemented or not. Avoid creating PR to solve issues in this state since it may be completely discarded. -![](./.github/images/solution-design.png) When a feature request is accepted by the team, but there is no consensus about the implementation, the issue is tagged with design. We recommend the team to agree in the solution design before anyone attempts to implement it, using text or UML. It is not recommended, but developers can also present their solution using code. +![Design](./.github/images/solution-design.png) When a feature request is accepted by the team, but there is no consensus about the implementation, the issue is tagged with design. We recommend the team to agree in the solution design before anyone attempts to implement it, using text or UML. It is not recommended, but developers can also present their solution using code. Note that PRs for issues in this state may also be discarded if the team disagree with the proposed solution. -![](./.github/images/ready-to-implement.png) Once the team has agreed on feature and the proposed solution, the issue is tagged with ready-to-implement. When implementing it, please follow the solution accepted by the team. +![Ready-to-implement](./.github/images/ready-to-implement.png) Once the team has agreed on feature and the proposed solution, the issue is tagged with ready-to-implement. When implementing it, please follow the solution accepted by the team. ### Tags for Issue Types -![](./.github/images/cosmetic.png) Issues with the cosmetic tag are usually changes in code or documentation that improve user experience without affecting current functionality. These issues are recommended for beginners because they require little to no knowledge about Neo platform. +![Cosmetic](./.github/images/cosmetic.png) Issues with the cosmetic tag are usually changes in code or documentation that improve user experience without affecting current functionality. These issues are recommended for beginners because they require little to no knowledge about Neo platform. -![](./.github/images/enhancement.png) Enhancements are platform changes that may affect performance, usability or add new features to existing modules. It is recommended that developers have previous knowledge in the platform to work in these improvements, specially in more complicated modules like the compiler, ledger and consensus. +![Enhancement](./.github/images/enhancement.png) Enhancements are platform changes that may affect performance, usability or add new features to existing modules. It is recommended that developers have previous knowledge in the platform to work in these improvements, specially in more complicated modules like the compiler, ledger and consensus. -![](./.github/images/new-feature.png) New features may include large changes in the code base. Some are complex, but some are not. So, a few issues with new-feature may be recommended for starters, specially those related to the rpc and the sdk module. +![Feature](./.github/images/new-feature.png) New features may include large changes in the code base. Some are complex, but some are not. So, a few issues with new-feature may be recommended for starters, specially those related to the rpc and the sdk module. -![](./.github/images/migration.png) Issues related to the migration from Neo 2 to Neo 3 are tagged with migration. These issues are usually the most complicated ones since they require a deep knowledge in both versions. +![Migration](./.github/images/migration.png) Issues related to the migration from Neo 2 to Neo 3 are tagged with migration. These issues are usually the most complicated ones since they require a deep knowledge in both versions. ### Tags for Project Modules These tags do not necessarily represent each module at code level. Modules consensus and compiler are not recommended for beginners. -![](./.github/images/compiler.png) Issues that are related or influence the behavior of our C# compiler. Note that the compiler itself is hosted in the [neo-devpack-dotnet](https://github.com/neo-project/neo-devpack-dotnet) repository. +![Compiler](./.github/images/compiler.png) Issues that are related or influence the behavior of our C# compiler. Note that the compiler itself is hosted in the [neo-devpack-dotnet](https://github.com/neo-project/neo-devpack-dotnet) repository. -![](./.github/images/consensus.png) Changes to consensus are usually harder to make and test. Avoid implementing issues in this module that are not yet decided. +![Consensus](./.github/images/consensus.png) Changes to consensus are usually harder to make and test. Avoid implementing issues in this module that are not yet decided. -![](./.github/images/ledger.png) The ledger is our 'database', any changes in the way we store information or the data-structures have this tag. +![Ledger](./.github/images/ledger.png) The ledger is our 'database', any changes in the way we store information or the data-structures have this tag. -![](./.github/images/house-keeping.png) 'Small' enhancements that need to be done in order to keep the project organised and ensure overall quality. These changes may be applied in any place in code, as long as they are small or do not alter current behavior. +![House-keeping](./.github/images/house-keeping.png) 'Small' enhancements that need to be done in order to keep the project organised and ensure overall quality. These changes may be applied in any place in code, as long as they are small or do not alter current behavior. -![](./.github/images/network-policy.png) Identify issues that affect the network-policy like fees, access list or other related issues. Voting may also be related to the network policy module. +![Network-policy](./.github/images/network-policy.png) Identify issues that affect the network-policy like fees, access list or other related issues. Voting may also be related to the network policy module. -![](./.github/images/p2p.png) This module includes peer-to-peer message exchange and network optimisations, at TCP or UDP level (not HTTP). +![P2P](./.github/images/p2p.png) This module includes peer-to-peer message exchange and network optimisations, at TCP or UDP level (not HTTP). -![](./.github/images/rpc.png) All HTTP communication is handled by the RPC module. This module usually provides support methods since the main communication protocol takes place at the p2p module. +![RPC](./.github/images/rpc.png) All HTTP communication is handled by the RPC module. This module usually provides support methods since the main communication protocol takes place at the p2p module. -![](./.github/images/vm.png) New features that affect the Neo Virtual Machine or the Interop layer have this tag. +![VM](./.github/images/vm.png) New features that affect the Neo Virtual Machine or the Interop layer. -![](./.github/images/sdk.png) Neo provides an SDK to help developers to interact with the blockchain. Changes in this module must not impact other parts of the software. +![SDK](./.github/images/sdk.png) Neo provides an SDK to help developers to interact with the blockchain. Changes in this module must not impact other parts of the software. -![](./.github/images/wallet.png) Wallets are used to track funds and interact with the blockchain. Note that this module depends on a full node implementation (data stored on local disk). +![Wallet](./.github/images/wallet.png) Wallets are used to track funds and interact with the blockchain. Note that this module depends on a full node implementation (data stored on local disk). From cee078e6098502c445b047322d2d668da898b884 Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Mon, 23 Sep 2019 21:20:09 +0800 Subject: [PATCH 102/305] Unit test For Legder module (#1038) * update test ledger * format * Add enter between classes * fix ut suggestion * Update UT_PoolItem.cs * Update UT_MemoryPool.cs * Reduce usings * Reduce changes * More details on TestReVerifyTopUnverifiedTransactionsIfNeeded and fixed comment on ReVerifyTopUnverifiedTransactionsIfNeeded * Minor fix on generic transaction generation fee * Applying dotnet format * Enhance functions in TestDataCache * dotnet format * Cast refactor * comment tx3 for mempool * Removing TODO comment --- neo.UnitTests/Ledger/UT_Blockchain.cs | 102 ++++++++ neo.UnitTests/Ledger/UT_ContractState.cs | 99 ++++++++ neo.UnitTests/Ledger/UT_HashIndexState.cs | 63 +++++ neo.UnitTests/Ledger/UT_HeaderHashList.cs | 59 +++++ neo.UnitTests/Ledger/UT_MemoryPool.cs | 252 +++++++++++++++++++- neo.UnitTests/Ledger/UT_PoolItem.cs | 1 - neo.UnitTests/Ledger/UT_StorageItem.cs | 10 + neo.UnitTests/Ledger/UT_StorageKey.cs | 26 ++ neo.UnitTests/Ledger/UT_TransactionState.cs | 67 ++++++ neo.UnitTests/Ledger/UT_TrimmedBlock.cs | 138 +++++++++++ neo.UnitTests/TestBlockchain.cs | 13 +- neo.UnitTests/TestDataCache.cs | 22 +- neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs | 11 +- neo/Ledger/MemoryPool.cs | 3 +- 14 files changed, 837 insertions(+), 29 deletions(-) create mode 100644 neo.UnitTests/Ledger/UT_Blockchain.cs create mode 100644 neo.UnitTests/Ledger/UT_ContractState.cs create mode 100644 neo.UnitTests/Ledger/UT_HashIndexState.cs create mode 100644 neo.UnitTests/Ledger/UT_HeaderHashList.cs create mode 100644 neo.UnitTests/Ledger/UT_TransactionState.cs create mode 100644 neo.UnitTests/Ledger/UT_TrimmedBlock.cs diff --git a/neo.UnitTests/Ledger/UT_Blockchain.cs b/neo.UnitTests/Ledger/UT_Blockchain.cs new file mode 100644 index 0000000000..eb17e71c62 --- /dev/null +++ b/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -0,0 +1,102 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; + +namespace Neo.UnitTests.Ledger +{ + internal class TestBlock : Block + { + public override bool Verify(Snapshot snapshot) + { + return true; + } + + public static TestBlock Cast(Block input) + { + return input.ToArray().AsSerializable(); + } + } + + internal class TestHeader : Header + { + public override bool Verify(Snapshot snapshot) + { + return true; + } + + public static TestHeader Cast(Header input) + { + return input.ToArray().AsSerializable(); + } + } + + [TestClass] + public class UT_Blockchain + { + private NeoSystem system; + private Store store; + Transaction txSample = Blockchain.GenesisBlock.Transactions[0]; + + [TestInitialize] + public void Initialize() + { + system = TestBlockchain.InitializeMockNeoSystem(); + store = TestBlockchain.GetStore(); + Blockchain.Singleton.MemPool.TryAdd(txSample.Hash, txSample); + } + + [TestMethod] + public void TestConstructor() + { + system.ActorSystem.ActorOf(Blockchain.Props(system, store)).Should().NotBeSameAs(system.Blockchain); + } + + [TestMethod] + public void TestContainsBlock() + { + Blockchain.Singleton.ContainsBlock(UInt256.Zero).Should().BeFalse(); + } + + [TestMethod] + public void TestContainsTransaction() + { + Blockchain.Singleton.ContainsTransaction(UInt256.Zero).Should().BeFalse(); + Blockchain.Singleton.ContainsTransaction(txSample.Hash).Should().BeTrue(); + } + + [TestMethod] + public void TestGetCurrentBlockHash() + { + Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("5662a113d8fa9532ea9c52046a463e2e3fcfcdd6192d99cad805b376fb643ceb")); + } + + [TestMethod] + public void TestGetCurrentHeaderHash() + { + Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("5662a113d8fa9532ea9c52046a463e2e3fcfcdd6192d99cad805b376fb643ceb")); + } + + [TestMethod] + public void TestGetBlock() + { + Blockchain.Singleton.GetBlock(UInt256.Zero).Should().BeNull(); + } + + [TestMethod] + public void TestGetBlockHash() + { + Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("5662a113d8fa9532ea9c52046a463e2e3fcfcdd6192d99cad805b376fb643ceb")); + Blockchain.Singleton.GetBlockHash(10).Should().BeNull(); + } + + [TestMethod] + public void TestGetTransaction() + { + Blockchain.Singleton.GetTransaction(UInt256.Zero).Should().BeNull(); + Blockchain.Singleton.GetTransaction(txSample.Hash).Should().NotBeNull(); + } + } +} diff --git a/neo.UnitTests/Ledger/UT_ContractState.cs b/neo.UnitTests/Ledger/UT_ContractState.cs new file mode 100644 index 0000000000..87f1d7ac88 --- /dev/null +++ b/neo.UnitTests/Ledger/UT_ContractState.cs @@ -0,0 +1,99 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.IO.Json; +using Neo.Ledger; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; +using System.IO; + +namespace Neo.UnitTests.Ledger +{ + [TestClass] + public class UT_ContractState + { + ContractState contract; + byte[] script = { 0x01 }; + ContractManifest manifest; + + [TestInitialize] + public void TestSetup() + { + manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")); + contract = new ContractState + { + Script = script, + Manifest = manifest + }; + } + + [TestMethod] + public void TestGetHasStorage() + { + contract.HasStorage.Should().BeFalse(); + } + + [TestMethod] + public void TestGetPayable() + { + contract.Payable.Should().BeFalse(); + } + + [TestMethod] + public void TestGetScriptHash() + { + // _scriptHash == null + contract.ScriptHash.Should().Be(script.ToScriptHash()); + // _scriptHash != null + contract.ScriptHash.Should().Be(script.ToScriptHash()); + } + + [TestMethod] + public void TestClone() + { + ICloneable cloneable = contract; + ContractState clone = cloneable.Clone(); + clone.ToJson().ToString().Should().Be(contract.ToJson().ToString()); + } + + [TestMethod] + public void TestFromReplica() + { + ICloneable cloneable = new ContractState(); + cloneable.FromReplica(contract); + ((ContractState)cloneable).ToJson().ToString().Should().Be(contract.ToJson().ToString()); + } + + [TestMethod] + public void TestDeserialize() + { + ISerializable newContract = new ContractState(); + using (MemoryStream ms = new MemoryStream(1024)) + using (BinaryWriter writer = new BinaryWriter(ms)) + using (BinaryReader reader = new BinaryReader(ms)) + { + ((ISerializable)contract).Serialize(writer); + ms.Seek(0, SeekOrigin.Begin); + newContract.Deserialize(reader); + } + ((ContractState)newContract).Manifest.ToJson().ToString().Should().Be(contract.Manifest.ToJson().ToString()); + ((ContractState)newContract).Script.Should().BeEquivalentTo(contract.Script); + } + + [TestMethod] + public void TestGetSize() + { + ISerializable newContract = contract; + newContract.Size.Should().Be(355); + } + + [TestMethod] + public void TestToJson() + { + JObject json = contract.ToJson(); + json["hash"].AsString().Should().Be("0x820944cfdc70976602d71b0091445eedbc661bc5"); + json["script"].AsString().Should().Be("01"); + json["manifest"].AsString().Should().Be(manifest.ToJson().AsString()); + } + } +} diff --git a/neo.UnitTests/Ledger/UT_HashIndexState.cs b/neo.UnitTests/Ledger/UT_HashIndexState.cs new file mode 100644 index 0000000000..bfc85479dd --- /dev/null +++ b/neo.UnitTests/Ledger/UT_HashIndexState.cs @@ -0,0 +1,63 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Ledger; +using System.IO; + +namespace Neo.UnitTests.Ledger +{ + [TestClass] + public class UT_HashIndexState + { + HashIndexState origin; + + [TestInitialize] + public void Initialize() + { + origin = new HashIndexState + { + Hash = UInt256.Zero, + Index = 10 + }; + } + + [TestMethod] + public void TestClone() + { + HashIndexState dest = ((ICloneable)origin).Clone(); + dest.Hash.Should().Be(origin.Hash); + dest.Index.Should().Be(origin.Index); + } + + [TestMethod] + public void TestFromReplica() + { + HashIndexState dest = new HashIndexState(); + ((ICloneable)dest).FromReplica(origin); + dest.Hash.Should().Be(origin.Hash); + dest.Index.Should().Be(origin.Index); + } + + [TestMethod] + public void TestGetSize() + { + ((ISerializable)origin).Size.Should().Be(36); + } + + [TestMethod] + public void TestDeserialize() + { + using (MemoryStream ms = new MemoryStream(1024)) + using (BinaryWriter writer = new BinaryWriter(ms)) + using (BinaryReader reader = new BinaryReader(ms)) + { + ((ISerializable)origin).Serialize(writer); + ms.Seek(0, SeekOrigin.Begin); + HashIndexState dest = new HashIndexState(); + ((ISerializable)dest).Deserialize(reader); + dest.Hash.Should().Be(origin.Hash); + dest.Index.Should().Be(origin.Index); + } + } + } +} diff --git a/neo.UnitTests/Ledger/UT_HeaderHashList.cs b/neo.UnitTests/Ledger/UT_HeaderHashList.cs new file mode 100644 index 0000000000..2f198f4c6c --- /dev/null +++ b/neo.UnitTests/Ledger/UT_HeaderHashList.cs @@ -0,0 +1,59 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Ledger; +using System.IO; + +namespace Neo.UnitTests.Ledger +{ + [TestClass] + public class UT_HeaderHashList + { + HeaderHashList origin; + + [TestInitialize] + public void Initialize() + { + origin = new HeaderHashList + { + Hashes = new UInt256[] { UInt256.Zero } + }; + } + + [TestMethod] + public void TestClone() + { + HeaderHashList dest = ((ICloneable)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/neo.UnitTests/Ledger/UT_MemoryPool.cs b/neo.UnitTests/Ledger/UT_MemoryPool.cs index ea797d7c03..e9a451952c 100644 --- a/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -1,19 +1,36 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; +using Neo.Cryptography; +using Neo.IO; +using Neo.IO.Caching; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; +using Neo.Plugins; +using Neo.SmartContract.Native; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; namespace Neo.UnitTests.Ledger { + internal class TestIMemoryPoolTxObserverPlugin : Plugin, IMemoryPoolTxObserverPlugin + { + public override void Configure() { } + public void TransactionAdded(Transaction tx) { } + public void TransactionsRemoved(MemoryPoolTxRemovalReason reason, IEnumerable transactions) { } + } + [TestClass] public class UT_MemoryPool { + private const byte Prefix_MaxTransactionsPerBlock = 23; + private const byte Prefix_FeePerByte = 10; private MemoryPool _unit; + private MemoryPool _unit2; + private TestIMemoryPoolTxObserverPlugin plugin; [TestInitialize] public void TestSetup() @@ -33,6 +50,14 @@ public void TestSetup() _unit.VerifiedCount.ShouldBeEquivalentTo(0); _unit.UnVerifiedCount.ShouldBeEquivalentTo(0); _unit.Count.ShouldBeEquivalentTo(0); + _unit2 = new MemoryPool(TheNeoSystem, 0); + plugin = new TestIMemoryPoolTxObserverPlugin(); + } + + [TestCleanup] + public void CleanUp() + { + Plugin.TxObserverPlugins.Remove(plugin); } long LongRandom(long min, long max, Random rand) @@ -66,8 +91,10 @@ private Transaction CreateTransactionWithFee(long fee) return mock.Object; } - private Transaction CreateTransaction() + private Transaction CreateTransaction(long fee = -1) { + if (fee != -1) + return CreateTransactionWithFee(fee); return CreateTransactionWithFee(LongRandom(100000, 100000000, TestUtils.TestRandom)); } @@ -82,6 +109,10 @@ private void AddTransactions(int count) Console.WriteLine($"created {count} tx"); } + private void AddTransaction(Transaction txToAdd) + { + _unit.TryAdd(txToAdd.Hash, txToAdd); + } [TestMethod] public void CapacityTest() @@ -139,7 +170,7 @@ public void BlockPersistMovesTxToUnverifiedAndReverification() _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); } - private void verifyTransactionsSortedDescending(IEnumerable transactions) + private void VerifyTransactionsSortedDescending(IEnumerable transactions) { Transaction lastTransaction = null; foreach (var tx in transactions) @@ -170,7 +201,7 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() var sortedVerifiedTxs = _unit.GetSortedVerifiedTransactions().ToList(); // verify all 100 transactions are returned in sorted order sortedVerifiedTxs.Count.ShouldBeEquivalentTo(100); - verifyTransactionsSortedDescending(sortedVerifiedTxs); + VerifyTransactionsSortedDescending(sortedVerifiedTxs); // move all to unverified var block = new Block { Transactions = new Transaction[0] }; @@ -185,7 +216,7 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() _unit.GetVerifiedAndUnverifiedTransactions(out var sortedVerifiedTransactions, out var sortedUnverifiedTransactions); sortedVerifiedTransactions.Count().ShouldBeEquivalentTo(0); var sortedUnverifiedArray = sortedUnverifiedTransactions.ToArray(); - verifyTransactionsSortedDescending(sortedUnverifiedArray); + VerifyTransactionsSortedDescending(sortedUnverifiedArray); var maxTransaction = sortedUnverifiedArray.First(); var minTransaction = sortedUnverifiedArray.Last(); @@ -253,5 +284,218 @@ public void TestInvalidateAll() _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(30); _unit.SortedTxCount.ShouldBeEquivalentTo(0); } + + [TestMethod] + public void TestContainsKey() + { + AddTransactions(10); + + var txToAdd = CreateTransaction(); + _unit.TryAdd(txToAdd.Hash, txToAdd); + _unit.ContainsKey(txToAdd.Hash).Should().BeTrue(); + _unit.InvalidateVerifiedTransactions(); + _unit.ContainsKey(txToAdd.Hash).Should().BeTrue(); + } + + [TestMethod] + public void TestGetEnumerator() + { + AddTransactions(10); + _unit.InvalidateVerifiedTransactions(); + IEnumerator enumerator = _unit.GetEnumerator(); + foreach (Transaction tx in _unit) + { + enumerator.MoveNext(); + enumerator.Current.Should().BeSameAs(tx); + } + } + + [TestMethod] + public void TestIEnumerableGetEnumerator() + { + AddTransactions(10); + _unit.InvalidateVerifiedTransactions(); + IEnumerable enumerable = _unit; + var enumerator = enumerable.GetEnumerator(); + foreach (Transaction tx in _unit) + { + enumerator.MoveNext(); + enumerator.Current.Should().BeSameAs(tx); + } + } + + [TestMethod] + public void TestGetVerifiedTransactions() + { + var tx1 = CreateTransaction(); + var tx2 = CreateTransaction(); + _unit.TryAdd(tx1.Hash, tx1); + _unit.InvalidateVerifiedTransactions(); + _unit.TryAdd(tx2.Hash, tx2); + IEnumerable enumerable = _unit.GetVerifiedTransactions(); + enumerable.Count().Should().Be(1); + var enumerator = enumerable.GetEnumerator(); + enumerator.MoveNext(); + enumerator.Current.Should().BeSameAs(tx2); + } + + [TestMethod] + public void TestReVerifyTopUnverifiedTransactionsIfNeeded() + { + NeoSystem TheNeoSystem = TestBlockchain.InitializeMockNeoSystem(); + var s = Blockchain.Singleton.Height; + _unit = new MemoryPool(TheNeoSystem, 600); + _unit.LoadPolicy(TestBlockchain.GetStore().GetSnapshot()); + AddTransaction(CreateTransaction(100000001)); + AddTransaction(CreateTransaction(100000001)); + AddTransaction(CreateTransaction(100000001)); + AddTransaction(CreateTransaction(1)); + _unit.VerifiedCount.Should().Be(4); + _unit.UnVerifiedCount.Should().Be(0); + + _unit.InvalidateVerifiedTransactions(); + _unit.VerifiedCount.Should().Be(0); + _unit.UnVerifiedCount.Should().Be(4); + + AddTransactions(511); // Max per block currently is 512 + _unit.VerifiedCount.Should().Be(511); + _unit.UnVerifiedCount.Should().Be(4); + + var result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(1, Blockchain.Singleton.GetSnapshot()); + result.Should().BeTrue(); + _unit.VerifiedCount.Should().Be(512); + _unit.UnVerifiedCount.Should().Be(3); + + result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(2, Blockchain.Singleton.GetSnapshot()); + result.Should().BeTrue(); + _unit.VerifiedCount.Should().Be(514); + _unit.UnVerifiedCount.Should().Be(1); + + result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(3, Blockchain.Singleton.GetSnapshot()); + result.Should().BeFalse(); + _unit.VerifiedCount.Should().Be(515); + _unit.UnVerifiedCount.Should().Be(0); + } + + [TestMethod] + public void TestTryAdd() + { + var tx1 = CreateTransaction(); + _unit.TryAdd(tx1.Hash, tx1).Should().BeTrue(); + _unit.TryAdd(tx1.Hash, tx1).Should().BeFalse(); + _unit2.TryAdd(tx1.Hash, tx1).Should().BeFalse(); + } + + [TestMethod] + public void TestTryGetValue() + { + var tx1 = CreateTransaction(); + _unit.TryAdd(tx1.Hash, tx1); + _unit.TryGetValue(tx1.Hash, out Transaction tx).Should().BeTrue(); + tx.ShouldBeEquivalentTo(tx1); + + _unit.InvalidateVerifiedTransactions(); + _unit.TryGetValue(tx1.Hash, out tx).Should().BeTrue(); + tx.ShouldBeEquivalentTo(tx1); + + var tx2 = CreateTransaction(); + _unit.TryGetValue(tx2.Hash, out tx).Should().BeFalse(); + } + + [TestMethod] + public void TestUpdatePoolForBlockPersisted() + { + var mockSnapshot = new Mock(); + byte[] transactionsPerBlock = { 0x18, 0x00, 0x00, 0x00 }; // 24 + byte[] feePerByte = { 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 }; // 1048576 + StorageItem item1 = new StorageItem + { + Value = transactionsPerBlock + }; + StorageItem item2 = new StorageItem + { + Value = feePerByte + }; + var myDataCache = new MyDataCache(); + var key1 = CreateStorageKey(Prefix_MaxTransactionsPerBlock); + var key2 = CreateStorageKey(Prefix_FeePerByte); + key1.ScriptHash = NativeContract.Policy.Hash; + key2.ScriptHash = NativeContract.Policy.Hash; + myDataCache.Add(key1, item1); + myDataCache.Add(key2, item2); + mockSnapshot.SetupGet(p => p.Storages).Returns(myDataCache); + + var tx1 = CreateTransaction(); + var tx2 = CreateTransaction(); + Transaction[] transactions = { tx1, tx2 }; + _unit.TryAdd(tx1.Hash, tx1); + + var block = new Block { Transactions = transactions }; + + _unit.UnVerifiedCount.Should().Be(0); + _unit.VerifiedCount.Should().Be(1); + + _unit.UpdatePoolForBlockPersisted(block, mockSnapshot.Object); + + _unit.UnVerifiedCount.Should().Be(0); + _unit.VerifiedCount.Should().Be(0); + } + + public StorageKey CreateStorageKey(byte prefix, byte[] key = null) + { + StorageKey storageKey = new StorageKey + { + ScriptHash = null, + Key = new byte[sizeof(byte) + (key?.Length ?? 0)] + }; + storageKey.Key[0] = prefix; + if (key != null) + Buffer.BlockCopy(key, 0, storageKey.Key, 1, key.Length); + return storageKey; + } + } + + public class MyDataCache : DataCache + where TKey : IEquatable, ISerializable + where TValue : class, ICloneable, ISerializable, new() + { + private readonly TValue _defaultValue; + + public MyDataCache() + { + _defaultValue = null; + } + + public MyDataCache(TValue defaultValue) + { + this._defaultValue = defaultValue; + } + public override void DeleteInternal(TKey key) + { + } + + protected override void AddInternal(TKey key, TValue value) + { + Add(key, value); + } + + protected override IEnumerable> FindInternal(byte[] key_prefix) + { + return Enumerable.Empty>(); + } + + protected override TValue GetInternal(TKey key) + { + return TryGet(key); + } + + protected override TValue TryGetInternal(TKey key) + { + return _defaultValue; + } + + protected override void UpdateInternal(TKey key, TValue value) + { + } } } diff --git a/neo.UnitTests/Ledger/UT_PoolItem.cs b/neo.UnitTests/Ledger/UT_PoolItem.cs index 10a57ecc90..d94cec8e4c 100644 --- a/neo.UnitTests/Ledger/UT_PoolItem.cs +++ b/neo.UnitTests/Ledger/UT_PoolItem.cs @@ -11,7 +11,6 @@ namespace Neo.UnitTests.Ledger [TestClass] public class UT_PoolItem { - //private PoolItem uut; private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism [TestInitialize] diff --git a/neo.UnitTests/Ledger/UT_StorageItem.cs b/neo.UnitTests/Ledger/UT_StorageItem.cs index 9afab26372..5ed24b5858 100644 --- a/neo.UnitTests/Ledger/UT_StorageItem.cs +++ b/neo.UnitTests/Ledger/UT_StorageItem.cs @@ -106,5 +106,15 @@ public void Serialize() } } + [TestMethod] + public void TestFromReplica() + { + uut.Value = TestUtils.GetByteArray(10, 0x42); + uut.IsConstant = true; + StorageItem dest = new StorageItem(); + ((ICloneable)dest).FromReplica(uut); + dest.Value.Should().BeEquivalentTo(uut.Value); + dest.IsConstant.Should().Be(uut.IsConstant); + } } } diff --git a/neo.UnitTests/Ledger/UT_StorageKey.cs b/neo.UnitTests/Ledger/UT_StorageKey.cs index 6e2223cdcb..1941cbc0cc 100644 --- a/neo.UnitTests/Ledger/UT_StorageKey.cs +++ b/neo.UnitTests/Ledger/UT_StorageKey.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Ledger; +using System.IO; namespace Neo.UnitTests.Ledger { @@ -127,5 +128,30 @@ public void GetHashCode_Get() uut.Key = TestUtils.GetByteArray(10, 0x42); uut.GetHashCode().Should().Be(806209853); } + + [TestMethod] + public void Equals_Obj() + { + uut.Equals(1u).Should().BeFalse(); + uut.Equals((object)uut).Should().BeTrue(); + } + + [TestMethod] + public void TestDeserialize() + { + using (MemoryStream ms = new MemoryStream(1024)) + using (BinaryWriter writer = new BinaryWriter(ms)) + using (BinaryReader reader = new BinaryReader(ms)) + { + uut.ScriptHash = new UInt160(TestUtils.GetByteArray(20, 0x42)); + uut.Key = TestUtils.GetByteArray(10, 0x42); + ((ISerializable)uut).Serialize(writer); + ms.Seek(0, SeekOrigin.Begin); + StorageKey dest = new StorageKey(); + ((ISerializable)dest).Deserialize(reader); + dest.ScriptHash.Should().Be(uut.ScriptHash); + dest.Key.Should().BeEquivalentTo(uut.Key); + } + } } } diff --git a/neo.UnitTests/Ledger/UT_TransactionState.cs b/neo.UnitTests/Ledger/UT_TransactionState.cs new file mode 100644 index 0000000000..7abf431849 --- /dev/null +++ b/neo.UnitTests/Ledger/UT_TransactionState.cs @@ -0,0 +1,67 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Ledger; +using System.IO; + +namespace Neo.UnitTests.Ledger +{ + [TestClass] + public class UT_TransactionState + { + TransactionState origin; + + [TestInitialize] + public void Initialize() + { + origin = new TransactionState + { + BlockIndex = 1, + VMState = VM.VMState.NONE, + Transaction = Blockchain.GenesisBlock.Transactions[0] + }; + } + + [TestMethod] + public void TestClone() + { + TransactionState dest = ((ICloneable)origin).Clone(); + dest.BlockIndex.Should().Be(origin.BlockIndex); + dest.VMState.Should().Be(origin.VMState); + dest.Transaction.Should().Be(origin.Transaction); + } + + [TestMethod] + public void TestFromReplica() + { + TransactionState dest = new TransactionState(); + ((ICloneable)dest).FromReplica(origin); + dest.BlockIndex.Should().Be(origin.BlockIndex); + dest.VMState.Should().Be(origin.VMState); + dest.Transaction.Should().Be(origin.Transaction); + } + + [TestMethod] + public void TestDeserialize() + { + using (MemoryStream ms = new MemoryStream(1024)) + using (BinaryWriter writer = new BinaryWriter(ms)) + using (BinaryReader reader = new BinaryReader(ms)) + { + ((ISerializable)origin).Serialize(writer); + ms.Seek(0, SeekOrigin.Begin); + TransactionState dest = new TransactionState(); + ((ISerializable)dest).Deserialize(reader); + dest.BlockIndex.Should().Be(origin.BlockIndex); + dest.VMState.Should().Be(origin.VMState); + dest.Transaction.Should().Be(origin.Transaction); + } + } + + [TestMethod] + public void TestGetSize() + { + ((ISerializable)origin).Size.Should().Be(62); + } + } +} diff --git a/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/neo.UnitTests/Ledger/UT_TrimmedBlock.cs new file mode 100644 index 0000000000..3704279701 --- /dev/null +++ b/neo.UnitTests/Ledger/UT_TrimmedBlock.cs @@ -0,0 +1,138 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.VM; +using System; +using System.IO; + +namespace Neo.UnitTests.Ledger +{ + [TestClass] + public class UT_TrimmedBlock + { + public static TrimmedBlock GetTrimmedBlockWithNoTransaction() + { + return new TrimmedBlock + { + ConsensusData = new ConsensusData(), + MerkleRoot = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02"), + PrevHash = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"), + Timestamp = new DateTime(1988, 06, 01, 0, 0, 0, DateTimeKind.Utc).ToTimestamp(), + Index = 1, + NextConsensus = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), + Witness = new Witness + { + InvocationScript = new byte[0], + VerificationScript = new[] { (byte)OpCode.PUSHT } + }, + Hashes = new UInt256[0] + }; + } + + [TestMethod] + public void TestGetIsBlock() + { + TrimmedBlock block = GetTrimmedBlockWithNoTransaction(); + block.Hashes = new UInt256[] { TestUtils.GetTransaction().Hash }; + block.IsBlock.Should().BeTrue(); + } + + [TestMethod] + public void TestGetBlock() + { + var cache = new TestDataCache(); + var tx1 = TestUtils.GetTransaction(); + tx1.Script = new byte[] { 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01 }; + var state1 = new TransactionState + { + Transaction = tx1, + BlockIndex = 1 + }; + var tx2 = TestUtils.GetTransaction(); + tx2.Script = new byte[] { 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x02 }; + var state2 = new TransactionState + { + Transaction = tx2, + BlockIndex = 1 + }; + cache.Add(tx1.Hash, state1); + cache.Add(tx2.Hash, state2); + + TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); + tblock.Hashes = new UInt256[] { tx1.Hash, tx2.Hash }; + Block block = tblock.GetBlock(cache); + + block.Index.Should().Be(1); + block.MerkleRoot.Should().Be(UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02")); + block.Transactions.Length.Should().Be(1); + block.Transactions[0].Hash.Should().Be(tx2.Hash); + } + + [TestMethod] + public void TestGetHeader() + { + TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); + Header header = tblock.Header; + header.PrevHash.Should().Be(UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01")); + header.MerkleRoot.Should().Be(UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02")); + } + + [TestMethod] + public void TestGetSize() + { + TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); + tblock.Hashes = new UInt256[] { TestUtils.GetTransaction().Hash }; + tblock.Size.Should().Be(146); + } + + [TestMethod] + public void TestDeserialize() + { + TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); + tblock.Hashes = new UInt256[] { TestUtils.GetTransaction().Hash }; + var newBlock = new TrimmedBlock(); + using (MemoryStream ms = new MemoryStream(1024)) + using (BinaryWriter writer = new BinaryWriter(ms)) + using (BinaryReader reader = new BinaryReader(ms)) + { + tblock.Serialize(writer); + ms.Seek(0, SeekOrigin.Begin); + newBlock.Deserialize(reader); + } + tblock.MerkleRoot.Should().Be(newBlock.MerkleRoot); + tblock.PrevHash.Should().Be(newBlock.PrevHash); + tblock.Timestamp.Should().Be(newBlock.Timestamp); + tblock.Hashes.Length.Should().Be(newBlock.Hashes.Length); + tblock.Witness.ScriptHash.Should().Be(newBlock.Witness.ScriptHash); + tblock.ToJson().ToString().Should().Be(newBlock.ToJson().ToString()); + } + + [TestMethod] + public void TestClone() + { + TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); + tblock.Hashes = new UInt256[] { TestUtils.GetTransaction().Hash }; + ICloneable cloneable = tblock; + var clonedBlock = cloneable.Clone(); + clonedBlock.ToJson().ToString().Should().Be(tblock.ToJson().ToString()); + } + + [TestMethod] + public void TestFromReplica() + { + TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); + tblock.Hashes = new UInt256[] { TestUtils.GetTransaction().Hash }; + ICloneable cloneable = new TrimmedBlock(); + cloneable.FromReplica(tblock); + ((TrimmedBlock)cloneable).ToJson().ToString().Should().Be(tblock.ToJson().ToString()); + } + } +} diff --git a/neo.UnitTests/TestBlockchain.cs b/neo.UnitTests/TestBlockchain.cs index 5276dc2e4c..290e054d10 100644 --- a/neo.UnitTests/TestBlockchain.cs +++ b/neo.UnitTests/TestBlockchain.cs @@ -33,14 +33,13 @@ public static NeoSystem InitializeMockNeoSystem() _Store = new Mock(); var defaultTx = TestUtils.CreateRandomHashTransaction(); + var txState = new TransactionState + { + BlockIndex = 1, + Transaction = defaultTx + }; _Store.Setup(p => p.GetBlocks()).Returns(new TestDataCache()); - _Store.Setup(p => p.GetTransactions()).Returns(new TestDataCache( - new TransactionState - { - BlockIndex = 1, - Transaction = defaultTx - })); - + _Store.Setup(p => p.GetTransactions()).Returns(new TestDataCache(defaultTx.Hash, txState)); _Store.Setup(p => p.GetContracts()).Returns(new TestDataCache()); _Store.Setup(p => p.GetStorages()).Returns(new TestDataCache()); _Store.Setup(p => p.GetHeaderHashList()).Returns(new TestDataCache()); diff --git a/neo.UnitTests/TestDataCache.cs b/neo.UnitTests/TestDataCache.cs index 44d86141b5..b468e09e4c 100644 --- a/neo.UnitTests/TestDataCache.cs +++ b/neo.UnitTests/TestDataCache.cs @@ -10,43 +10,43 @@ public class TestDataCache : DataCache where TKey : IEquatable, ISerializable where TValue : class, ICloneable, ISerializable, new() { - private readonly TValue _defaultValue; + private readonly Dictionary dic = new Dictionary(); - public TestDataCache() - { - _defaultValue = null; - } + public TestDataCache() { } - public TestDataCache(TValue defaultValue) + public TestDataCache(TKey key, TValue value) { - this._defaultValue = defaultValue; + dic.Add(key, value); } public override void DeleteInternal(TKey key) { + dic.Remove(key); } protected override void AddInternal(TKey key, TValue value) { + dic.Add(key, value); } protected override IEnumerable> FindInternal(byte[] key_prefix) { - return Enumerable.Empty>(); + return dic.ToList(); } protected override TValue GetInternal(TKey key) { - if (_defaultValue == null) throw new NotImplementedException(); - return _defaultValue; + if (dic[key] == null) throw new NotImplementedException(); + return dic[key]; } protected override TValue TryGetInternal(TKey key) { - return _defaultValue; + return dic.TryGetValue(key, out TValue value) ? value : null; } protected override void UpdateInternal(TKey key, TValue value) { + dic[key] = value; } } } diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index 294afd91cd..6a8c16dbb6 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -17,10 +17,11 @@ namespace Neo.UnitTests.Wallets.NEP6 public class UT_NEP6Wallet { private NEP6Wallet uut; - private static string wPath; + private string wPath; private static KeyPair keyPair; private static string nep2key; private static UInt160 testScriptHash; + private string rootPath; public static string GetRandomPath() { @@ -48,9 +49,9 @@ private NEP6Wallet CreateWallet() private string CreateWalletFile() { - string path = GetRandomPath(); - if (!Directory.Exists(path)) Directory.CreateDirectory(path); - path = Path.Combine(path, "wallet.json"); + rootPath = GetRandomPath(); + if (!Directory.Exists(rootPath)) Directory.CreateDirectory(rootPath); + string path = Path.Combine(rootPath, "wallet.json"); File.WriteAllText(path, "{\"name\":\"name\",\"version\":\"0.0\",\"scrypt\":{\"n\":0,\"r\":0,\"p\":0},\"accounts\":[],\"extra\":{}}"); return path; } @@ -66,6 +67,7 @@ public void TestSetup() public void TestCleanUp() { if (File.Exists(wPath)) File.Delete(wPath); + if (Directory.Exists(rootPath)) Directory.Delete(rootPath); } [TestMethod] @@ -328,6 +330,7 @@ public void TestMigrate() NEP6Wallet nw = NEP6Wallet.Migrate(npath, path, "123"); bool result = nw.Contains(testScriptHash); Assert.AreEqual(true, result); + if (File.Exists(path)) File.Delete(path); } [TestMethod] diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index a6af9fc721..10ee5f9d0b 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -469,8 +469,7 @@ internal void InvalidateAllTransactions() /// /// Note: this must only be called from a single thread (the Blockchain actor) /// - /// Max transactions to reverify, the value passed should be >=2. If 1 is passed it - /// will still potentially use 2. + /// Max transactions to reverify, the value passed cam be >=1 /// The snapshot to use for verifying. /// true if more unsorted messages exist, otherwise false internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, Snapshot snapshot) From 3a4327b8170398fd5271353293cd9efca44a1bba Mon Sep 17 00:00:00 2001 From: Luchuan Date: Thu, 26 Sep 2019 21:26:42 +0800 Subject: [PATCH 103/305] Fix tx reverify (#1116) * add internal to DB and WriteBatch * tx.reverify add hashes.length != witnesses.length condition * reset db.cs writebatch.cs * format --- .../Network/P2P/Payloads/UT_Transaction.cs | 28 +++++++++++++++++++ neo/Network/P2P/Payloads/Transaction.cs | 1 + 2 files changed, 29 insertions(+) diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index fc32744b96..74efe64dd2 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -774,6 +774,34 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() } } + [TestMethod] + public void Transaction_Reverify_Hashes_Length_Unequal_To_Witnesses_Length() + { + var snapshot = store.GetSnapshot(); + Transaction txSimple = new Transaction + { + Version = 0x00, + Nonce = 0x01020304, + Sender = UInt160.Zero, + SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS + NetworkFee = 0x0000000000000001, + ValidUntilBlock = 0x01020304, + Attributes = new TransactionAttribute[0] { }, + Cosigners = new Cosigner[] { + new Cosigner + { + Account = UInt160.Parse("0x0001020304050607080900010203040506070809"), + Scopes = WitnessScope.Global + } + }, + Script = new byte[] { (byte)OpCode.PUSH1 }, + Witnesses = new Witness[0] { } + }; + UInt160[] hashes = txSimple.GetScriptHashesForVerifying(snapshot); + Assert.AreEqual(2, hashes.Length); + Assert.IsFalse(txSimple.Reverify(snapshot, new Transaction[0])); + } + [TestMethod] public void Transaction_Serialize_Deserialize_Simple() { diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index a2586e6da2..74fc1ef3c7 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -140,6 +140,7 @@ public virtual bool Reverify(Snapshot snapshot, IEnumerable mempool fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Select(p => (BigInteger)(p.SystemFee + p.NetworkFee)).Sum(); if (balance < fee) return false; UInt160[] hashes = GetScriptHashesForVerifying(snapshot); + if (hashes.Length != Witnesses.Length) return false; for (int i = 0; i < hashes.Length; i++) { if (Witnesses[i].VerificationScript.Length > 0) continue; From 74f5acf040ab3eda6088967d9b7673d3fe977dea Mon Sep 17 00:00:00 2001 From: Luchuan Date: Thu, 26 Sep 2019 23:17:48 +0800 Subject: [PATCH 104/305] Add `vmState` field in rpc getrawtransaction result (#1117) * add internal to DB and WriteBatch * add vmstate field in rpc getrawtransaction result * reset db.cs writebatch.cs * Optimize * Update RpcServer.cs * Format * Update RpcServer.cs * Update RpcTransaction.cs * Update UT_RpcClient.cs * Fixes `RpcTransaction.VMState` --- neo.UnitTests/Network/RPC/UT_RpcClient.cs | 2 ++ neo/Network/RPC/Models/RpcTransaction.cs | 5 +++++ neo/Network/RPC/RpcServer.cs | 7 ++++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/neo.UnitTests/Network/RPC/UT_RpcClient.cs b/neo.UnitTests/Network/RPC/UT_RpcClient.cs index 0875a5a793..7294d3ccb3 100644 --- a/neo.UnitTests/Network/RPC/UT_RpcClient.cs +++ b/neo.UnitTests/Network/RPC/UT_RpcClient.cs @@ -355,11 +355,13 @@ public void TestGetRawTransaction() json["blockhash"] = UInt256.Zero.ToString(); json["confirmations"] = 100; json["blocktime"] = 10; + json["vmState"] = VMState.HALT; MockResponse(response.ToString()); result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); Assert.AreEqual(transaction.Hash, result.Transaction.Hash); Assert.AreEqual(100, result.Confirmations); + Assert.AreEqual(VMState.HALT, result.VMState); Assert.AreEqual(json.ToString(), result.ToJson().ToString()); } diff --git a/neo/Network/RPC/Models/RpcTransaction.cs b/neo/Network/RPC/Models/RpcTransaction.cs index f41c04710a..f96179c358 100644 --- a/neo/Network/RPC/Models/RpcTransaction.cs +++ b/neo/Network/RPC/Models/RpcTransaction.cs @@ -1,5 +1,6 @@ using Neo.IO.Json; using Neo.Network.P2P.Payloads; +using Neo.VM; namespace Neo.Network.RPC.Models { @@ -13,6 +14,8 @@ public class RpcTransaction public uint? BlockTime { get; set; } + public VMState? VMState { get; set; } + public JObject ToJson() { JObject json = Transaction.ToJson(); @@ -21,6 +24,7 @@ public JObject ToJson() json["blockhash"] = BlockHash.ToString(); json["confirmations"] = Confirmations; json["blocktime"] = BlockTime; + json["vmState"] = VMState; } return json; } @@ -34,6 +38,7 @@ public static RpcTransaction FromJson(JObject json) transaction.BlockHash = UInt256.Parse(json["blockhash"].AsString()); transaction.Confirmations = (int)json["confirmations"].AsNumber(); transaction.BlockTime = (uint)json["blocktime"].AsNumber(); + transaction.VMState = json["vmState"].TryGetEnum(); } return transaction; } diff --git a/neo/Network/RPC/RpcServer.cs b/neo/Network/RPC/RpcServer.cs index c47e6644e7..7414d54d10 100644 --- a/neo/Network/RPC/RpcServer.cs +++ b/neo/Network/RPC/RpcServer.cs @@ -564,13 +564,14 @@ private JObject GetRawTransaction(UInt256 hash, bool verbose) if (verbose) { JObject json = tx.ToJson(); - uint? height = Blockchain.Singleton.Store.GetTransactions().TryGet(hash)?.BlockIndex; - if (height != null) + TransactionState txState = Blockchain.Singleton.Store.GetTransactions().TryGet(hash); + if (txState != null) { - Header header = Blockchain.Singleton.Store.GetHeader((uint)height); + Header header = Blockchain.Singleton.Store.GetHeader(txState.BlockIndex); json["blockhash"] = header.Hash.ToString(); json["confirmations"] = Blockchain.Singleton.Height - header.Index + 1; json["blocktime"] = header.Timestamp; + json["vmState"] = txState.VMState; } return json; } From 18bf2f2f87f7776e2b1ba8ffa6cf471c4da539fe Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 3 Oct 2019 09:04:48 +0200 Subject: [PATCH 105/305] Speed up travis (#1136) * Speed up * Update UT_NEP6Wallet.cs * Speed ut * More speed up * Format * Remove blank line * Reduce travis verbosity * Coverage only on linux * CollectCoverage only if is needed * Update .travis.yml * Already build * no-restore * Update .travis.yml * Back verbosity * Update netcore version * Remove BOM * Update nugets * Fix fluent update * Paralellize * format * Update .travis.yml * Fix * Update .travis.yml * Min verbose * Remove parallel execution * Fix change * Revert AsParallel() --- .travis.yml | 20 +++-- neo.UnitTests/Consensus/UT_Consensus.cs | 34 ++++---- neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs | 8 +- .../Cryptography/ECC/UT_ECFieldElement.cs | 4 +- neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs | 24 +++--- neo.UnitTests/Cryptography/UT_Base58.cs | 2 +- neo.UnitTests/Cryptography/UT_BloomFilter.cs | 4 +- neo.UnitTests/Cryptography/UT_Crypto.cs | 2 +- .../Cryptography/UT_Cryptography_Helper.cs | 30 +++---- neo.UnitTests/Cryptography/UT_MerkleTree.cs | 2 +- neo.UnitTests/IO/Caching/UT_Cache.cs | 10 +-- neo.UnitTests/IO/Caching/UT_CloneCache.cs | 2 +- neo.UnitTests/IO/Caching/UT_DataCache.cs | 8 +- neo.UnitTests/IO/Caching/UT_FIFOSet.cs | 6 +- .../IO/Caching/UT_ReflectionCache.cs | 2 +- neo.UnitTests/IO/Json/UT_JArray.cs | 4 +- neo.UnitTests/IO/Json/UT_JBoolean.cs | 6 +- neo.UnitTests/IO/Json/UT_JNumber.cs | 8 +- neo.UnitTests/IO/Json/UT_JObject.cs | 12 +-- neo.UnitTests/IO/UT_IOHelper.cs | 14 ++-- neo.UnitTests/Ledger/UT_MemoryPool.cs | 82 +++++++++---------- .../Network/P2P/Payloads/UT_Transaction.cs | 3 +- .../Network/P2P/Payloads/UT_Witness.cs | 77 ++++------------- .../SmartContract/Native/UT_PolicyContract.cs | 4 +- neo.UnitTests/TestBlockchain.cs | 6 ++ neo.UnitTests/UT_BigDecimal.cs | 4 +- neo.UnitTests/UT_Culture.cs | 11 ++- neo.UnitTests/UT_ProtocolSettings.cs | 2 - neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs | 62 +++++++------- neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs | 3 +- neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs | 6 +- neo.UnitTests/Wallets/UT_AssetDescriptor.cs | 5 +- neo.UnitTests/Wallets/UT_KeyPair.cs | 2 +- neo.UnitTests/Wallets/UT_Wallet.cs | 28 +++---- neo.UnitTests/Wallets/UT_Wallets_Helper.cs | 4 +- neo.UnitTests/neo.UnitTests.csproj | 16 ++-- 36 files changed, 243 insertions(+), 274 deletions(-) diff --git a/.travis.yml b/.travis.yml index f08f8110bb..59b745ca31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ dist: bionic osx_image: xcode9.1 mono: none -dotnet: 2.2.300 +dotnet: 2.2.402 env: - TEST_SUITE="without-cultures" @@ -25,15 +25,21 @@ before_script: - dotnet format --check --dry-run -w . -v diagnostic # check C# formatting for neo.sln - cd neo.UnitTests script: | - dotnet restore - if [[ "$TEST_SUITE" == cultures ]]; then - dotnet test + if [[ "$TEST_SUITE" == "cultures" ]]; then + dotnet test -v m --filter FullyQualifiedName=Neo.UnitTests.UT_Culture.All_Tests_Cultures else - find * -name *.csproj | xargs -I % dotnet add % package coverlet.msbuild - dotnet test -v n --filter FullyQualifiedName!=Neo.UnitTests.UT_Culture.All_Tests_Cultures /p:CollectCoverage=true /p:CoverletOutputFormat=opencover + if [[ "$TEST_SUITE" == "without-cultures" && "$TRAVIS_OS_NAME" == "linux" ]]; then + # Test & Calculate coverage + find * -name *.csproj | xargs -I % dotnet add % package coverlet.msbuild + dotnet test -v m --filter FullyQualifiedName!=Neo.UnitTests.UT_Culture.All_Tests_Cultures /p:CollectCoverage=true /p:CoverletOutputFormat=opencover + else + # Only test + dotnet test -v m --filter FullyQualifiedName!=Neo.UnitTests.UT_Culture.All_Tests_Cultures + fi fi after_success: | - if [[ "$TEST_SUITE" == "without-cultures" ]]; then + if [[ "$TEST_SUITE" == "without-cultures" && "$TRAVIS_OS_NAME" == "linux" ]]; then + # Send coverage echo "Test Success - Branch($TRAVIS_BRANCH) Pull Request($TRAVIS_PULL_REQUEST) Tag($TRAVIS_TAG)" bash <(curl -s https://codecov.io/bash) -v fi diff --git a/neo.UnitTests/Consensus/UT_Consensus.cs b/neo.UnitTests/Consensus/UT_Consensus.cs index 14c287bcff..c8d8fbf6c4 100644 --- a/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/neo.UnitTests/Consensus/UT_Consensus.cs @@ -215,17 +215,17 @@ public void TestSerializeAndDeserializeConsensusContext() copiedContext.Block.PrevHash.Should().Be(consensusContext.Block.PrevHash); copiedContext.Block.Index.Should().Be(consensusContext.Block.Index); copiedContext.ViewNumber.Should().Be(consensusContext.ViewNumber); - copiedContext.Validators.ShouldAllBeEquivalentTo(consensusContext.Validators); + 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.ShouldAllBeEquivalentTo(consensusContext.TransactionHashes); - copiedContext.Transactions.ShouldAllBeEquivalentTo(consensusContext.Transactions); - copiedContext.Transactions.Values.ShouldAllBeEquivalentTo(consensusContext.Transactions.Values); - copiedContext.PreparationPayloads.ShouldAllBeEquivalentTo(consensusContext.PreparationPayloads); - copiedContext.CommitPayloads.ShouldAllBeEquivalentTo(consensusContext.CommitPayloads); - copiedContext.ChangeViewPayloads.ShouldAllBeEquivalentTo(consensusContext.ChangeViewPayloads); + 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] @@ -315,9 +315,9 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndNoPrepar var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ; - copiedMsg.ChangeViewMessages.ShouldAllBeEquivalentTo(msg.ChangeViewMessages); + copiedMsg.ChangeViewMessages.Should().BeEquivalentTo(msg.ChangeViewMessages); copiedMsg.PreparationHash.Should().Be(msg.PreparationHash); - copiedMsg.PreparationMessages.ShouldAllBeEquivalentTo(msg.PreparationMessages); + copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages); copiedMsg.CommitMessages.Count.Should().Be(0); } @@ -409,10 +409,10 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ; - copiedMsg.ChangeViewMessages.ShouldAllBeEquivalentTo(msg.ChangeViewMessages); - copiedMsg.PrepareRequestMessage.ShouldBeEquivalentTo(msg.PrepareRequestMessage); + copiedMsg.ChangeViewMessages.Should().BeEquivalentTo(msg.ChangeViewMessages); + copiedMsg.PrepareRequestMessage.Should().BeEquivalentTo(msg.PrepareRequestMessage); copiedMsg.PreparationHash.Should().Be(null); - copiedMsg.PreparationMessages.ShouldAllBeEquivalentTo(msg.PreparationMessages); + copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages); copiedMsg.CommitMessages.Count.Should().Be(0); } @@ -470,9 +470,9 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithoutC var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ; copiedMsg.ChangeViewMessages.Count.Should().Be(0); - copiedMsg.PrepareRequestMessage.ShouldBeEquivalentTo(msg.PrepareRequestMessage); + copiedMsg.PrepareRequestMessage.Should().BeEquivalentTo(msg.PrepareRequestMessage); copiedMsg.PreparationHash.Should().Be(null); - copiedMsg.PreparationMessages.ShouldAllBeEquivalentTo(msg.PreparationMessages); + copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages); copiedMsg.CommitMessages.Count.Should().Be(0); } @@ -550,10 +550,10 @@ public void TestSerializeAndDeserializeRecoveryMessageWithoutChangeViewsWithComm var copiedMsg = TestUtils.CopyMsgBySerialization(msg, new RecoveryMessage()); ; copiedMsg.ChangeViewMessages.Count.Should().Be(0); - copiedMsg.PrepareRequestMessage.ShouldBeEquivalentTo(msg.PrepareRequestMessage); + copiedMsg.PrepareRequestMessage.Should().BeEquivalentTo(msg.PrepareRequestMessage); copiedMsg.PreparationHash.Should().Be(null); - copiedMsg.PreparationMessages.ShouldAllBeEquivalentTo(msg.PreparationMessages); - copiedMsg.CommitMessages.ShouldAllBeEquivalentTo(msg.CommitMessages); + copiedMsg.PreparationMessages.Should().BeEquivalentTo(msg.PreparationMessages); + copiedMsg.CommitMessages.Should().BeEquivalentTo(msg.CommitMessages); } private static ConsensusPayload MakeSignedPayload(ConsensusContext context, ConsensusMessage message, ushort validatorIndex, byte[] witnessInvocationScript) diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs b/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs index 6c24fcb922..62206741cb 100644 --- a/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs +++ b/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs @@ -22,9 +22,9 @@ public void TestSetup() public void TestECDsaConstructor() { Action action = () => new ECDsa(key.PublicKey); - action.ShouldNotThrow(); + action.Should().NotThrow(); action = () => new ECDsa(key.PrivateKey, key.PublicKey.Curve); - action.ShouldNotThrow(); + action.Should().NotThrow(); } [TestMethod] @@ -32,14 +32,14 @@ public void TestGenerateSignature() { ECDsa sa = new ECDsa(key.PrivateKey, key.PublicKey.Curve); byte[] message = System.Text.Encoding.Default.GetBytes("HelloWorld"); - for (int i = 0; i < 30; i++) + for (int i = 0; i < 10; i++) { BigInteger[] result = sa.GenerateSignature(message); result.Length.Should().Be(2); } sa = new ECDsa(key.PublicKey); Action action = () => sa.GenerateSignature(message); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs b/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs index 90a31b495d..6b2965b41f 100644 --- a/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs +++ b/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs @@ -16,11 +16,11 @@ public void TestECFieldElementConstructor() { BigInteger input = new BigInteger(100); Action action = () => new ECFieldElement(input, ECCurve.Secp256k1); - action.ShouldNotThrow(); + action.Should().NotThrow(); input = ECCurve.Secp256k1.Q; action = () => new ECFieldElement(input, ECCurve.Secp256k1); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs b/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs index 26d71cc3e6..b38df0fa44 100644 --- a/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs +++ b/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs @@ -57,9 +57,9 @@ public void TestECPointConstructor() point.Y.Should().Be(Y); point.Curve.Should().Be(ECCurve.Secp256k1); Action action = () => new ECPoint(X, null, ECCurve.Secp256k1); - action.ShouldThrow(); + action.Should().Throw(); action = () => new ECPoint(null, Y, ECCurve.Secp256k1); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -67,13 +67,13 @@ public void TestDecodePoint() { byte[] input1 = { 0 }; Action action = () => ECPoint.DecodePoint(input1, ECCurve.Secp256k1); - action.ShouldThrow(); + action.Should().Throw(); byte[] input2 = { 4, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 72, 58, 218, 119, 38, 163, 196, 101, 93, 164, 251, 252, 14, 17, 8, 168, 253, 23, 180, 72, 166, 133, 84, 25, 156, 71, 208, 143, 251, 16, 212, 184 }; ECPoint.DecodePoint(input2, ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); action = () => ECPoint.DecodePoint(input2.Take(32).ToArray(), ECCurve.Secp256k1); - action.ShouldThrow(); + action.Should().Throw(); byte[] input3 = { 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152 }; byte[] input4 = { 3, 107, 23, 209, 242, 225, 44, 66, 71, 248, 188, 230, 229, 99, 164, 64, 242, 119, 3, 125, 129, 45, 235, 51, 160, 244, 161, 57, 69, 216, 152, 194, 150 }; @@ -81,7 +81,7 @@ public void TestDecodePoint() ECPoint.DecodePoint(input4, ECCurve.Secp256r1).Should().Be(ECCurve.Secp256r1.G); action = () => ECPoint.DecodePoint(input3.Take(input3.Length - 1).ToArray(), ECCurve.Secp256k1); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -89,13 +89,13 @@ public void TestDeserializeFrom() { byte[] input1 = { 0 }; Action action = () => ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input1)), ECCurve.Secp256k1); - action.ShouldThrow(); + action.Should().Throw(); byte[] input2 = { 4, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 72, 58, 218, 119, 38, 163, 196, 101, 93, 164, 251, 252, 14, 17, 8, 168, 253, 23, 180, 72, 166, 133, 84, 25, 156, 71, 208, 143, 251, 16, 212, 184 }; ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input2)), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); action = () => ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input2.Take(32).ToArray())), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); - action.ShouldThrow(); + action.Should().Throw(); byte[] input3 = { 2, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152 }; ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input3)), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); @@ -103,7 +103,7 @@ public void TestDeserializeFrom() ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input4)), ECCurve.Secp256r1).Should().Be(ECCurve.Secp256r1.G); action = () => ECPoint.DeserializeFrom(new BinaryReader(new MemoryStream(input3.Take(input3.Length - 1).ToArray())), ECCurve.Secp256k1).Should().Be(ECCurve.Secp256k1.G); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -178,7 +178,7 @@ public void TestFromBytes() { byte[] input1 = { 0 }; Action action = () => ECPoint.FromBytes(input1, ECCurve.Secp256k1); - action.ShouldThrow(); + action.Should().Throw(); byte[] input2 = { 4, 121, 190, 102, 126, 249, 220, 187, 172, 85, 160, 98, 149, 206, 135, 11, 7, 2, 155, 252, 219, 45, 206, 40, 217, 89, 242, 129, 91, 22, 248, 23, 152, 72, 58, 218, 119, 38, 163, 196, 101, 93, 164, 251, 252, 14, 17, 8, 168, 253, 23, 180, 72, 166, 133, 84, 25, 156, 71, 208, 143, 251, 16, 212, 184 }; @@ -293,14 +293,14 @@ public void TestOpMultiply() ECPoint p = null; byte[] n = new byte[] { 1 }; Action action = () => p = p * n; - action.ShouldThrow(); + action.Should().Throw(); p = ECCurve.Secp256k1.G; n = null; - action.ShouldThrow(); + action.Should().Throw(); n = new byte[] { 1 }; - action.ShouldThrow(); + action.Should().Throw(); p = ECCurve.Secp256k1.Infinity; n = new byte[32]; diff --git a/neo.UnitTests/Cryptography/UT_Base58.cs b/neo.UnitTests/Cryptography/UT_Base58.cs index 1a539cc42c..5f2d750382 100644 --- a/neo.UnitTests/Cryptography/UT_Base58.cs +++ b/neo.UnitTests/Cryptography/UT_Base58.cs @@ -25,7 +25,7 @@ public void TestDecode() Base58.Decode(encoded1).Should().BeEquivalentTo(decoded1); Base58.Decode(encoded2).Should().BeEquivalentTo(decoded2); Action action = () => Base58.Decode(encoded1 + "l").Should().BeEquivalentTo(decoded1); - action.ShouldThrow(); + action.Should().Throw(); } } } diff --git a/neo.UnitTests/Cryptography/UT_BloomFilter.cs b/neo.UnitTests/Cryptography/UT_BloomFilter.cs index 5deabe1b8e..7ed470e3e1 100644 --- a/neo.UnitTests/Cryptography/UT_BloomFilter.cs +++ b/neo.UnitTests/Cryptography/UT_BloomFilter.cs @@ -27,12 +27,12 @@ public void TestBloomFIlterConstructorGetKMTweak() int m = -7, n = 10; uint nTweak = 123456; Action action = () => new BloomFilter(m, n, nTweak); - action.ShouldThrow(); + action.Should().Throw(); m = 7; n = -10; action = () => new BloomFilter(m, n, nTweak); - action.ShouldThrow(); + action.Should().Throw(); n = 10; BloomFilter filter = new BloomFilter(m, n, nTweak); diff --git a/neo.UnitTests/Cryptography/UT_Crypto.cs b/neo.UnitTests/Cryptography/UT_Crypto.cs index c46f25aa69..0fff2a26c3 100644 --- a/neo.UnitTests/Cryptography/UT_Crypto.cs +++ b/neo.UnitTests/Cryptography/UT_Crypto.cs @@ -58,7 +58,7 @@ public void TestVerifySignature() wrongKey = new byte[36]; Action action = () => Crypto.Default.VerifySignature(message, signature, wrongKey).Should().BeFalse(); - action.ShouldThrow(); + action.Should().Throw(); } } } diff --git a/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index 67ed2a920d..58071f4994 100644 --- a/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -46,27 +46,27 @@ public void TestAesEncrypt() byte[] nullData = null; Action action = () => nullData.AesEncrypt(key, iv); - action.ShouldThrow(); + action.Should().Throw(); byte[] nullKey = null; action = () => data.AesEncrypt(nullKey, iv); - action.ShouldThrow(); + action.Should().Throw(); byte[] nullIv = null; action = () => data.AesEncrypt(key, nullIv); - action.ShouldThrow(); + action.Should().Throw(); byte[] wrongData = Encoding.ASCII.GetBytes("000000000000000000000000000000001"); ; action = () => wrongData.AesEncrypt(key, iv); - action.ShouldThrow(); + action.Should().Throw(); byte[] wrongKey = Encoding.ASCII.GetBytes("123456781234567812345678123456780"); ; action = () => data.AesEncrypt(wrongKey, iv); - action.ShouldThrow(); + action.Should().Throw(); byte[] wrongIv = Encoding.ASCII.GetBytes("12345678123456780"); ; action = () => data.AesEncrypt(key, wrongIv); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -83,27 +83,27 @@ public void TestAesDecrypt() byte[] nullData = null; Action action = () => nullData.AesDecrypt(key, iv); - action.ShouldThrow(); + action.Should().Throw(); byte[] nullKey = null; action = () => data.AesDecrypt(nullKey, iv); - action.ShouldThrow(); + action.Should().Throw(); byte[] nullIv = null; action = () => data.AesDecrypt(key, nullIv); - action.ShouldThrow(); + action.Should().Throw(); byte[] wrongData = Encoding.ASCII.GetBytes("00000000000000001"); ; action = () => wrongData.AesDecrypt(key, iv); - action.ShouldThrow(); + action.Should().Throw(); byte[] wrongKey = Encoding.ASCII.GetBytes("123456781234567812345678123456780"); ; action = () => data.AesDecrypt(wrongKey, iv); - action.ShouldThrow(); + action.Should().Throw(); byte[] wrongIv = Encoding.ASCII.GetBytes("12345678123456780"); ; action = () => data.AesDecrypt(key, wrongIv); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -116,11 +116,11 @@ public void TestBase58CheckDecode() input = "3v"; Action action = () => input.Base58CheckDecode(); - action.ShouldThrow(); + action.Should().Throw(); input = "3vQB7B6MrGQZaxCuFg4og"; action = () => input.Base58CheckDecode(); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -215,7 +215,7 @@ public void TestToArray() SecureString nullString = null; Action action = () => nullString.ToArray(); - action.ShouldThrow(); + action.Should().Throw(); var zeroString = new SecureString(); var result = zeroString.ToArray(); diff --git a/neo.UnitTests/Cryptography/UT_MerkleTree.cs b/neo.UnitTests/Cryptography/UT_MerkleTree.cs index 4b56380957..870405caf8 100644 --- a/neo.UnitTests/Cryptography/UT_MerkleTree.cs +++ b/neo.UnitTests/Cryptography/UT_MerkleTree.cs @@ -23,7 +23,7 @@ public void TestBuildAndDepthFirstSearch() { IReadOnlyList hashNull = new UInt256[] { }; Action action = () => new MerkleTree(hashNull); - action.ShouldThrow(); + action.Should().Throw(); byte[] array1 = { 0x01 }; var hash1 = GetByteArrayHash(array1); diff --git a/neo.UnitTests/IO/Caching/UT_Cache.cs b/neo.UnitTests/IO/Caching/UT_Cache.cs index 930547f314..65728bd82d 100644 --- a/neo.UnitTests/IO/Caching/UT_Cache.cs +++ b/neo.UnitTests/IO/Caching/UT_Cache.cs @@ -140,13 +140,13 @@ public void TestCopyTo() string[] temp = new string[2]; Action action = () => cache.CopyTo(null, 1); - action.ShouldThrow(); + action.Should().Throw(); action = () => cache.CopyTo(temp, -1); - action.ShouldThrow(); + action.Should().Throw(); action = () => cache.CopyTo(temp, 1); - action.ShouldThrow(); + action.Should().Throw(); cache.CopyTo(temp, 0); temp[0].Should().Be("hello"); @@ -207,7 +207,7 @@ public void TestArrayIndexAccess() { string temp = cache["non exist string".GetHashCode()]; }; - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -250,7 +250,7 @@ public void TestDispose() { int count = cache.Count; }; - action.ShouldThrow(); + action.Should().Throw(); } } } diff --git a/neo.UnitTests/IO/Caching/UT_CloneCache.cs b/neo.UnitTests/IO/Caching/UT_CloneCache.cs index afa550e45c..b75a70ee97 100644 --- a/neo.UnitTests/IO/Caching/UT_CloneCache.cs +++ b/neo.UnitTests/IO/Caching/UT_CloneCache.cs @@ -89,7 +89,7 @@ public void TestGetInternal() { var item = cloneCache[new MyKey("key4")]; }; - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/IO/Caching/UT_DataCache.cs b/neo.UnitTests/IO/Caching/UT_DataCache.cs index f21624814d..437f2b92f1 100644 --- a/neo.UnitTests/IO/Caching/UT_DataCache.cs +++ b/neo.UnitTests/IO/Caching/UT_DataCache.cs @@ -175,7 +175,7 @@ public void TestAccessByNotFoundKey() { var item = myDataCache[new MyKey("key1")]; }; - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -188,7 +188,7 @@ public void TestAccessByDeletedKey() { var item = myDataCache[new MyKey("key1")]; }; - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -198,14 +198,14 @@ public void TestAdd() myDataCache[new MyKey("key1")].Should().Be(new MyValue("value1")); Action action = () => myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - action.ShouldThrow(); + action.Should().Throw(); myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); myDataCache.Delete(new MyKey("key2")); // trackable.State = TrackState.Deleted myDataCache.Add(new MyKey("key2"), new MyValue("value2")); // trackable.State = TrackState.Changed action = () => myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/IO/Caching/UT_FIFOSet.cs b/neo.UnitTests/IO/Caching/UT_FIFOSet.cs index 9b781858ee..e6e01710d9 100644 --- a/neo.UnitTests/IO/Caching/UT_FIFOSet.cs +++ b/neo.UnitTests/IO/Caching/UT_FIFOSet.cs @@ -58,13 +58,13 @@ public void FIFOSetTest() public void TestConstructor() { Action action1 = () => new FIFOSet(-1); - action1.ShouldThrow(); + action1.Should().Throw(); Action action2 = () => new FIFOSet(1, -1); - action2.ShouldThrow(); + action2.Should().Throw(); Action action3 = () => new FIFOSet(1, 2); - action3.ShouldThrow(); + action3.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs index e51f6f81d6..4e1fcce634 100644 --- a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs +++ b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs @@ -43,7 +43,7 @@ public void TestCreateFromEnum() public void TestCreateFromObjectNotEnum() { Action action = () => ReflectionCache.CreateFromEnum(); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/IO/Json/UT_JArray.cs b/neo.UnitTests/IO/Json/UT_JArray.cs index 5b7a30c9ae..913fb9b10b 100644 --- a/neo.UnitTests/IO/Json/UT_JArray.cs +++ b/neo.UnitTests/IO/Json/UT_JArray.cs @@ -80,7 +80,7 @@ public void TestSetItem() Assert.AreEqual(jArray[0], bob); Action action = () => jArray[1] = alice; - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -100,7 +100,7 @@ public void TestClear() jArray.Clear(); Action action = () => jArray[0].ToString(); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/IO/Json/UT_JBoolean.cs b/neo.UnitTests/IO/Json/UT_JBoolean.cs index 8e5f4acdd6..302d48c52a 100644 --- a/neo.UnitTests/IO/Json/UT_JBoolean.cs +++ b/neo.UnitTests/IO/Json/UT_JBoolean.cs @@ -39,7 +39,7 @@ public void TestParse() TextReader tr3 = new StringReader("aaa"); Action action = () => JBoolean.Parse(tr3); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -51,7 +51,7 @@ public void TestParseFalse() TextReader tr2 = new StringReader("aaa"); Action action = () => JBoolean.ParseFalse(tr2); - action.ShouldThrow(); + action.Should().Throw(); TextReader tr3 = new StringReader("\t\rfalse"); JBoolean ret3 = JBoolean.ParseFalse(tr3); @@ -67,7 +67,7 @@ public void TestParseTrue() TextReader tr2 = new StringReader("aaa"); Action action = () => JBoolean.ParseTrue(tr2); - action.ShouldThrow(); + action.Should().Throw(); TextReader tr3 = new StringReader(" true"); JBoolean ret3 = JBoolean.ParseTrue(tr3); diff --git a/neo.UnitTests/IO/Json/UT_JNumber.cs b/neo.UnitTests/IO/Json/UT_JNumber.cs index 16adbe9d80..e3cbe6d300 100644 --- a/neo.UnitTests/IO/Json/UT_JNumber.cs +++ b/neo.UnitTests/IO/Json/UT_JNumber.cs @@ -39,10 +39,10 @@ public void TestAsBoolean() public void TestAsString() { Action action1 = () => new JNumber(double.PositiveInfinity).AsString(); - action1.ShouldThrow(); + action1.Should().Throw(); Action action2 = () => new JNumber(double.NegativeInfinity).AsString(); - action2.ShouldThrow(); + action2.Should().Throw(); } [TestMethod] @@ -58,10 +58,10 @@ public void TestTryGetEnum() public void TestParse() { Action action1 = () => JNumber.Parse(new StringReader("100.a")); - action1.ShouldThrow(); + action1.Should().Throw(); Action action2 = () => JNumber.Parse(new StringReader("100.+")); - action2.ShouldThrow(); + action2.Should().Throw(); } } } diff --git a/neo.UnitTests/IO/Json/UT_JObject.cs b/neo.UnitTests/IO/Json/UT_JObject.cs index 990363bcb1..9645b04011 100644 --- a/neo.UnitTests/IO/Json/UT_JObject.cs +++ b/neo.UnitTests/IO/Json/UT_JObject.cs @@ -54,14 +54,14 @@ public void TestAsNumber() public void TestParse() { Action action = () => JObject.Parse(new StringReader(""), -1); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] public void TestParseNull() { Action action = () => JObject.Parse(new StringReader("naaa")); - action.ShouldThrow(); + action.Should().Throw(); JObject.Parse(new StringReader("null")).Should().BeNull(); } @@ -70,16 +70,16 @@ public void TestParseNull() public void TestParseObject() { Action action1 = () => JObject.Parse(new StringReader("{\"k1\":\"v1\",\"k1\":\"v2\"}"), 100); - action1.ShouldThrow(); + action1.Should().Throw(); Action action2 = () => JObject.Parse(new StringReader("{\"k1\",\"k1\"}"), 100); - action2.ShouldThrow(); + action2.Should().Throw(); Action action3 = () => JObject.Parse(new StringReader("{\"k1\":\"v1\""), 100); - action3.ShouldThrow(); + action3.Should().Throw(); Action action4 = () => JObject.Parse(new StringReader("aaa"), 100); - action4.ShouldThrow(); + action4.Should().Throw(); JObject.Parse(new StringReader("{\"k1\":\"v1\"}"), 100).ToString().Should().Be("{\"k1\":\"v1\"}"); } diff --git a/neo.UnitTests/IO/UT_IOHelper.cs b/neo.UnitTests/IO/UT_IOHelper.cs index 350cc31425..114ceffc8d 100644 --- a/neo.UnitTests/IO/UT_IOHelper.cs +++ b/neo.UnitTests/IO/UT_IOHelper.cs @@ -40,7 +40,7 @@ public void TestAsSerializable() else { Action action = () => Neo.IO.Helper.AsSerializable(new byte[0], typeof(Double)); - action.ShouldThrow(); + action.Should().Throw(); } } } @@ -245,7 +245,7 @@ public void TestReadBytesWithGrouping() stream.Seek(0, SeekOrigin.Begin); BinaryReader reader = new BinaryReader(stream); Action action = () => Neo.IO.Helper.ReadBytesWithGrouping(reader); - action.ShouldThrow(); + action.Should().Throw(); } } } @@ -332,7 +332,7 @@ public void TestReadVarInt() stream.Seek(0, SeekOrigin.Begin); BinaryReader reader = new BinaryReader(stream); Action action = () => Neo.IO.Helper.ReadVarInt(reader, 0xFFFFFFFF); - action.ShouldThrow(); + action.Should().Throw(); } } } @@ -432,21 +432,21 @@ public void TestWriteFixedString() MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); Action action = () => Neo.IO.Helper.WriteFixedString(writer, null, 0); - action.ShouldThrow(); + action.Should().Throw(); } else if (i == 1) { MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); Action action = () => Neo.IO.Helper.WriteFixedString(writer, "AA", Encoding.UTF8.GetBytes("AA").Length - 1); - action.ShouldThrow(); + action.Should().Throw(); } else if (i == 2) { MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); Action action = () => Neo.IO.Helper.WriteFixedString(writer, "拉拉", Encoding.UTF8.GetBytes("拉拉").Length - 1); - action.ShouldThrow(); + action.Should().Throw(); } else if (i == 3) { @@ -485,7 +485,7 @@ public void TestWriteVarInt() MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); Action action = () => Neo.IO.Helper.WriteVarInt(writer, -1); - action.ShouldThrow(); + action.Should().Throw(); } else if (i == 1) { diff --git a/neo.UnitTests/Ledger/UT_MemoryPool.cs b/neo.UnitTests/Ledger/UT_MemoryPool.cs index e9a451952c..27c4d37715 100644 --- a/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -45,11 +45,11 @@ public void TestSetup() _unit.LoadPolicy(TestBlockchain.GetStore().GetSnapshot()); // Verify capacity equals the amount specified - _unit.Capacity.ShouldBeEquivalentTo(100); + _unit.Capacity.Should().Be(100); - _unit.VerifiedCount.ShouldBeEquivalentTo(0); - _unit.UnVerifiedCount.ShouldBeEquivalentTo(0); - _unit.Count.ShouldBeEquivalentTo(0); + _unit.VerifiedCount.Should().Be(0); + _unit.UnVerifiedCount.Should().Be(0); + _unit.Count.Should().Be(0); _unit2 = new MemoryPool(TheNeoSystem, 0); plugin = new TestIMemoryPoolTxObserverPlugin(); } @@ -122,10 +122,10 @@ public void CapacityTest() Console.WriteLine($"VerifiedCount: {_unit.VerifiedCount} Count {_unit.SortedTxCount}"); - _unit.SortedTxCount.ShouldBeEquivalentTo(100); - _unit.VerifiedCount.ShouldBeEquivalentTo(100); - _unit.UnVerifiedCount.ShouldBeEquivalentTo(0); - _unit.Count.ShouldBeEquivalentTo(100); + _unit.SortedTxCount.Should().Be(100); + _unit.VerifiedCount.Should().Be(100); + _unit.UnVerifiedCount.Should().Be(0); + _unit.Count.Should().Be(100); } [TestMethod] @@ -133,7 +133,7 @@ public void BlockPersistMovesTxToUnverifiedAndReverification() { AddTransactions(70); - _unit.SortedTxCount.ShouldBeEquivalentTo(70); + _unit.SortedTxCount.Should().Be(70); var block = new Block { @@ -142,32 +142,32 @@ public void BlockPersistMovesTxToUnverifiedAndReverification() }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); - _unit.SortedTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(60); + _unit.SortedTxCount.Should().Be(0); + _unit.UnverifiedSortedTxCount.Should().Be(60); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(10); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(50); + _unit.SortedTxCount.Should().Be(10); + _unit.UnverifiedSortedTxCount.Should().Be(50); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(20); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(40); + _unit.SortedTxCount.Should().Be(20); + _unit.UnverifiedSortedTxCount.Should().Be(40); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(30); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(30); + _unit.SortedTxCount.Should().Be(30); + _unit.UnverifiedSortedTxCount.Should().Be(30); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(40); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(20); + _unit.SortedTxCount.Should().Be(40); + _unit.UnverifiedSortedTxCount.Should().Be(20); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(50); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(10); + _unit.SortedTxCount.Should().Be(50); + _unit.UnverifiedSortedTxCount.Should().Be(10); _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); - _unit.SortedTxCount.ShouldBeEquivalentTo(60); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); + _unit.SortedTxCount.Should().Be(60); + _unit.UnverifiedSortedTxCount.Should().Be(0); } private void VerifyTransactionsSortedDescending(IEnumerable transactions) @@ -200,21 +200,21 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() var sortedVerifiedTxs = _unit.GetSortedVerifiedTransactions().ToList(); // verify all 100 transactions are returned in sorted order - sortedVerifiedTxs.Count.ShouldBeEquivalentTo(100); + sortedVerifiedTxs.Count.Should().Be(100); VerifyTransactionsSortedDescending(sortedVerifiedTxs); // move all to unverified var block = new Block { Transactions = new Transaction[0] }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); - _unit.SortedTxCount.ShouldBeEquivalentTo(0); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(100); + _unit.SortedTxCount.Should().Be(0); + _unit.UnverifiedSortedTxCount.Should().Be(100); // We can verify the order they are re-verified by reverifying 2 at a time while (_unit.UnVerifiedCount > 0) { _unit.GetVerifiedAndUnverifiedTransactions(out var sortedVerifiedTransactions, out var sortedUnverifiedTransactions); - sortedVerifiedTransactions.Count().ShouldBeEquivalentTo(0); + sortedVerifiedTransactions.Count().Should().Be(0); var sortedUnverifiedArray = sortedUnverifiedTransactions.ToArray(); VerifyTransactionsSortedDescending(sortedUnverifiedArray); var maxTransaction = sortedUnverifiedArray.First(); @@ -223,15 +223,15 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() // reverify 1 high priority and 1 low priority transaction _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(1, Blockchain.Singleton.GetSnapshot()); var verifiedTxs = _unit.GetSortedVerifiedTransactions().ToArray(); - verifiedTxs.Length.ShouldBeEquivalentTo(1); - verifiedTxs[0].ShouldBeEquivalentTo(maxTransaction); + verifiedTxs.Length.Should().Be(1); + verifiedTxs[0].Should().BeEquivalentTo(maxTransaction); var blockWith2Tx = new Block { Transactions = new[] { maxTransaction, minTransaction } }; // verify and remove the 2 transactions from the verified pool _unit.UpdatePoolForBlockPersisted(blockWith2Tx, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); - _unit.SortedTxCount.ShouldBeEquivalentTo(0); + _unit.SortedTxCount.Should().Be(0); } - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); + _unit.UnverifiedSortedTxCount.Should().Be(0); } void VerifyCapacityThresholdForAttemptingToAddATransaction() @@ -239,9 +239,9 @@ void VerifyCapacityThresholdForAttemptingToAddATransaction() var sortedVerified = _unit.GetSortedVerifiedTransactions().ToArray(); var txBarelyWontFit = CreateTransactionWithFee(sortedVerified.Last().NetworkFee - 1); - _unit.CanTransactionFitInPool(txBarelyWontFit).ShouldBeEquivalentTo(false); + _unit.CanTransactionFitInPool(txBarelyWontFit).Should().Be(false); var txBarelyFits = CreateTransactionWithFee(sortedVerified.Last().NetworkFee + 1); - _unit.CanTransactionFitInPool(txBarelyFits).ShouldBeEquivalentTo(true); + _unit.CanTransactionFitInPool(txBarelyFits).Should().Be(true); } [TestMethod] @@ -268,9 +268,9 @@ public void CapacityTestWithUnverifiedHighProirtyTransactions() var block = new Block { Transactions = new Transaction[0] }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); - _unit.CanTransactionFitInPool(CreateTransaction()).ShouldBeEquivalentTo(true); + _unit.CanTransactionFitInPool(CreateTransaction()).Should().Be(true); AddTransactions(1); - _unit.CanTransactionFitInPool(CreateTransactionWithFee(0)).ShouldBeEquivalentTo(false); + _unit.CanTransactionFitInPool(CreateTransactionWithFee(0)).Should().Be(false); } [TestMethod] @@ -278,11 +278,11 @@ public void TestInvalidateAll() { AddTransactions(30); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(0); - _unit.SortedTxCount.ShouldBeEquivalentTo(30); + _unit.UnverifiedSortedTxCount.Should().Be(0); + _unit.SortedTxCount.Should().Be(30); _unit.InvalidateAllTransactions(); - _unit.UnverifiedSortedTxCount.ShouldBeEquivalentTo(30); - _unit.SortedTxCount.ShouldBeEquivalentTo(0); + _unit.UnverifiedSortedTxCount.Should().Be(30); + _unit.SortedTxCount.Should().Be(0); } [TestMethod] @@ -392,11 +392,11 @@ public void TestTryGetValue() var tx1 = CreateTransaction(); _unit.TryAdd(tx1.Hash, tx1); _unit.TryGetValue(tx1.Hash, out Transaction tx).Should().BeTrue(); - tx.ShouldBeEquivalentTo(tx1); + tx.Should().BeEquivalentTo(tx1); _unit.InvalidateVerifiedTransactions(); _unit.TryGetValue(tx1.Hash, out tx).Should().BeTrue(); - tx.ShouldBeEquivalentTo(tx1); + tx.Should().BeEquivalentTo(tx1); var tx2 = CreateTransaction(); _unit.TryGetValue(tx2.Hash, out tx).Should().BeFalse(); diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 74efe64dd2..d52867e99e 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -11,7 +11,6 @@ using Neo.SmartContract.Native.Tokens; using Neo.VM; using Neo.Wallets; -using Neo.Wallets.NEP6; using System; using System.Numerics; @@ -225,7 +224,7 @@ public void FeeIsSignatureContractDetailed() // 'from' is always required as witness // if not included on cosigner with a scope, its scope should be considered 'CalledByEntry' data.ScriptHashes.Count.Should().Be(1); - data.ScriptHashes[0].ShouldBeEquivalentTo(acc.ScriptHash); + data.ScriptHashes[0].Should().BeEquivalentTo(acc.ScriptHash); // will sign tx bool signed = wallet.Sign(data); Assert.IsTrue(signed); diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index 5114d524b2..1ef1e2d120 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -3,12 +3,10 @@ using Neo.IO; using Neo.IO.Json; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract; using Neo.Wallets; using Neo.Wallets.NEP6; using System; -using System.IO; using System.Linq; namespace Neo.UnitTests.Network.P2P.Payloads @@ -16,47 +14,6 @@ namespace Neo.UnitTests.Network.P2P.Payloads [TestClass] public class UT_Witness { - class DummyVerificable : IVerifiable - { - private UInt160 _hash; - - public Witness[] Witnesses { get; set; } - - public int Size => 1; - - public DummyVerificable(UInt160 hash) - { - _hash = hash; - } - - public void Deserialize(BinaryReader reader) - { - DeserializeUnsigned(reader); - Witnesses = reader.ReadSerializableArray(16); - } - - public void DeserializeUnsigned(BinaryReader reader) - { - reader.ReadByte(); - } - - public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) - { - return new UInt160[] { _hash }; - } - - public void Serialize(BinaryWriter writer) - { - SerializeUnsigned(writer); - writer.Write(Witnesses); - } - - public void SerializeUnsigned(BinaryWriter writer) - { - writer.Write((byte)1); - } - } - Witness uut; [TestInitialize] @@ -73,12 +30,6 @@ public void InvocationScript_Get() private Witness PrepareDummyWitness(int maxAccounts) { - var store = TestBlockchain.GetStore(); - var wallet = TestUtils.GenerateTestWallet(); - var snapshot = store.GetSnapshot(); - - // Prepare - var address = new WalletAccount[maxAccounts]; var wallets = new NEP6Wallet[maxAccounts]; var walletsUnlocks = new IDisposable[maxAccounts]; @@ -101,7 +52,19 @@ private Witness PrepareDummyWitness(int maxAccounts) // Sign - var data = new ContractParametersContext(new DummyVerificable(multiSignContract.ScriptHash)); + var data = new ContractParametersContext(new Transaction() + { + Cosigners = new Cosigner[0], + Sender = multiSignContract.ScriptHash, + Attributes = new TransactionAttribute[0], + NetworkFee = 0, + Nonce = 0, + Script = new byte[0], + SystemFee = 0, + ValidUntilBlock = 0, + Version = 0, + Witnesses = new Witness[0] + }); for (int x = 0; x < maxAccounts; x++) { @@ -151,7 +114,7 @@ public void InvocationScript_Set() Assert.AreEqual(uut.InvocationScript.ToHexString(), "002020142020"); } - private void setupWitnessWithValues(Witness uut, int lenghtInvocation, int lengthVerification, out byte[] invocationScript, out byte[] verificationScript) + private void SetupWitnessWithValues(Witness uut, int lenghtInvocation, int lengthVerification, out byte[] invocationScript, out byte[] verificationScript) { invocationScript = TestUtils.GetByteArray(lenghtInvocation, 0x20); verificationScript = TestUtils.GetByteArray(lengthVerification, 0x20); @@ -162,9 +125,7 @@ private void setupWitnessWithValues(Witness uut, int lenghtInvocation, int lengt [TestMethod] public void SizeWitness_Small_Arrary() { - byte[] invocationScript; - byte[] verificationScript; - setupWitnessWithValues(uut, 252, 253, out invocationScript, out verificationScript); + SetupWitnessWithValues(uut, 252, 253, out _, out _); uut.Size.Should().Be(509); // (1 + 252*1) + (1 + 2 + 253*1) } @@ -172,9 +133,7 @@ public void SizeWitness_Small_Arrary() [TestMethod] public void SizeWitness_Large_Arrary() { - byte[] invocationScript; - byte[] verificationScript; - setupWitnessWithValues(uut, 65535, 65536, out invocationScript, out verificationScript); + SetupWitnessWithValues(uut, 65535, 65536, out _, out _); uut.Size.Should().Be(131079); // (1 + 2 + 65535*1) + (1 + 4 + 65536*1) } @@ -182,9 +141,7 @@ public void SizeWitness_Large_Arrary() [TestMethod] public void ToJson() { - byte[] invocationScript; - byte[] verificationScript; - setupWitnessWithValues(uut, 2, 3, out invocationScript, out verificationScript); + SetupWitnessWithValues(uut, 2, 3, out _, out _); JObject json = uut.ToJson(); Assert.IsTrue(json.ContainsProperty("invocation")); diff --git a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 8442904f5b..940230660a 100644 --- a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -200,7 +200,7 @@ public void Check_Block_UnblockAccount() ret = NativeContract.Policy.Call(snapshot, "getBlockedAccounts"); ret.Should().BeOfType(); ((VM.Types.Array)ret).Count.Should().Be(1); - ((VM.Types.Array)ret)[0].GetByteArray().ShouldBeEquivalentTo(UInt160.Zero.ToArray()); + ((VM.Types.Array)ret)[0].GetByteArray().Should().BeEquivalentTo(UInt160.Zero.ToArray()); // Unblock without signature @@ -212,7 +212,7 @@ public void Check_Block_UnblockAccount() ret = NativeContract.Policy.Call(snapshot, "getBlockedAccounts"); ret.Should().BeOfType(); ((VM.Types.Array)ret).Count.Should().Be(1); - ((VM.Types.Array)ret)[0].GetByteArray().ShouldBeEquivalentTo(UInt160.Zero.ToArray()); + ((VM.Types.Array)ret)[0].GetByteArray().Should().BeEquivalentTo(UInt160.Zero.ToArray()); // Unblock with signature diff --git a/neo.UnitTests/TestBlockchain.cs b/neo.UnitTests/TestBlockchain.cs index 290e054d10..5740c13b08 100644 --- a/neo.UnitTests/TestBlockchain.cs +++ b/neo.UnitTests/TestBlockchain.cs @@ -17,6 +17,12 @@ public static Store GetStore() return _Store.Object; } + static TestBlockchain() + { + InitializeMockNeoSystem(); + GetStore(); + } + public static NeoSystem InitializeMockNeoSystem() { if (TheNeoSystem == null) diff --git a/neo.UnitTests/UT_BigDecimal.cs b/neo.UnitTests/UT_BigDecimal.cs index 2d359da891..cdde9f9e89 100644 --- a/neo.UnitTests/UT_BigDecimal.cs +++ b/neo.UnitTests/UT_BigDecimal.cs @@ -21,7 +21,7 @@ public void TestChangeDecimals() BigDecimal result3 = originalValue.ChangeDecimals(5); result3.Value.Should().Be(originalValue.Value); Action action = () => originalValue.ChangeDecimals(2); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] @@ -69,7 +69,7 @@ public void TestParse() s = "abcdEfg"; Action action = () => BigDecimal.Parse(s, decimals); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/UT_Culture.cs b/neo.UnitTests/UT_Culture.cs index 37a44b24ef..a16de9717a 100644 --- a/neo.UnitTests/UT_Culture.cs +++ b/neo.UnitTests/UT_Culture.cs @@ -1,9 +1,9 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -using System.Collections.Generic; +using System.Collections; using System.Globalization; using System.Linq; using System.Reflection; -using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Neo.UnitTests { @@ -83,7 +83,12 @@ select new public class UnitTestContext : TestContext { - public override IDictionary Properties => throw new NotImplementedException(); + public override IDictionary Properties => throw new NotImplementedException(); + + public override void AddResultFile(string fileName) + { + Console.WriteLine(fileName); + } public override void WriteLine(string message) { diff --git a/neo.UnitTests/UT_ProtocolSettings.cs b/neo.UnitTests/UT_ProtocolSettings.cs index a5fa189231..77c56df424 100644 --- a/neo.UnitTests/UT_ProtocolSettings.cs +++ b/neo.UnitTests/UT_ProtocolSettings.cs @@ -1,10 +1,8 @@ using FluentAssertions; using Microsoft.Extensions.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; using System.Collections.Generic; using System.Reflection; -using System.Text; namespace Neo.UnitTests { diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index e2f4ca13f4..ed84b1bad3 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -11,46 +11,46 @@ namespace Neo.UnitTests.Wallets.NEP6 [TestClass] public class UT_NEP6Account { - NEP6Account account; - UInt160 hash; - NEP6Wallet wallet; - private static string nep2; - private static KeyPair keyPair; + NEP6Account _account; + UInt160 _hash; + NEP6Wallet _wallet; + private static string _nep2; + private static KeyPair _keyPair; [ClassInitialize] public static void ClassSetup(TestContext context) { byte[] privateKey = { 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}; - keyPair = new KeyPair(privateKey); - nep2 = keyPair.Export("Satoshi", 0, 0, 0); + _keyPair = new KeyPair(privateKey); + _nep2 = _keyPair.Export("Satoshi", 0, 0, 0); } [TestInitialize] public void TestSetup() { - wallet = TestUtils.GenerateTestWallet(); + _wallet = TestUtils.GenerateTestWallet(); byte[] array1 = { 0x01 }; - hash = new UInt160(Crypto.Default.Hash160(array1)); - account = new NEP6Account(wallet, hash); + _hash = new UInt160(Crypto.Default.Hash160(array1)); + _account = new NEP6Account(_wallet, _hash); } [TestMethod] public void TestConstructorWithNep2Key() { - account.ScriptHash.Should().Be(hash); - account.Decrypted.Should().BeTrue(); - account.HasKey.Should().BeFalse(); + _account.ScriptHash.Should().Be(_hash); + _account.Decrypted.Should().BeTrue(); + _account.HasKey.Should().BeFalse(); } [TestMethod] public void TestConstructorWithKeyPair() { - NEP6Wallet wallet = new NEP6Wallet("a"); + var wallet = TestUtils.GenerateTestWallet(); byte[] array1 = { 0x01 }; var hash = new UInt160(Crypto.Default.Hash160(array1)); string password = "hello world"; - NEP6Account account = new NEP6Account(wallet, hash, keyPair, password); + NEP6Account account = new NEP6Account(wallet, hash, _keyPair, password); account.ScriptHash.Should().Be(hash); account.Decrypted.Should().BeTrue(); account.HasKey.Should().BeTrue(); @@ -67,7 +67,7 @@ public void TestFromJson() json["lock"] = false; json["contract"] = null; json["extra"] = null; - NEP6Account account = NEP6Account.FromJson(json, wallet); + NEP6Account account = NEP6Account.FromJson(json, _wallet); account.ScriptHash.Should().Be("ARxgjcH2K1yeW5f5ryuRQNaBzSa9TZzmVS".ToScriptHash()); account.Label.Should().BeNull(); account.IsDefault.Should().BeTrue(); @@ -78,7 +78,7 @@ public void TestFromJson() json["key"] = "6PYRjVE1gAbCRyv81FTiFz62cxuPGw91vMjN4yPa68bnoqJtioreTznezn"; json["label"] = "label"; - account = NEP6Account.FromJson(json, wallet); + account = NEP6Account.FromJson(json, _wallet); account.Label.Should().Be("label"); account.HasKey.Should().BeTrue(); } @@ -86,18 +86,18 @@ public void TestFromJson() [TestMethod] public void TestGetKey() { - account.GetKey().Should().BeNull(); - wallet.Unlock("Satoshi"); - account = new NEP6Account(wallet, hash, nep2); - account.GetKey().Should().Be(keyPair); + _account.GetKey().Should().BeNull(); + _wallet.Unlock("Satoshi"); + _account = new NEP6Account(_wallet, _hash, _nep2); + _account.GetKey().Should().Be(_keyPair); } [TestMethod] public void TestGetKeyWithString() { - account.GetKey("Satoshi").Should().BeNull(); - account = new NEP6Account(wallet, hash, nep2); - account.GetKey("Satoshi").Should().Be(keyPair); + _account.GetKey("Satoshi").Should().BeNull(); + _account = new NEP6Account(_wallet, _hash, _nep2); + _account.GetKey("Satoshi").Should().Be(_keyPair); } [TestMethod] @@ -114,8 +114,8 @@ public void TestToJson() }; nep6contract["parameters"] = array; nep6contract["deployed"] = false; - account.Contract = NEP6Contract.FromJson(nep6contract); - JObject json = account.ToJson(); + _account.Contract = NEP6Contract.FromJson(nep6contract); + JObject json = _account.ToJson(); json["address"].Should().Equals("AZk5bAanTtD6AvpeesmYgL8CLRYUt5JQsX"); json["label"].Should().BeNull(); json["isDefault"].ToString().Should().Be("false"); @@ -124,17 +124,17 @@ public void TestToJson() json["contract"]["script"].ToString().Should().Be("\"2103603f3880eb7aea0ad4500893925e4a42fea48a44ee6f898a10b3c7ce05d2a267ac\""); json["extra"].Should().BeNull(); - account.Contract = null; - json = account.ToJson(); + _account.Contract = null; + json = _account.ToJson(); json["contract"].Should().BeNull(); } [TestMethod] public void TestVerifyPassword() { - account = new NEP6Account(wallet, hash, nep2); - account.VerifyPassword("Satoshi").Should().BeTrue(); - account.VerifyPassword("b").Should().BeFalse(); + _account = new NEP6Account(_wallet, _hash, _nep2); + _account.VerifyPassword("Satoshi").Should().BeTrue(); + _account.VerifyPassword("b").Should().BeFalse(); } } } diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index 6a8c16dbb6..de902a115e 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -326,11 +326,12 @@ public void TestMigrate() string path = GetRandomPath(); UserWallet uw = UserWallet.Create(path, "123"); uw.CreateAccount(keyPair.PrivateKey); - string npath = Path.Combine(path, "w.json"); + string npath = CreateWalletFile(); // Scrypt test values NEP6Wallet nw = NEP6Wallet.Migrate(npath, path, "123"); bool result = nw.Contains(testScriptHash); Assert.AreEqual(true, result); if (File.Exists(path)) File.Delete(path); + if (File.Exists(npath)) File.Delete(npath); } [TestMethod] diff --git a/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs b/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs index dcba52bc11..87993f12cd 100644 --- a/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs +++ b/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs @@ -46,7 +46,7 @@ public void TestGetName() public void TestGetVersion() { Action action = () => wallet.Version.ToString(); - action.ShouldNotThrow(); + action.Should().NotThrow(); } [TestMethod] @@ -66,7 +66,7 @@ public void TestCreateAndOpenSecureString() ss.AppendChar('d'); Action action = () => UserWallet.Open(myPath, ss); - action.ShouldThrow(); + action.Should().Throw(); TestUtils.DeleteFile(myPath); } @@ -84,7 +84,7 @@ public void TestOpen() w1.Should().NotBeNull(); Action action = () => UserWallet.Open(path, "123"); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/Wallets/UT_AssetDescriptor.cs b/neo.UnitTests/Wallets/UT_AssetDescriptor.cs index 14eb6766b1..937a0d89f7 100644 --- a/neo.UnitTests/Wallets/UT_AssetDescriptor.cs +++ b/neo.UnitTests/Wallets/UT_AssetDescriptor.cs @@ -9,13 +9,10 @@ namespace Neo.UnitTests.Wallets [TestClass] public class UT_AssetDescriptor { - private Store Store; - [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - Store = TestBlockchain.GetStore(); } [TestMethod] @@ -25,7 +22,7 @@ public void TestConstructorWithNonexistAssetId() { var descriptor = new Neo.Wallets.AssetDescriptor(UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4")); }; - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/Wallets/UT_KeyPair.cs b/neo.UnitTests/Wallets/UT_KeyPair.cs index 57f06bb5a9..cb2bb20a6e 100644 --- a/neo.UnitTests/Wallets/UT_KeyPair.cs +++ b/neo.UnitTests/Wallets/UT_KeyPair.cs @@ -35,7 +35,7 @@ public void TestConstructor() for (int i = 0; i < privateKey31.Length; i++) privateKey31[i] = (byte)random.Next(256); Action action = () => new KeyPair(privateKey31); - action.ShouldThrow(); + action.Should().Throw(); } [TestMethod] diff --git a/neo.UnitTests/Wallets/UT_Wallet.cs b/neo.UnitTests/Wallets/UT_Wallet.cs index c09f8e26c2..ed48a047e5 100644 --- a/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/neo.UnitTests/Wallets/UT_Wallet.cs @@ -112,7 +112,7 @@ public void TestContains() { MyWallet wallet = new MyWallet(); Action action = () => wallet.Contains(UInt160.Zero); - action.ShouldNotThrow(); + action.Should().NotThrow(); } [TestMethod] @@ -178,7 +178,7 @@ public void TestGetAccount2() { MyWallet wallet = new MyWallet(); Action action = () => wallet.GetAccount(UInt160.Zero); - action.ShouldNotThrow(); + action.Should().NotThrow(); } [TestMethod] @@ -186,7 +186,7 @@ public void TestGetAccounts() { MyWallet wallet = new MyWallet(); Action action = () => wallet.GetAccounts(); - action.ShouldNotThrow(); + action.Should().NotThrow(); } [TestMethod] @@ -254,16 +254,16 @@ public void TestGetBalance() public void TestGetPrivateKeyFromNEP2() { Action action = () => Wallet.GetPrivateKeyFromNEP2(null, null, 0, 0, 0); - action.ShouldThrow(); + action.Should().Throw(); action = () => Wallet.GetPrivateKeyFromNEP2("TestGetPrivateKeyFromNEP2", null, 0, 0, 0); - action.ShouldThrow(); + action.Should().Throw(); action = () => Wallet.GetPrivateKeyFromNEP2("3vQB7B6MrGQZaxCuFg4oh", "TestGetPrivateKeyFromNEP2", 0, 0, 0); - action.ShouldThrow(); + action.Should().Throw(); action = () => Wallet.GetPrivateKeyFromNEP2(nep2Key, "Test", 0, 0, 0); - action.ShouldThrow(); + action.Should().Throw(); Wallet.GetPrivateKeyFromNEP2(nep2Key, "pwd", 0, 0, 0).Should().BeEquivalentTo(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }); } @@ -272,10 +272,10 @@ public void TestGetPrivateKeyFromNEP2() public void TestGetPrivateKeyFromWIF() { Action action = () => Wallet.GetPrivateKeyFromWIF(null); - action.ShouldThrow(); + action.Should().Throw(); action = () => Wallet.GetPrivateKeyFromWIF("3vQB7B6MrGQZaxCuFg4oh"); - action.ShouldThrow(); + action.Should().Throw(); Wallet.GetPrivateKeyFromWIF("L3tgppXLgdaeqSGSFw1Go3skBiy8vQAM7YMXvTHsKQtE16PBncSU").Should().BeEquivalentTo(new byte[] { 199, 19, 77, 111, 216, 231, 61, 129, 158, 130, 117, 92, 100, 201, 55, 136, 216, 219, 9, 97, 146, 158, 2, 90, 83, 54, 60, 76, 192, 42, 105, 98 }); } @@ -311,7 +311,7 @@ public void TestMakeTransaction1() Value = new BigDecimal(1,8) } }, UInt160.Zero); - action.ShouldThrow(); + action.Should().Throw(); action = () => wallet.MakeTransaction(new TransferOutput[] { @@ -322,7 +322,7 @@ public void TestMakeTransaction1() Value = new BigDecimal(1,8) } }, account.ScriptHash); - action.ShouldThrow(); + action.Should().Throw(); action = () => wallet.MakeTransaction(new TransferOutput[] { @@ -333,7 +333,7 @@ public void TestMakeTransaction1() Value = new BigDecimal(1,8) } }, account.ScriptHash); - action.ShouldThrow(); + action.Should().Throw(); // Fake balance var snapshot = store.GetSnapshot(); @@ -393,7 +393,7 @@ public void TestMakeTransaction2() { MyWallet wallet = new MyWallet(); Action action = () => wallet.MakeTransaction(new byte[] { }, UInt160.Zero, new TransactionAttribute[] { }); - action.ShouldThrow(); + action.Should().Throw(); Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 }); WalletAccount account = wallet.CreateAccount(contract, glkey.PrivateKey); @@ -430,7 +430,7 @@ public void TestVerifyPassword() { MyWallet wallet = new MyWallet(); Action action = () => wallet.VerifyPassword("Test"); - action.ShouldNotThrow(); + action.Should().NotThrow(); } } } diff --git a/neo.UnitTests/Wallets/UT_Wallets_Helper.cs b/neo.UnitTests/Wallets/UT_Wallets_Helper.cs index 4abf10d9ed..c14b4566b3 100644 --- a/neo.UnitTests/Wallets/UT_Wallets_Helper.cs +++ b/neo.UnitTests/Wallets/UT_Wallets_Helper.cs @@ -17,7 +17,7 @@ public void TestToScriptHash() "AZk5bAanTtD6AvpeesmYgL8CLRYUt5JQsX".ToScriptHash().Should().Be(scriptHash); Action action = () => "3vQB7B6MrGQZaxCuFg4oh".ToScriptHash(); - action.ShouldThrow(); + action.Should().Throw(); var address = scriptHash.ToAddress(); byte[] data = new byte[21]; @@ -26,7 +26,7 @@ public void TestToScriptHash() Buffer.BlockCopy(scriptHash.ToArray(), 0, data, 1, 20); address = data.Base58CheckEncode(); action = () => address.ToScriptHash(); - action.ShouldThrow(); + action.Should().Throw(); } } } diff --git a/neo.UnitTests/neo.UnitTests.csproj b/neo.UnitTests/neo.UnitTests.csproj index 4cbc7f4164..2da1ba4968 100644 --- a/neo.UnitTests/neo.UnitTests.csproj +++ b/neo.UnitTests/neo.UnitTests.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.0 + netcoreapp2.2 Neo.UnitTests Neo.UnitTests true @@ -15,13 +15,13 @@ - - - - - - - + + + + + + + From 480d55e3435820bdfb82d4abc73ab98aa5726773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Tue, 15 Oct 2019 17:10:55 +0900 Subject: [PATCH 106/305] Simplify and improve consensus payload serialization and deserialization (#1149) * Simplify and improve consensus payload serialiation * Simplyfing read * Function return instead of out parameter * Reorganize * Update UT_IOHelper.cs --- neo.UnitTests/IO/UT_IOHelper.cs | 42 ++++++++++++++++++++++++- neo/Consensus/ConsensusContext.cs | 52 +++++-------------------------- neo/IO/Helper.cs | 20 ++++++++++++ 3 files changed, 69 insertions(+), 45 deletions(-) diff --git a/neo.UnitTests/IO/UT_IOHelper.cs b/neo.UnitTests/IO/UT_IOHelper.cs index 114ceffc8d..d0f63e5c1c 100644 --- a/neo.UnitTests/IO/UT_IOHelper.cs +++ b/neo.UnitTests/IO/UT_IOHelper.cs @@ -23,6 +23,46 @@ public void TestAsSerializableGeneric() Assert.AreEqual(UInt160.Zero, result); } + [TestMethod] + public void TestNullableArray() + { + var caseArray = new UInt160[] + { + null, UInt160.Zero, new UInt160( + new byte[] { + 0xAA,0x00,0x00,0x00,0x00, + 0xBB,0x00,0x00,0x00,0x00, + 0xCC,0x00,0x00,0x00,0x00, + 0xDD,0x00,0x00,0x00,0x00 + }) + }; + + byte[] data; + using (var stream = new MemoryStream()) + using (var writter = new BinaryWriter(stream)) + { + Neo.IO.Helper.WriteNullableArray(writter, caseArray); + data = stream.ToArray(); + } + + // Read Error + + using (var stream = new MemoryStream(data)) + using (var reader = new BinaryReader(stream)) + { + Assert.ThrowsException(() => Neo.IO.Helper.ReadNullableArray(reader, 2)); + } + + // Read 100% + + using (var stream = new MemoryStream(data)) + using (var reader = new BinaryReader(stream)) + { + var read = Neo.IO.Helper.ReadNullableArray(reader); + CollectionAssert.AreEqual(caseArray, read); + } + } + [TestMethod] public void TestAsSerializable() { @@ -48,7 +88,7 @@ public void TestAsSerializable() [TestMethod] public void TestAsSerializableArray() { - byte[] byteArray = Neo.IO.Helper.ToByteArray(new UInt160[] { UInt160.Zero }); + byte[] byteArray = Neo.IO.Helper.ToByteArray(new UInt160[] { UInt160.Zero }); UInt160[] result = Neo.IO.Helper.AsSerializableArray(byteArray); Assert.AreEqual(1, result.Length); Assert.AreEqual(UInt160.Zero, result[0]); diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 0d9edbb3dc..ddbf6caeea 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -104,18 +104,10 @@ public void Deserialize(BinaryReader reader) ViewNumber = reader.ReadByte(); TransactionHashes = reader.ReadSerializableArray(); Transaction[] transactions = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); - PreparationPayloads = new ConsensusPayload[reader.ReadVarInt(Blockchain.MaxValidators)]; - for (int i = 0; i < PreparationPayloads.Length; i++) - PreparationPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; - CommitPayloads = new ConsensusPayload[reader.ReadVarInt(Blockchain.MaxValidators)]; - for (int i = 0; i < CommitPayloads.Length; i++) - CommitPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; - ChangeViewPayloads = new ConsensusPayload[reader.ReadVarInt(Blockchain.MaxValidators)]; - for (int i = 0; i < ChangeViewPayloads.Length; i++) - ChangeViewPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; - LastChangeViewPayloads = new ConsensusPayload[reader.ReadVarInt(Blockchain.MaxValidators)]; - for (int i = 0; i < LastChangeViewPayloads.Length; i++) - LastChangeViewPayloads[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; + PreparationPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); + CommitPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); + ChangeViewPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); + LastChangeViewPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); if (TransactionHashes.Length == 0 && !RequestSentOrReceived) TransactionHashes = null; Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash); @@ -416,38 +408,10 @@ public void Serialize(BinaryWriter writer) writer.Write(ViewNumber); writer.Write(TransactionHashes ?? new UInt256[0]); writer.Write(Transactions?.Values.ToArray() ?? new Transaction[0]); - writer.WriteVarInt(PreparationPayloads.Length); - foreach (var payload in PreparationPayloads) - { - bool hasPayload = !(payload is null); - writer.Write(hasPayload); - if (!hasPayload) continue; - writer.Write(payload); - } - writer.WriteVarInt(CommitPayloads.Length); - foreach (var payload in CommitPayloads) - { - bool hasPayload = !(payload is null); - writer.Write(hasPayload); - if (!hasPayload) continue; - writer.Write(payload); - } - writer.WriteVarInt(ChangeViewPayloads.Length); - foreach (var payload in ChangeViewPayloads) - { - bool hasPayload = !(payload is null); - writer.Write(hasPayload); - if (!hasPayload) continue; - writer.Write(payload); - } - writer.WriteVarInt(LastChangeViewPayloads.Length); - foreach (var payload in LastChangeViewPayloads) - { - bool hasPayload = !(payload is null); - writer.Write(hasPayload); - if (!hasPayload) continue; - writer.Write(payload); - } + writer.WriteNullableArray(PreparationPayloads); + writer.WriteNullableArray(CommitPayloads); + writer.WriteNullableArray(ChangeViewPayloads); + writer.WriteNullableArray(LastChangeViewPayloads); } } } diff --git a/neo/IO/Helper.cs b/neo/IO/Helper.cs index c985e676b2..c47878579d 100644 --- a/neo/IO/Helper.cs +++ b/neo/IO/Helper.cs @@ -112,6 +112,14 @@ public static string ReadFixedString(this BinaryReader reader, int length) return Encoding.UTF8.GetString(data.TakeWhile(p => p != 0).ToArray()); } + public static T[] ReadNullableArray(this BinaryReader reader, int max = 0x1000000) where T : class, ISerializable, new() + { + T[] array = new T[reader.ReadVarInt((ulong)max)]; + for (int i = 0; i < array.Length; i++) + array[i] = reader.ReadBoolean() ? reader.ReadSerializable() : null; + return array; + } + public static T ReadSerializable(this BinaryReader reader) where T : ISerializable, new() { T obj = new T(); @@ -225,6 +233,18 @@ public static void WriteFixedString(this BinaryWriter writer, string value, int writer.Write(new byte[length - bytes.Length]); } + public static void WriteNullableArray(this BinaryWriter writer, T[] value) where T : class, ISerializable + { + writer.WriteVarInt(value.Length); + foreach (var item in value) + { + bool isNull = item is null; + writer.Write(!isNull); + if (isNull) continue; + item.Serialize(writer); + } + } + public static void WriteVarBytes(this BinaryWriter writer, byte[] value) { writer.WriteVarInt(value.Length); From 1fb1d5bf47533f10bc5f3e466385aa3ea3a5719b Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 15 Oct 2019 10:59:46 +0200 Subject: [PATCH 107/305] Improve the random security (#1145) * Remove global randoms * Wallet * Optimize * Use random class * Revert wallet --- neo/Consensus/ConsensusContext.cs | 2 +- neo/Network/RPC/TransactionManager.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index ddbf6caeea..463bdc0407 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -42,7 +42,6 @@ internal class ConsensusContext : IDisposable, ISerializable private int _witnessSize; private readonly Wallet wallet; private readonly Store store; - private readonly Random random = new Random(); public int F => (Validators.Length - 1) / 3; public int M => Validators.Length - F; @@ -266,6 +265,7 @@ internal void EnsureMaxBlockSize(IEnumerable txs) public ConsensusPayload MakePrepareRequest() { + var random = new Random(); byte[] buffer = new byte[sizeof(ulong)]; random.NextBytes(buffer); Block.ConsensusData.Nonce = BitConverter.ToUInt64(buffer, 0); diff --git a/neo/Network/RPC/TransactionManager.cs b/neo/Network/RPC/TransactionManager.cs index 7b4db697ab..d4a651fd78 100644 --- a/neo/Network/RPC/TransactionManager.cs +++ b/neo/Network/RPC/TransactionManager.cs @@ -1,4 +1,4 @@ -using Neo.Cryptography.ECC; +using Neo.Cryptography.ECC; using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Network.RPC.Models; @@ -15,7 +15,6 @@ namespace Neo.Network.RPC /// public class TransactionManager { - private static readonly Random rand = new Random(); private readonly RpcClient rpcClient; private readonly UInt160 sender; @@ -50,11 +49,12 @@ public TransactionManager(RpcClient rpc, UInt160 sender) /// public TransactionManager MakeTransaction(byte[] script, TransactionAttribute[] attributes = null, Cosigner[] cosigners = null, long networkFee = 0) { + var random = new Random(); uint height = rpcClient.GetBlockCount() - 1; Tx = new Transaction { Version = 0, - Nonce = (uint)rand.Next(), + Nonce = (uint)random.Next(), Script = script, Sender = sender, ValidUntilBlock = height + Transaction.MaxValidUntilBlockIncrement, From 2200df1017da687eb18daa35c4c7c40c11c92643 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 16 Oct 2019 09:50:36 +0200 Subject: [PATCH 108/305] Simplifying access to Transactions and Blocks in syscalls (#1081) * Add Transaction.Sender and Transaction.Script * Allow to get the current Transaction * Update unit test, refactor transactions methods * UT * Summary TX object inside VM * Revert some changes * Refactor to new model and UT * Fix * Reduce conditional * Fix ut * Fix ut * Change order * Block * Some fixes * Fix comment * Fix comments * Move hash to the top * Remove GetHeader * Remove GetHeader * Block with transactions count * Migrate ContractState * Format * Close https://github.com/neo-project/neo/issues/1096 Close https://github.com/neo-project/neo/issues/1096 * Update nulls * Remove Neo.Account.IsStandard * Revert last change * Fix Unit tests * Remove unused var * TrimmedBlock * Change fee * Neo.VM v3.0.0-CI00041 * Neo.VM v3.0.0-CI00042 * Rename * ContractNullParameter * Clean using * Revert Null in ContractParameterType --- .../SmartContract/UT_InteropService.cs | 2 +- neo.UnitTests/SmartContract/UT_Syscalls.cs | 198 ++++++++++++++--- neo/Ledger/ContractState.cs | 17 +- neo/Network/P2P/Payloads/Block.cs | 31 ++- neo/Network/P2P/Payloads/Transaction.cs | 30 ++- .../ApplicationEngine.OpCodePrices.cs | 2 + neo/SmartContract/Helper.cs | 6 + neo/SmartContract/IInteroperable.cs | 9 + neo/SmartContract/InteropService.NEO.cs | 107 --------- neo/SmartContract/InteropService.cs | 204 +++++------------- neo/SmartContract/JsonSerializer.cs | 8 + neo/SmartContract/StackItemType.cs | 1 + neo/SmartContract/WitnessWrapper.cs | 27 --- neo/VM/Helper.cs | 5 +- neo/neo.csproj | 2 +- 15 files changed, 319 insertions(+), 330 deletions(-) create mode 100644 neo/SmartContract/IInteroperable.cs delete mode 100644 neo/SmartContract/WitnessWrapper.cs diff --git a/neo.UnitTests/SmartContract/UT_InteropService.cs b/neo.UnitTests/SmartContract/UT_InteropService.cs index 6a33a32abc..dff17f2025 100644 --- a/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -85,7 +85,7 @@ public void Runtime_GetNotifications_Test() // Receive all notifications - script.EmitPush(new byte[0]); + script.Emit(OpCode.PUSHNULL); script.EmitSysCall(InteropService.System_Runtime_GetNotifications); // Execute diff --git a/neo.UnitTests/SmartContract/UT_Syscalls.cs b/neo.UnitTests/SmartContract/UT_Syscalls.cs index 65066b9079..658d125e7f 100644 --- a/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -1,7 +1,9 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; +using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.VM; +using Neo.VM.Types; using System.Linq; namespace Neo.UnitTests.SmartContract @@ -9,58 +11,184 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_Syscalls { + [TestMethod] + public void System_Blockchain_GetBlock() + { + var tx = new Transaction() + { + Script = new byte[] { 0x01 }, + Attributes = new TransactionAttribute[0], + Cosigners = new Cosigner[0], + NetworkFee = 0x02, + SystemFee = 0x03, + Nonce = 0x04, + ValidUntilBlock = 0x05, + Version = 0x06, + Witnesses = new Witness[] { new Witness() { VerificationScript = new byte[] { 0x07 } } }, + Sender = UInt160.Parse("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + }; + + var block = new Block() + { + Index = 1, + Timestamp = 2, + Version = 3, + Witness = new Witness() + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + }, + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + ConsensusData = new ConsensusData() { Nonce = 1, PrimaryIndex = 1 }, + Transactions = new Transaction[] { tx } + }; + + var snapshot = TestBlockchain.GetStore().GetSnapshot(); + + using (var script = new ScriptBuilder()) + { + script.EmitPush(block.Hash.ToArray()); + script.EmitSysCall(InteropService.System_Blockchain_GetBlock); + + // Without block + + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Peek().IsNull); + + // With block + + var blocks = (TestDataCache)snapshot.Blocks; + var txs = (TestDataCache)snapshot.Transactions; + blocks.Add(block.Hash, block.Trim()); + txs.Add(tx.Hash, new TransactionState() { Transaction = tx, BlockIndex = block.Index, VMState = VMState.HALT }); + + script.EmitSysCall(InteropService.Neo_Json_Serialize); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); + Assert.AreEqual(engine.ResultStack.Pop().GetByteArray().ToHexString(), + "5b22515c7546464644795c75464646445c75464646445c75303030335c75464646445c75464646445c754646464475465c7530303046715c75303132415c625b595c75303434335c75464646445c75464646447d5d767b385c7546464644785c75303032375c75464646445c7546464644222c332c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c322c312c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c315d"); + Assert.AreEqual(0, engine.ResultStack.Count); + + // Clean + blocks.Delete(block.Hash); + txs.Delete(tx.Hash); + } + } + + [TestMethod] + public void System_ExecutionEngine_GetScriptContainer() + { + var snapshot = TestBlockchain.GetStore().GetSnapshot(); + using (var script = new ScriptBuilder()) + { + script.EmitSysCall(InteropService.System_ExecutionEngine_GetScriptContainer); + + // Without tx + + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Peek().IsNull); + + // With tx + + script.EmitSysCall(InteropService.Neo_Json_Serialize); + + var tx = new Transaction() + { + Script = new byte[] { 0x01 }, + Attributes = new TransactionAttribute[0], + Cosigners = new Cosigner[0], + NetworkFee = 0x02, + SystemFee = 0x03, + Nonce = 0x04, + ValidUntilBlock = 0x05, + Version = 0x06, + Witnesses = new Witness[] { new Witness() { VerificationScript = new byte[] { 0x07 } } }, + Sender = UInt160.Parse("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + }; + + engine = new ApplicationEngine(TriggerType.Application, tx, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); + Assert.AreEqual(engine.ResultStack.Pop().GetByteArray().ToHexString(), + @"5b225c7546464644445c7546464644615c7546464644732c5c75464646445c7546464644665c75303030375d5c75303030305c75464646445c75303632325c7546464644545c7546464644375c7530303133335c75303031385c7530303033655c75464646445c75464646445c75303032375a5c75464646445c2f5c7546464644222c362c342c225c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c7546464644222c332c322c352c225c7530303031225d"); + Assert.AreEqual(0, engine.ResultStack.Count); + } + } + [TestMethod] public void System_Runtime_GetInvocationCounter() { + ContractState contractA, contractB, contractC; var snapshot = TestBlockchain.GetStore().GetSnapshot(); var contracts = (TestDataCache)snapshot.Contracts; - // Call System.Runtime.GetInvocationCounter syscall + // Create dummy contracts - var script = new ScriptBuilder(); - script.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); + using (var script = new ScriptBuilder()) + { + script.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); - // Init A,B,C contracts - // First two drops is for drop method and arguments + 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() }; - var contractA = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script.ToArray()).ToArray() }; - var contractB = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; - var contractC = new ContractState() { Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP, (byte)OpCode.NOP, (byte)OpCode.NOP }.Concat(script.ToArray()).ToArray() }; + // Init A,B,C contracts + // First two drops is for drop method and arguments - contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractA.ScriptHash.ToArray())); - contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractB.ScriptHash.ToArray())); - contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractC.ScriptHash.ToArray())); - contracts.Add(contractA.ScriptHash, contractA); - contracts.Add(contractB.ScriptHash, contractB); - contracts.Add(contractC.ScriptHash, contractC); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractA.ScriptHash.ToArray())); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractB.ScriptHash.ToArray())); + contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractC.ScriptHash.ToArray())); + contracts.Add(contractA.ScriptHash, contractA); + contracts.Add(contractB.ScriptHash, contractB); + contracts.Add(contractC.ScriptHash, contractC); + } // Call A,B,B,C - script = new ScriptBuilder(); - script.EmitSysCall(InteropService.System_Contract_Call, contractA.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.System_Contract_Call, contractC.ScriptHash.ToArray(), "dummyMain", 0); + using (var script = new ScriptBuilder()) + { + script.EmitSysCall(InteropService.System_Contract_Call, contractA.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.System_Contract_Call, contractC.ScriptHash.ToArray(), "dummyMain", 0); - // Execute + // Execute - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); - engine.LoadScript(script.ToArray()); - Assert.AreEqual(engine.Execute(), VMState.HALT); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(engine.Execute(), VMState.HALT); - // Check the results + // Check the results - CollectionAssert.AreEqual - ( - engine.ResultStack.Select(u => (int)((VM.Types.Integer)u).GetBigInteger()).ToArray(), - new int[] - { - 1, /* A */ - 1, /* B */ - 2, /* B */ - 1 /* C */ - } - ); + CollectionAssert.AreEqual + ( + engine.ResultStack.Select(u => (int)((VM.Types.Integer)u).GetBigInteger()).ToArray(), + new int[] + { + 1, /* A */ + 1, /* B */ + 2, /* B */ + 1 /* C */ + } + ); + } } } } diff --git a/neo/Ledger/ContractState.cs b/neo/Ledger/ContractState.cs index f6c641d7d5..c369e5d59f 100644 --- a/neo/Ledger/ContractState.cs +++ b/neo/Ledger/ContractState.cs @@ -2,11 +2,13 @@ using Neo.IO.Json; using Neo.SmartContract; using Neo.SmartContract.Manifest; +using Neo.VM; +using Neo.VM.Types; using System.IO; namespace Neo.Ledger { - public class ContractState : ICloneable, ISerializable + public class ContractState : ICloneable, ISerializable, IInteroperable { public byte[] Script; public ContractManifest Manifest; @@ -72,5 +74,18 @@ public static ContractState FromJson(JObject json) contractState.Manifest = ContractManifest.FromJson(json["manifest"]); return contractState; } + + public StackItem ToStackItem() + { + return new VM.Types.Array + ( + new StackItem[] + { + new ByteArray(Script), + new Boolean(HasStorage), + new Boolean(Payable), + } + ); + } } } diff --git a/neo/Network/P2P/Payloads/Block.cs b/neo/Network/P2P/Payloads/Block.cs index d339b536a0..305536c38d 100644 --- a/neo/Network/P2P/Payloads/Block.cs +++ b/neo/Network/P2P/Payloads/Block.cs @@ -2,6 +2,9 @@ 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; @@ -9,7 +12,7 @@ namespace Neo.Network.P2P.Payloads { - public class Block : BlockBase, IInventory, IEquatable + public class Block : BlockBase, IInventory, IEquatable, IInteroperable { public const int MaxContentsPerBlock = ushort.MaxValue; public const int MaxTransactionsPerBlock = MaxContentsPerBlock - 1; @@ -131,5 +134,31 @@ public TrimmedBlock Trim() ConsensusData = ConsensusData }; } + + public StackItem ToStackItem() + { + return new VM.Types.Array + ( + new StackItem[] + { + // Computed properties + new ByteArray(Hash.ToArray()), + + // BlockBase properties + new Integer(Version), + new ByteArray(PrevHash.ToArray()), + new ByteArray(MerkleRoot.ToArray()), + new Integer(Timestamp), + new Integer(Index), + new ByteArray(NextConsensus.ToArray()), + // Witness + + // Block properties + // Count + // ConsensusData + new Integer(Transactions.Length) + } + ); + } } } diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index 74fc1ef3c7..af38ef6dcc 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -4,6 +4,8 @@ using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; +using Neo.VM; +using Neo.VM.Types; using Neo.Wallets; using System; using System.Collections.Generic; @@ -13,7 +15,7 @@ namespace Neo.Network.P2P.Payloads { - public class Transaction : IEquatable, IInventory + public class Transaction : IEquatable, IInventory, IInteroperable { public const int MaxTransactionSize = 102400; public const uint MaxValidUntilBlockIncrement = 2102400; @@ -68,7 +70,7 @@ public UInt256 Hash sizeof(byte) + //Version sizeof(uint) + //Nonce 20 + //Sender - sizeof(long) + //Gas + sizeof(long) + //SystemFee sizeof(long) + //NetworkFee sizeof(uint); //ValidUntilBlock @@ -216,5 +218,29 @@ public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) if (net_fee < 0) return false; return this.VerifyWitnesses(snapshot, net_fee); } + + public StackItem ToStackItem() + { + return new VM.Types.Array + ( + new StackItem[] + { + // Computed properties + new ByteArray(Hash.ToArray()), + + // Transaction properties + new Integer(Version), + new Integer(Nonce), + new ByteArray(Sender.ToArray()), + new Integer(SystemFee), + new Integer(NetworkFee), + new Integer(ValidUntilBlock), + // Attributes + // Cosigners + new ByteArray(Script), + // Witnesses + } + ); + } } } diff --git a/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index 271a76f935..b6b65b37da 100644 --- a/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -87,6 +87,7 @@ partial class ApplicationEngine [OpCode.PUSHDATA2] = 13000, [OpCode.PUSHDATA4] = 110000, [OpCode.PUSHM1] = 30, + [OpCode.PUSHNULL] = 30, [OpCode.PUSH1] = 30, [OpCode.PUSH2] = 30, [OpCode.PUSH3] = 30, @@ -114,6 +115,7 @@ partial class ApplicationEngine [OpCode.DUPFROMALTSTACK] = 60, [OpCode.TOALTSTACK] = 60, [OpCode.FROMALTSTACK] = 60, + [OpCode.ISNULL] = 60, [OpCode.XDROP] = 400, [OpCode.XSWAP] = 60, [OpCode.XTUCK] = 400, diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index 6a22249cd7..809ec8345f 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -67,6 +67,9 @@ private static StackItem DeserializeStackItem(BinaryReader reader, uint maxArray undeserialized += count * 2; } break; + case StackItemType.Null: + deserialized.Push(StackItem.Null); + break; default: throw new FormatException(); } @@ -232,6 +235,9 @@ private static void SerializeStackItem(StackItem item, BinaryWriter writer) unserialized.Push(pair.Key); } break; + case Null _: + writer.Write((byte)StackItemType.Null); + break; } } } diff --git a/neo/SmartContract/IInteroperable.cs b/neo/SmartContract/IInteroperable.cs new file mode 100644 index 0000000000..972147877c --- /dev/null +++ b/neo/SmartContract/IInteroperable.cs @@ -0,0 +1,9 @@ +using Neo.VM; + +namespace Neo.SmartContract +{ + public interface IInteroperable + { + StackItem ToStackItem(); + } +} diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index 7f0d857fe9..e500eeb235 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -2,7 +2,6 @@ using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; using Neo.SmartContract.Manifest; @@ -20,17 +19,9 @@ static partial class InteropService public static readonly uint Neo_Native_Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application); public static readonly uint Neo_Crypto_CheckSig = Register("Neo.Crypto.CheckSig", Crypto_CheckSig, 0_01000000, TriggerType.All); public static readonly uint Neo_Crypto_CheckMultiSig = Register("Neo.Crypto.CheckMultiSig", Crypto_CheckMultiSig, GetCheckMultiSigPrice, TriggerType.All); - public static readonly uint Neo_Header_GetVersion = Register("Neo.Header.GetVersion", Header_GetVersion, 0_00000400, TriggerType.Application); - public static readonly uint Neo_Header_GetMerkleRoot = Register("Neo.Header.GetMerkleRoot", Header_GetMerkleRoot, 0_00000400, TriggerType.Application); - public static readonly uint Neo_Header_GetNextConsensus = Register("Neo.Header.GetNextConsensus", Header_GetNextConsensus, 0_00000400, TriggerType.Application); - public static readonly uint Neo_Transaction_GetScript = Register("Neo.Transaction.GetScript", Transaction_GetScript, 0_00000400, TriggerType.All); - public static readonly uint Neo_Transaction_GetWitnesses = Register("Neo.Transaction.GetWitnesses", Transaction_GetWitnesses, 0_00010000, TriggerType.All); - public static readonly uint Neo_Witness_GetVerificationScript = Register("Neo.Witness.GetVerificationScript", Witness_GetVerificationScript, 0_00000400, TriggerType.All); public static readonly uint Neo_Account_IsStandard = Register("Neo.Account.IsStandard", Account_IsStandard, 0_00030000, TriggerType.All); public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application); public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application); - public static readonly uint Neo_Contract_GetScript = Register("Neo.Contract.GetScript", Contract_GetScript, 0_00000400, TriggerType.Application); - public static readonly uint Neo_Contract_IsPayable = Register("Neo.Contract.IsPayable", Contract_IsPayable, 0_00000400, TriggerType.Application); public static readonly uint Neo_Storage_Find = Register("Neo.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application); public static readonly uint Neo_Enumerator_Create = Register("Neo.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All); public static readonly uint Neo_Enumerator_Next = Register("Neo.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All); @@ -157,80 +148,6 @@ private static bool Crypto_CheckMultiSig(ApplicationEngine engine) return true; } - private static bool Header_GetVersion(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.Version); - return true; - } - return false; - } - - private static bool Header_GetMerkleRoot(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.MerkleRoot.ToArray()); - return true; - } - return false; - } - - private static bool Header_GetNextConsensus(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.NextConsensus.ToArray()); - return true; - } - return false; - } - - private static bool Transaction_GetScript(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - Transaction tx = _interface.GetInterface(); - if (tx == null) return false; - engine.CurrentContext.EvaluationStack.Push(tx.Script); - return true; - } - return false; - } - - private static bool Transaction_GetWitnesses(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - Transaction tx = _interface.GetInterface(); - if (tx == null) return false; - if (tx.Witnesses.Length > engine.MaxArraySize) - return false; - engine.CurrentContext.EvaluationStack.Push(WitnessWrapper.Create(tx, engine.Snapshot).Select(p => StackItem.FromInterface(p)).ToArray()); - return true; - } - return false; - } - - private static bool Witness_GetVerificationScript(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - WitnessWrapper witness = _interface.GetInterface(); - if (witness == null) return false; - engine.CurrentContext.EvaluationStack.Push(witness.VerificationScript); - return true; - } - return false; - } - private static bool Account_IsStandard(ApplicationEngine engine) { UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetByteArray()); @@ -313,30 +230,6 @@ private static bool Contract_Update(ApplicationEngine engine) return true; } - private static bool Contract_GetScript(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - ContractState contract = _interface.GetInterface(); - if (contract == null) return false; - engine.CurrentContext.EvaluationStack.Push(contract.Script); - return true; - } - return false; - } - - private static bool Contract_IsPayable(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - ContractState contract = _interface.GetInterface(); - if (contract == null) return false; - engine.CurrentContext.EvaluationStack.Push(contract.Payable); - return true; - } - return false; - } - private static bool Storage_Find(ApplicationEngine engine) { if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 7aa7fd7284..0e891de162 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -41,19 +41,11 @@ public static partial class InteropService public static readonly uint System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", Runtime_GetNotifications, 0_00010000, TriggerType.All); public static readonly uint System_Crypto_Verify = Register("System.Crypto.Verify", Crypto_Verify, 0_01000000, TriggerType.All); public static readonly uint System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); - public static readonly uint System_Blockchain_GetHeader = Register("System.Blockchain.GetHeader", Blockchain_GetHeader, 0_00007000, TriggerType.Application); public static readonly uint System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application); public static readonly uint System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application); public static readonly uint System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application); + public static readonly uint System_Blockchain_GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application); public static readonly uint System_Blockchain_GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application); - public static readonly uint System_Header_GetIndex = Register("System.Header.GetIndex", Header_GetIndex, 0_00000400, TriggerType.Application); - public static readonly uint System_Header_GetHash = Register("System.Header.GetHash", Header_GetHash, 0_00000400, TriggerType.Application); - public static readonly uint System_Header_GetPrevHash = Register("System.Header.GetPrevHash", Header_GetPrevHash, 0_00000400, TriggerType.Application); - public static readonly uint System_Header_GetTimestamp = Register("System.Header.GetTimestamp", Header_GetTimestamp, 0_00000400, TriggerType.Application); - public static readonly uint System_Block_GetTransactionCount = Register("System.Block.GetTransactionCount", Block_GetTransactionCount, 0_00000400, TriggerType.Application); - public static readonly uint System_Block_GetTransactions = Register("System.Block.GetTransactions", Block_GetTransactions, 0_00010000, TriggerType.Application); - public static readonly uint System_Block_GetTransaction = Register("System.Block.GetTransaction", Block_GetTransaction, 0_00000400, TriggerType.Application); - public static readonly uint System_Transaction_GetHash = Register("System.Transaction.GetHash", Transaction_GetHash, 0_00000400, TriggerType.All); public static readonly uint System_Contract_Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application); public static readonly uint System_Contract_Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application); public static readonly uint System_Storage_GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application); @@ -112,7 +104,9 @@ private static uint Register(string method, Func handle private static bool ExecutionEngine_GetScriptContainer(ApplicationEngine engine) { - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(engine.ScriptContainer)); + engine.CurrentContext.EvaluationStack.Push( + engine.ScriptContainer is IInteroperable value ? value.ToStackItem() : + StackItem.FromInterface(engine.ScriptContainer)); return true; } @@ -124,7 +118,7 @@ private static bool ExecutionEngine_GetExecutingScriptHash(ApplicationEngine eng private static bool ExecutionEngine_GetCallingScriptHash(ApplicationEngine engine) { - engine.CurrentContext.EvaluationStack.Push(engine.CallingScriptHash?.ToArray() ?? new byte[0]); + engine.CurrentContext.EvaluationStack.Push(engine.CallingScriptHash?.ToArray() ?? StackItem.Null); return true; } @@ -236,13 +230,12 @@ private static bool Runtime_Serialize(ApplicationEngine engine) private static bool Runtime_GetNotifications(ApplicationEngine engine) { - byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - if ((data.Length != 0) && (data.Length != UInt160.Length)) return false; + StackItem item = engine.CurrentContext.EvaluationStack.Pop(); IEnumerable notifications = engine.Notifications; - if (data.Length == UInt160.Length) // must filter by scriptHash + if (!item.IsNull) // must filter by scriptHash { - var hash = new UInt160(data); + var hash = new UInt160(item.GetByteArray()); notifications = notifications.Where(p => p.ScriptHash == hash); } @@ -304,28 +297,6 @@ private static bool Blockchain_GetHeight(ApplicationEngine engine) return true; } - private static bool Blockchain_GetHeader(ApplicationEngine engine) - { - byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - UInt256 hash; - if (data.Length <= 5) - hash = Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); - else if (data.Length == 32) - hash = new UInt256(data); - else - return false; - if (hash == null) - { - engine.CurrentContext.EvaluationStack.Push(new byte[0]); - } - else - { - Header header = engine.Snapshot.GetHeader(hash); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(header)); - } - return true; - } - private static bool Blockchain_GetBlock(ApplicationEngine engine) { byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); @@ -336,15 +307,12 @@ private static bool Blockchain_GetBlock(ApplicationEngine engine) hash = new UInt256(data); else return false; - if (hash == null) - { - engine.CurrentContext.EvaluationStack.Push(new byte[0]); - } + + Block block = hash != null ? engine.Snapshot.GetBlock(hash) : null; + if (block == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); else - { - Block block = engine.Snapshot.GetBlock(hash); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(block)); - } + engine.CurrentContext.EvaluationStack.Push(block.ToStackItem()); return true; } @@ -352,128 +320,60 @@ private static bool Blockchain_GetTransaction(ApplicationEngine engine) { byte[] hash = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); Transaction tx = engine.Snapshot.GetTransaction(new UInt256(hash)); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(tx)); + if (tx == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + else + engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem()); return true; } private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine) { byte[] hash = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - int? height = (int?)engine.Snapshot.Transactions.TryGet(new UInt256(hash))?.BlockIndex; - engine.CurrentContext.EvaluationStack.Push(height ?? -1); + var tx = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); + engine.CurrentContext.EvaluationStack.Push(tx != null ? new BigInteger(tx.BlockIndex) : BigInteger.MinusOne); return true; } - private static bool Blockchain_GetContract(ApplicationEngine engine) + private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) { - UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetByteArray()); - ContractState contract = engine.Snapshot.Contracts.TryGet(hash); - if (contract == null) - engine.CurrentContext.EvaluationStack.Push(new byte[0]); + byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + UInt256 hash; + if (data.Length <= 5) + hash = Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); + else if (data.Length == 32) + hash = new UInt256(data); else - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(contract)); - return true; - } - - private static bool Header_GetIndex(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.Index); - return true; - } - return false; - } - - private static bool Header_GetHash(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.Hash.ToArray()); - return true; - } - return false; - } - - private static bool Header_GetPrevHash(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.PrevHash.ToArray()); - return true; - } - return false; - } - - private static bool Header_GetTimestamp(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - BlockBase header = _interface.GetInterface(); - if (header == null) return false; - engine.CurrentContext.EvaluationStack.Push(header.Timestamp); - return true; - } - return false; - } + return false; - private static bool Block_GetTransactionCount(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + TrimmedBlock block = hash != null ? engine.Snapshot.Blocks.TryGet(hash) : null; + if (block == null) { - Block block = _interface.GetInterface(); - if (block == null) return false; - engine.CurrentContext.EvaluationStack.Push(block.Transactions.Length); - return true; + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); } - return false; - } - - private static bool Block_GetTransactions(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - Block block = _interface.GetInterface(); - if (block == null) return false; - if (block.Transactions.Length > engine.MaxArraySize) - return false; - engine.CurrentContext.EvaluationStack.Push(block.Transactions.Select(p => StackItem.FromInterface(p)).ToArray()); - return true; - } - return false; - } - - private static bool Block_GetTransaction(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + else { - Block block = _interface.GetInterface(); int index = (int)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger(); - if (block == null) return false; - if (index < 0 || index >= block.Transactions.Length) return false; - Transaction tx = block.Transactions[index]; - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(tx)); - return true; + if (index < 0 || index >= block.Hashes.Length) return false; + + Transaction tx = engine.Snapshot.GetTransaction(block.Hashes[index]); + if (tx == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + else + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(tx)); } - return false; + return true; } - private static bool Transaction_GetHash(ApplicationEngine engine) + private static bool Blockchain_GetContract(ApplicationEngine engine) { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - Transaction tx = _interface.GetInterface(); - if (tx == null) return false; - engine.CurrentContext.EvaluationStack.Push(tx.Hash.ToArray()); - return true; - } - return false; + UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetByteArray()); + ContractState contract = engine.Snapshot.Contracts.TryGet(hash); + if (contract == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + else + engine.CurrentContext.EvaluationStack.Push(contract.ToStackItem()); + return true; } private static bool Storage_GetContext(ApplicationEngine engine) @@ -508,7 +408,7 @@ private static bool Storage_Get(ApplicationEngine engine) ScriptHash = context.ScriptHash, Key = key }); - engine.CurrentContext.EvaluationStack.Push(item?.Value ?? new byte[0]); + engine.CurrentContext.EvaluationStack.Push(item?.Value ?? StackItem.Null); return true; } return false; @@ -533,13 +433,9 @@ private static bool StorageContext_AsReadOnly(ApplicationEngine engine) private static bool Contract_Call(ApplicationEngine engine) { - StackItem contractOrHash = engine.CurrentContext.EvaluationStack.Pop(); + StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop(); - ContractState contract; - if (contractOrHash is InteropInterface _interface) - contract = _interface; - else - contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractOrHash.GetByteArray())); + ContractState contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractHash.GetByteArray())); if (contract is null) return false; StackItem method = engine.CurrentContext.EvaluationStack.Pop(); diff --git a/neo/SmartContract/JsonSerializer.cs b/neo/SmartContract/JsonSerializer.cs index a4853fc39a..1c674540f0 100644 --- a/neo/SmartContract/JsonSerializer.cs +++ b/neo/SmartContract/JsonSerializer.cs @@ -53,6 +53,10 @@ public static JObject Serialize(StackItem item) return ret; } + case Null _: + { + return JObject.Null; + } default: throw new FormatException(); } } @@ -66,6 +70,10 @@ public static StackItem Deserialize(JObject json) { switch (json) { + case null: + { + return StackItem.Null; + } case JArray array: { return array.Select(p => Deserialize(p)).ToList(); diff --git a/neo/SmartContract/StackItemType.cs b/neo/SmartContract/StackItemType.cs index b47999e157..7e19c2982b 100644 --- a/neo/SmartContract/StackItemType.cs +++ b/neo/SmartContract/StackItemType.cs @@ -9,5 +9,6 @@ internal enum StackItemType : byte Array = 0x80, Struct = 0x81, Map = 0x82, + Null = 0xff } } diff --git a/neo/SmartContract/WitnessWrapper.cs b/neo/SmartContract/WitnessWrapper.cs deleted file mode 100644 index 28019be0c2..0000000000 --- a/neo/SmartContract/WitnessWrapper.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Neo.Network.P2P.Payloads; -using Neo.Persistence; -using System.Linq; - -namespace Neo.SmartContract -{ - internal class WitnessWrapper - { - public byte[] VerificationScript; - - public static WitnessWrapper[] Create(IVerifiable verifiable, Snapshot snapshot) - { - WitnessWrapper[] wrappers = verifiable.Witnesses.Select(p => new WitnessWrapper - { - VerificationScript = p.VerificationScript - }).ToArray(); - if (wrappers.Any(p => p.VerificationScript.Length == 0)) - { - UInt160[] hashes = verifiable.GetScriptHashesForVerifying(snapshot); - for (int i = 0; i < wrappers.Length; i++) - if (wrappers[i].VerificationScript.Length == 0) - wrappers[i].VerificationScript = snapshot.Contracts[hashes[i]].Script; - } - return wrappers; - } - } -} diff --git a/neo/VM/Helper.cs b/neo/VM/Helper.cs index d63bebb89a..5d2a2aa485 100644 --- a/neo/VM/Helper.cs +++ b/neo/VM/Helper.cs @@ -149,6 +149,9 @@ public static ScriptBuilder EmitPush(this ScriptBuilder sb, object obj) case Enum data: sb.EmitPush(BigInteger.Parse(data.ToString("d"))); break; + case null: + sb.Emit(OpCode.PUSHNULL); + break; default: throw new ArgumentException(); } @@ -242,7 +245,7 @@ private static ContractParameter ToParameter(StackItem item, List - + From a0b3bfcd5d7ca963a8a695efde430f6e866560cc Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 16 Oct 2019 12:02:22 +0200 Subject: [PATCH 109/305] Fix two ToLower calls (#1155) --- neo/Network/UPnP.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/Network/UPnP.cs b/neo/Network/UPnP.cs index ad1b3baa39..5cac628daa 100644 --- a/neo/Network/UPnP.cs +++ b/neo/Network/UPnP.cs @@ -50,10 +50,10 @@ public static bool Discover() { length = s.Receive(buffer); - string resp = Encoding.ASCII.GetString(buffer, 0, length).ToLower(); + string resp = Encoding.ASCII.GetString(buffer, 0, length).ToLowerInvariant(); if (resp.Contains("upnp:rootdevice")) { - resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9); + resp = resp.Substring(resp.IndexOf("location:") + 9); resp = resp.Substring(0, resp.IndexOf("\r")).Trim(); if (!string.IsNullOrEmpty(_serviceUrl = GetServiceUrl(resp))) { From e91c5879911fc9d943f674748c75d0394c6d0427 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 18 Oct 2019 13:05:51 +0900 Subject: [PATCH 110/305] Notification limits (#1100) --- neo/SmartContract/InteropService.cs | 63 +++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index 0e891de162..c647cb84fc 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -14,6 +14,8 @@ using System.Linq; using System.Numerics; using System.Text; +using Array = Neo.VM.Types.Array; +using Boolean = Neo.VM.Types.Boolean; namespace Neo.SmartContract { @@ -22,6 +24,7 @@ public static partial class InteropService public const long GasPerByte = 100000; public const int MaxStorageKeySize = 64; public const int MaxStorageValueSize = ushort.MaxValue; + public const int MaxNotificationSize = 1024; private static readonly Dictionary methods = new Dictionary(); @@ -32,8 +35,8 @@ public static partial class InteropService public static readonly uint System_Runtime_Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250, TriggerType.All); public static readonly uint System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250, TriggerType.All); public static readonly uint System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All); - public static readonly uint System_Runtime_Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_00000250, TriggerType.All); - public static readonly uint System_Runtime_Log = Register("System.Runtime.Log", Runtime_Log, 0_00300000, TriggerType.All); + public static readonly uint System_Runtime_Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_01000000, TriggerType.All); + public static readonly uint System_Runtime_Log = Register("System.Runtime.Log", Runtime_Log, 0_01000000, TriggerType.All); public static readonly uint System_Runtime_GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application); public static readonly uint System_Runtime_Serialize = Register("System.Runtime.Serialize", Runtime_Serialize, 0_00100000, TriggerType.All); public static readonly uint System_Runtime_Deserialize = Register("System.Runtime.Deserialize", Runtime_Deserialize, 0_00500000, TriggerType.All); @@ -56,6 +59,54 @@ public static partial class InteropService public static readonly uint System_Storage_Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application); public static readonly uint System_StorageContext_AsReadOnly = Register("System.StorageContext.AsReadOnly", StorageContext_AsReadOnly, 0_00000400, TriggerType.Application); + private static bool CheckItemForNotification(StackItem state) + { + int size = 0; + List items_checked = new List(); + Queue items_unchecked = new Queue(); + while (true) + { + switch (state) + { + case Struct array: + foreach (StackItem item in array) + items_unchecked.Enqueue(item); + break; + case Array array: + if (items_checked.All(p => !ReferenceEquals(p, array))) + { + items_checked.Add(array); + foreach (StackItem item in array) + items_unchecked.Enqueue(item); + } + break; + case Boolean _: + case ByteArray _: + case Integer _: + size += state.GetByteLength(); + break; + case Null _: + break; + case InteropInterface _: + return false; + case Map map: + if (items_checked.All(p => !ReferenceEquals(p, map))) + { + items_checked.Add(map); + foreach (var pair in map) + { + size += pair.Key.GetByteLength(); + items_unchecked.Enqueue(pair.Value); + } + } + break; + } + if (size > MaxNotificationSize) return false; + if (items_unchecked.Count == 0) return true; + state = items_unchecked.Dequeue(); + } + } + private static bool CheckStorageContext(ApplicationEngine engine, StorageContext context) { ContractState contract = engine.Snapshot.Contracts.TryGet(context.ScriptHash); @@ -194,13 +245,17 @@ private static bool Runtime_CheckWitness(ApplicationEngine engine) private static bool Runtime_Notify(ApplicationEngine engine) { - engine.SendNotification(engine.CurrentScriptHash, engine.CurrentContext.EvaluationStack.Pop()); + StackItem state = engine.CurrentContext.EvaluationStack.Pop(); + if (!CheckItemForNotification(state)) return false; + engine.SendNotification(engine.CurrentScriptHash, state); return true; } private static bool Runtime_Log(ApplicationEngine engine) { - string message = Encoding.UTF8.GetString(engine.CurrentContext.EvaluationStack.Pop().GetByteArray()); + byte[] state = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + if (state.Length > MaxNotificationSize) return false; + string message = Encoding.UTF8.GetString(state); engine.SendLog(engine.CurrentScriptHash, message); return true; } From 24f76ea49794e253ce2ec03bdf2d08add224e13e Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 22 Oct 2019 12:21:16 +0200 Subject: [PATCH 111/305] Fix GetTransactionFromBlock Syscall (#1170) * Fix Syscall * Fix --- neo/SmartContract/InteropService.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index c647cb84fc..eed16fe17a 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -409,13 +409,13 @@ private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) else { int index = (int)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger(); - if (index < 0 || index >= block.Hashes.Length) return false; + if (index < 0 || index >= block.Hashes.Length - 1) return false; - Transaction tx = engine.Snapshot.GetTransaction(block.Hashes[index]); + Transaction tx = engine.Snapshot.GetTransaction(block.Hashes[index + 1]); if (tx == null) engine.CurrentContext.EvaluationStack.Push(StackItem.Null); else - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(tx)); + engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem()); } return true; } From 964c9c9ac724f8c805198c0f79cd02c007a28f07 Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Thu, 24 Oct 2019 01:42:34 +0800 Subject: [PATCH 112/305] Unit Test for Smartcontract Module (#1090) * submit ut * fix * Enhance functions in TestDataCache * git amend blockchain * fix test related to TestDataCache * dotnet format * dotnet format * add blank line * fix test * Optimize random * Optimize Random * fix test * add decimal test * fix * 2019/9/25 16:54 change format * Fixes events * update assertion sentence * update UT following code change * format * add type check * recommit * recommit --- .../Enumerators/UT_ConcatenatedEnumerator.cs | 55 ++ .../Enumerators/UT_IteratorKeysWrapper.cs | 36 + .../Enumerators/UT_IteratorValuesWrapper.cs | 37 + .../Iterators/UT_ArrayWrapper.cs | 50 ++ .../Iterators/UT_ConcatenatedIterator.cs | 14 +- .../SmartContract/Iterators/UT_MapWrapper.cs | 43 ++ .../Iterators/UT_StorageIterator.cs | 38 + .../Manifest/UT_ContractEventDescriptor.cs | 22 + .../Manifest/UT_ContractGroup.cs | 42 ++ .../Manifest/UT_ContractManifest.cs | 55 ++ .../Manifest/UT_ContractPermission.cs | 52 ++ .../UT_ContractPermissionDescriptor.cs | 37 + .../Manifest/UT_WildCardContainer.cs | 91 +++ .../Native/Tokens/UT_GasToken.cs | 42 ++ .../Native/Tokens/UT_NeoToken.cs | 343 +++++++++ .../Native/Tokens/UT_Nep5Token.cs | 112 +++ .../SmartContract/Native/UT_NativeContract.cs | 100 +++ .../SmartContract/Native/UT_PolicyContract.cs | 25 + .../SmartContract/UT_ApplicationEngine.cs | 185 +++++ .../SmartContract/UT_ContainerPlaceholder.cs | 43 ++ neo.UnitTests/SmartContract/UT_Contract.cs | 156 +++++ .../SmartContract/UT_ContractParameter.cs | 201 ++++++ .../UT_ContractParameterContext.cs | 161 +++++ .../SmartContract/UT_InteropDescriptor.cs | 27 + .../SmartContract/UT_InteropService.NEO.cs | 479 +++++++++++++ .../SmartContract/UT_InteropService.cs | 654 +++++++++++++++++- .../SmartContract/UT_LogEventArgs.cs | 23 + neo.UnitTests/SmartContract/UT_NefFile.cs | 148 ++++ .../SmartContract/UT_NotifyEventArgs.cs | 22 + .../SmartContract/UT_SmartContractHelper.cs | 306 ++++++++ .../SmartContract/UT_StorageContext.cs | 22 + neo.UnitTests/TestDataCache.cs | 1 + 32 files changed, 3620 insertions(+), 2 deletions(-) create mode 100644 neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs create mode 100644 neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs create mode 100644 neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs create mode 100644 neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs create mode 100644 neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs create mode 100644 neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs create mode 100644 neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs create mode 100644 neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs create mode 100644 neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs create mode 100644 neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs create mode 100644 neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs create mode 100644 neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs create mode 100644 neo.UnitTests/SmartContract/Native/UT_NativeContract.cs create mode 100644 neo.UnitTests/SmartContract/UT_ApplicationEngine.cs create mode 100644 neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs create mode 100644 neo.UnitTests/SmartContract/UT_Contract.cs create mode 100644 neo.UnitTests/SmartContract/UT_ContractParameter.cs create mode 100644 neo.UnitTests/SmartContract/UT_ContractParameterContext.cs create mode 100644 neo.UnitTests/SmartContract/UT_InteropDescriptor.cs create mode 100644 neo.UnitTests/SmartContract/UT_InteropService.NEO.cs create mode 100644 neo.UnitTests/SmartContract/UT_LogEventArgs.cs create mode 100644 neo.UnitTests/SmartContract/UT_NefFile.cs create mode 100644 neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs create mode 100644 neo.UnitTests/SmartContract/UT_SmartContractHelper.cs create mode 100644 neo.UnitTests/SmartContract/UT_StorageContext.cs diff --git a/neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs b/neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs new file mode 100644 index 0000000000..bbcfcdde78 --- /dev/null +++ b/neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs @@ -0,0 +1,55 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Enumerators; +using Neo.SmartContract.Iterators; +using Neo.VM; +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/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs b/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs new file mode 100644 index 0000000000..2a345893e0 --- /dev/null +++ b/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs @@ -0,0 +1,36 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Enumerators; +using Neo.SmartContract.Iterators; +using Neo.VM; +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/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs b/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs new file mode 100644 index 0000000000..f236faab1a --- /dev/null +++ b/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs @@ -0,0 +1,37 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Enumerators; +using Neo.SmartContract.Iterators; +using Neo.VM; +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/neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs b/neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs new file mode 100644 index 0000000000..9aebf0861c --- /dev/null +++ b/neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs @@ -0,0 +1,50 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Iterators; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Collections.Generic; + +namespace Neo.UnitTests.SmartContract.Iterators +{ + [TestClass] + public class UT_ArrayWrapper + { + [TestMethod] + public void TestGeneratorAndDispose() + { + ArrayWrapper arrayWrapper = new ArrayWrapper(new List()); + Assert.IsNotNull(arrayWrapper); + Action action = () => arrayWrapper.Dispose(); + action.Should().NotThrow(); + } + + [TestMethod] + public void TestKeyAndValue() + { + List list = new List(); + 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()); + } + + [TestMethod] + public void TestNext() + { + List list = new List(); + ArrayWrapper arrayWrapper = new ArrayWrapper(list); + Assert.AreEqual(false, arrayWrapper.Next()); + StackItem stackItem = new Integer(0); + list.Add(stackItem); + Assert.AreEqual(true, arrayWrapper.Next()); + } + } +} diff --git a/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs b/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs index 5fad01c94b..84b3ffe041 100644 --- a/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs +++ b/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs @@ -2,11 +2,11 @@ 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 { @@ -65,5 +65,17 @@ 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/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs b/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs new file mode 100644 index 0000000000..b111b0aa54 --- /dev/null +++ b/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs @@ -0,0 +1,43 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Iterators; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Collections.Generic; + +namespace Neo.UnitTests.SmartContract.Iterators +{ + [TestClass] + public class UT_MapWrapper + { + [TestMethod] + public void TestGeneratorAndDispose() + { + MapWrapper mapWrapper = new MapWrapper(new List>()); + Assert.IsNotNull(mapWrapper); + Action action = () => mapWrapper.Dispose(); + action.Should().NotThrow(); + } + + [TestMethod] + public void TestKeyAndValue() + { + List> list = new List>(); + StackItem stackItem1 = new Integer(0); + StackItem stackItem2 = new Integer(1); + list.Add(new KeyValuePair(stackItem1, stackItem2)); + MapWrapper mapWrapper = new MapWrapper(list); + mapWrapper.Next(); + Assert.AreEqual(stackItem1, mapWrapper.Key()); + Assert.AreEqual(stackItem2, mapWrapper.Value()); + } + + [TestMethod] + public void TestNext() + { + MapWrapper mapWrapper = new MapWrapper(new List>()); + Assert.AreEqual(false, mapWrapper.Next()); + } + } +} diff --git a/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs b/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs new file mode 100644 index 0000000000..aad833b04e --- /dev/null +++ b/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs @@ -0,0 +1,38 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Ledger; +using Neo.SmartContract.Iterators; +using Neo.VM.Types; +using System; +using System.Collections.Generic; + +namespace Neo.UnitTests.SmartContract.Iterators +{ + [TestClass] + public class UT_StorageIterator + { + [TestMethod] + public void TestGeneratorAndDispose() + { + StorageIterator storageIterator = new StorageIterator(new List>().GetEnumerator()); + Assert.IsNotNull(storageIterator); + Action action = () => storageIterator.Dispose(); + action.Should().NotThrow(); + } + + [TestMethod] + public void TestKeyAndValueAndNext() + { + List> list = new List>(); + StorageKey storageKey = new StorageKey(); + storageKey.Key = new byte[1]; + StorageItem storageItem = new StorageItem(); + storageItem.Value = new byte[1]; + list.Add(new KeyValuePair(storageKey, storageItem)); + StorageIterator storageIterator = new StorageIterator(list.GetEnumerator()); + storageIterator.Next(); + Assert.AreEqual(new ByteArray(new byte[1]), storageIterator.Key()); + Assert.AreEqual(new ByteArray(new byte[1]), storageIterator.Value()); + } + } +} diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs b/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs new file mode 100644 index 0000000000..abbc4af5a0 --- /dev/null +++ b/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs @@ -0,0 +1,22 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Manifest; + +namespace Neo.UnitTests.SmartContract.Manifest +{ + [TestClass] + public class UT_ContractEventDescriptor + { + [TestMethod] + public void TestFromJson() + { + ContractEventDescriptor expected = new ContractEventDescriptor + { + Name = "AAA", + Parameters = new ContractParameterDefinition[0] + }; + ContractEventDescriptor actual = ContractEventDescriptor.FromJson(expected.ToJson()); + Assert.AreEqual(expected.Name, actual.Name); + Assert.AreEqual(0, actual.Parameters.Length); + } + } +} diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs b/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs new file mode 100644 index 0000000000..92519e26e5 --- /dev/null +++ b/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs @@ -0,0 +1,42 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.SmartContract.Manifest; +using Neo.Wallets; +using System; +using System.Linq; + +namespace Neo.UnitTests.SmartContract.Manifest +{ + [TestClass] + public class UT_ContractGroup + { + [TestMethod] + public void TestIsValid() + { + Random random = new Random(); + byte[] privateKey = new byte[32]; + random.NextBytes(privateKey); + KeyPair keyPair = new KeyPair(privateKey); + ContractGroup contractGroup = new ContractGroup + { + PubKey = keyPair.PublicKey, + Signature = new byte[20] + }; + Assert.AreEqual(false, contractGroup.IsValid(UInt160.Zero)); + + + byte[] message = new byte[] { 0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01, + 0x01,0x01,0x01,0x01,0x01 }; + byte[] signature = Crypto.Default.Sign(message, keyPair.PrivateKey, keyPair.PublicKey.EncodePoint(false).Skip(1).ToArray()); + contractGroup = new ContractGroup + { + PubKey = keyPair.PublicKey, + Signature = signature + }; + Assert.AreEqual(true, contractGroup.IsValid(new UInt160(message))); + } + } +} diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index a96c3ff4c5..910f30b019 100644 --- a/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.SmartContract.Manifest; +using System.IO; namespace Neo.UnitTests.SmartContract.Manifest { @@ -84,5 +85,59 @@ public void ParseFromJson_Groups() check.Groups = new ContractGroup[] { new ContractGroup() { PubKey = ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1), Signature = "41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141".HexToBytes() } }; Assert.AreEqual(manifest.ToString(), check.ToString()); } + + [TestMethod] + public void TestDeserializeAndSerialize() + { + MemoryStream stream = new MemoryStream(); + BinaryWriter writer = new BinaryWriter(stream); + BinaryReader reader = new BinaryReader(stream); + var expected = ContractManifest.CreateDefault(UInt160.Zero); + expected.SafeMethods = WildCardContainer.Create(new string[] { "AAA" }); + expected.Serialize(writer); + stream.Seek(0, SeekOrigin.Begin); + var actual = ContractManifest.CreateDefault(UInt160.Zero); + actual.Deserialize(reader); + Assert.AreEqual(expected.SafeMethods.ToString(), actual.SafeMethods.ToString()); + Assert.AreEqual(expected.SafeMethods.Count, 1); + } + + [TestMethod] + public void TestGetHash() + { + var temp = ContractManifest.CreateDefault(UInt160.Zero); + Assert.AreEqual(temp.Abi.Hash, temp.Hash); + } + + [TestMethod] + public void TestGetSize() + { + var temp = ContractManifest.CreateDefault(UInt160.Zero); + Assert.AreEqual(353, temp.Size); + } + + [TestMethod] + public void TestGenerator() + { + ContractManifest contractManifest = new ContractManifest(); + Assert.IsNotNull(contractManifest); + } + + [TestMethod] + public void TestCanCall() + { + var temp = ContractManifest.CreateDefault(UInt160.Zero); + temp.SafeMethods = WildCardContainer.Create(new string[] { "AAA" }); + Assert.AreEqual(true, temp.CanCall(ContractManifest.CreateDefault(UInt160.Zero), "AAA")); + } + + [TestMethod] + public void TestClone() + { + var expected = ContractManifest.CreateDefault(UInt160.Zero); + expected.SafeMethods = WildCardContainer.Create(new string[] { "AAA" }); + var actual = expected.Clone(); + Assert.AreEqual(actual.SafeMethods.ToString(), expected.SafeMethods.ToString()); + } } } diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs b/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs new file mode 100644 index 0000000000..7517458423 --- /dev/null +++ b/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs @@ -0,0 +1,52 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; +using Neo.SmartContract.Manifest; +using System; + +namespace Neo.UnitTests.SmartContract.Manifest +{ + [TestClass] + public class UT_ContractPermission + { + [TestMethod] + public void TestIsAllowed() + { + ContractManifest contractManifest1 = ContractManifest.CreateDefault(UInt160.Zero); + ContractPermission contractPermission1 = ContractPermission.DefaultPermission; + contractPermission1.Contract = ContractPermissionDescriptor.Create(UInt160.Zero); + Assert.AreEqual(true, contractPermission1.IsAllowed(contractManifest1, "AAA")); + contractPermission1.Contract = ContractPermissionDescriptor.CreateWildcard(); + + ContractManifest contractManifest2 = ContractManifest.CreateDefault(UInt160.Zero); + ContractPermission contractPermission2 = ContractPermission.DefaultPermission; + contractPermission2.Contract = ContractPermissionDescriptor.Create(UInt160.Parse("0x0000000000000000000000000000000000000001")); + Assert.AreEqual(false, contractPermission2.IsAllowed(contractManifest2, "AAA")); + contractPermission2.Contract = ContractPermissionDescriptor.CreateWildcard(); + + Random random3 = new Random(); + byte[] privateKey3 = new byte[32]; + random3.NextBytes(privateKey3); + ECPoint publicKey3 = ECCurve.Secp256r1.G * privateKey3; + ContractManifest contractManifest3 = ContractManifest.CreateDefault(UInt160.Zero); + contractManifest3.Groups = new ContractGroup[] { new ContractGroup() { PubKey = publicKey3 } }; + ContractPermission contractPermission3 = ContractPermission.DefaultPermission; + contractPermission3.Contract = ContractPermissionDescriptor.Create(publicKey3); + Assert.AreEqual(true, contractPermission3.IsAllowed(contractManifest3, "AAA")); + contractPermission3.Contract = ContractPermissionDescriptor.CreateWildcard(); + + Random random4 = new Random(); + byte[] privateKey41 = new byte[32]; + random4.NextBytes(privateKey41); + ECPoint publicKey41 = ECCurve.Secp256r1.G * privateKey41; + byte[] privateKey42 = new byte[32]; + random4.NextBytes(privateKey42); + ECPoint publicKey42 = ECCurve.Secp256r1.G * privateKey42; + ContractManifest contractManifest4 = ContractManifest.CreateDefault(UInt160.Zero); + contractManifest4.Groups = new ContractGroup[] { new ContractGroup() { PubKey = publicKey42 } }; + ContractPermission contractPermission4 = ContractPermission.DefaultPermission; + contractPermission4.Contract = ContractPermissionDescriptor.Create(publicKey41); + Assert.AreEqual(false, contractPermission4.IsAllowed(contractManifest4, "AAA")); + contractPermission4.Contract = ContractPermissionDescriptor.CreateWildcard(); + } + } +} diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs b/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs new file mode 100644 index 0000000000..299ff2e49d --- /dev/null +++ b/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs @@ -0,0 +1,37 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Manifest; +using Neo.Wallets; +using System.Security.Cryptography; + +namespace Neo.UnitTests.SmartContract.Manifest +{ + [TestClass] + public class UT_ContractPermissionDescriptor + { + [TestMethod] + public void TestCreateByECPointAndIsWildcard() + { + byte[] privateKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(privateKey); + KeyPair key = new KeyPair(privateKey); + ContractPermissionDescriptor contractPermissionDescriptor = ContractPermissionDescriptor.Create(key.PublicKey); + Assert.IsNotNull(contractPermissionDescriptor); + Assert.AreEqual(key.PublicKey, contractPermissionDescriptor.Group); + Assert.AreEqual(false, contractPermissionDescriptor.IsWildcard); + } + + [TestMethod] + public void TestFromAndToJson() + { + byte[] privateKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(privateKey); + KeyPair key = new KeyPair(privateKey); + ContractPermissionDescriptor temp = ContractPermissionDescriptor.Create(key.PublicKey); + ContractPermissionDescriptor result = ContractPermissionDescriptor.FromJson(temp.ToJson()); + Assert.AreEqual(null, result.Hash); + Assert.AreEqual(result.Group, result.Group); + } + } +} diff --git a/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs b/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs new file mode 100644 index 0000000000..96bec21d2d --- /dev/null +++ b/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs @@ -0,0 +1,91 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.SmartContract.Manifest; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Neo.UnitTests.SmartContract.Manifest +{ + [TestClass] + public class UT_WildCardContainer + { + [TestMethod] + public void TestFromJson() + { + JString jstring = new JString("*"); + WildCardContainer s = WildCardContainer.FromJson(jstring, u => u.AsString()); + s.Should().BeEmpty(); + + jstring = new JString("hello world"); + Action action = () => WildCardContainer.FromJson(jstring, u => u.AsString()); + action.Should().Throw(); + + JObject alice = new JObject(); + alice["name"] = "alice"; + alice["age"] = 30; + JArray jarray = new JArray { alice }; + WildCardContainer r = WildCardContainer.FromJson(jarray, u => u.AsString()); + r[0].Should().Be("{\"name\":\"alice\",\"age\":30}"); + + JBoolean jbool = new JBoolean(); + action = () => WildCardContainer.FromJson(jbool, u => u.AsString()); + action.Should().Throw(); + } + + [TestMethod] + public void TestGetCount() + { + string[] s = new string[] { "hello", "world" }; + WildCardContainer container = WildCardContainer.Create(s); + container.Count.Should().Be(2); + + s = null; + container = WildCardContainer.Create(s); + container.Count.Should().Be(0); + } + + [TestMethod] + public void TestGetItem() + { + string[] s = new string[] { "hello", "world" }; + WildCardContainer container = WildCardContainer.Create(s); + container[0].Should().Be("hello"); + container[1].Should().Be("world"); + } + + [TestMethod] + public void TestGetEnumerator() + { + string[] s = null; + IReadOnlyList rs = (IReadOnlyList)new string[0]; + WildCardContainer container = WildCardContainer.Create(s); + IEnumerator enumerator = container.GetEnumerator(); + enumerator.Should().Be(rs.GetEnumerator()); + + s = new string[] { "hello", "world" }; + container = WildCardContainer.Create(s); + enumerator = container.GetEnumerator(); + foreach (string _ in s) + { + enumerator.MoveNext(); + enumerator.Current.Should().Be(_); + } + } + + [TestMethod] + public void TestIEnumerableGetEnumerator() + { + string[] s = new string[] { "hello", "world" }; + WildCardContainer container = WildCardContainer.Create(s); + IEnumerable enumerable = container; + var enumerator = enumerable.GetEnumerator(); + foreach (string _ in s) + { + enumerator.MoveNext(); + enumerator.Current.Should().Be(_); + } + } + } +} diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index 0202e9d6d0..7e811d3eda 100644 --- a/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -7,6 +7,7 @@ using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; using Neo.VM; +using Neo.VM.Types; using System; using System.Linq; using System.Numerics; @@ -140,5 +141,46 @@ public void Check_BadScript() NativeContract.GAS.Invoke(engine).Should().BeFalse(); } + + [TestMethod] + public void TestGetSysFeeAmount1() + { + using (ApplicationEngine engine = NativeContract.GAS.TestCall("getSysFeeAmount", 2u)) + { + engine.ResultStack.Peek().GetBigInteger().Should().Be(new BigInteger(0)); + engine.ResultStack.Peek().GetType().Should().Be(typeof(Integer)); + } + + using (ApplicationEngine engine = NativeContract.GAS.TestCall("getSysFeeAmount", 0u)) + { + engine.ResultStack.Peek().GetBigInteger().Should().Be(new BigInteger(0)); + } + } + + [TestMethod] + public void TestGetSysFeeAmount2() + { + var snapshot = Store.GetSnapshot().Clone(); + NativeContract.GAS.GetSysFeeAmount(snapshot, 0).Should().Be(new BigInteger(0)); + NativeContract.GAS.GetSysFeeAmount(snapshot, 1).Should().Be(new BigInteger(0)); + + byte[] key = BitConverter.GetBytes(1); + StorageKey storageKey = new StorageKey + { + ScriptHash = NativeContract.GAS.Hash, + Key = new byte[sizeof(byte) + (key?.Length ?? 0)] + }; + storageKey.Key[0] = 15; + Buffer.BlockCopy(key, 0, storageKey.Key, 1, key.Length); + + BigInteger sys_fee = new BigInteger(10); + snapshot.Storages.Add(storageKey, new StorageItem + { + Value = sys_fee.ToByteArray(), + IsConstant = true + }); + + NativeContract.GAS.GetSysFeeAmount(snapshot, 1).Should().Be(sys_fee); + } } } diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 38befbe790..6df9f7074e 100644 --- a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -14,6 +14,7 @@ using System; using System.Linq; using System.Numerics; +using static Neo.SmartContract.Native.Tokens.NeoToken; namespace Neo.UnitTests.SmartContract.Native.Tokens { @@ -248,6 +249,335 @@ public void Check_BadScript() NativeContract.NEO.Invoke(engine).Should().BeFalse(); } + [TestMethod] + public void TestCalculateBonus() + { + Snapshot snapshot = Store.GetSnapshot().Clone(); + StorageKey key = CreateStorageKey(20, UInt160.Zero.ToArray()); + snapshot.Storages.Add(key, new StorageItem + { + Value = new AccountState() + { + Balance = -100 + }.ToByteArray() + }); + Action action = () => NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); + action.Should().Throw(); + snapshot.Storages.Delete(key); + snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new AccountState() + { + Balance = 100 + }.ToByteArray() + }); + NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 30 * Blockchain.DecrementInterval).Should().Be(new BigInteger(7000000000)); + } + + [TestMethod] + public void TestGetNextBlockValidators1() + { + using (ApplicationEngine engine = NativeContract.NEO.TestCall("getNextBlockValidators")) + { + var result = engine.ResultStack.Peek(); + result.GetType().Should().Be(typeof(VM.Types.Array)); + ((VM.Types.Array)result).Count.Should().Be(7); + ((VM.Types.ByteArray)((VM.Types.Array)result)[0]).GetByteArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[1]).GetByteArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[2]).GetByteArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[3]).GetByteArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[4]).GetByteArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[5]).GetByteArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[6]).GetByteArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + } + } + + [TestMethod] + public void TestGetNextBlockValidators2() + { + Snapshot snapshot = Store.GetSnapshot().Clone(); + var result = NativeContract.NEO.GetNextBlockValidators(snapshot); + result.Length.Should().Be(7); + result[0].ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + result[1].ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); + result[2].ToArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + result[3].ToArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + result[4].ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + result[5].ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + result[6].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + + snapshot.Storages.Add(CreateStorageKey(14), new StorageItem() + { + Value = new ECPoint[] { ECCurve.Secp256r1.G }.ToByteArray() + }); + result = NativeContract.NEO.GetNextBlockValidators(snapshot); + result.Length.Should().Be(1); + result[0].ToArray().ToHexString().Should().Be("036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"); + } + + [TestMethod] + public void TestGetRegisteredValidators1() + { + using (ApplicationEngine engine = NativeContract.NEO.TestCall("getRegisteredValidators")) + { + var result = engine.ResultStack.Peek(); + result.GetType().Should().Be(typeof(VM.Types.Array)); + ((VM.Types.Array)result).Count.Should().Be(7); + ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[0])[0]).GetByteArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + ((VM.Types.Struct)((VM.Types.Array)result)[0])[1].GetBigInteger().Should().Be(new BigInteger(0)); + ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[1])[0]).GetByteArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + ((VM.Types.Struct)((VM.Types.Array)result)[1])[1].GetBigInteger().Should().Be(new BigInteger(0)); + ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[2])[0]).GetByteArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + ((VM.Types.Struct)((VM.Types.Array)result)[2])[1].GetBigInteger().Should().Be(new BigInteger(0)); + ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[3])[0]).GetByteArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + ((VM.Types.Struct)((VM.Types.Array)result)[3])[1].GetBigInteger().Should().Be(new BigInteger(0)); + ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[4])[0]).GetByteArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); + ((VM.Types.Struct)((VM.Types.Array)result)[4])[1].GetBigInteger().Should().Be(new BigInteger(0)); + ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[5])[0]).GetByteArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + ((VM.Types.Struct)((VM.Types.Array)result)[5])[1].GetBigInteger().Should().Be(new BigInteger(0)); + ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[6])[0]).GetByteArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + ((VM.Types.Struct)((VM.Types.Array)result)[6])[1].GetBigInteger().Should().Be(new BigInteger(0)); + } + } + + [TestMethod] + public void TestGetRegisteredValidators2() + { + Snapshot snapshot = Store.GetSnapshot().Clone(); + var result = NativeContract.NEO.GetRegisteredValidators(snapshot).ToArray(); + result.Length.Should().Be(7); + result[0].PublicKey.ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + result[0].Votes.Should().Be(new BigInteger(0)); + result[1].PublicKey.ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + result[1].Votes.Should().Be(new BigInteger(0)); + result[2].PublicKey.ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + result[2].Votes.Should().Be(new BigInteger(0)); + result[3].PublicKey.ToArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + result[3].Votes.Should().Be(new BigInteger(0)); + result[4].PublicKey.ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); + result[4].Votes.Should().Be(new BigInteger(0)); + result[5].PublicKey.ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + result[5].Votes.Should().Be(new BigInteger(0)); + result[6].PublicKey.ToArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + result[6].Votes.Should().Be(new BigInteger(0)); + + StorageKey key = NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G); + snapshot.Storages.Add(key, new StorageItem + { + Value = new ValidatorState().ToByteArray() + }); + NativeContract.NEO.GetRegisteredValidators(snapshot).ToArray().Length.Should().Be(8); + } + + [TestMethod] + public void TestGetValidators1() + { + using (ApplicationEngine engine = NativeContract.NEO.TestCall("getValidators")) + { + var result = engine.ResultStack.Peek(); + result.GetType().Should().Be(typeof(VM.Types.Array)); + ((VM.Types.Array)result).Count.Should().Be(7); + ((VM.Types.ByteArray)((VM.Types.Array)result)[0]).GetByteArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[1]).GetByteArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[2]).GetByteArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[3]).GetByteArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[4]).GetByteArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[5]).GetByteArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + ((VM.Types.ByteArray)((VM.Types.Array)result)[6]).GetByteArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + } + } + + [TestMethod] + public void TestGetValidators2() + { + Snapshot snapshot = Store.GetSnapshot().Clone(); + var result = NativeContract.NEO.GetValidators(snapshot); + result[0].ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + result[1].ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); + result[2].ToArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + result[3].ToArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + result[4].ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + result[5].ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + result[6].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + + StorageKey key = CreateStorageKey(15); + ValidatorsCountState state = new ValidatorsCountState(); + for (int i = 0; i < 100; i++) + { + state.Votes[i] = new BigInteger(i + 1); + } + snapshot.Storages.Add(key, new StorageItem() + { + Value = state.ToByteArray() + }); + NativeContract.NEO.GetValidators(snapshot).ToArray().Length.Should().Be(7); + } + + [TestMethod] + public void TestInitialize() + { + Snapshot snapshot = Store.GetSnapshot().Clone(); + var engine = new ApplicationEngine(TriggerType.System, null, snapshot, 0, true); + Action action = () => NativeContract.NEO.Initialize(engine); + action.Should().Throw(); + + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + NativeContract.NEO.Initialize(engine).Should().BeFalse(); + + snapshot.Storages.Delete(CreateStorageKey(11)); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + NativeContract.NEO.Initialize(engine).Should().BeTrue(); + } + + [TestMethod] + public void TestOnBalanceChanging() + { + var ret = Transfer4TesingOnBalanceChanging(new BigInteger(0), false); + ret.Result.Should().BeTrue(); + ret.State.Should().BeTrue(); + + ret = Transfer4TesingOnBalanceChanging(new BigInteger(1), false); + ret.Result.Should().BeTrue(); + ret.State.Should().BeTrue(); + + ret = Transfer4TesingOnBalanceChanging(new BigInteger(1), true); + ret.Result.Should().BeTrue(); + ret.State.Should().BeTrue(); + } + + [TestMethod] + public void TestTotalSupply() + { + Snapshot snapshot = Store.GetSnapshot().Clone(); + NativeContract.NEO.TotalSupply(snapshot).Should().Be(new BigInteger(100000000)); + } + + [TestMethod] + public void TestUnclaimedGas() + { + Snapshot snapshot = Store.GetSnapshot().Clone(); + NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); + snapshot.Storages.Add(CreateStorageKey(20, UInt160.Zero.ToArray()), new StorageItem + { + Value = new AccountState().ToByteArray() + }); + NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); + } + + [TestMethod] + public void TestVote() + { + Snapshot snapshot = Store.GetSnapshot().Clone(); + 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(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, false); + ret.State.Should().BeTrue(); + ret.Result.Should().BeFalse(); + + ret = Check_Vote(snapshot, account.ToArray(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, true); + ret.State.Should().BeTrue(); + ret.Result.Should().BeFalse(); + + snapshot.Storages.Add(keyAccount, new StorageItem + { + Value = new AccountState().ToByteArray() + }); + ret = Check_Vote(snapshot, account.ToArray(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, true); + ret.State.Should().BeTrue(); + ret.Result.Should().BeTrue(); + + snapshot.Storages.Delete(keyAccount); + snapshot.Storages.GetAndChange(keyAccount, () => new StorageItem + { + Value = new AccountState() + { + Votes = new ECPoint[] { ECCurve.Secp256r1.G } + }.ToByteArray() + }); + snapshot.Storages.Add(keyValidator, new StorageItem + { + Value = new ValidatorState().ToByteArray() + }); + ret = Check_Vote(snapshot, account.ToArray(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, true); + ret.State.Should().BeTrue(); + ret.Result.Should().BeTrue(); + } + + [TestMethod] + public void TestValidatorsCountState_FromByteArray() + { + ValidatorsCountState input = new ValidatorsCountState { Votes = new BigInteger[] { new BigInteger(1000) } }; + ValidatorsCountState output = ValidatorsCountState.FromByteArray(input.ToByteArray()); + output.Should().BeEquivalentTo(input); + } + + [TestMethod] + public void TestValidatorState_FromByteArray() + { + ValidatorState input = new ValidatorState { Votes = new BigInteger(1000) }; + ValidatorState output = ValidatorState.FromByteArray(input.ToByteArray()); + output.Should().BeEquivalentTo(input); + } + + [TestMethod] + public void TestValidatorState_ToByteArray() + { + ValidatorState input = new ValidatorState { Votes = new BigInteger(1000) }; + input.ToByteArray().ToHexString().Should().Be("e803"); + } + + internal (bool State, bool Result) Transfer4TesingOnBalanceChanging(BigInteger amount, bool addVotes) + { + Snapshot snapshot = Store.GetSnapshot().Clone(); + var engine = new ApplicationEngine(TriggerType.Application, Blockchain.GenesisBlock, snapshot, 0, true); + 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 + { + Value = new AccountState() + { + Votes = new ECPoint[] { ECCurve.Secp256r1.G }, + Balance = new BigInteger(1000) + }.ToByteArray() + }); + snapshot.Storages.Add(NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G), new StorageItem + { + Value = new ValidatorState().ToByteArray() + }); + + ValidatorsCountState state = new ValidatorsCountState(); + for (int i = 0; i < 100; i++) + { + state.Votes[i] = new BigInteger(i + 1); + } + snapshot.Storages.Add(CreateStorageKey(15), new StorageItem() + { + Value = state.ToByteArray() + }); + } + else + { + snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem + { + Value = new AccountState() + { + Balance = new BigInteger(1000) + }.ToByteArray() + }); + } + + sb.EmitAppCall(NativeContract.NEO.Hash, "transfer", from, UInt160.Zero, amount); + engine.LoadScript(sb.ToArray()); + engine.Execute(); + var result = engine.ResultStack.Peek(); + result.GetType().Should().Be(typeof(VM.Types.Boolean)); + return (true, (result as VM.Types.Boolean).GetBoolean()); + } + internal static (bool State, bool Result) Check_Vote(Snapshot snapshot, byte[] account, byte[][] pubkeys, bool signAccount) { var engine = new ApplicationEngine(TriggerType.Application, @@ -370,5 +700,18 @@ internal static void CheckBalance(byte[] account, DataCache(); + var myDataCache = new TestDataCache(); + StorageItem item = new StorageItem + { + Value = new byte[] { 0x01 } + }; + var key = CreateStorageKey(Prefix_TotalSupply); + + var ServiceHash = "test".ToInteropMethodHash(); + byte[] script = null; + using (ScriptBuilder sb = new ScriptBuilder()) + { + sb.EmitSysCall(ServiceHash); + script = sb.ToArray(); + } + var Hash = script.ToScriptHash(); + key.ScriptHash = Hash; + + myDataCache.Add(key, item); + mockSnapshot.SetupGet(p => p.Storages).Returns(myDataCache); + TestNep5Token test = new TestNep5Token(); + ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + StackItem stackItem = test.TotalSupply(ae, null); + stackItem.GetBigInteger().Should().Be(1); + } + + [TestMethod] + public void TestTotalSupplyDecimal() + { + var mockSnapshot = new Mock(); + var myDataCache = new TestDataCache(); + + TestNep5Token test = new TestNep5Token(); + BigInteger totalSupply = 100_000_000; + totalSupply *= test.Factor; + + byte[] value = totalSupply.ToByteArray(); + StorageItem item = new StorageItem + { + Value = value + }; + var key = CreateStorageKey(Prefix_TotalSupply); + + var ServiceHash = "test".ToInteropMethodHash(); + byte[] script = null; + using (ScriptBuilder sb = new ScriptBuilder()) + { + sb.EmitSysCall(ServiceHash); + script = sb.ToArray(); + } + var Hash = script.ToScriptHash(); + key.ScriptHash = Hash; + + myDataCache.Add(key, item); + mockSnapshot.SetupGet(p => p.Storages).Returns(myDataCache); + + ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + StackItem stackItem = test.TotalSupply(ae, null); + stackItem.GetBigInteger().Should().Be(10_000_000_000_000_000); + } + + public StorageKey CreateStorageKey(byte prefix, byte[] key = null) + { + StorageKey storageKey = new StorageKey + { + ScriptHash = null, + Key = new byte[sizeof(byte) + (key?.Length ?? 0)] + }; + storageKey.Key[0] = prefix; + if (key != null) + Buffer.BlockCopy(key, 0, storageKey.Key, 1, key.Length); + return storageKey; + } + } + + public class TestNep5Token : Nep5Token + { + public override string Name => throw new NotImplementedException(); + + public override string Symbol => throw new NotImplementedException(); + + public override byte Decimals => 8; + + public override string ServiceName => "test"; + + public new StackItem TotalSupply(ApplicationEngine engine, VM.Types.Array args) + { + return base.TotalSupply(engine, args); + } + } +} diff --git a/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs new file mode 100644 index 0000000000..61e93de6ad --- /dev/null +++ b/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -0,0 +1,100 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.VM.Types; +using System; +using VMArray = Neo.VM.Types.Array; + +namespace Neo.UnitTests.SmartContract.Native +{ + [TestClass] + public class UT_NativeContract + { + Store Store; + + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + Store = TestBlockchain.GetStore(); + } + + [TestMethod] + public void TestInitialize() + { + ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0); + TestNativeContract pc = new TestNativeContract(); + pc.Initialize(ae).Should().BeTrue(); + + ae = new ApplicationEngine(TriggerType.System, null, null, 0); + Action action = () => pc.Initialize(ae); + action.Should().Throw(); + } + + [TestMethod] + public void TestInvoke() + { + ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, Store.GetSnapshot(), 0); + TestNativeContract testNativeContract = new TestNativeContract(); + + ScriptBuilder sb1 = new ScriptBuilder(); + + sb1.EmitSysCall("null".ToInteropMethodHash()); + engine1.LoadScript(sb1.ToArray()); + testNativeContract.Invoke(engine1).Should().BeFalse(); + + ApplicationEngine engine2 = new ApplicationEngine(TriggerType.Application, null, Store.GetSnapshot(), 0); + + ScriptBuilder sb2 = new ScriptBuilder(); + sb2.EmitSysCall("test".ToInteropMethodHash()); + engine2.LoadScript(sb2.ToArray()); + + ByteArray method1 = new ByteArray(System.Text.Encoding.Default.GetBytes("wrongMethod")); + VMArray args1 = new VMArray(); + engine2.CurrentContext.EvaluationStack.Push(args1); + engine2.CurrentContext.EvaluationStack.Push(method1); + testNativeContract.Invoke(engine2).Should().BeFalse(); + + ByteArray method2 = new ByteArray(System.Text.Encoding.Default.GetBytes("onPersist")); + VMArray args2 = new VMArray(); + engine2.CurrentContext.EvaluationStack.Push(args2); + engine2.CurrentContext.EvaluationStack.Push(method2); + testNativeContract.Invoke(engine2).Should().BeTrue(); + } + + [TestMethod] + public void TestOnPersistWithArgs() + { + ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, Store.GetSnapshot(), 0); + TestNativeContract testNativeContract = new TestNativeContract(); + VMArray args = new VMArray(); + + VM.Types.Boolean result1 = new VM.Types.Boolean(false); + testNativeContract.TestOnPersist(engine1, args).Should().Be(result1); + + ApplicationEngine engine2 = new ApplicationEngine(TriggerType.System, null, Store.GetSnapshot(), 0); + VM.Types.Boolean result2 = new VM.Types.Boolean(true); + testNativeContract.TestOnPersist(engine2, args).Should().Be(result2); + } + + [TestMethod] + public void TestTestCall() + { + TestNativeContract testNativeContract = new TestNativeContract(); + ApplicationEngine engine = testNativeContract.TestCall("System.Blockchain.GetHeight", 0); + engine.ResultStack.Should().BeEmpty(); + } + } + + public class TestNativeContract : NativeContract + { + public override string ServiceName => "test"; + public StackItem TestOnPersist(ApplicationEngine engine, VMArray args) + { + return OnPersist(engine, args); + } + } +} diff --git a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 940230660a..e0f87182a6 100644 --- a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -1,5 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -225,5 +227,28 @@ public void Check_Block_UnblockAccount() ret.Should().BeOfType(); ((VM.Types.Array)ret).Count.Should().Be(0); } + + [TestMethod] + public void TestCheckPolicy() + { + Transaction tx = Blockchain.GenesisBlock.Transactions[0]; + Snapshot snapshot = Store.GetSnapshot().Clone(); + + StorageKey storageKey = new StorageKey + { + ScriptHash = NativeContract.Policy.Hash, + Key = new byte[sizeof(byte)] + }; + storageKey.Key[0] = 15; + snapshot.Storages.Add(storageKey, new StorageItem + { + Value = new UInt160[] { tx.Sender }.ToByteArray(), + }); + + NativeContract.Policy.CheckPolicy(tx, snapshot).Should().BeFalse(); + + snapshot = Store.GetSnapshot().Clone(); + NativeContract.Policy.CheckPolicy(tx, snapshot).Should().BeTrue(); + } } } diff --git a/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs new file mode 100644 index 0000000000..fae3938a22 --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -0,0 +1,185 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.IO; +using Neo.IO.Caching; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.UnitTests.Ledger; +using Neo.VM; +using System; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_ApplicationEngine + { + private string message = null; + private StackItem item = null; + private Store Store; + + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + Store = TestBlockchain.GetStore(); + } + + [TestMethod] + public void TestLog() + { + var snapshot = Store.GetSnapshot().Clone(); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + ApplicationEngine.Log += Test_Log1; + string logMessage = "TestMessage"; + + engine.SendLog(UInt160.Zero, logMessage); + message.Should().Be(logMessage); + + ApplicationEngine.Log += Test_Log2; + engine.SendLog(UInt160.Zero, logMessage); + message.Should().Be(null); + + message = logMessage; + ApplicationEngine.Log -= Test_Log1; + engine.SendLog(UInt160.Zero, logMessage); + message.Should().Be(null); + + ApplicationEngine.Log -= Test_Log2; + engine.SendLog(UInt160.Zero, logMessage); + message.Should().Be(null); + } + + [TestMethod] + public void TestNotify() + { + var snapshot = Store.GetSnapshot().Clone(); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + ApplicationEngine.Notify += Test_Notify1; + StackItem notifyItem = "TestItem"; + + engine.SendNotification(UInt160.Zero, notifyItem); + item.Should().Be(notifyItem); + + ApplicationEngine.Notify += Test_Notify2; + engine.SendNotification(UInt160.Zero, notifyItem); + item.Should().Be(null); + + item = notifyItem; + ApplicationEngine.Notify -= Test_Notify1; + engine.SendNotification(UInt160.Zero, notifyItem); + item.Should().Be(null); + + ApplicationEngine.Notify -= Test_Notify2; + engine.SendNotification(UInt160.Zero, notifyItem); + item.Should().Be(null); + } + + [TestMethod] + public void TestDisposable() + { + var snapshot = Store.GetSnapshot().Clone(); + var replica = snapshot.Clone(); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.AddDisposable(replica).Should().Be(replica); + Action action = () => engine.Dispose(); + action.Should().NotThrow(); + } + + private void Test_Log1(object sender, LogEventArgs e) + { + message = e.Message; + } + + private void Test_Log2(object sender, LogEventArgs e) + { + message = null; + } + + private void Test_Notify1(object sender, NotifyEventArgs e) + { + item = e.State; + } + + private void Test_Notify2(object sender, NotifyEventArgs e) + { + item = null; + } + + [TestMethod] + public void TestCreateDummyBlock() + { + var mockSnapshot = new Mock(); + UInt256 currentBlockHash = UInt256.Parse("0x0000000000000000000000000000000000000000000000000000000000000000"); + TrimmedBlock block = new TrimmedBlock(); + var cache = new TestDataCache(); + cache.Add(currentBlockHash, block); + mockSnapshot.SetupGet(p => p.Blocks).Returns(cache); + TestMetaDataCache testCache = new TestMetaDataCache(); + mockSnapshot.SetupGet(p => p.BlockHashIndex).Returns(testCache); + byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; + ApplicationEngine.Run(SyscallSystemRuntimeCheckWitnessHash, mockSnapshot.Object); + mockSnapshot.Object.PersistingBlock.Version.Should().Be(0); + mockSnapshot.Object.PersistingBlock.PrevHash.Should().Be(currentBlockHash); + mockSnapshot.Object.PersistingBlock.MerkleRoot.Should().Be(new UInt256()); + } + + [TestMethod] + public void TestOnSysCall() + { + InteropDescriptor descriptor = new InteropDescriptor("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); + TestApplicationEngine engine = new TestApplicationEngine(TriggerType.Application, null, null, 0); + byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; + engine.LoadScript(SyscallSystemRuntimeCheckWitnessHash); + engine.GetOnSysCall(descriptor.Hash).Should().BeFalse(); + + var mockSnapshot = new Mock(); + TestMetaDataCache testCache = new TestMetaDataCache(); + mockSnapshot.SetupGet(p => p.BlockHashIndex).Returns(testCache); + engine = new TestApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0, true); + engine.LoadScript(SyscallSystemRuntimeCheckWitnessHash); + engine.GetOnSysCall(descriptor.Hash).Should().BeTrue(); + } + + private static bool Blockchain_GetHeight(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.Height); + return true; + } + } + + public class TestApplicationEngine : ApplicationEngine + { + public TestApplicationEngine(TriggerType trigger, IVerifiable container, Snapshot snapshot, long gas, bool testMode = false) : base(trigger, container, snapshot, gas, testMode) + { + } + + public bool GetOnSysCall(uint method) + { + return OnSysCall(method); + } + } + + public class TestMetaDataCache : MetaDataCache where T : class, ICloneable, ISerializable, new() + { + public TestMetaDataCache() + : base(null) + { + } + + protected override void AddInternal(T item) + { + } + + protected override T TryGetInternal() + { + return new T(); + } + + protected override void UpdateInternal(T item) + { + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs b/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs new file mode 100644 index 0000000000..e7ed55ac42 --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs @@ -0,0 +1,43 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.VM.Types; +using System; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_ContainerPlaceholder + { + [TestMethod] + public void TestGenerator() + { + ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(); + Assert.IsNotNull(containerPlaceholder); + } + + [TestMethod] + public void TestEquals() + { + ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(); + Action action = () => containerPlaceholder.Equals(new Integer(0)); + action.Should().Throw(); + } + + [TestMethod] + public void TestGetBoolean() + { + ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(); + Action action = () => containerPlaceholder.GetBoolean(); + action.Should().Throw(); + } + + [TestMethod] + public void TestGetByteArray() + { + ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(); + Action action = () => containerPlaceholder.GetByteArray(); + action.Should().Throw(); + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_Contract.cs b/neo.UnitTests/SmartContract/UT_Contract.cs new file mode 100644 index 0000000000..458128f569 --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_Contract.cs @@ -0,0 +1,156 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.Wallets; +using System; +using System.Linq; +using System.Security.Cryptography; +using System.Text; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_Contract + { + [TestMethod] + public void TestGetAddress() + { + byte[] privateKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(privateKey); + KeyPair key = new KeyPair(privateKey); + Contract contract = Contract.CreateSignatureContract(key.PublicKey); + byte[] script = contract.Script; + byte[] expectedArray = new byte[39]; + expectedArray[0] = 0x21; + Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); + expectedArray[34] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckSig), 0, expectedArray, 35, 4); + Assert.AreEqual(expectedArray.ToScriptHash().ToAddress(), contract.Address); + } + + [TestMethod] + public void TestGetScriptHash() + { + byte[] privateKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(privateKey); + KeyPair key = new KeyPair(privateKey); + Contract contract = Contract.CreateSignatureContract(key.PublicKey); + byte[] script = contract.Script; + byte[] expectedArray = new byte[39]; + expectedArray[0] = 0x21; + Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); + expectedArray[34] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckSig), 0, expectedArray, 35, 4); + Assert.AreEqual(expectedArray.ToScriptHash(), contract.ScriptHash); + } + + [TestMethod] + public void TestCreate() + { + byte[] script = new byte[32]; + ContractParameterType[] parameterList = new ContractParameterType[] { ContractParameterType.Signature }; + Contract contract = Contract.Create(parameterList, script); + Assert.AreEqual(contract.Script, script); + Assert.AreEqual(1, contract.ParameterList.Length); + Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); + } + + [TestMethod] + public void TestCreateMultiSigContract() + { + byte[] privateKey1 = new byte[32]; + RandomNumberGenerator rng1 = RandomNumberGenerator.Create(); + rng1.GetBytes(privateKey1); + KeyPair key1 = new KeyPair(privateKey1); + byte[] privateKey2 = new byte[32]; + RandomNumberGenerator rng2 = RandomNumberGenerator.Create(); + rng2.GetBytes(privateKey2); + KeyPair key2 = new KeyPair(privateKey2); + Neo.Cryptography.ECC.ECPoint[] publicKeys = new Neo.Cryptography.ECC.ECPoint[2]; + publicKeys[0] = key1.PublicKey; + publicKeys[1] = key2.PublicKey; + publicKeys = publicKeys.OrderBy(p => p).ToArray(); + Contract contract = Contract.CreateMultiSigContract(2, publicKeys); + byte[] expectedArray = new byte[75]; + expectedArray[0] = 0x52; + expectedArray[1] = 0x21; + Array.Copy(publicKeys[0].EncodePoint(true), 0, expectedArray, 2, 33); + expectedArray[35] = 0x21; + Array.Copy(publicKeys[1].EncodePoint(true), 0, expectedArray, 36, 33); + expectedArray[69] = 0x52; + expectedArray[70] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckMultiSig), 0, expectedArray, 71, 4); + Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(contract.Script)); + Assert.AreEqual(2, contract.ParameterList.Length); + Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); + Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[1]); + } + + [TestMethod] + public void TestCreateMultiSigRedeemScript() + { + byte[] privateKey1 = new byte[32]; + RandomNumberGenerator rng1 = RandomNumberGenerator.Create(); + rng1.GetBytes(privateKey1); + KeyPair key1 = new KeyPair(privateKey1); + byte[] privateKey2 = new byte[32]; + RandomNumberGenerator rng2 = RandomNumberGenerator.Create(); + rng2.GetBytes(privateKey2); + KeyPair key2 = new KeyPair(privateKey2); + Neo.Cryptography.ECC.ECPoint[] publicKeys = new Neo.Cryptography.ECC.ECPoint[2]; + publicKeys[0] = key1.PublicKey; + publicKeys[1] = key2.PublicKey; + publicKeys = publicKeys.OrderBy(p => p).ToArray(); + Action action = () => Contract.CreateMultiSigRedeemScript(0, publicKeys); + action.Should().Throw(); + byte[] script = Contract.CreateMultiSigRedeemScript(2, publicKeys); + byte[] expectedArray = new byte[75]; + expectedArray[0] = 0x52; + expectedArray[1] = 0x21; + Array.Copy(publicKeys[0].EncodePoint(true), 0, expectedArray, 2, 33); + expectedArray[35] = 0x21; + Array.Copy(publicKeys[1].EncodePoint(true), 0, expectedArray, 36, 33); + expectedArray[69] = 0x52; + expectedArray[70] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckMultiSig), 0, expectedArray, 71, 4); + Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(script)); + } + + [TestMethod] + public void TestCreateSignatureContract() + { + byte[] privateKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(privateKey); + KeyPair key = new KeyPair(privateKey); + Contract contract = Contract.CreateSignatureContract(key.PublicKey); + byte[] script = contract.Script; + byte[] expectedArray = new byte[39]; + expectedArray[0] = 0x21; + Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); + expectedArray[34] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckSig), 0, expectedArray, 35, 4); + Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(script)); + Assert.AreEqual(1, contract.ParameterList.Length); + Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); + } + + [TestMethod] + public void TestCreateSignatureRedeemScript() + { + byte[] privateKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(privateKey); + KeyPair key = new KeyPair(privateKey); + byte[] script = Contract.CreateSignatureRedeemScript(key.PublicKey); + byte[] expectedArray = new byte[39]; + expectedArray[0] = 0x21; + Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); + expectedArray[34] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckSig), 0, expectedArray, 35, 4); + Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(script)); + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_ContractParameter.cs b/neo.UnitTests/SmartContract/UT_ContractParameter.cs new file mode 100644 index 0000000000..124f852e5e --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_ContractParameter.cs @@ -0,0 +1,201 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; +using Neo.IO.Json; +using Neo.SmartContract; +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_ContractParameter + { + [TestMethod] + public void TestGenerator1() + { + ContractParameter contractParameter = new ContractParameter(); + Assert.IsNotNull(contractParameter); + } + + [TestMethod] + public void TestGenerator2() + { + ContractParameter contractParameter1 = new ContractParameter(ContractParameterType.Signature); + byte[] expectedArray1 = new byte[64]; + Assert.IsNotNull(contractParameter1); + Assert.AreEqual(Encoding.Default.GetString(expectedArray1), Encoding.Default.GetString((byte[])contractParameter1.Value)); + + ContractParameter contractParameter2 = new ContractParameter(ContractParameterType.Boolean); + Assert.IsNotNull(contractParameter2); + Assert.AreEqual(false, contractParameter2.Value); + + ContractParameter contractParameter3 = new ContractParameter(ContractParameterType.Integer); + Assert.IsNotNull(contractParameter3); + Assert.AreEqual(0, contractParameter3.Value); + + ContractParameter contractParameter4 = new ContractParameter(ContractParameterType.Hash160); + Assert.IsNotNull(contractParameter4); + Assert.AreEqual(new UInt160(), contractParameter4.Value); + + ContractParameter contractParameter5 = new ContractParameter(ContractParameterType.Hash256); + Assert.IsNotNull(contractParameter5); + Assert.AreEqual(new UInt256(), contractParameter5.Value); + + ContractParameter contractParameter6 = new ContractParameter(ContractParameterType.ByteArray); + byte[] expectedArray6 = new byte[0]; + Assert.IsNotNull(contractParameter6); + Assert.AreEqual(Encoding.Default.GetString(expectedArray6), Encoding.Default.GetString((byte[])contractParameter6.Value)); + + ContractParameter contractParameter7 = new ContractParameter(ContractParameterType.PublicKey); + Assert.IsNotNull(contractParameter7); + Assert.AreEqual(ECCurve.Secp256r1.G, contractParameter7.Value); + + ContractParameter contractParameter8 = new ContractParameter(ContractParameterType.String); + Assert.IsNotNull(contractParameter8); + Assert.AreEqual("", contractParameter8.Value); + + ContractParameter contractParameter9 = new ContractParameter(ContractParameterType.Array); + Assert.IsNotNull(contractParameter9); + Assert.AreEqual(0, ((List)contractParameter9.Value).Count); + + ContractParameter contractParameter10 = new ContractParameter(ContractParameterType.Map); + Assert.IsNotNull(contractParameter10); + Assert.AreEqual(0, ((List>)contractParameter10.Value).Count); + + Action action = () => new ContractParameter(ContractParameterType.Void); + action.Should().Throw(); + } + + [TestMethod] + public void TestFromAndToJson() + { + ContractParameter contractParameter1 = new ContractParameter(ContractParameterType.Signature); + JObject jobject1 = contractParameter1.ToJson(); + Assert.AreEqual(jobject1.ToString(), ContractParameter.FromJson(jobject1).ToJson().ToString()); + + ContractParameter contractParameter2 = new ContractParameter(ContractParameterType.Boolean); + JObject jobject2 = contractParameter2.ToJson(); + Assert.AreEqual(jobject2.ToString(), ContractParameter.FromJson(jobject2).ToJson().ToString()); + + ContractParameter contractParameter3 = new ContractParameter(ContractParameterType.Integer); + JObject jobject3 = contractParameter3.ToJson(); + Assert.AreEqual(jobject3.ToString(), ContractParameter.FromJson(jobject3).ToJson().ToString()); + + ContractParameter contractParameter4 = new ContractParameter(ContractParameterType.Hash160); + JObject jobject4 = contractParameter4.ToJson(); + Assert.AreEqual(jobject4.ToString(), ContractParameter.FromJson(jobject4).ToJson().ToString()); + + ContractParameter contractParameter5 = new ContractParameter(ContractParameterType.Hash256); + JObject jobject5 = contractParameter5.ToJson(); + Assert.AreEqual(jobject5.ToString(), ContractParameter.FromJson(jobject5).ToJson().ToString()); + + ContractParameter contractParameter6 = new ContractParameter(ContractParameterType.ByteArray); + JObject jobject6 = contractParameter6.ToJson(); + Assert.AreEqual(jobject6.ToString(), ContractParameter.FromJson(jobject6).ToJson().ToString()); + + ContractParameter contractParameter7 = new ContractParameter(ContractParameterType.PublicKey); + JObject jobject7 = contractParameter7.ToJson(); + Assert.AreEqual(jobject7.ToString(), ContractParameter.FromJson(jobject7).ToJson().ToString()); + + ContractParameter contractParameter8 = new ContractParameter(ContractParameterType.String); + JObject jobject8 = contractParameter8.ToJson(); + Assert.AreEqual(jobject8.ToString(), ContractParameter.FromJson(jobject8).ToJson().ToString()); + + ContractParameter contractParameter9 = new ContractParameter(ContractParameterType.Array); + JObject jobject9 = contractParameter9.ToJson(); + Assert.AreEqual(jobject9.ToString(), ContractParameter.FromJson(jobject9).ToJson().ToString()); + + ContractParameter contractParameter10 = new ContractParameter(ContractParameterType.Map); + JObject jobject10 = contractParameter10.ToJson(); + Assert.AreEqual(jobject10.ToString(), ContractParameter.FromJson(jobject10).ToJson().ToString()); + + ContractParameter contractParameter11 = new ContractParameter(ContractParameterType.String); + JObject jobject11 = contractParameter11.ToJson(); + jobject11["type"] = "Void"; + Action action = () => ContractParameter.FromJson(jobject11); + action.Should().Throw(); + } + + [TestMethod] + public void TestSetValue() + { + ContractParameter contractParameter1 = new ContractParameter(ContractParameterType.Signature); + byte[] expectedArray1 = new byte[64]; + contractParameter1.SetValue(new byte[64].ToHexString()); + Assert.AreEqual(Encoding.Default.GetString(expectedArray1), Encoding.Default.GetString((byte[])contractParameter1.Value)); + Action action1 = () => contractParameter1.SetValue(new byte[50].ToHexString()); + action1.Should().Throw(); + + ContractParameter contractParameter2 = new ContractParameter(ContractParameterType.Boolean); + contractParameter2.SetValue("true"); + Assert.AreEqual(true, contractParameter2.Value); + + ContractParameter contractParameter3 = new ContractParameter(ContractParameterType.Integer); + contractParameter3.SetValue("11"); + Assert.AreEqual(new BigInteger(11), contractParameter3.Value); + + ContractParameter contractParameter4 = new ContractParameter(ContractParameterType.Hash160); + contractParameter4.SetValue("0x0000000000000000000000000000000000000001"); + Assert.AreEqual(UInt160.Parse("0x0000000000000000000000000000000000000001"), contractParameter4.Value); + + ContractParameter contractParameter5 = new ContractParameter(ContractParameterType.Hash256); + contractParameter5.SetValue("0x0000000000000000000000000000000000000000000000000000000000000000"); + Assert.AreEqual(UInt256.Parse("0x0000000000000000000000000000000000000000000000000000000000000000"), contractParameter5.Value); + + ContractParameter contractParameter6 = new ContractParameter(ContractParameterType.ByteArray); + contractParameter6.SetValue("2222"); + byte[] expectedArray6 = new byte[2]; + expectedArray6[0] = 0x22; + expectedArray6[1] = 0x22; + Assert.AreEqual(Encoding.Default.GetString(expectedArray6), Encoding.Default.GetString((byte[])contractParameter6.Value)); + + ContractParameter contractParameter7 = new ContractParameter(ContractParameterType.PublicKey); + Random random7 = new Random(); + byte[] privateKey7 = new byte[32]; + for (int j = 0; j < privateKey7.Length; j++) + privateKey7[j] = (byte)random7.Next(256); + ECPoint publicKey7 = ECCurve.Secp256r1.G * privateKey7; + contractParameter7.SetValue(publicKey7.ToString()); + Assert.AreEqual(true, publicKey7.Equals(contractParameter7.Value)); + + ContractParameter contractParameter8 = new ContractParameter(ContractParameterType.String); + contractParameter8.SetValue("AAA"); + Assert.AreEqual("AAA", contractParameter8.Value); + + ContractParameter contractParameter9 = new ContractParameter(ContractParameterType.Array); + Action action9 = () => contractParameter9.SetValue("AAA"); + action9.Should().Throw(); + } + + [TestMethod] + public void TestToString() + { + ContractParameter contractParameter1 = new ContractParameter(); + Assert.AreEqual("(null)", contractParameter1.ToString()); + + ContractParameter contractParameter2 = new ContractParameter(ContractParameterType.ByteArray); + contractParameter2.Value = new byte[1]; + Assert.AreEqual("00", contractParameter2.ToString()); + + ContractParameter contractParameter3 = new ContractParameter(ContractParameterType.Array); + Assert.AreEqual("[]", contractParameter3.ToString()); + ContractParameter internalContractParameter3 = new ContractParameter(ContractParameterType.Boolean); + ((IList)contractParameter3.Value).Add(internalContractParameter3); + Assert.AreEqual("[False]", contractParameter3.ToString()); + + ContractParameter contractParameter4 = new ContractParameter(ContractParameterType.Map); + Assert.AreEqual("[]", contractParameter4.ToString()); + ContractParameter internalContractParameter4 = new ContractParameter(ContractParameterType.Boolean); + ((IList>)contractParameter4.Value).Add(new KeyValuePair( + internalContractParameter4, internalContractParameter4 + )); + Assert.AreEqual("[{False,False}]", contractParameter4.ToString()); + + ContractParameter contractParameter5 = new ContractParameter(ContractParameterType.String); + Assert.AreEqual("", contractParameter5.ToString()); + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs new file mode 100644 index 0000000000..0b06260b24 --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -0,0 +1,161 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.Wallets; +using System; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_ContractParameterContext + { + private static Contract contract; + private static KeyPair key; + + [ClassInitialize] + public static void ClassSetUp(TestContext context) + { + if (contract == null) + { + byte[] privateKey = 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 }; + key = new KeyPair(privateKey); + contract = Contract.CreateSignatureContract(key.PublicKey); + } + TestBlockchain.InitializeMockNeoSystem(); + } + + [TestMethod] + public void TestGetComplete() + { + Transaction tx = TestUtils.GetTransaction(); + tx.Sender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + var context = new ContractParametersContext(tx); + context.Completed.Should().BeFalse(); + } + + [TestMethod] + public void TestToString() + { + Transaction tx = TestUtils.GetTransaction(); + tx.Sender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + 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\":\"0000000000f277f1144035a43897f9fa115258eac015adcabe000000000000000000000000000000000000000000000100\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1668747476aa\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}"); + } + + [TestMethod] + public void TestParse() + { + var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"0000000000f277f1144035a43897f9fa115258eac015adcabe000000000000000000000000000000000000000000000100\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1668747476aa\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}"); + ret.ScriptHashes[0].ToString().Should().Be("0xbecaad15c0ea585211faf99738a4354014f177f2"); + ((Transaction)ret.Verifiable).Script.ToHexString().Should().Be(new byte[1].ToHexString()); + } + + [TestMethod] + public void TestFromJson() + { + Action action = () => ContractParametersContext.Parse("{\"type\":\"wrongType\",\"hex\":\"0000000000f277f1144035a43897f9fa115258eac015adcabe0000000000000000000000000000000000000000000100\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1668747476aa\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}"); + action.Should().Throw(); + } + + [TestMethod] + public void TestAdd() + { + Transaction tx = TestUtils.GetTransaction(); + var context1 = new ContractParametersContext(tx); + context1.Add(contract, 0, new byte[] { 0x01 }).Should().BeFalse(); + + tx.Sender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + var context2 = new ContractParametersContext(tx); + context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); + //test repeatlly createItem + context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); + } + + [TestMethod] + public void TestGetParameter() + { + Transaction tx = TestUtils.GetTransaction(); + tx.Sender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + var context = new ContractParametersContext(tx); + context.GetParameter(tx.Sender, 0).Should().BeNull(); + + context.Add(contract, 0, new byte[] { 0x01 }); + var ret = context.GetParameter(tx.Sender, 0); + ((byte[])ret.Value).ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + } + + [TestMethod] + public void TestGetWitnesses() + { + Transaction tx = TestUtils.GetTransaction(); + tx.Sender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + var context = new ContractParametersContext(tx); + context.Add(contract, 0, new byte[] { 0x01 }); + Witness[] witnesses = context.GetWitnesses(); + witnesses.Length.Should().Be(1); + witnesses[0].InvocationScript.ToHexString().Should().Be(new byte[] { 0x01, 0x01 }.ToHexString()); + witnesses[0].VerificationScript.ToHexString().Should().Be(contract.Script.ToHexString()); + } + + [TestMethod] + public void TestAddSignature() + { + Transaction tx = TestUtils.GetTransaction(); + var singleSender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + tx.Sender = singleSender; + + //singleSign + + var context = new ContractParametersContext(tx); + context.AddSignature(contract, key.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); + + var contract1 = Contract.CreateSignatureContract(key.PublicKey); + contract1.ParameterList = new ContractParameterType[0]; + context = new ContractParametersContext(tx); + context.AddSignature(contract1, key.PublicKey, new byte[] { 0x01 }).Should().BeFalse(); + + contract1.ParameterList = new[] { ContractParameterType.Signature, ContractParameterType.Signature }; + Action action1 = () => context.AddSignature(contract1, key.PublicKey, new byte[] { 0x01 }); + action1.Should().Throw(); + + //multiSign + + byte[] privateKey2 = 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, 0x02 }; + var key2 = new KeyPair(privateKey2); + var multiSignContract = Contract.CreateMultiSigContract(2, + new ECPoint[] + { + key.PublicKey, + key2.PublicKey + }); + var multiSender = UInt160.Parse("0xa4712ed1a8d813561b28ec828930d85e6e08ec7a"); + tx.Sender = multiSender; + context = new ContractParametersContext(tx); + context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); + context.AddSignature(multiSignContract, key2.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); + + tx.Sender = singleSender; + context = new ContractParametersContext(tx); + context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeFalse(); + + tx.Sender = multiSender; + context = new ContractParametersContext(tx); + byte[] privateKey3 = 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, 0x03 }; + var key3 = new KeyPair(privateKey3); + context.AddSignature(multiSignContract, key3.PublicKey, new byte[] { 0x01 }).Should().BeFalse(); + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs b/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs new file mode 100644 index 0000000000..fcccab5dc7 --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs @@ -0,0 +1,27 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using System; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_InteropDescriptor + { + [TestMethod] + public void TestGetMethod() + { + string method = @"System.ExecutionEngine.GetScriptContainer"; + long price = 0_00000250; + TriggerType allowedTriggers = TriggerType.All; + InteropDescriptor descriptor = new InteropDescriptor(method, TestHandler, price, allowedTriggers); + descriptor.Method.Should().Be(method); + descriptor.Price.Should().Be(price); + } + + private bool TestHandler(ApplicationEngine engine) + { + return true; + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs new file mode 100644 index 0000000000..885f3d3498 --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -0,0 +1,479 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.Ledger; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Enumerators; +using Neo.SmartContract.Iterators; +using Neo.SmartContract.Manifest; +using Neo.VM.Types; +using Neo.Wallets; +using System.Linq; +using VMArray = Neo.VM.Types.Array; + +namespace Neo.UnitTests.SmartContract +{ + public partial class UT_InteropService + { + [TestMethod] + public void TestCheckSig() + { + var engine = GetEngine(true); + IVerifiable iv = engine.ScriptContainer; + byte[] message = iv.GetHashData(); + byte[] privateKey = { 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}; + KeyPair keyPair = new KeyPair(privateKey); + ECPoint pubkey = keyPair.PublicKey; + byte[] signature = Crypto.Default.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); + engine.CurrentContext.EvaluationStack.Push(signature); + engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); + InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckSig).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); + + engine.CurrentContext.EvaluationStack.Push(signature); + engine.CurrentContext.EvaluationStack.Push(new byte[70]); + InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckSig).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + } + + [TestMethod] + public void TestCrypto_CheckMultiSig() + { + var engine = GetEngine(true); + IVerifiable iv = engine.ScriptContainer; + byte[] message = iv.GetHashData(); + + byte[] privkey1 = { 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}; + KeyPair key1 = new KeyPair(privkey1); + ECPoint pubkey1 = key1.PublicKey; + byte[] signature1 = Crypto.Default.Sign(message, privkey1, pubkey1.EncodePoint(false).Skip(1).ToArray()); + + byte[] privkey2 = { 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, 0x02}; + KeyPair key2 = new KeyPair(privkey2); + ECPoint pubkey2 = key2.PublicKey; + byte[] signature2 = Crypto.Default.Sign(message, privkey2, pubkey2.EncodePoint(false).Skip(1).ToArray()); + + var pubkeys = new VMArray + { + pubkey1.EncodePoint(false), + pubkey2.EncodePoint(false) + }; + var signatures = new VMArray + { + signature1, + signature2 + }; + engine.CurrentContext.EvaluationStack.Push(signatures); + engine.CurrentContext.EvaluationStack.Push(pubkeys); + InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckMultiSig).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); + + pubkeys = new VMArray(); + engine.CurrentContext.EvaluationStack.Push(signatures); + engine.CurrentContext.EvaluationStack.Push(pubkeys); + InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckMultiSig).Should().BeFalse(); + + pubkeys = new VMArray + { + pubkey1.EncodePoint(false), + pubkey2.EncodePoint(false) + }; + signatures = new VMArray(); + engine.CurrentContext.EvaluationStack.Push(signatures); + engine.CurrentContext.EvaluationStack.Push(pubkeys); + InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckMultiSig).Should().BeFalse(); + + pubkeys = new VMArray + { + pubkey1.EncodePoint(false), + pubkey2.EncodePoint(false) + }; + signatures = new VMArray + { + signature1, + new byte[64] + }; + engine.CurrentContext.EvaluationStack.Push(signatures); + engine.CurrentContext.EvaluationStack.Push(pubkeys); + InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckMultiSig).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + + pubkeys = new VMArray + { + pubkey1.EncodePoint(false), + new byte[70] + }; + signatures = new VMArray + { + signature1, + signature2 + }; + engine.CurrentContext.EvaluationStack.Push(signatures); + engine.CurrentContext.EvaluationStack.Push(pubkeys); + InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckMultiSig).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + } + + [TestMethod] + public void TestAccount_IsStandard() + { + var engine = GetEngine(false, true); + var hash = new byte[] { 0x01, 0x01, 0x01 ,0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01 }; + engine.CurrentContext.EvaluationStack.Push(hash); + InteropService.Invoke(engine, InteropService.Neo_Account_IsStandard).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); + + var mockSnapshot = new Mock(); + var state = TestUtils.GetContract(); + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[] { 0x01 }); + engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); + InteropService.Invoke(engine, InteropService.Neo_Account_IsStandard).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + } + + [TestMethod] + public void TestContract_Create() + { + var engine = GetEngine(false, true); + var script = new byte[1024 * 1024 + 1]; + engine.CurrentContext.EvaluationStack.Push(script); + InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeFalse(); + + string manifestStr = new string(new char[ContractManifest.MaxLength + 1]); + script = new byte[] { 0x01 }; + engine.CurrentContext.EvaluationStack.Push(manifestStr); + engine.CurrentContext.EvaluationStack.Push(script); + InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeFalse(); + + var manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")); + engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); + engine.CurrentContext.EvaluationStack.Push(script); + InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeFalse(); + + manifest.Abi.Hash = script.ToScriptHash(); + engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); + engine.CurrentContext.EvaluationStack.Push(script); + InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeTrue(); + + var mockSnapshot = new Mock(); + var state = TestUtils.GetContract(); + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[] { 0x01 }); + engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); + engine.CurrentContext.EvaluationStack.Push(state.Script); + InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeFalse(); + } + + [TestMethod] + public void TestContract_Update() + { + var engine = GetEngine(false, true); + var script = new byte[1024 * 1024 + 1]; + engine.CurrentContext.EvaluationStack.Push(script); + InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeFalse(); + + string manifestStr = new string(new char[ContractManifest.MaxLength + 1]); + script = new byte[] { 0x01 }; + engine.CurrentContext.EvaluationStack.Push(manifestStr); + engine.CurrentContext.EvaluationStack.Push(script); + InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeFalse(); + + manifestStr = ""; + engine.CurrentContext.EvaluationStack.Push(manifestStr); + engine.CurrentContext.EvaluationStack.Push(script); + InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeFalse(); + + var manifest = ContractManifest.CreateDefault(script.ToScriptHash()); + byte[] privkey = { 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}; + KeyPair key = new KeyPair(privkey); + ECPoint pubkey = key.PublicKey; + byte[] signature = Crypto.Default.Sign(script.ToScriptHash().ToArray(), privkey, pubkey.EncodePoint(false).Skip(1).ToArray()); + manifest.Groups = new ContractGroup[] + { + new ContractGroup() + { + PubKey = pubkey, + Signature = signature + } + }; + var mockSnapshot = new Mock(); + var state = TestUtils.GetContract(); + state.Manifest.Features = ContractFeatures.HasStorage; + var storageItem = new StorageItem + { + Value = new byte[] { 0x01 }, + IsConstant = false + }; + + var storageKey = new StorageKey + { + ScriptHash = state.ScriptHash, + Key = new byte[] { 0x01 } + }; + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(state.Script); + engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); + engine.CurrentContext.EvaluationStack.Push(script); + InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeTrue(); + } + + [TestMethod] + public void TestStorage_Find() + { + var mockSnapshot = new Mock(); + var state = TestUtils.GetContract(); + state.Manifest.Features = ContractFeatures.HasStorage; + + var storageItem = new StorageItem + { + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, + IsConstant = true + }; + var storageKey = new StorageKey + { + ScriptHash = state.ScriptHash, + Key = new byte[] { 0x01 } + }; + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); + var engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[] { 0x01 }); + + engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext + { + ScriptHash = state.ScriptHash, + IsReadOnly = false + })); + InteropService.Invoke(engine, InteropService.Neo_Storage_Find).Should().BeTrue(); + var iterator = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + iterator.Next(); + var ele = iterator.Value(); + ele.GetByteArray().ToHexString().Should().Be(storageItem.Value.ToHexString()); + + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.Neo_Storage_Find).Should().BeFalse(); + } + + [TestMethod] + public void TestEnumerator_Create() + { + var engine = GetEngine(); + var arr = new VMArray { + new byte[]{ 0x01 }, + new byte[]{ 0x02 } + }; + engine.CurrentContext.EvaluationStack.Push(arr); + InteropService.Invoke(engine, InteropService.Neo_Enumerator_Create).Should().BeTrue(); + var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + ret.GetInterface().Next(); + ret.GetInterface().Value().GetByteArray().ToHexString() + .Should().Be(new byte[] { 0x01 }.ToHexString()); + + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.Neo_Enumerator_Create).Should().BeFalse(); + } + + [TestMethod] + public void TestEnumerator_Next() + { + var engine = GetEngine(); + var arr = new VMArray { + new byte[]{ 0x01 }, + new byte[]{ 0x02 } + }; + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new ArrayWrapper(arr))); + InteropService.Invoke(engine, InteropService.Neo_Enumerator_Next).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); + + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.Neo_Enumerator_Next).Should().BeFalse(); + } + + [TestMethod] + public void TestEnumerator_Value() + { + var engine = GetEngine(); + var arr = new VMArray { + new byte[]{ 0x01 }, + new byte[]{ 0x02 } + }; + var wrapper = new ArrayWrapper(arr); + wrapper.Next(); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); + InteropService.Invoke(engine, InteropService.Neo_Enumerator_Value).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.Neo_Enumerator_Value).Should().BeFalse(); + } + + [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); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper2)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper1)); + InteropService.Invoke(engine, InteropService.Neo_Enumerator_Concat).Should().BeTrue(); + var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + ret.Next().Should().BeTrue(); + ret.Value().GetByteArray().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + } + + [TestMethod] + public void TestIterator_Create() + { + var engine = GetEngine(); + var arr = new VMArray { + new byte[]{ 0x01 }, + new byte[]{ 0x02 } + }; + engine.CurrentContext.EvaluationStack.Push(arr); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeTrue(); + var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + ret.GetInterface().Next(); + ret.GetInterface().Value().GetByteArray().ToHexString() + .Should().Be(new byte[] { 0x01 }.ToHexString()); + + var map = new Map + { + { new Integer(1), new Integer(2) }, + { new Integer(3), new Integer(4) } + }; + engine.CurrentContext.EvaluationStack.Push(map); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeTrue(); + ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + ret.GetInterface().Next(); + ret.GetInterface().Key().GetBigInteger().Should().Be(1); + ret.GetInterface().Value().GetBigInteger().Should().Be(2); + + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeFalse(); + } + + [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.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Key).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(0); + + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Key).Should().BeFalse(); + } + + [TestMethod] + public void TestIterator_Keys() + { + var engine = GetEngine(); + var arr = new VMArray { + new byte[]{ 0x01 }, + new byte[]{ 0x02 } + }; + var wrapper = new ArrayWrapper(arr); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Keys).Should().BeTrue(); + var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + ret.Next(); + ret.Value().GetBigInteger().Should().Be(0); + + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Keys).Should().BeFalse(); + } + + [TestMethod] + public void TestIterator_Values() + { + var engine = GetEngine(); + var arr = new VMArray { + new byte[]{ 0x01 }, + new byte[]{ 0x02 } + }; + var wrapper = new ArrayWrapper(arr); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Values).Should().BeTrue(); + var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + ret.Next(); + ret.Value().GetByteArray().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Values).Should().BeFalse(); + } + + [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); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper2)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper1)); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Concat).Should().BeTrue(); + var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + ret.Next().Should().BeTrue(); + ret.Value().GetByteArray().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + } + + [TestMethod] + public void TestJson_Deserialize() + { + var engine = GetEngine(); + engine.CurrentContext.EvaluationStack.Push("1"); + InteropService.Invoke(engine, InteropService.Neo_Json_Deserialize).Should().BeTrue(); + var ret = engine.CurrentContext.EvaluationStack.Pop(); + ret.GetBigInteger().Should().Be(1); + } + + [TestMethod] + public void TestJson_Serialize() + { + var engine = GetEngine(); + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.Neo_Json_Serialize).Should().BeTrue(); + var ret = engine.CurrentContext.EvaluationStack.Pop(); + ret.GetString().Should().Be("1"); + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_InteropService.cs b/neo.UnitTests/SmartContract/UT_InteropService.cs index dff17f2025..fe28f1503d 100644 --- a/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -1,12 +1,25 @@ +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.Ledger; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Manifest; using Neo.VM; +using Neo.VM.Types; +using Neo.Wallets; +using System; +using System.Linq; +using System.Text; namespace Neo.UnitTests.SmartContract { [TestClass] - public class UT_InteropService + public partial class UT_InteropService { [TestInitialize] public void TestSetup() @@ -188,5 +201,644 @@ private void AssertNotification(StackItem stackItem, UInt160 scriptHash, int not CollectionAssert.AreEqual(scriptHash.ToArray(), array[0].GetByteArray()); Assert.AreEqual(notification, array[1].GetBigInteger()); } + + [TestMethod] + public void TestExecutionEngine_GetScriptContainer() + { + var engine = GetEngine(true); + InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetScriptContainer).Should().BeTrue(); + var stackItem = ((VM.Types.Array)engine.CurrentContext.EvaluationStack.Pop()).ToArray(); + stackItem.Length.Should().Be(8); + stackItem[0].GetByteArray().ToHexString().Should().Be(TestUtils.GetTransaction().Hash.ToArray().ToHexString()); + } + + [TestMethod] + public void TestExecutionEngine_GetExecutingScriptHash() + { + var engine = GetEngine(); + InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetExecutingScriptHash).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString() + .Should().Be(engine.CurrentScriptHash.ToArray().ToHexString()); + } + + [TestMethod] + public void TestExecutionEngine_GetCallingScriptHash() + { + var engine = GetEngine(true); + InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetCallingScriptHash).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); + + engine = GetEngine(true); + engine.LoadScript(new byte[] { 0x01 }); + InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetCallingScriptHash).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString() + .Should().Be(engine.CallingScriptHash.ToArray().ToHexString()); + } + + [TestMethod] + public void TestExecutionEngine_GetEntryScriptHash() + { + var engine = GetEngine(); + InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetEntryScriptHash).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString() + .Should().Be(engine.EntryScriptHash.ToArray().ToHexString()); + } + + [TestMethod] + public void TestRuntime_Platform() + { + var engine = GetEngine(); + InteropService.Invoke(engine, InteropService.System_Runtime_Platform).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString() + .Should().Be(Encoding.ASCII.GetBytes("NEO").ToHexString()); + } + + [TestMethod] + public void TestRuntime_GetTrigger() + { + var engine = GetEngine(); + InteropService.Invoke(engine, InteropService.System_Runtime_GetTrigger).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBigInteger() + .Should().Be((int)engine.Trigger); + } + + [TestMethod] + public void TestRuntime_CheckWitness() + { + byte[] privateKey = { 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}; + KeyPair keyPair = new KeyPair(privateKey); + ECPoint pubkey = keyPair.PublicKey; + + var engine = GetEngine(true); + ((Transaction)engine.ScriptContainer).Sender = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); + + engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(true)); + InteropService.Invoke(engine, InteropService.System_Runtime_CheckWitness).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Peek().GetType().Should().Be(typeof(Neo.VM.Types.Boolean)); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().Be(false); + + engine.CurrentContext.EvaluationStack.Push(((Transaction)engine.ScriptContainer).Sender.ToArray()); + InteropService.Invoke(engine, InteropService.System_Runtime_CheckWitness).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Peek().GetType().Should().Be(typeof(Neo.VM.Types.Boolean)); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().Be(false); + + engine.CurrentContext.EvaluationStack.Push(new byte[0]); + InteropService.Invoke(engine, InteropService.System_Runtime_CheckWitness).Should().BeFalse(); + } + + [TestMethod] + public void TestRuntime_Log() + { + var engine = GetEngine(true); + string message = "hello"; + engine.CurrentContext.EvaluationStack.Push(Encoding.UTF8.GetBytes(message)); + ApplicationEngine.Log += LogEvent; + InteropService.Invoke(engine, InteropService.System_Runtime_Log).Should().BeTrue(); + ((Transaction)engine.ScriptContainer).Script.ToHexString().Should().Be(new byte[] { 0x01, 0x02, 0x03 }.ToHexString()); + ApplicationEngine.Log -= LogEvent; + } + + [TestMethod] + public void TestRuntime_GetTime() + { + Block block = new Block(); + TestUtils.SetupBlockWithValues(block, UInt256.Zero, out var merkRootVal, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 0); + var engine = GetEngine(true, true); + engine.Snapshot.PersistingBlock = block; + + InteropService.Invoke(engine, InteropService.System_Runtime_GetTime).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(block.Timestamp); + } + + [TestMethod] + public void TestRuntime_Serialize() + { + var engine = GetEngine(); + engine.CurrentContext.EvaluationStack.Push(100); + InteropService.Invoke(engine, InteropService.System_Runtime_Serialize).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString() + .Should().Be(new byte[] { 0x02, 0x01, 0x64 }.ToHexString()); + + engine.CurrentContext.EvaluationStack.Push(new byte[1024 * 1024 * 2]); //Larger than MaxItemSize + InteropService.Invoke(engine, InteropService.System_Runtime_Serialize).Should().BeFalse(); + + engine.CurrentContext.EvaluationStack.Push(new TestInteropInterface()); //NotSupportedException + InteropService.Invoke(engine, InteropService.System_Runtime_Serialize).Should().BeFalse(); + } + + [TestMethod] + public void TestRuntime_Deserialize() + { + var engine = GetEngine(); + engine.CurrentContext.EvaluationStack.Push(100); + InteropService.Invoke(engine, InteropService.System_Runtime_Serialize).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.System_Runtime_Deserialize).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(100); + + engine.CurrentContext.EvaluationStack.Push(new byte[] { 0xfa, 0x01 }); //FormatException + InteropService.Invoke(engine, InteropService.System_Runtime_Deserialize).Should().BeFalse(); + } + + [TestMethod] + public void TestRuntime_GetInvocationCounter() + { + var engine = GetEngine(); + InteropService.Invoke(engine, InteropService.System_Runtime_GetInvocationCounter).Should().BeFalse(); + engine.InvocationCounter.TryAdd(engine.CurrentScriptHash, 10); + InteropService.Invoke(engine, InteropService.System_Runtime_GetInvocationCounter).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(10); + } + + [TestMethod] + public void TestCrypto_Verify() + { + var engine = GetEngine(true); + IVerifiable iv = engine.ScriptContainer; + byte[] message = iv.GetHashData(); + byte[] privateKey = { 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}; + KeyPair keyPair = new KeyPair(privateKey); + ECPoint pubkey = keyPair.PublicKey; + byte[] signature = Crypto.Default.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); + + engine.CurrentContext.EvaluationStack.Push(signature); + engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); + engine.CurrentContext.EvaluationStack.Push(message); + InteropService.Invoke(engine, InteropService.System_Crypto_Verify).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); + + byte[] wrongkey = pubkey.EncodePoint(false); + wrongkey[0] = 5; + engine.CurrentContext.EvaluationStack.Push(signature); + engine.CurrentContext.EvaluationStack.Push(wrongkey); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(engine.ScriptContainer)); + InteropService.Invoke(engine, InteropService.System_Crypto_Verify).Should().BeFalse(); + + } + + [TestMethod] + public void TestBlockchain_GetHeight() + { + var engine = GetEngine(true, true); + InteropService.Invoke(engine, InteropService.System_Blockchain_GetHeight).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(0); + } + + [TestMethod] + public void TestBlockchain_GetBlock() + { + var engine = GetEngine(true, true); + + engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); + InteropService.Invoke(engine, InteropService.System_Blockchain_GetBlock).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); + + 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.CurrentContext.EvaluationStack.Push(data1); + InteropService.Invoke(engine, InteropService.System_Blockchain_GetBlock).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + + byte[] data2 = new byte[] { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; + engine.CurrentContext.EvaluationStack.Push(data2); + InteropService.Invoke(engine, InteropService.System_Blockchain_GetBlock).Should().BeFalse(); + } + + [TestMethod] + public void TestBlockchain_GetTransaction() + { + 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.CurrentContext.EvaluationStack.Push(data1); + InteropService.Invoke(engine, InteropService.System_Blockchain_GetTransaction).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + } + + [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.CurrentContext.EvaluationStack.Push(data1); + InteropService.Invoke(engine, InteropService.System_Blockchain_GetTransactionHeight).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(-1); + } + + [TestMethod] + public void TestBlockchain_GetContract() + { + 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 }; + engine.CurrentContext.EvaluationStack.Push(data1); + InteropService.Invoke(engine, InteropService.System_Blockchain_GetContract).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); + + var mockSnapshot = new Mock(); + var state = TestUtils.GetContract(); + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[] { 0x01 }); + engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); + InteropService.Invoke(engine, InteropService.System_Blockchain_GetContract).Should().BeTrue(); + var stackItems = ((VM.Types.Array)engine.CurrentContext.EvaluationStack.Pop()).ToArray(); + stackItems.Length.Should().Be(3); + stackItems[0].GetType().Should().Be(typeof(ByteArray)); + stackItems[0].GetByteArray().ToHexString().Should().Be(state.Script.ToHexString()); + stackItems[1].GetBoolean().Should().BeFalse(); + stackItems[2].GetBoolean().Should().BeFalse(); + } + + [TestMethod] + public void TestStorage_GetContext() + { + var engine = GetEngine(); + InteropService.Invoke(engine, InteropService.System_Storage_GetContext).Should().BeTrue(); + var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + ret.GetInterface().ScriptHash.Should().Be(engine.CurrentScriptHash); + ret.GetInterface().IsReadOnly.Should().BeFalse(); + } + + [TestMethod] + public void TestStorage_GetReadOnlyContext() + { + var engine = GetEngine(); + InteropService.Invoke(engine, InteropService.System_Storage_GetReadOnlyContext).Should().BeTrue(); + var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + ret.GetInterface().ScriptHash.Should().Be(engine.CurrentScriptHash); + ret.GetInterface().IsReadOnly.Should().BeTrue(); + } + + [TestMethod] + public void TestStorage_Get() + { + var mockSnapshot = new Mock(); + var state = TestUtils.GetContract(); + state.Manifest.Features = ContractFeatures.HasStorage; + + var storageKey = new StorageKey + { + ScriptHash = state.ScriptHash, + Key = new byte[] { 0x01 } + }; + + var storageItem = new StorageItem + { + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, + IsConstant = true + }; + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); + var engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[] { 0x01 }); + + engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext + { + ScriptHash = state.ScriptHash, + IsReadOnly = false + })); + InteropService.Invoke(engine, InteropService.System_Storage_Get).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString().Should().Be(storageItem.Value.ToHexString()); + + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache()); + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[] { 0x01 }); + engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext + { + ScriptHash = state.ScriptHash, + IsReadOnly = false + })); + InteropService.Invoke(engine, InteropService.System_Storage_Get).Should().BeFalse(); + + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.System_Storage_Get).Should().BeFalse(); + } + + [TestMethod] + public void TestStorage_Put() + { + var engine = GetEngine(false, true); + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + + //CheckStorageContext fail + var key = new byte[] { 0x01 }; + var value = new byte[] { 0x02 }; + engine.CurrentContext.EvaluationStack.Push(value); + engine.CurrentContext.EvaluationStack.Push(key); + var state = TestUtils.GetContract(); + var storageContext = new StorageContext + { + ScriptHash = state.ScriptHash, + IsReadOnly = false + }; + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + + //key.Length > MaxStorageKeySize + key = new byte[InteropService.MaxStorageKeySize + 1]; + value = new byte[] { 0x02 }; + engine.CurrentContext.EvaluationStack.Push(value); + engine.CurrentContext.EvaluationStack.Push(key); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + + //value.Length > MaxStorageValueSize + key = new byte[] { 0x01 }; + value = new byte[ushort.MaxValue + 1]; + engine.CurrentContext.EvaluationStack.Push(value); + engine.CurrentContext.EvaluationStack.Push(key); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + + //context.IsReadOnly + key = new byte[] { 0x01 }; + value = new byte[] { 0x02 }; + storageContext.IsReadOnly = true; + engine.CurrentContext.EvaluationStack.Push(value); + engine.CurrentContext.EvaluationStack.Push(key); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + + //storage value is constant + var mockSnapshot = new Mock(); + state.Manifest.Features = ContractFeatures.HasStorage; + + var storageKey = new StorageKey + { + ScriptHash = state.ScriptHash, + Key = new byte[] { 0x01 } + }; + var storageItem = new StorageItem + { + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, + IsConstant = true + }; + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[] { 0x01 }); + key = new byte[] { 0x01 }; + value = new byte[] { 0x02 }; + storageContext.IsReadOnly = false; + engine.CurrentContext.EvaluationStack.Push(value); + engine.CurrentContext.EvaluationStack.Push(key); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + + //success + storageItem.IsConstant = false; + engine.CurrentContext.EvaluationStack.Push(value); + engine.CurrentContext.EvaluationStack.Push(key); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeTrue(); + + //value length == 0 + key = new byte[] { 0x01 }; + value = new byte[0]; + engine.CurrentContext.EvaluationStack.Push(value); + engine.CurrentContext.EvaluationStack.Push(key); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeTrue(); + } + + [TestMethod] + public void TestStorage_PutEx() + { + var engine = GetEngine(false, true); + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.System_Storage_PutEx).Should().BeFalse(); + + var mockSnapshot = new Mock(); + var state = TestUtils.GetContract(); + state.Manifest.Features = ContractFeatures.HasStorage; + var storageKey = new StorageKey + { + ScriptHash = new UInt160(TestUtils.GetByteArray(20, 0x42)), + Key = new byte[] { 0x01 } + }; + var storageItem = new StorageItem + { + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, + IsConstant = false + }; + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[] { 0x01 }); + var key = new byte[] { 0x01 }; + var value = new byte[] { 0x02 }; + var storageContext = new StorageContext + { + ScriptHash = state.ScriptHash, + IsReadOnly = false + }; + engine.CurrentContext.EvaluationStack.Push((int)StorageFlags.None); + engine.CurrentContext.EvaluationStack.Push(value); + engine.CurrentContext.EvaluationStack.Push(key); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_PutEx).Should().BeTrue(); + } + + [TestMethod] + public void TestStorage_Delete() + { + var engine = GetEngine(false, true); + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.System_Storage_Delete).Should().BeFalse(); + + + var mockSnapshot = new Mock(); + var state = TestUtils.GetContract(); + state.Manifest.Features = ContractFeatures.HasStorage; + var storageKey = new StorageKey + { + ScriptHash = new UInt160(TestUtils.GetByteArray(20, 0x42)), + Key = new byte[] { 0x01 } + }; + var storageItem = new StorageItem + { + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, + IsConstant = false + }; + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[] { 0x01 }); + state.Manifest.Features = ContractFeatures.HasStorage; + var key = new byte[] { 0x01 }; + var storageContext = new StorageContext + { + ScriptHash = state.ScriptHash, + IsReadOnly = false + }; + engine.CurrentContext.EvaluationStack.Push(key); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_Delete).Should().BeTrue(); + + //context is readonly + storageContext.IsReadOnly = true; + engine.CurrentContext.EvaluationStack.Push(key); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_Delete).Should().BeFalse(); + + //CheckStorageContext fail + storageContext.IsReadOnly = false; + state.Manifest.Features = ContractFeatures.NoProperty; + engine.CurrentContext.EvaluationStack.Push(key); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_Storage_Delete).Should().BeFalse(); + } + + [TestMethod] + public void TestStorageContext_AsReadOnly() + { + var engine = GetEngine(); + engine.CurrentContext.EvaluationStack.Push(1); + InteropService.Invoke(engine, InteropService.System_StorageContext_AsReadOnly).Should().BeFalse(); + + var state = TestUtils.GetContract(); + var storageContext = new StorageContext + { + ScriptHash = state.ScriptHash, + IsReadOnly = false + }; + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + InteropService.Invoke(engine, InteropService.System_StorageContext_AsReadOnly).Should().BeTrue(); + var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + ret.GetInterface().IsReadOnly.Should().Be(true); + } + + [TestMethod] + public void TestInvoke() + { + var engine = new ApplicationEngine(TriggerType.Verification, null, null, 0); + InteropService.Invoke(engine, 10000).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.System_StorageContext_AsReadOnly).Should().BeFalse(); + } + + [TestMethod] + public void TestContract_Call() + { + var mockSnapshot = new Mock(); + var state = TestUtils.GetContract(); + state.Manifest.Features = ContractFeatures.HasStorage; + byte[] method = Encoding.UTF8.GetBytes("method"); + byte[] args = new byte[0]; + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + var engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[] { 0x01 }); + + engine.CurrentContext.EvaluationStack.Push(args); + engine.CurrentContext.EvaluationStack.Push(method); + engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); + InteropService.Invoke(engine, InteropService.System_Contract_Call).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString().Should().Be(method.ToHexString()); + engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString().Should().Be(args.ToHexString()); + + state.Manifest.Permissions[0].Methods = WildCardContainer.Create("a"); + engine.CurrentContext.EvaluationStack.Push(args); + engine.CurrentContext.EvaluationStack.Push(method); + engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); + InteropService.Invoke(engine, InteropService.System_Contract_Call).Should().BeFalse(); + state.Manifest.Permissions[0].Methods = WildCardContainer.CreateWildcard(); + + engine.CurrentContext.EvaluationStack.Push(args); + engine.CurrentContext.EvaluationStack.Push(method); + engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); + InteropService.Invoke(engine, InteropService.System_Contract_Call).Should().BeTrue(); + + engine.CurrentContext.EvaluationStack.Push(args); + engine.CurrentContext.EvaluationStack.Push(method); + engine.CurrentContext.EvaluationStack.Push(UInt160.Zero.ToArray()); + InteropService.Invoke(engine, InteropService.System_Contract_Call).Should().BeFalse(); + } + + [TestMethod] + public void TestContract_Destroy() + { + var engine = GetEngine(false, true); + InteropService.Invoke(engine, InteropService.System_Contract_Destroy).Should().BeTrue(); + + var mockSnapshot = new Mock(); + var state = TestUtils.GetContract(); + state.Manifest.Features = ContractFeatures.HasStorage; + var scriptHash = UInt160.Parse("0xcb9f3b7c6fb1cf2c13a40637c189bdd066a272b4"); + var storageItem = new StorageItem + { + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, + IsConstant = false + }; + + var storageKey = new StorageKey + { + ScriptHash = scriptHash, + Key = new byte[] { 0x01 } + }; + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(scriptHash, state)); + mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[0]); + InteropService.Invoke(engine, InteropService.System_Contract_Destroy).Should().BeTrue(); + + //storages are removed + mockSnapshot = new Mock(); + state = TestUtils.GetContract(); + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(scriptHash, state)); + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(new byte[0]); + InteropService.Invoke(engine, InteropService.System_Contract_Destroy).Should().BeTrue(); + } + + public static void LogEvent(object sender, LogEventArgs args) + { + Transaction tx = (Transaction)args.ScriptContainer; + tx.Script = new byte[] { 0x01, 0x02, 0x03 }; + } + + private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSnapshot = false) + { + var tx = TestUtils.GetTransaction(); + var snapshot = TestBlockchain.GetStore().GetSnapshot().Clone(); + ApplicationEngine engine; + if (hasContainer && hasSnapshot) + { + engine = new ApplicationEngine(TriggerType.Application, tx, snapshot, 0); + } + else if (hasContainer && !hasSnapshot) + { + engine = new ApplicationEngine(TriggerType.Application, tx, null, 0); + } + else if (!hasContainer && hasSnapshot) + { + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + } + else + { + engine = new ApplicationEngine(TriggerType.Application, null, null, 0); + } + engine.LoadScript(new byte[] { 0x01 }); + return engine; + } + } + + internal class TestInteropInterface : InteropInterface + { + public override bool Equals(StackItem other) => true; + public override bool GetBoolean() => true; + public override T GetInterface() => throw new NotImplementedException(); } } diff --git a/neo.UnitTests/SmartContract/UT_LogEventArgs.cs b/neo.UnitTests/SmartContract/UT_LogEventArgs.cs new file mode 100644 index 0000000000..8dc3699c82 --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_LogEventArgs.cs @@ -0,0 +1,23 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_LogEventArgs + { + [TestMethod] + public void TestGeneratorAndGet() + { + IVerifiable container = new Header(); + UInt160 scripthash = UInt160.Zero; + string message = "lalala"; + LogEventArgs logEventArgs = new LogEventArgs(container, scripthash, message); + Assert.IsNotNull(logEventArgs); + Assert.AreEqual(container, logEventArgs.ScriptContainer); + Assert.AreEqual(scripthash, logEventArgs.ScriptHash); + Assert.AreEqual(message, logEventArgs.Message); + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_NefFile.cs b/neo.UnitTests/SmartContract/UT_NefFile.cs new file mode 100644 index 0000000000..23d8040c79 --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_NefFile.cs @@ -0,0 +1,148 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.SmartContract; +using System; +using System.IO; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_NefFile + { + public NefFile file = new NefFile() + { + Compiler = "".PadLeft(32, ' '), + Version = new Version(1, 2, 3, 4), + Script = new byte[] { 0x01, 0x02, 0x03 } + }; + + [TestInitialize] + public void TestSetup() + { + file.ScriptHash = file.Script.ToScriptHash(); + file.CheckSum = NefFile.ComputeChecksum(file); + } + + [TestMethod] + public void TestDeserialize() + { + byte[] wrongMagic = { 0x00, 0x00, 0x00, 0x00 }; + using (MemoryStream ms = new MemoryStream(1024)) + using (BinaryWriter writer = new BinaryWriter(ms)) + using (BinaryReader reader = new BinaryReader(ms)) + { + ((ISerializable)file).Serialize(writer); + ms.Seek(0, SeekOrigin.Begin); + ms.Write(wrongMagic, 0, 4); + ms.Seek(0, SeekOrigin.Begin); + ISerializable newFile = new NefFile(); + Action action = () => newFile.Deserialize(reader); + action.Should().Throw(); + } + + file.CheckSum = 0; + using (MemoryStream ms = new MemoryStream(1024)) + using (BinaryWriter writer = new BinaryWriter(ms)) + using (BinaryReader reader = new BinaryReader(ms)) + { + ((ISerializable)file).Serialize(writer); + ms.Seek(0, SeekOrigin.Begin); + ISerializable newFile = new NefFile(); + Action action = () => newFile.Deserialize(reader); + action.Should().Throw(); + } + + file.CheckSum = NefFile.ComputeChecksum(file); + file.ScriptHash = new byte[] { 0x01 }.ToScriptHash(); + using (MemoryStream ms = new MemoryStream(1024)) + using (BinaryWriter writer = new BinaryWriter(ms)) + using (BinaryReader reader = new BinaryReader(ms)) + { + ((ISerializable)file).Serialize(writer); + ms.Seek(0, SeekOrigin.Begin); + ISerializable newFile = new NefFile(); + Action action = () => newFile.Deserialize(reader); + action.Should().Throw(); + } + + file.ScriptHash = file.Script.ToScriptHash(); + var data = file.ToArray(); + var newFile1 = data.AsSerializable(); + newFile1.Version.Should().Be(file.Version); + newFile1.Compiler.Should().Be(file.Compiler); + newFile1.ScriptHash.Should().Be(file.ScriptHash); + newFile1.CheckSum.Should().Be(file.CheckSum); + newFile1.Script.Should().BeEquivalentTo(file.Script); + } + + [TestMethod] + public void TestGetSize() + { + file.Size.Should().Be(4 + 32 + 16 + 20 + 4 + 4); + } + + [TestMethod] + public void ParseTest() + { + var file = new NefFile() + { + Compiler = "".PadLeft(32, ' '), + Version = new Version(1, 2, 3, 4), + Script = new byte[] { 0x01, 0x02, 0x03 } + }; + + file.ScriptHash = file.Script.ToScriptHash(); + file.CheckSum = NefFile.ComputeChecksum(file); + + var data = file.ToArray(); + file = data.AsSerializable(); + + Assert.AreEqual("".PadLeft(32, ' '), file.Compiler); + Assert.AreEqual(new Version(1, 2, 3, 4), file.Version); + Assert.AreEqual(file.Script.ToScriptHash(), file.ScriptHash); + CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, file.Script); + } + + [TestMethod] + public void LimitTest() + { + var file = new NefFile() + { + Compiler = "".PadLeft(byte.MaxValue, ' '), + Version = new Version(1, 2, 3, 4), + Script = new byte[1024 * 1024], + ScriptHash = new byte[1024 * 1024].ToScriptHash(), + CheckSum = 0 + }; + + // Wrong compiler + + Assert.ThrowsException(() => file.ToArray()); + + // Wrong script + + file.Compiler = ""; + file.Script = new byte[(1024 * 1024) + 1]; + file.ScriptHash = file.Script.ToScriptHash(); + var data = file.ToArray(); + + Assert.ThrowsException(() => data.AsSerializable()); + + // Wrong script hash + + file.Script = new byte[1024 * 1024]; + data = file.ToArray(); + + Assert.ThrowsException(() => data.AsSerializable()); + + // Wrong checksum + + file.Script = new byte[1024]; + data = file.ToArray(); + file.CheckSum = NefFile.ComputeChecksum(file) + 1; + + Assert.ThrowsException(() => data.AsSerializable()); + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs b/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs new file mode 100644 index 0000000000..a1dd95b6ee --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs @@ -0,0 +1,22 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.VM; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_NotifyEventArgs + { + [TestMethod] + public void TestGetScriptContainer() + { + IVerifiable container = new TestVerifiable(); + UInt160 script_hash = new byte[] { 0x00 }.ToScriptHash(); + StackItem state = new ContainerPlaceholder(); + NotifyEventArgs args = new NotifyEventArgs(container, script_hash, state); + args.ScriptContainer.Should().Be(container); + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs new file mode 100644 index 0000000000..8379fccd6b --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -0,0 +1,306 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; +using Neo.Wallets; +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_SmartContractHelper + { + [TestMethod] + public void TestIsMultiSigContract() + { + Neo.Cryptography.ECC.ECPoint[] publicKeys1 = new Neo.Cryptography.ECC.ECPoint[20]; + for (int i = 0; i < 20; i++) + { + byte[] privateKey1 = new byte[32]; + RandomNumberGenerator rng1 = RandomNumberGenerator.Create(); + rng1.GetBytes(privateKey1); + KeyPair key1 = new KeyPair(privateKey1); + publicKeys1[i] = key1.PublicKey; + } + byte[] script1 = Contract.CreateMultiSigRedeemScript(20, publicKeys1); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script1, out int m1, out int n1)); + + Neo.Cryptography.ECC.ECPoint[] publicKeys2 = new Neo.Cryptography.ECC.ECPoint[256]; + for (int i = 0; i < 256; i++) + { + byte[] privateKey2 = new byte[32]; + RandomNumberGenerator rng2 = RandomNumberGenerator.Create(); + rng2.GetBytes(privateKey2); + KeyPair key2 = new KeyPair(privateKey2); + publicKeys2[i] = key2.PublicKey; + } + byte[] script2 = Contract.CreateMultiSigRedeemScript(256, publicKeys2); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script2, out int m2, out int n2)); + + Neo.Cryptography.ECC.ECPoint[] publicKeys3 = new Neo.Cryptography.ECC.ECPoint[3]; + for (int i = 0; i < 3; i++) + { + byte[] privateKey3 = new byte[32]; + RandomNumberGenerator rng3 = RandomNumberGenerator.Create(); + rng3.GetBytes(privateKey3); + KeyPair key3 = new KeyPair(privateKey3); + publicKeys3[i] = key3.PublicKey; + } + byte[] script3 = Contract.CreateMultiSigRedeemScript(3, publicKeys3); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script3, out int m3, out int n3)); + + Neo.Cryptography.ECC.ECPoint[] publicKeys4 = new Neo.Cryptography.ECC.ECPoint[3]; + for (int i = 0; i < 3; i++) + { + byte[] privateKey4 = new byte[32]; + RandomNumberGenerator rng4 = RandomNumberGenerator.Create(); + rng4.GetBytes(privateKey4); + KeyPair key4 = new KeyPair(privateKey4); + publicKeys4[i] = key4.PublicKey; + } + byte[] script4 = Contract.CreateMultiSigRedeemScript(3, publicKeys4); + script4[script4.Length - 1] = 0x00; + Assert.AreEqual(false, Neo.SmartContract.Helper.IsMultiSigContract(script4, out int m4, out int n4)); + + } + + [TestMethod] + public void TestIsSignatureContract() + { + byte[] privateKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(privateKey); + KeyPair key = new KeyPair(privateKey); + byte[] script = Contract.CreateSignatureRedeemScript(key.PublicKey); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsSignatureContract(script)); + script[0] = 0x22; + Assert.AreEqual(false, Neo.SmartContract.Helper.IsSignatureContract(script)); + } + + [TestMethod] + public void TestIsStandardContract() + { + byte[] privateKey1 = new byte[32]; + RandomNumberGenerator rng1 = RandomNumberGenerator.Create(); + rng1.GetBytes(privateKey1); + KeyPair key1 = new KeyPair(privateKey1); + byte[] script1 = Contract.CreateSignatureRedeemScript(key1.PublicKey); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsStandardContract(script1)); + + Neo.Cryptography.ECC.ECPoint[] publicKeys2 = new Neo.Cryptography.ECC.ECPoint[3]; + for (int i = 0; i < 3; i++) + { + byte[] privateKey2 = new byte[32]; + RandomNumberGenerator rng2 = RandomNumberGenerator.Create(); + rng2.GetBytes(privateKey2); + KeyPair key2 = new KeyPair(privateKey2); + publicKeys2[i] = key2.PublicKey; + } + byte[] script2 = Contract.CreateMultiSigRedeemScript(3, publicKeys2); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsStandardContract(script2)); + } + + [TestMethod] + public void TestSerialize() + { + StackItem stackItem1 = new ByteArray(new byte[5]); + byte[] result1 = Neo.SmartContract.Helper.Serialize(stackItem1); + byte[] expectedArray1 = new byte[] { + 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray1), Encoding.Default.GetString(result1)); + + StackItem stackItem2 = new VM.Types.Boolean(true); + byte[] result2 = Neo.SmartContract.Helper.Serialize(stackItem2); + byte[] expectedArray2 = new byte[] { + 0x01, 0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray2), Encoding.Default.GetString(result2)); + + StackItem stackItem3 = new VM.Types.Integer(1); + byte[] result3 = Neo.SmartContract.Helper.Serialize(stackItem3); + byte[] expectedArray3 = new byte[] { + 0x02, 0x01, 0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray3), Encoding.Default.GetString(result3)); + + StackItem stackItem4 = new InteropInterface(new object()); + Action action4 = () => Neo.SmartContract.Helper.Serialize(stackItem4); + action4.Should().Throw(); + + StackItem stackItem5 = new VM.Types.Integer(1); + byte[] result5 = Neo.SmartContract.Helper.Serialize(stackItem5); + byte[] expectedArray5 = new byte[] { + 0x02, 0x01, 0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray5), Encoding.Default.GetString(result5)); + + + StackItem stackItem61 = new VM.Types.Integer(1); + List list6 = new List + { + stackItem61 + }; + StackItem stackItem62 = new VM.Types.Array(list6); + byte[] result6 = Neo.SmartContract.Helper.Serialize(stackItem62); + byte[] expectedArray6 = new byte[] { + 0x80,0x01,0x02,0x01,0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray6), Encoding.Default.GetString(result6)); + + StackItem stackItem71 = new VM.Types.Integer(1); + List list7 = new List + { + stackItem71 + }; + StackItem stackItem72 = new VM.Types.Struct(list7); + byte[] result7 = Neo.SmartContract.Helper.Serialize(stackItem72); + byte[] expectedArray7 = new byte[] { + 0x81,0x01,0x02,0x01,0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray7), Encoding.Default.GetString(result7)); + + StackItem stackItem81 = new VM.Types.Integer(1); + Dictionary list8 = new Dictionary + { + { new VM.Types.Integer(2), stackItem81 } + }; + StackItem stackItem82 = new VM.Types.Map(list8); + byte[] result8 = Neo.SmartContract.Helper.Serialize(stackItem82); + byte[] expectedArray8 = new byte[] { + 0x82,0x01,0x02,0x01,0x02,0x02,0x01,0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray8), Encoding.Default.GetString(result8)); + + StackItem stackItem9 = new VM.Types.Integer(1); + Map stackItem91 = new VM.Types.Map(); + stackItem91.Add(stackItem9, stackItem91); + Action action9 = () => Neo.SmartContract.Helper.Serialize(stackItem91); + action9.Should().Throw(); + + VM.Types.Array stackItem10 = new VM.Types.Array(); + stackItem10.Add(stackItem10); + Action action10 = () => Neo.SmartContract.Helper.Serialize(stackItem10); + action10.Should().Throw(); + } + + [TestMethod] + public void TestDeserializeStackItem() + { + StackItem stackItem1 = new ByteArray(new byte[5]); + byte[] byteArray1 = Neo.SmartContract.Helper.Serialize(stackItem1); + StackItem result1 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray1, 1, (uint)byteArray1.Length); + Assert.AreEqual(stackItem1, result1); + + StackItem stackItem2 = new VM.Types.Boolean(true); + byte[] byteArray2 = Neo.SmartContract.Helper.Serialize(stackItem2); + StackItem result2 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray2, 1, (uint)byteArray2.Length); + Assert.AreEqual(stackItem2, result2); + + StackItem stackItem3 = new VM.Types.Integer(1); + byte[] byteArray3 = Neo.SmartContract.Helper.Serialize(stackItem3); + StackItem result3 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray3, 1, (uint)byteArray3.Length); + Assert.AreEqual(stackItem3, result3); + + StackItem stackItem4 = new VM.Types.Integer(1); + byte[] byteArray4 = Neo.SmartContract.Helper.Serialize(stackItem4); + byteArray4[0] = 0x40; + Action action4 = () => Neo.SmartContract.Helper.DeserializeStackItem(byteArray4, 1, (uint)byteArray4.Length); + action4.Should().Throw(); + + StackItem stackItem51 = new VM.Types.Integer(1); + List list5 = new List(); + list5.Add(stackItem51); + StackItem stackItem52 = new VM.Types.Array(list5); + byte[] byteArray5 = Neo.SmartContract.Helper.Serialize(stackItem52); + StackItem result5 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray5, 1, (uint)byteArray5.Length); + Assert.AreEqual(((VM.Types.Array)stackItem52).Count, ((VM.Types.Array)result5).Count); + Assert.AreEqual(((VM.Types.Array)stackItem52).GetEnumerator().Current, ((VM.Types.Array)result5).GetEnumerator().Current); + + StackItem stackItem61 = new VM.Types.Integer(1); + List list6 = new List(); + list6.Add(stackItem61); + StackItem stackItem62 = new VM.Types.Struct(list6); + byte[] byteArray6 = Neo.SmartContract.Helper.Serialize(stackItem62); + StackItem result6 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray6, 1, (uint)byteArray6.Length); + Assert.AreEqual(((VM.Types.Struct)stackItem62).Count, ((VM.Types.Struct)result6).Count); + Assert.AreEqual(((VM.Types.Struct)stackItem62).GetEnumerator().Current, ((VM.Types.Struct)result6).GetEnumerator().Current); + + StackItem stackItem71 = new VM.Types.Integer(1); + Dictionary list7 = new Dictionary(); + list7.Add(new VM.Types.Integer(2), stackItem71); + StackItem stackItem72 = new VM.Types.Map(list7); + byte[] byteArray7 = Neo.SmartContract.Helper.Serialize(stackItem72); + StackItem result7 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray7, 1, (uint)byteArray7.Length); + Assert.AreEqual(((VM.Types.Map)stackItem72).Count, ((VM.Types.Map)result7).Count); + Assert.AreEqual(((VM.Types.Map)stackItem72).Keys.GetEnumerator().Current, ((VM.Types.Map)result7).Keys.GetEnumerator().Current); + Assert.AreEqual(((VM.Types.Map)stackItem72).Values.GetEnumerator().Current, ((VM.Types.Map)result7).Values.GetEnumerator().Current); + } + + [TestMethod] + public void TestToInteropMethodHash() + { + byte[] temp1 = Encoding.ASCII.GetBytes("AAAA"); + byte[] temp2 = Neo.Cryptography.Helper.Sha256(temp1); + uint result = BitConverter.ToUInt32(temp2, 0); + Assert.AreEqual(result, Neo.SmartContract.Helper.ToInteropMethodHash("AAAA")); + } + + [TestMethod] + public void TestToScriptHash() + { + byte[] temp1 = Encoding.ASCII.GetBytes("AAAA"); + byte[] temp2 = Neo.Cryptography.Helper.Sha256(temp1); + uint result = BitConverter.ToUInt32(temp2, 0); + Assert.AreEqual(result, Neo.SmartContract.Helper.ToInteropMethodHash("AAAA")); + } + + [TestMethod] + public void TestVerifyWitnesses() + { + var mockSnapshot1 = new Mock(); + UInt256 index1 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); + TestDataCache testDataCache1 = new TestDataCache(); + testDataCache1.Add(index1, new TrimmedBlock()); + testDataCache1.Delete(index1); + mockSnapshot1.SetupGet(p => p.Blocks).Returns(testDataCache1); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(new Header() { PrevHash = index1 }, mockSnapshot1.Object, 100)); + + var mockSnapshot2 = new Mock(); + UInt256 index2 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); + TrimmedBlock block2 = new TrimmedBlock(); + block2.NextConsensus = UInt160.Zero; + TestDataCache testDataCache21 = new TestDataCache(); + testDataCache21.Add(index2, block2); + Header header2 = new Header() { PrevHash = index2, Witness = new Witness { VerificationScript = new byte[0] } }; + mockSnapshot2.SetupGet(p => p.Blocks).Returns(testDataCache21); + + TestDataCache testDataCache22 = new TestDataCache(); + testDataCache22.Add(UInt160.Zero, new ContractState()); + testDataCache22.Delete(UInt160.Zero); + mockSnapshot2.SetupGet(p => p.Contracts).Returns(testDataCache22); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header2, mockSnapshot2.Object, 100)); + + var mockSnapshot3 = new Mock(); + UInt256 index3 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); + TrimmedBlock block3 = new TrimmedBlock(); + block3.NextConsensus = UInt160.Zero; + TestDataCache testDataCache31 = new TestDataCache(); + testDataCache31.Add(index3, block3); + Header header3 = new Header() { PrevHash = index3, Witness = new Witness { VerificationScript = new byte[0] } }; + mockSnapshot3.SetupGet(p => p.Blocks).Returns(testDataCache31); + TestDataCache testDataCache32 = new TestDataCache(); + testDataCache32.Add(UInt160.Zero, new ContractState()); + mockSnapshot3.SetupGet(p => p.Contracts).Returns(testDataCache32); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header3, mockSnapshot3.Object, 100)); + } + } +} diff --git a/neo.UnitTests/SmartContract/UT_StorageContext.cs b/neo.UnitTests/SmartContract/UT_StorageContext.cs new file mode 100644 index 0000000000..eff7166a66 --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_StorageContext.cs @@ -0,0 +1,22 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_StorageContext + { + [TestMethod] + public void TestToArray() + { + UInt160 script_hash = new byte[] { 0x00 }.ToScriptHash(); + StorageContext context = new StorageContext() + { + ScriptHash = script_hash, + IsReadOnly = true + }; + context.ToArray().Should().BeEquivalentTo(new byte[] { 0x00 }.ToScriptHash().ToArray()); + } + } +} diff --git a/neo.UnitTests/TestDataCache.cs b/neo.UnitTests/TestDataCache.cs index b468e09e4c..37300f22e1 100644 --- a/neo.UnitTests/TestDataCache.cs +++ b/neo.UnitTests/TestDataCache.cs @@ -18,6 +18,7 @@ public TestDataCache(TKey key, TValue value) { dic.Add(key, value); } + public override void DeleteInternal(TKey key) { dic.Remove(key); From 78e14061c56bd703a51f13a8fbcdcbf47a4ab7a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Thu, 24 Oct 2019 14:35:12 +0900 Subject: [PATCH 113/305] Ensure txs are cleared before Blockchain actor (#1166) --- neo/Ledger/MemoryPool.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 10ee5f9d0b..f31e07e56c 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -365,11 +365,11 @@ internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) if (item.Tx.FeePerByte >= _feePerByte) tx.Add(item.Tx); - if (tx.Count > 0) - _system.Blockchain.Tell(tx.ToArray(), ActorRefs.NoSender); - _unverifiedTransactions.Clear(); _unverifiedSortedTransactions.Clear(); + + if (tx.Count > 0) + _system.Blockchain.Tell(tx.ToArray(), ActorRefs.NoSender); } } finally From a483cbc75287aa77af4964b97716819ea378d6ee Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Thu, 24 Oct 2019 14:02:12 +0800 Subject: [PATCH 114/305] Unit Test For RPC Module (#1111) --- .../Network/RPC/Models/UT_RpcBlock.cs | 29 ++++++++ .../Network/RPC/Models/UT_RpcBlockHeader.cs | 29 ++++++++ .../Network/RPC/Models/UT_RpcNep5Balance.cs | 66 +++++++++++++++++++ .../Network/RPC/Models/UT_RpcNep5Balances.cs | 66 +++++++++++++++++++ .../Network/RPC/Models/UT_RpcPeer.cs | 23 +++++++ .../Network/RPC/Models/UT_RpcPeers.cs | 44 +++++++++++++ .../Network/RPC/Models/UT_RpcRawMemPool.cs | 29 ++++++++ .../Network/RPC/Models/UT_RpcRequest.cs | 31 +++++++++ .../Network/RPC/Models/UT_RpcResponse.cs | 34 ++++++++++ .../Network/RPC/Models/UT_RpcVersion.cs | 27 ++++++++ neo.UnitTests/Network/RPC/UT_RpcClient.cs | 36 +++++++++- neo.UnitTests/Network/RPC/UT_RpcServer.cs | 48 ++++++++++++++ 12 files changed, 459 insertions(+), 3 deletions(-) create mode 100644 neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs create mode 100644 neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs create mode 100644 neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs create mode 100644 neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs create mode 100644 neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs create mode 100644 neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs create mode 100644 neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs create mode 100644 neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs create mode 100644 neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs create mode 100644 neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs create mode 100644 neo.UnitTests/Network/RPC/UT_RpcServer.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs new file mode 100644 index 0000000000..860eafa076 --- /dev/null +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs @@ -0,0 +1,29 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.P2P.Payloads; +using Neo.Network.RPC.Models; +using System; + +namespace Neo.UnitTests.Network.RPC.Models +{ + [TestClass] + public class UT_RpcBlock + { + [TestMethod] + public void TestToJson() + { + var rpcBlock = new RpcBlock(); + var block = new Block(); + TestUtils.SetupBlockWithValues(block, UInt256.Zero, out UInt256 _, out UInt160 _, out ulong _, out uint _, out Witness _, out Transaction[] _, 1); + rpcBlock.Block = block; + var json = rpcBlock.ToJson(); + json["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); + json.Should().NotBeNull(); + + rpcBlock.Confirmations = 1; + rpcBlock.NextBlockHash = UInt256.Zero; + json = rpcBlock.ToJson(); + json["confirmations"].AsNumber().Should().Be(1); + } + } +} diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs new file mode 100644 index 0000000000..85a604d3e8 --- /dev/null +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs @@ -0,0 +1,29 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.P2P.Payloads; +using Neo.Network.RPC.Models; +using System; + +namespace Neo.UnitTests.Network.RPC.Models +{ + [TestClass] + public class UT_RpcBlockHeader + { + [TestMethod] + public void TestToJson() + { + var rpcBlockHeader = new RpcBlockHeader(); + var header = new Header(); + TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 _, out UInt160 _, out ulong _, out uint _, out Witness _); + rpcBlockHeader.Header = header; + var json = rpcBlockHeader.ToJson(); + json["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); + json.Should().NotBeNull(); + + rpcBlockHeader.Confirmations = 1; + rpcBlockHeader.NextBlockHash = UInt256.Zero; + json = rpcBlockHeader.ToJson(); + json["confirmations"].AsNumber().Should().Be(1); + } + } +} diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs new file mode 100644 index 0000000000..ee32c1998e --- /dev/null +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs @@ -0,0 +1,66 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.Network.RPC.Models; +using System.Numerics; + +namespace Neo.UnitTests.Network.RPC.Models +{ + [TestClass] + public class UT_RpcNep5Balance + { + private RpcNep5Balance balance; + + [TestInitialize] + public void Setup() + { + balance = new RpcNep5Balance(); + } + + [TestMethod] + public void TestAssetHash() + { + balance.AssetHash = UInt160.Zero; + balance.AssetHash.Should().Be(UInt160.Zero); + } + + [TestMethod] + public void TestAmount() + { + balance.Amount = BigInteger.Zero; + balance.Amount.Should().Be(BigInteger.Zero); + } + + [TestMethod] + public void TestLastUpdatedBlock() + { + balance.LastUpdatedBlock = 0; + balance.LastUpdatedBlock.Should().Be(0); + } + + [TestMethod] + public void TestToJson() + { + balance.AssetHash = UInt160.Zero; + balance.Amount = BigInteger.Zero; + balance.LastUpdatedBlock = 0; + var json = balance.ToJson(); + json["asset_hash"].AsString().Should().Be("0000000000000000000000000000000000000000"); + json["amount"].AsNumber().Should().Be(0); + json["last_updated_block"].AsNumber().Should().Be(0); + } + + [TestMethod] + public void TestFromJson() + { + var json = new JObject(); + json["asset_hash"] = "0000000000000000000000000000000000000000"; + json["amount"] = "0"; + json["last_updated_block"] = "0"; + var rpcNep5Balance = RpcNep5Balance.FromJson(json); + rpcNep5Balance.AssetHash.Should().Be(UInt160.Zero); + rpcNep5Balance.Amount.Should().Be(BigInteger.Zero); + rpcNep5Balance.LastUpdatedBlock.Should().Be(0); + } + } +} diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs new file mode 100644 index 0000000000..57601626ab --- /dev/null +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs @@ -0,0 +1,66 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.Network.RPC.Models; +using System.Numerics; + +namespace Neo.UnitTests.Network.RPC.Models +{ + [TestClass] + public class UT_RpcNep5Balances + { + private RpcNep5Balances balances; + + [TestInitialize] + public void Setup() + { + balances = new RpcNep5Balances() + { + Address = "abc", + Balances = new RpcNep5Balance[] { + new RpcNep5Balance() + { + AssetHash = UInt160.Zero, + Amount = BigInteger.Zero, + LastUpdatedBlock = 0 + }, + new RpcNep5Balance() + { + AssetHash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), + Amount = new BigInteger(1), + LastUpdatedBlock = 1 + } + } + }; + } + + [TestMethod] + public void TestAddress() + { + balances.Address.Should().Be("abc"); + } + + [TestMethod] + public void TestBalances() + { + balances.Balances.Length.Should().Be(2); + } + + [TestMethod] + public void TestToJson() + { + var json = balances.ToJson(); + json["address"].AsString().Should().Be("abc"); + ((JArray)json["balance"]).Count.Should().Be(2); + } + + [TestMethod] + public void TestFromJson() + { + var json = balances.ToJson(); + var rpcNep5Balances = RpcNep5Balances.FromJson(json); + rpcNep5Balances.Address.Should().Be("abc"); + rpcNep5Balances.Balances.Length.Should().Be(2); + } + } +} diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs new file mode 100644 index 0000000000..b5c5044d13 --- /dev/null +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs @@ -0,0 +1,23 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.RPC.Models; + +namespace Neo.UnitTests.Network.RPC.Models +{ + [TestClass] + public class UT_RpcPeer + { + [TestMethod] + public void TestToJson() + { + var rpcPeer = new RpcPeer() + { + Address = "abc", + Port = 800 + }; + var json = rpcPeer.ToJson(); + json["address"].AsString().Should().Be("abc"); + json["port"].AsNumber().Should().Be(800); + } + } +} diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs new file mode 100644 index 0000000000..cb6f6ff611 --- /dev/null +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs @@ -0,0 +1,44 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.Network.RPC.Models; + +namespace Neo.UnitTests.Network.RPC.Models +{ + [TestClass] + public class UT_RpcPeers + { + [TestMethod] + public void TestToJson() + { + var rpcPeers = new RpcPeers() + { + Unconnected = new RpcPeer[] { + new RpcPeer() + { + Address = "Unconnected", + Port = 600 + } + }, + Bad = new RpcPeer[] { + new RpcPeer() + { + Address = "Bad", + Port = 700 + } + }, + Connected = new RpcPeer[] { + new RpcPeer() + { + Address = "Connected", + Port = 800 + } + } + }; + var json = rpcPeers.ToJson(); + ((JArray)json["unconnected"]).Count.Should().Be(1); + ((JArray)json["bad"]).Count.Should().Be(1); + ((JArray)json["connected"]).Count.Should().Be(1); + } + } +} diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs new file mode 100644 index 0000000000..e8cb9bad7e --- /dev/null +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs @@ -0,0 +1,29 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.RPC.Models; + +namespace Neo.UnitTests.Network.RPC.Models +{ + [TestClass] + public class UT_RpcRawMemPool + { + [TestMethod] + public void TestToJson() + { + var pool = new RpcRawMemPool + { + Height = 1, + Verified = new string[] { + "a", "b" + }, + UnVerified = new string[] { + "c", "d" + } + }; + var json = pool.ToJson(); + json["height"].AsNumber().Should().Be(1); + json["verified"].AsString().Should().Be("a,b"); + json["unverified"].AsString().Should().Be("c,d"); + } + } +} diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs new file mode 100644 index 0000000000..8f2a3b4f74 --- /dev/null +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs @@ -0,0 +1,31 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.Network.RPC.Models; + +namespace Neo.UnitTests.Network.RPC.Models +{ + [TestClass] + public class UT_RpcRequest + { + [TestMethod] + public void TestFromJson() + { + var req = new RpcRequest() + { + Id = 1, + Jsonrpc = "myrpc", + Method = "get", + Params = new JObject[] { + new JBoolean(true) + } + }; + var json = req.ToJson(); + var rpcRequest = RpcRequest.FromJson(json); + rpcRequest.Jsonrpc.Should().Be("myrpc"); + rpcRequest.Method.Should().Be("get"); + rpcRequest.Id.Should().Be(1); + rpcRequest.Params.Length.Should().Be(1); + } + } +} diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs new file mode 100644 index 0000000000..5c90eed92d --- /dev/null +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs @@ -0,0 +1,34 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO.Json; +using Neo.Network.RPC.Models; + +namespace Neo.UnitTests.Network.RPC.Models +{ + [TestClass] + public class UT_RpcResponse + { + [TestMethod] + public void TestToJson() + { + var error = new RpcResponseError() + { + Code = 0, + Message = "msg", + Data = new JBoolean(true) + }; + var rep = new RpcResponse() + { + Id = 1, + Jsonrpc = "rpc", + Error = error, + Result = new JBoolean(true) + }; + var json = rep.ToJson(); + json["id"].AsNumber().Should().Be(1); + json["jsonrpc"].AsString().Should().Be("rpc"); + json["error"].AsString().Should().Be(error.ToJson().AsString()); + json["result"].AsBoolean().Should().BeTrue(); + } + } +} diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs new file mode 100644 index 0000000000..cbb9603907 --- /dev/null +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs @@ -0,0 +1,27 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.RPC.Models; + +namespace Neo.UnitTests.Network.RPC.Models +{ + [TestClass] + public class UT_RpcVersion + { + [TestMethod] + public void TestToJson() + { + var version = new RpcVersion() + { + TcpPort = 800, + WsPort = 900, + Nonce = 1, + UserAgent = "agent" + }; + var json = version.ToJson(); + json["topPort"].AsNumber().Should().Be(800); + json["wsPort"].AsNumber().Should().Be(900); + json["nonce"].AsNumber().Should().Be(1); + json["useragent"].AsString().Should().Be("agent"); + } + } +} diff --git a/neo.UnitTests/Network/RPC/UT_RpcClient.cs b/neo.UnitTests/Network/RPC/UT_RpcClient.cs index 7294d3ccb3..eb256cc6d1 100644 --- a/neo.UnitTests/Network/RPC/UT_RpcClient.cs +++ b/neo.UnitTests/Network/RPC/UT_RpcClient.cs @@ -1,3 +1,4 @@ +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Moq.Protected; @@ -108,10 +109,14 @@ public void TestGetBlockHex() { JObject response = CreateResponse(1); response["result"] = "000000002deadfa82cbc4682f5800"; - MockResponse(response.ToString()); + MockResponse(response.ToString()); var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); Assert.AreEqual("000000002deadfa82cbc4682f5800", result); + + MockResponse(response.ToString()); + result = rpc.GetBlockHex("100"); + Assert.AreEqual("000000002deadfa82cbc4682f5800", result); } [TestMethod] @@ -131,14 +136,21 @@ public void TestGetBlock() JObject json = block.ToJson(); JObject response = CreateResponse(1); response["result"] = json; - MockResponse(response.ToString()); + MockResponse(response.ToString()); var result = rpc.GetBlock("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); Assert.IsNull(result.Confirmations); Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); + MockResponse(response.ToString()); + result = rpc.GetBlock("100"); + Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); + Assert.IsNull(result.Confirmations); + Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); + Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); + // verbose with confirmations json["confirmations"] = 20; json["nextblockhash"] = "773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"; @@ -177,10 +189,14 @@ public void TestGetBlockHeaderHex() { JObject response = CreateResponse(1); response["result"] = "0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; - MockResponse(response.ToString()); + MockResponse(response.ToString()); var result = rpc.GetBlockHeaderHex("100"); Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); + + MockResponse(response.ToString()); + result = rpc.GetBlockHeaderHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); } [TestMethod] @@ -198,6 +214,11 @@ public void TestGetBlockHeader() Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); Assert.IsNull(result.Confirmations); + MockResponse(response.ToString()); + result = rpc.GetBlockHeader("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); + Assert.IsNull(result.Confirmations); + json["confirmations"] = 20; json["nextblockhash"] = "4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; MockResponse(response.ToString()); @@ -542,5 +563,14 @@ public void TestValidateAddress() var result = rpc.ValidateAddress("AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"); Assert.AreEqual(json.ToString(), result.ToJson().ToString()); } + + [TestMethod] + public void TestConstructorByUrlAndDispose() + { + //dummy url for test + var client = new RpcClient("http://www.xxx.yyy"); + Action action = () => client.Dispose(); + action.Should().NotThrow(); + } } } diff --git a/neo.UnitTests/Network/RPC/UT_RpcServer.cs b/neo.UnitTests/Network/RPC/UT_RpcServer.cs new file mode 100644 index 0000000000..fae945a21a --- /dev/null +++ b/neo.UnitTests/Network/RPC/UT_RpcServer.cs @@ -0,0 +1,48 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.RPC; +using System; +using System.Net; + +namespace Neo.UnitTests.Network.RPC +{ + [TestClass] + public class UT_RpcServer + { + private RpcServer server; + + [TestInitialize] + public void Setup() + { + var system = TestBlockchain.InitializeMockNeoSystem(); + server = new RpcServer(system); + } + + [TestCleanup] + public void TestDispose() + { + server.Dispose(); + } + + [TestMethod] + public void TestWallet() + { + var wallet = TestUtils.GenerateTestWallet(); + server.Wallet = wallet; + server.Wallet.Should().Be(wallet); + } + + [TestMethod] + public void TestMaxGasInvoke() + { + server.MaxGasInvoke.Should().Be(0); + } + + [TestMethod] + public void TestStart() + { + Action action = () => server.Start(IPAddress.Parse("127.0.0.1"), 8999); + action.Should().NotThrow(); + } + } +} From 24cd3df5d73d4009b406a20ef4f1b77b211b7d5d Mon Sep 17 00:00:00 2001 From: Erik van den Brink Date: Tue, 29 Oct 2019 12:59:17 +0100 Subject: [PATCH 115/305] fix payload limits (#1194) --- neo/Network/P2P/ProtocolHandler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/Network/P2P/ProtocolHandler.cs b/neo/Network/P2P/ProtocolHandler.cs index aa7f0e06f2..ea645b79db 100644 --- a/neo/Network/P2P/ProtocolHandler.cs +++ b/neo/Network/P2P/ProtocolHandler.cs @@ -158,7 +158,7 @@ private void OnGetAddrMessageReceived() private void OnGetBlocksMessageReceived(GetBlocksPayload payload) { UInt256 hash = payload.HashStart; - int count = payload.Count < 0 ? InvPayload.MaxHashesCount : payload.Count; + int count = payload.Count < 0 || payload.Count > InvPayload.MaxHashesCount ? InvPayload.MaxHashesCount : payload.Count; TrimmedBlock state = Blockchain.Singleton.Store.GetBlocks().TryGet(hash); if (state == null) return; List hashes = new List(); @@ -213,7 +213,7 @@ private void OnGetDataMessageReceived(InvPayload payload) private void OnGetHeadersMessageReceived(GetBlocksPayload payload) { UInt256 hash = payload.HashStart; - int count = payload.Count < 0 ? HeadersPayload.MaxHeadersCount : payload.Count; + int count = payload.Count < 0 || payload.Count > HeadersPayload.MaxHeadersCount ? HeadersPayload.MaxHeadersCount : payload.Count; DataCache cache = Blockchain.Singleton.Store.GetBlocks(); TrimmedBlock state = cache.TryGet(hash); if (state == null) return; From a401b372a64e5ef23e5467c51521d4ed951abd6c Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 30 Oct 2019 20:25:47 +0900 Subject: [PATCH 116/305] Fix JsonSerializer (#1197) --- neo.UnitTests/SmartContract/UT_JsonSerializer.cs | 4 ++-- neo.UnitTests/SmartContract/UT_Syscalls.cs | 4 ++-- neo/SmartContract/JsonSerializer.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs index e7fdf30667..9863d72b0d 100644 --- a/neo.UnitTests/SmartContract/UT_JsonSerializer.cs +++ b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs @@ -268,7 +268,7 @@ public void Serialize_Array_Bool_Str_Num() var json = JsonSerializer.Serialize(entry).ToString(); - Assert.AreEqual(json, "[true,\"test\",123]"); + Assert.AreEqual(json, "[true,\"74657374\",123]"); } [TestMethod] @@ -297,7 +297,7 @@ public void Serialize_Array_OfArray() var json = JsonSerializer.Serialize(entry).ToString(); - Assert.AreEqual(json, "[[true,\"test1\",123],[true,\"test2\",321]]"); + Assert.AreEqual(json, "[[true,\"7465737431\",123],[true,\"7465737432\",321]]"); } [TestMethod] diff --git a/neo.UnitTests/SmartContract/UT_Syscalls.cs b/neo.UnitTests/SmartContract/UT_Syscalls.cs index 658d125e7f..d24850de40 100644 --- a/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -76,7 +76,7 @@ public void System_Blockchain_GetBlock() Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); Assert.AreEqual(engine.ResultStack.Pop().GetByteArray().ToHexString(), - "5b22515c7546464644795c75464646445c75464646445c75303030335c75464646445c75464646445c754646464475465c7530303046715c75303132415c625b595c75303434335c75464646445c75464646447d5d767b385c7546464644785c75303032375c75464646445c7546464644222c332c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c322c312c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c315d"); + "5b2235316138373966636161303339626461663437353436306637316334616130383562353964313833616239313764356437363762333865613738323766356266222c332c2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c322c312c2230303030303030303030303030303030303030303030303030303030303030303030303030303030222c315d"); Assert.AreEqual(0, engine.ResultStack.Count); // Clean @@ -127,7 +127,7 @@ public void System_ExecutionEngine_GetScriptContainer() Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); Assert.AreEqual(engine.ResultStack.Pop().GetByteArray().ToHexString(), - @"5b225c7546464644445c7546464644615c7546464644732c5c75464646445c7546464644665c75303030375d5c75303030305c75464646445c75303632325c7546464644545c7546464644375c7530303133335c75303031385c7530303033655c75464646445c75464646445c75303032375a5c75464646445c2f5c7546464644222c362c342c225c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c7546464644222c332c322c352c225c7530303031225d"); + @"5b2266613434383036313834373332636138613136363037356430306133643861326531353462333337313333333138303336356561643732373561663132666264222c362c342c2266666666666666666666666666666666666666666666666666666666666666666666666666666666222c332c322c352c223031225d"); Assert.AreEqual(0, engine.ResultStack.Count); } } diff --git a/neo/SmartContract/JsonSerializer.cs b/neo/SmartContract/JsonSerializer.cs index 1c674540f0..030585420e 100644 --- a/neo/SmartContract/JsonSerializer.cs +++ b/neo/SmartContract/JsonSerializer.cs @@ -26,7 +26,7 @@ public static JObject Serialize(StackItem item) } case ByteArray buffer: { - return buffer.GetString(); + return buffer.GetByteArray().ToHexString(); } case Integer num: { From 93e453fc37a87e8bf959cb5db649177567c0235a Mon Sep 17 00:00:00 2001 From: Igor Machado Coelho Date: Wed, 30 Oct 2019 14:27:43 -0300 Subject: [PATCH 117/305] Fix #1128 (#1129) * Fix #1128 * Update BlockBase.cs --- neo/Network/P2P/Payloads/BlockBase.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/neo/Network/P2P/Payloads/BlockBase.cs index 90b1531cd0..4274cb990a 100644 --- a/neo/Network/P2P/Payloads/BlockBase.cs +++ b/neo/Network/P2P/Payloads/BlockBase.cs @@ -40,7 +40,7 @@ public UInt256 Hash sizeof(ulong) + //Timestamp sizeof(uint) + //Index UInt160.Length + //NextConsensus - 1 + // + 1 + //Witness array count Witness.Size; //Witness Witness[] IVerifiable.Witnesses @@ -59,8 +59,9 @@ Witness[] IVerifiable.Witnesses public virtual void Deserialize(BinaryReader reader) { ((IVerifiable)this).DeserializeUnsigned(reader); - if (reader.ReadByte() != 1) throw new FormatException(); - Witness = reader.ReadSerializable(); + Witness[] witnesses = reader.ReadSerializableArray(1); + if (witnesses.Length != 1) throw new FormatException(); + Witness = witnesses[0]; } void IVerifiable.DeserializeUnsigned(BinaryReader reader) @@ -84,7 +85,7 @@ UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) public virtual void Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write((byte)1); writer.Write(Witness); + writer.Write(new Witness[] { Witness }); } void IVerifiable.SerializeUnsigned(BinaryWriter writer) From 610c4a012d0210aefa3f800c511452b85a4f7536 Mon Sep 17 00:00:00 2001 From: Erik van den Brink Date: Thu, 31 Oct 2019 05:18:20 +0100 Subject: [PATCH 118/305] Add shutdown event for plugins (#1195) * add shutdown event for plugins * use IDisposable * dispose plugins first * Removing brackets --- neo/NeoSystem.cs | 2 ++ neo/Plugins/Plugin.cs | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/neo/NeoSystem.cs b/neo/NeoSystem.cs index 867d4e4201..9265f47645 100644 --- a/neo/NeoSystem.cs +++ b/neo/NeoSystem.cs @@ -42,6 +42,8 @@ public NeoSystem(Store store) public void Dispose() { + foreach (var p in Plugin.Plugins) + p.Dispose(); RpcServer?.Dispose(); EnsureStoped(LocalNode); // Dispose will call ActorSystem.Terminate() diff --git a/neo/Plugins/Plugin.cs b/neo/Plugins/Plugin.cs index aa04b0778d..14345d9de1 100644 --- a/neo/Plugins/Plugin.cs +++ b/neo/Plugins/Plugin.cs @@ -8,7 +8,7 @@ namespace Neo.Plugins { - public abstract class Plugin + public abstract class Plugin : IDisposable { public static readonly List Plugins = new List(); private static readonly List Loggers = new List(); @@ -169,5 +169,9 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven return null; } } + + public virtual void Dispose() + { + } } } From 83053eb8df90c2f2683b47c6cfad87b7cffacc5d Mon Sep 17 00:00:00 2001 From: Qiao-Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Tue, 5 Nov 2019 15:22:07 +0800 Subject: [PATCH 119/305] Replace function exceptwith and unionwith with faster functions (#1174) * Replace ExceptWith & UnionWith with equal but faster functionality * Optimization * Optimization * Optimize remove * Update neo/Network/P2P/TaskManager.cs Co-Authored-By: Erik Zhang * Code optimization * Update Helper.cs * Small change * Optimization * Update Helper.cs * Revert * Optimization * Optimize FIFOSet * Rename * Inline * Update UT_FIFOSet.cs * Fix * Update UT_FIFOSet.cs * Update FIFOSet.cs * Update FIFOSet.cs * Revert FIFOSet * Update Helper.cs * Optimize * Reverting independet byte checks to SequenceEqual --- neo.UnitTests/IO/Caching/UT_FIFOSet.cs | 18 ++++++++++++ neo/Helper.cs | 39 +++++++++++++++++++++++++- neo/IO/Caching/FIFOSet.cs | 10 ++++--- neo/Network/P2P/TaskManager.cs | 8 +++--- neo/UIntBase.cs | 2 ++ 5 files changed, 68 insertions(+), 9 deletions(-) diff --git a/neo.UnitTests/IO/Caching/UT_FIFOSet.cs b/neo.UnitTests/IO/Caching/UT_FIFOSet.cs index e6e01710d9..d785127778 100644 --- a/neo.UnitTests/IO/Caching/UT_FIFOSet.cs +++ b/neo.UnitTests/IO/Caching/UT_FIFOSet.cs @@ -144,6 +144,24 @@ public void TestExceptWith() }; set.ExceptWith(new UInt256[] { b, c }); CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a }); + + set = new FIFOSet(10) + { + a, + b, + c + }; + set.ExceptWith(new UInt256[] { a }); + CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { b, c }); + + set = new FIFOSet(10) + { + a, + b, + c + }; + set.ExceptWith(new UInt256[] { c }); + CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a, b }); } } } diff --git a/neo/Helper.cs b/neo/Helper.cs index 1a513ec79e..fa3b384a35 100644 --- a/neo/Helper.cs +++ b/neo/Helper.cs @@ -1,5 +1,5 @@ using Microsoft.Extensions.Configuration; -using Neo.Plugins; +using Neo.IO.Caching; using System; using System.Collections.Generic; using System.Globalization; @@ -54,6 +54,43 @@ internal static int GetLowestSetBit(this BigInteger i) throw new Exception(); } + internal static void Remove(this HashSet set, ISet other) + { + if (set.Count > other.Count) + { + set.ExceptWith(other); + } + else + { + set.RemoveWhere(u => other.Contains(u)); + } + } + + internal static void Remove(this HashSet set, FIFOSet other) + where T : IEquatable + { + if (set.Count > other.Count) + { + set.ExceptWith(other); + } + else + { + set.RemoveWhere(u => other.Contains(u)); + } + } + + internal static void Remove(this HashSet set, IReadOnlyDictionary other) + { + if (set.Count > other.Count) + { + set.ExceptWith(other.Keys); + } + else + { + set.RemoveWhere(u => other.ContainsKey(u)); + } + } + internal static string GetVersion(this Assembly assembly) { CustomAttributeData attribute = assembly.CustomAttributes.FirstOrDefault(p => p.AttributeType == typeof(AssemblyInformationalVersionAttribute)); diff --git a/neo/IO/Caching/FIFOSet.cs b/neo/IO/Caching/FIFOSet.cs index 98733ce444..af65db1b8b 100644 --- a/neo/IO/Caching/FIFOSet.cs +++ b/neo/IO/Caching/FIFOSet.cs @@ -6,12 +6,14 @@ namespace Neo.IO.Caching { - internal class FIFOSet : IEnumerable where T : IEquatable + internal class FIFOSet : IReadOnlyCollection where T : IEquatable { private readonly int maxCapacity; private readonly int removeCount; private readonly OrderedDictionary dictionary; + public int Count => dictionary.Count; + public FIFOSet(int maxCapacity, decimal batchSize = 0.1m) { if (maxCapacity <= 0) throw new ArgumentOutOfRangeException(nameof(maxCapacity)); @@ -46,11 +48,11 @@ public bool Contains(T item) return dictionary.Contains(item); } - public void ExceptWith(IEnumerable hashes) + public void ExceptWith(IEnumerable entries) { - foreach (var hash in hashes) + foreach (var entry in entries) { - dictionary.Remove(hash); + dictionary.Remove(entry); } } diff --git a/neo/Network/P2P/TaskManager.cs b/neo/Network/P2P/TaskManager.cs index 2c0f7ae07b..82e2e9b87a 100644 --- a/neo/Network/P2P/TaskManager.cs +++ b/neo/Network/P2P/TaskManager.cs @@ -58,11 +58,11 @@ private void OnNewTasks(InvPayload payload) return; } HashSet hashes = new HashSet(payload.Hashes); - hashes.ExceptWith(knownHashes); + hashes.Remove(knownHashes); if (payload.Type == InventoryType.Block) session.AvailableTasks.UnionWith(hashes.Where(p => globalTasks.ContainsKey(p))); - hashes.ExceptWith(globalTasks.Keys); + hashes.Remove(globalTasks); if (hashes.Count == 0) { RequestTasks(session); @@ -203,7 +203,7 @@ private void RequestTasks(TaskSession session) if (session.HasTask) return; if (session.AvailableTasks.Count > 0) { - session.AvailableTasks.ExceptWith(knownHashes); + session.AvailableTasks.Remove(knownHashes); session.AvailableTasks.RemoveWhere(p => Blockchain.Singleton.ContainsBlock(p)); HashSet hashes = new HashSet(session.AvailableTasks); if (hashes.Count > 0) @@ -213,7 +213,7 @@ private void RequestTasks(TaskSession session) if (!IncrementGlobalTask(hash)) hashes.Remove(hash); } - session.AvailableTasks.ExceptWith(hashes); + session.AvailableTasks.Remove(hashes); foreach (UInt256 hash in hashes) session.Tasks[hash] = DateTime.UtcNow; foreach (InvPayload group in InvPayload.CreateGroup(InventoryType.Block, hashes.ToArray())) diff --git a/neo/UIntBase.cs b/neo/UIntBase.cs index af26e36d0a..6b5f6d6acd 100644 --- a/neo/UIntBase.cs +++ b/neo/UIntBase.cs @@ -2,6 +2,7 @@ using System; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; namespace Neo { @@ -110,6 +111,7 @@ void ISerializable.Serialize(BinaryWriter writer) /// /// Method ToArray() returns the byte array data_bytes, which stores the little-endian unsigned int /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public byte[] ToArray() { return data_bytes; From 09cb4fad22a211d0ffddcfbda6947510411d6322 Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Wed, 6 Nov 2019 11:13:31 +0800 Subject: [PATCH 120/305] Unit tests for some auxiliary classes (#1192) --- neo.UnitTests/IO/UT_ByteArrayComparer.cs | 27 ++ neo.UnitTests/Plugins/TestLogPlugin.cs | 51 ++++ neo.UnitTests/Plugins/UT_Plugin.cs | 83 +++++ neo.UnitTests/UT_Helper.cs | 261 ++++++++++++++++ neo.UnitTests/UT_NefFile.cs | 75 ----- neo.UnitTests/UT_NeoSystem.cs | 32 ++ neo.UnitTests/UT_ProtocolSettings.cs | 18 ++ neo.UnitTests/UT_UInt160.cs | 94 ++++++ neo.UnitTests/UT_UInt256.cs | 94 ++++++ neo.UnitTests/UT_UIntBase.cs | 102 +++++++ neo.UnitTests/VM/UT_Helper.cs | 373 +++++++++++++++++++++++ 11 files changed, 1135 insertions(+), 75 deletions(-) create mode 100644 neo.UnitTests/IO/UT_ByteArrayComparer.cs create mode 100644 neo.UnitTests/Plugins/TestLogPlugin.cs create mode 100644 neo.UnitTests/Plugins/UT_Plugin.cs delete mode 100644 neo.UnitTests/UT_NefFile.cs create mode 100644 neo.UnitTests/UT_NeoSystem.cs create mode 100644 neo.UnitTests/UT_UInt160.cs create mode 100644 neo.UnitTests/UT_UInt256.cs create mode 100644 neo.UnitTests/UT_UIntBase.cs diff --git a/neo.UnitTests/IO/UT_ByteArrayComparer.cs b/neo.UnitTests/IO/UT_ByteArrayComparer.cs new file mode 100644 index 0000000000..dab1adcd8b --- /dev/null +++ b/neo.UnitTests/IO/UT_ByteArrayComparer.cs @@ -0,0 +1,27 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; + +namespace Neo.UnitTests.IO +{ + [TestClass] + public class UT_ByteArrayComparer + { + [TestMethod] + public void TestCompare() + { + ByteArrayComparer comparer = new ByteArrayComparer(); + byte[] x = new byte[0], y = new byte[0]; + comparer.Compare(x, y).Should().Be(0); + + x = new byte[] { 1 }; + comparer.Compare(x, y).Should().Be(1); + y = x; + comparer.Compare(x, y).Should().Be(0); + + x = new byte[] { 1 }; + y = new byte[] { 2 }; + comparer.Compare(x, y).Should().Be(-1); + } + } +} diff --git a/neo.UnitTests/Plugins/TestLogPlugin.cs b/neo.UnitTests/Plugins/TestLogPlugin.cs new file mode 100644 index 0000000000..d9f0c824d2 --- /dev/null +++ b/neo.UnitTests/Plugins/TestLogPlugin.cs @@ -0,0 +1,51 @@ +using Microsoft.Extensions.Configuration; +using Neo.Plugins; + +namespace Neo.UnitTests.Plugins +{ + public class TestLogPlugin : Plugin, ILogPlugin + { + public TestLogPlugin() : base() { } + + public string Output { set; get; } + + public override void Configure() { } + + public new void Log(string source, LogLevel level, string message) + { + Output = source + "_" + level.ToString() + "_" + message; + } + + public void LogMessage(string message) + { + Log(message); + } + + public bool TestOnMessage(object message) + { + return OnMessage(message); + } + + public IConfigurationSection TestGetConfiguration() + { + return GetConfiguration(); + } + + protected override bool OnMessage(object message) => true; + + public static bool TestResumeNodeStartup() + { + return ResumeNodeStartup(); + } + + public static void TestSuspendNodeStartup() + { + SuspendNodeStartup(); + } + + public static void TestLoadPlugins(NeoSystem system) + { + LoadPlugins(system); + } + } +} diff --git a/neo.UnitTests/Plugins/UT_Plugin.cs b/neo.UnitTests/Plugins/UT_Plugin.cs new file mode 100644 index 0000000000..3c6098e47a --- /dev/null +++ b/neo.UnitTests/Plugins/UT_Plugin.cs @@ -0,0 +1,83 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Plugins; +using System; + +namespace Neo.UnitTests.Plugins +{ + [TestClass] + public class UT_Plugin + { + private static readonly object locker = new object(); + + [TestMethod] + public void TestGetConfigFile() + { + var pp = new TestLogPlugin(); + var file = pp.ConfigFile; + file.EndsWith("config.json").Should().BeTrue(); + } + + [TestMethod] + public void TestGetName() + { + var pp = new TestLogPlugin(); + pp.Name.Should().Be("TestLogPlugin"); + } + + [TestMethod] + public void TestGetVersion() + { + var pp = new TestLogPlugin(); + Action action = () => pp.Version.ToString(); + action.Should().NotThrow(); + } + + [TestMethod] + public void TestLog() + { + var lp = new TestLogPlugin(); + lp.LogMessage("Hello"); + lp.Output.Should().Be("Plugin:TestLogPlugin_Info_Hello"); + } + + [TestMethod] + public void TestSendMessage() + { + lock (locker) + { + Plugin.Plugins.Clear(); + Plugin.SendMessage("hey1").Should().BeFalse(); + + var lp = new TestLogPlugin(); + Plugin.SendMessage("hey2").Should().BeTrue(); + } + } + + [TestMethod] + public void TestNotifyPluginsLoadedAfterSystemConstructed() + { + var pp = new TestLogPlugin(); + Action action = () => Plugin.NotifyPluginsLoadedAfterSystemConstructed(); + action.Should().NotThrow(); + } + + [TestMethod] + public void TestResumeNodeStartupAndSuspendNodeStartup() + { + var system = TestBlockchain.InitializeMockNeoSystem(); + TestLogPlugin.TestLoadPlugins(system); + TestLogPlugin.TestSuspendNodeStartup(); + TestLogPlugin.TestSuspendNodeStartup(); + TestLogPlugin.TestResumeNodeStartup().Should().BeFalse(); + TestLogPlugin.TestResumeNodeStartup().Should().BeTrue(); + } + + [TestMethod] + public void TestGetConfiguration() + { + var pp = new TestLogPlugin(); + pp.TestGetConfiguration().Key.Should().Be("PluginConfiguration"); + } + } +} diff --git a/neo.UnitTests/UT_Helper.cs b/neo.UnitTests/UT_Helper.cs index aaad993bb1..45da27146c 100644 --- a/neo.UnitTests/UT_Helper.cs +++ b/neo.UnitTests/UT_Helper.cs @@ -3,6 +3,11 @@ using Neo.Network.P2P; using Neo.SmartContract; using Neo.Wallets; +using System; +using System.Collections.Generic; +using System.Net; +using System.Numerics; +using System.Security.Cryptography; namespace Neo.UnitTests { @@ -38,5 +43,261 @@ public void ToScriptHash() res.Should().Be(UInt160.Parse("2d3b96ae1bcc5a585e075e3b81920210dec16302")); } + [TestMethod] + public void TestGetLowestSetBit() + { + var big1 = new BigInteger(0); + big1.GetLowestSetBit().Should().Be(-1); + + var big2 = new BigInteger(512); + big2.GetLowestSetBit().Should().Be(9); + } + + [TestMethod] + public void TestGetBitLength() + { + var b1 = new BigInteger(100); + b1.GetBitLength().Should().Be(7); + + var b2 = new BigInteger(-100); + b2.GetBitLength().Should().Be(7); + } + + [TestMethod] + public void TestHexToBytes() + { + string nullStr = null; + nullStr.HexToBytes().ToHexString().Should().Be(new byte[0].ToHexString()); + string emptyStr = ""; + emptyStr.HexToBytes().ToHexString().Should().Be(new byte[0].ToHexString()); + string str1 = "hab"; + Action action = () => str1.HexToBytes(); + action.Should().Throw(); + string str2 = "0102"; + byte[] bytes = str2.HexToBytes(); + bytes.ToHexString().Should().Be(new byte[] { 0x01, 0x02 }.ToHexString()); + } + + [TestMethod] + public void TestNextBigIntegerForRandom() + { + Random ran = new Random(); + Action action1 = () => ran.NextBigInteger(-1); + action1.Should().Throw(); + + ran.NextBigInteger(0).Should().Be(0); + ran.NextBigInteger(8).Should().NotBeNull(); + ran.NextBigInteger(9).Should().NotBeNull(); + } + + [TestMethod] + public void TestNextBigIntegerForRandomNumberGenerator() + { + var ran = RandomNumberGenerator.Create(); + Action action1 = () => ran.NextBigInteger(-1); + action1.Should().Throw(); + + ran.NextBigInteger(0).Should().Be(0); + ran.NextBigInteger(8).Should().NotBeNull(); + ran.NextBigInteger(9).Should().NotBeNull(); + } + + [TestMethod] + public void TestToInt64() + { + byte[] bytes = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var ret = bytes.ToInt64(0); + ret.GetType().Should().Be(typeof(long)); + ret.Should().Be(67305985); + } + + [TestMethod] + public void TestToUInt16() + { + byte[] bytes = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var ret = bytes.ToUInt16(0); + ret.GetType().Should().Be(typeof(ushort)); + ret.Should().Be(513); + } + + [TestMethod] + public void TestToUInt64() + { + byte[] bytes = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var ret = bytes.ToUInt64(0); + ret.GetType().Should().Be(typeof(ulong)); + ret.Should().Be(67305985); + } + + [TestMethod] + public void TestUnmapForIPAddress() + { + var addr = new IPAddress(new byte[] { 127, 0, 0, 1 }); + addr.Unmap().Should().Be(addr); + + var addr2 = addr.MapToIPv6(); + addr2.Unmap().Should().Be(addr); + } + + [TestMethod] + public void TestUnmapForIPEndPoin() + { + var addr = new IPAddress(new byte[] { 127, 0, 0, 1 }); + var endPoint = new IPEndPoint(addr, 8888); + endPoint.Unmap().Should().Be(endPoint); + + var addr2 = addr.MapToIPv6(); + var endPoint2 = new IPEndPoint(addr2, 8888); + endPoint2.Unmap().Should().Be(endPoint); + } + + [TestMethod] + public void TestWeightedAverage() + { + var foo1 = new Foo + { + Value = 1, + Weight = 2 + }; + var foo2 = new Foo + { + Value = 2, + Weight = 3 + }; + var list = new List + { + foo1,foo2 + }; + list.WeightedAverage(p => p.Value, p => p.Weight).Should().Be(new BigInteger(1)); + + var foo3 = new Foo + { + Value = 1, + Weight = 0 + }; + var foo4 = new Foo + { + Value = 2, + Weight = 0 + }; + var list2 = new List + { + foo3, foo4 + }; + list2.WeightedAverage(p => p.Value, p => p.Weight).Should().Be(BigInteger.Zero); + } + + [TestMethod] + public void WeightFilter() + { + var w1 = new Woo + { + Value = 1 + }; + var w2 = new Woo + { + Value = 2 + }; + var list = new List + { + w1, w2 + }; + var ret = list.WeightedFilter(0.3, 0.6, p => p.Value, (p, w) => new Result + { + Info = p, + Weight = w + }); + var sum = BigInteger.Zero; + foreach (Result res in ret) + { + sum = BigInteger.Add(res.Weight, sum); + } + sum.Should().Be(BigInteger.Zero); + + var w3 = new Woo + { + Value = 3 + }; + + var list2 = new List + { + w1, w2, w3 + }; + var ret2 = list2.WeightedFilter(0.3, 0.4, p => p.Value, (p, w) => new Result + { + Info = p, + Weight = w + }); + sum = BigInteger.Zero; + foreach (Result res in ret2) + { + sum = BigInteger.Add(res.Weight, sum); + } + sum.Should().Be(BigInteger.Zero); + + CheckArgumentOutOfRangeException(-1, 0.4, p => p.Value, list2); + + CheckArgumentOutOfRangeException(0.2, 1.4, p => p.Value, list2); + + CheckArgumentOutOfRangeException(0.8, 0.3, p => p.Value, list2); + + CheckArgumentOutOfRangeException(0.3, 0.8, p => p.Value, list2); + + CheckArgumentNullException(0.3, 0.6, null, list2); + + CheckArgumentNullException(0.3, 0.4, p => p.Value, null); + + list2.WeightedFilter(0.3, 0.3, p => p.Value, (p, w) => new Result + { + Info = p, + Weight = w + }).WeightedAverage(p => p.Weight, p => p.Weight).Should().Be(0); + + + var list3 = new List(); + list3.WeightedFilter(0.3, 0.6, p => p.Value, (p, w) => new Result + { + Info = p, + Weight = w + }).WeightedAverage(p => p.Weight, p => p.Weight).Should().Be(0); + + } + + private static void CheckArgumentOutOfRangeException(double start, double end, Func func, List list) + { + Action action = () => list.WeightedFilter(start, end, func, (p, w) => new Result + { + Info = p, + Weight = w + }).WeightedAverage(p => p.Weight, p => p.Weight); + action.Should().Throw(); + } + + private static void CheckArgumentNullException(double start, double end, Func func, List list) + { + Action action = () => list.WeightedFilter(start, end, func, (p, w) => new Result + { + Info = p, + Weight = w + }).WeightedAverage(p => p.Weight, p => p.Weight); + action.Should().Throw(); + } + } + + class Foo + { + public int Weight { set; get; } + public int Value { set; get; } + } + + class Woo + { + public int Value { set; get; } + } + + class Result + { + public Woo Info { set; get; } + public BigInteger Weight { set; get; } } } diff --git a/neo.UnitTests/UT_NefFile.cs b/neo.UnitTests/UT_NefFile.cs deleted file mode 100644 index 056333163d..0000000000 --- a/neo.UnitTests/UT_NefFile.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Cryptography; -using Neo.IO; -using Neo.SmartContract; -using System; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_NefFile - { - [TestMethod] - public void ParseTest() - { - var file = new NefFile() - { - Compiler = "".PadLeft(32, ' '), - Version = new Version(1, 2, 3, 4), - Script = new byte[] { 0x01, 0x02, 0x03 } - }; - - file.ScriptHash = file.Script.ToScriptHash(); - file.CheckSum = NefFile.ComputeChecksum(file); - - var data = file.ToArray(); - file = data.AsSerializable(); - - Assert.AreEqual("".PadLeft(32, ' '), file.Compiler); - Assert.AreEqual(new Version(1, 2, 3, 4), file.Version); - Assert.AreEqual(file.Script.ToScriptHash(), file.ScriptHash); - CollectionAssert.AreEqual(new byte[] { 0x01, 0x02, 0x03 }, file.Script); - } - - [TestMethod] - public void LimitTest() - { - var file = new NefFile() - { - Compiler = "".PadLeft(byte.MaxValue, ' '), - Version = new Version(1, 2, 3, 4), - Script = new byte[1024 * 1024], - ScriptHash = new byte[1024 * 1024].ToScriptHash(), - CheckSum = 0 - }; - - // Wrong compiler - - Assert.ThrowsException(() => file.ToArray()); - - // Wrong script - - file.Compiler = ""; - file.Script = new byte[(1024 * 1024) + 1]; - file.ScriptHash = file.Script.ToScriptHash(); - var data = file.ToArray(); - - Assert.ThrowsException(() => data.AsSerializable()); - - // Wrong script hash - - file.Script = new byte[1024 * 1024]; - data = file.ToArray(); - - Assert.ThrowsException(() => data.AsSerializable()); - - // Wrong checksum - - file.Script = new byte[1024]; - data = file.ToArray(); - file.CheckSum = NefFile.ComputeChecksum(file) + 1; - - Assert.ThrowsException(() => data.AsSerializable()); - } - } -} diff --git a/neo.UnitTests/UT_NeoSystem.cs b/neo.UnitTests/UT_NeoSystem.cs new file mode 100644 index 0000000000..2d71db97df --- /dev/null +++ b/neo.UnitTests/UT_NeoSystem.cs @@ -0,0 +1,32 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_NeoSystem + { + private NeoSystem neoSystem; + + [TestInitialize] + public void Setup() + { + neoSystem = TestBlockchain.InitializeMockNeoSystem(); + } + + [TestMethod] + public void TestGetBlockchain() => neoSystem.Blockchain.Should().NotBeNull(); + + [TestMethod] + public void TestGetLocalNode() => neoSystem.LocalNode.Should().NotBeNull(); + + [TestMethod] + public void TestGetTaskManager() => neoSystem.TaskManager.Should().NotBeNull(); + + [TestMethod] + public void TestGetConsensus() => neoSystem.Consensus.Should().BeNull(); + + [TestMethod] + public void TestGetRpcServer() => neoSystem.RpcServer.Should().BeNull(); + } +} diff --git a/neo.UnitTests/UT_ProtocolSettings.cs b/neo.UnitTests/UT_ProtocolSettings.cs index 77c56df424..2e1b8c3143 100644 --- a/neo.UnitTests/UT_ProtocolSettings.cs +++ b/neo.UnitTests/UT_ProtocolSettings.cs @@ -90,5 +90,23 @@ public void Cant_initialize_ProtocolSettings_twice() ProtocolSettings.Initialize(config).Should().BeFalse(); ProtocolSettings.Default.Magic.Should().Be(expectedMagic); } + + [TestMethod] + public void TestGetMemoryPoolMaxTransactions() + { + ProtocolSettings.Default.MemoryPoolMaxTransactions.Should().Be(50000); + } + + [TestMethod] + public void TestGetMillisecondsPerBlock() + { + ProtocolSettings.Default.MillisecondsPerBlock.Should().Be(2000); + } + + [TestMethod] + public void TestGetSeedList() + { + ProtocolSettings.Default.SeedList.Should().BeEquivalentTo(new string[] { "seed1.neo.org:10333", "seed2.neo.org:10333", "seed3.neo.org:10333", "seed4.neo.org:10333", "seed5.neo.org:10333", }); + } } } diff --git a/neo.UnitTests/UT_UInt160.cs b/neo.UnitTests/UT_UInt160.cs new file mode 100644 index 0000000000..99cf91a5c3 --- /dev/null +++ b/neo.UnitTests/UT_UInt160.cs @@ -0,0 +1,94 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace Neo.UnitTests.IO +{ + [TestClass] + public class UT_UInt160 + { + [TestMethod] + public void TestGernerator1() + { + UInt160 uInt160 = new UInt160(); + Assert.IsNotNull(uInt160); + } + + [TestMethod] + public void TestGernerator2() + { + UInt160 uInt160 = new UInt160(new byte[20]); + Assert.IsNotNull(uInt160); + } + + [TestMethod] + public void TestCompareTo() + { + byte[] temp = new byte[20]; + temp[19] = 0x01; + UInt160 result = new UInt160(temp); + Assert.AreEqual(0, UInt160.Zero.CompareTo(UInt160.Zero)); + Assert.AreEqual(-1, UInt160.Zero.CompareTo(result)); + Assert.AreEqual(1, result.CompareTo(UInt160.Zero)); + } + + [TestMethod] + public void TestEquals() + { + byte[] temp = new byte[20]; + temp[19] = 0x01; + UInt160 result = new UInt160(temp); + Assert.AreEqual(true, UInt160.Zero.Equals(UInt160.Zero)); + Assert.AreEqual(false, UInt160.Zero.Equals(result)); + Assert.AreEqual(false, result.Equals(null)); + } + + [TestMethod] + public void TestParse() + { + Action action = () => UInt160.Parse(null); + action.Should().Throw(); + UInt160 result = UInt160.Parse("0x0000000000000000000000000000000000000000"); + Assert.AreEqual(UInt160.Zero, result); + Action action1 = () => UInt160.Parse("000000000000000000000000000000000000000"); + action1.Should().Throw(); + UInt160 result1 = UInt160.Parse("0000000000000000000000000000000000000000"); + Assert.AreEqual(UInt160.Zero, result1); + } + + [TestMethod] + public void TestTryParse() + { + UInt160 temp = new UInt160(); + Assert.AreEqual(false, UInt160.TryParse(null, out temp)); + Assert.AreEqual(true, UInt160.TryParse("0x0000000000000000000000000000000000000000", out temp)); + Assert.AreEqual(UInt160.Zero, temp); + Assert.AreEqual(false, UInt160.TryParse("000000000000000000000000000000000000000", out temp)); + Assert.AreEqual(false, UInt160.TryParse("0xKK00000000000000000000000000000000000000", out temp)); + } + + [TestMethod] + public void TestOperatorLarger() + { + Assert.AreEqual(false, UInt160.Zero > UInt160.Zero); + } + + [TestMethod] + public void TestOperatorLargerAndEqual() + { + Assert.AreEqual(true, UInt160.Zero >= UInt160.Zero); + } + + [TestMethod] + public void TestOperatorSmaller() + { + Assert.AreEqual(false, UInt160.Zero < UInt160.Zero); + } + + [TestMethod] + public void TestOperatorSmallerAndEqual() + { + Assert.AreEqual(true, UInt160.Zero <= UInt160.Zero); + } + } +} diff --git a/neo.UnitTests/UT_UInt256.cs b/neo.UnitTests/UT_UInt256.cs new file mode 100644 index 0000000000..179e991110 --- /dev/null +++ b/neo.UnitTests/UT_UInt256.cs @@ -0,0 +1,94 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace Neo.UnitTests.IO +{ + [TestClass] + public class UT_UInt256 + { + [TestMethod] + public void TestGernerator1() + { + UInt256 uInt256 = new UInt256(); + Assert.IsNotNull(uInt256); + } + + [TestMethod] + public void TestGernerator2() + { + UInt256 uInt256 = new UInt256(new byte[32]); + Assert.IsNotNull(uInt256); + } + + [TestMethod] + public void TestCompareTo() + { + byte[] temp = new byte[32]; + temp[31] = 0x01; + UInt256 result = new UInt256(temp); + Assert.AreEqual(0, UInt256.Zero.CompareTo(UInt256.Zero)); + Assert.AreEqual(-1, UInt256.Zero.CompareTo(result)); + Assert.AreEqual(1, result.CompareTo(UInt256.Zero)); + } + + [TestMethod] + public void TestEquals() + { + byte[] temp = new byte[32]; + temp[31] = 0x01; + UInt256 result = new UInt256(temp); + Assert.AreEqual(true, UInt256.Zero.Equals(UInt256.Zero)); + Assert.AreEqual(false, UInt256.Zero.Equals(result)); + Assert.AreEqual(false, result.Equals(null)); + } + + [TestMethod] + public void TestParse() + { + Action action = () => UInt256.Parse(null); + action.Should().Throw(); + UInt256 result = UInt256.Parse("0x0000000000000000000000000000000000000000000000000000000000000000"); + Assert.AreEqual(UInt256.Zero, result); + Action action1 = () => UInt256.Parse("000000000000000000000000000000000000000000000000000000000000000"); + action1.Should().Throw(); + UInt256 result1 = UInt256.Parse("0000000000000000000000000000000000000000000000000000000000000000"); + Assert.AreEqual(UInt256.Zero, result1); + } + + [TestMethod] + public void TestTryParse() + { + UInt256 temp = new UInt256(); + Assert.AreEqual(false, UInt256.TryParse(null, out temp)); + Assert.AreEqual(true, UInt256.TryParse("0x0000000000000000000000000000000000000000000000000000000000000000", out temp)); + Assert.AreEqual(UInt256.Zero, temp); + Assert.AreEqual(false, UInt256.TryParse("000000000000000000000000000000000000000000000000000000000000000", out temp)); + Assert.AreEqual(false, UInt256.TryParse("0xKK00000000000000000000000000000000000000000000000000000000000000", out temp)); + } + + [TestMethod] + public void TestOperatorLarger() + { + Assert.AreEqual(false, UInt256.Zero > UInt256.Zero); + } + + [TestMethod] + public void TestOperatorLargerAndEqual() + { + Assert.AreEqual(true, UInt256.Zero >= UInt256.Zero); + } + + [TestMethod] + public void TestOperatorSmaller() + { + Assert.AreEqual(false, UInt256.Zero < UInt256.Zero); + } + + [TestMethod] + public void TestOperatorSmallerAndEqual() + { + Assert.AreEqual(true, UInt256.Zero <= UInt256.Zero); + } + } +} diff --git a/neo.UnitTests/UT_UIntBase.cs b/neo.UnitTests/UT_UIntBase.cs new file mode 100644 index 0000000000..516b3d0504 --- /dev/null +++ b/neo.UnitTests/UT_UIntBase.cs @@ -0,0 +1,102 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using System; +using System.IO; + +namespace Neo.UnitTests.IO +{ + [TestClass] + public class UT_UIntBase + { + [TestMethod] + public void TestDeserialize() + { + using (MemoryStream stream = new MemoryStream()) + using (BinaryWriter writer = new BinaryWriter(stream)) + using (BinaryReader reader = new BinaryReader(stream)) + { + writer.Write(new byte[20]); + stream.Seek(0, SeekOrigin.Begin); + MyUIntBase uIntBase = new MyUIntBase(); + Action action = () => ((ISerializable)uIntBase).Deserialize(reader); + action.Should().Throw(); + } + } + + [TestMethod] + public void TestEquals1() + { + MyUIntBase temp1 = new MyUIntBase(); + MyUIntBase temp2 = new MyUIntBase(); + UInt160 temp3 = new UInt160(); + Assert.AreEqual(false, temp1.Equals(null)); + Assert.AreEqual(true, temp1.Equals(temp1)); + Assert.AreEqual(true, temp1.Equals(temp2)); + Assert.AreEqual(false, temp1.Equals(temp3)); + } + + [TestMethod] + public void TestEquals2() + { + MyUIntBase temp1 = new MyUIntBase(); + object temp2 = null; + object temp3 = new object(); + Assert.AreEqual(false, temp1.Equals(temp2)); + Assert.AreEqual(false, temp1.Equals(temp3)); + } + + [TestMethod] + public void TestParse() + { + UInt160 uInt1601 = (UInt160)UIntBase.Parse("0x0000000000000000000000000000000000000000"); + UInt256 uInt2561 = (UInt256)UIntBase.Parse("0x0000000000000000000000000000000000000000000000000000000000000000"); + UInt160 uInt1602 = (UInt160)UIntBase.Parse("0000000000000000000000000000000000000000"); + UInt256 uInt2562 = (UInt256)UIntBase.Parse("0000000000000000000000000000000000000000000000000000000000000000"); + Assert.AreEqual(UInt160.Zero, uInt1601); + Assert.AreEqual(UInt256.Zero, uInt2561); + Assert.AreEqual(UInt160.Zero, uInt1602); + Assert.AreEqual(UInt256.Zero, uInt2562); + Action action = () => UIntBase.Parse("0000000"); + action.Should().Throw(); + } + + [TestMethod] + public void TestTryParse() + { + UInt160 uInt160 = new UInt160(); + Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000", out uInt160)); + Assert.AreEqual(UInt160.Zero, uInt160); + Assert.AreEqual(false, UIntBase.TryParse("0x00000000000000000000000000000000000000", out uInt160)); + UInt256 uInt256 = new UInt256(); + Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000000000000000000000000000", out uInt256)); + Assert.AreEqual(UInt256.Zero, uInt256); + Assert.AreEqual(false, UIntBase.TryParse("0x00000000000000000000000000000000000000000000000000000000000000", out uInt256)); + UIntBase uIntBase = new UInt160(); + Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000", out uIntBase)); + Assert.AreEqual(UInt160.Zero, uIntBase); + Assert.AreEqual(true, UIntBase.TryParse("0000000000000000000000000000000000000000", out uIntBase)); + Assert.AreEqual(UInt160.Zero, uIntBase); + uIntBase = new UInt256(); + Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000000000000000000000000000", out uIntBase)); + Assert.AreEqual(UInt256.Zero, uIntBase); + Assert.AreEqual(true, UIntBase.TryParse("0000000000000000000000000000000000000000000000000000000000000000", out uIntBase)); + Assert.AreEqual(UInt256.Zero, uIntBase); + Assert.AreEqual(false, UIntBase.TryParse("00000000000000000000000000000000000000000000000000000000000000", out uIntBase)); + } + + [TestMethod] + public void TestOperatorEqual() + { + Assert.AreEqual(false, new MyUIntBase() == null); + Assert.AreEqual(false, null == new MyUIntBase()); + } + } + + internal class MyUIntBase : UIntBase + { + public const int Length = 32; + public MyUIntBase() : this(null) { } + public MyUIntBase(byte[] value) : base(Length, value) { } + } +} diff --git a/neo.UnitTests/VM/UT_Helper.cs b/neo.UnitTests/VM/UT_Helper.cs index cf5ad0bff5..39f1ec104a 100644 --- a/neo.UnitTests/VM/UT_Helper.cs +++ b/neo.UnitTests/VM/UT_Helper.cs @@ -1,3 +1,4 @@ +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.SmartContract; @@ -138,5 +139,377 @@ public void TestToStackItem() ContractParameter mapParameter = new ContractParameter { Type = ContractParameterType.Map, Value = new[] { new KeyValuePair(byteParameter, pkParameter) } }; Assert.AreEqual(30000000000000L, (long)((VM.Types.Map)mapParameter.ToStackItem()).Keys.First().GetBigInteger()); } + + [TestMethod] + public void TestEmitPush1() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(UInt160.Zero); + byte[] tempArray = new byte[21]; + tempArray[0] = 0x14; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + [TestMethod] + public void TestEmitPush2() + { + TestEmitPush2Signature(); + TestEmitPush2ByteArray(); + TestEmitPush2Boolean(); + TestEmitPush2Integer(); + TestEmitPush2BigInteger(); + TestEmitPush2Hash160(); + TestEmitPush2Hash256(); + TestEmitPush2PublicKey(); + TestEmitPush2String(); + TestEmitPush2Array(); + + ScriptBuilder sb = new ScriptBuilder(); + Action action = () => sb.EmitPush(new ContractParameter(ContractParameterType.Map)); + action.Should().Throw(); + } + + private void TestEmitPush2Array() + { + ScriptBuilder sb = new ScriptBuilder(); + ContractParameter parameter = new ContractParameter(ContractParameterType.Array); + IList values = new List(); + values.Add(new ContractParameter(ContractParameterType.Integer)); + values.Add(new ContractParameter(ContractParameterType.Integer)); + parameter.Value = values; + sb.EmitPush(parameter); + byte[] tempArray = new byte[4]; + tempArray[0] = 0x00; + tempArray[1] = 0x00; + tempArray[2] = 0x52; + tempArray[3] = 0xC1; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush2String() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(new ContractParameter(ContractParameterType.String)); + byte[] tempArray = new byte[1]; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush2PublicKey() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(new ContractParameter(ContractParameterType.PublicKey)); + byte[] tempArray = new byte[34]; + tempArray[0] = 0x21; + Array.Copy(ECCurve.Secp256r1.G.EncodePoint(true), 0, tempArray, 1, 33); + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush2Hash256() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(new ContractParameter(ContractParameterType.Hash256)); + byte[] tempArray = new byte[33]; + tempArray[0] = 0x20; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush2Hash160() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(new ContractParameter(ContractParameterType.Hash160)); + byte[] tempArray = new byte[21]; + tempArray[0] = 0x14; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush2BigInteger() + { + ScriptBuilder sb = new ScriptBuilder(); + ContractParameter parameter = new ContractParameter(ContractParameterType.Integer) + { + Value = BigInteger.Zero + }; + sb.EmitPush(parameter); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush2Integer() + { + ScriptBuilder sb = new ScriptBuilder(); + ContractParameter parameter = new ContractParameter(ContractParameterType.Integer); + sb.EmitPush(parameter); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush2Boolean() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(new ContractParameter(ContractParameterType.Boolean)); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush2ByteArray() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(new ContractParameter(ContractParameterType.ByteArray)); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush2Signature() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(new ContractParameter(ContractParameterType.Signature)); + byte[] tempArray = new byte[65]; + tempArray[0] = 0x40; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + enum TestEnum : byte + { + case1 = 0 + } + + [TestMethod] + public void TestEmitPush3() + { + TestEmitPush3Bool(); + TestEmitPush3ByteArray(); + TestEmitPush3String(); + TestEmitPush3BigInteger(); + TestEmitPush3ISerializable(); + TestEmitPush3Sbyte(); + TestEmitPush3Byte(); + TestEmitPush3Short(); + TestEmitPush3Ushort(); + TestEmitPush3Int(); + TestEmitPush3Uint(); + TestEmitPush3Long(); + TestEmitPush3Ulong(); + TestEmitPush3Enum(); + + ScriptBuilder sb = new ScriptBuilder(); + Action action = () => sb.EmitPush(new object()); + action.Should().Throw(); + } + + + private void TestEmitPush3Enum() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(TestEnum.case1); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3Ulong() + { + ScriptBuilder sb = new ScriptBuilder(); + ulong temp = 0; + VM.Helper.EmitPush(sb, temp); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3Long() + { + ScriptBuilder sb = new ScriptBuilder(); + long temp = 0; + VM.Helper.EmitPush(sb, temp); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3Uint() + { + ScriptBuilder sb = new ScriptBuilder(); + uint temp = 0; + VM.Helper.EmitPush(sb, temp); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3Int() + { + ScriptBuilder sb = new ScriptBuilder(); + int temp = 0; + VM.Helper.EmitPush(sb, temp); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3Ushort() + { + ScriptBuilder sb = new ScriptBuilder(); + ushort temp = 0; + VM.Helper.EmitPush(sb, temp); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3Short() + { + ScriptBuilder sb = new ScriptBuilder(); + short temp = 0; + VM.Helper.EmitPush(sb, temp); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3Byte() + { + ScriptBuilder sb = new ScriptBuilder(); + byte temp = 0; + VM.Helper.EmitPush(sb, temp); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3Sbyte() + { + ScriptBuilder sb = new ScriptBuilder(); + sbyte temp = 0; + VM.Helper.EmitPush(sb, temp); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3ISerializable() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(UInt160.Zero); + byte[] tempArray = new byte[21]; + tempArray[0] = 0x14; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3BigInteger() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(BigInteger.Zero); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3String() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(""); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3ByteArray() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(new byte[] { 0x01 }); + byte[] tempArray = new byte[2]; + tempArray[0] = 0x01; + tempArray[1] = 0x01; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + private void TestEmitPush3Bool() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitPush(true); + byte[] tempArray = new byte[1]; + tempArray[0] = 0x51; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + [TestMethod] + public void TestEmitSysCall() + { + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitSysCall(0, true); + byte[] tempArray = new byte[6]; + tempArray[0] = 0x51; + tempArray[1] = 0x68; + tempArray[2] = 0x00; + tempArray[3] = 0x00; + tempArray[4] = 0x00; + tempArray[5] = 0x00; + Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + } + + [TestMethod] + public void TestToParameter2() + { + TestToParaMeter2VMArray(); + TestToParameter2Map(); + TestToParameter2VMBoolean(); + TestToParameter2ByteArray(); + TestToParameter2Integer(); + TestToParameter2InteropInterface(); + + Action action = () => VM.Helper.ToParameter(null); + action.Should().Throw(); + } + + private void TestToParameter2InteropInterface() + { + StackItem item = new VM.Types.InteropInterface(new VM.Types.Boolean(true)); + ContractParameter parameter = VM.Helper.ToParameter(item); + Assert.AreEqual(ContractParameterType.InteropInterface, parameter.Type); + } + + private void TestToParameter2Integer() + { + StackItem item = new VM.Types.Integer(0); + ContractParameter parameter = VM.Helper.ToParameter(item); + Assert.AreEqual(ContractParameterType.Integer, parameter.Type); + Assert.AreEqual(BigInteger.Zero, parameter.Value); + } + + private void TestToParameter2ByteArray() + { + StackItem item = new VM.Types.ByteArray(new byte[] { 0x00 }); + ContractParameter parameter = VM.Helper.ToParameter(item); + Assert.AreEqual(ContractParameterType.ByteArray, parameter.Type); + Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00 }), Encoding.Default.GetString((byte[])parameter.Value)); + } + + private void TestToParameter2VMBoolean() + { + StackItem item = new VM.Types.Boolean(true); + ContractParameter parameter = VM.Helper.ToParameter(item); + Assert.AreEqual(ContractParameterType.Boolean, parameter.Type); + Assert.AreEqual(true, parameter.Value); + } + + private void TestToParameter2Map() + { + StackItem item = new VM.Types.Map(); + ContractParameter parameter = VM.Helper.ToParameter(item); + Assert.AreEqual(ContractParameterType.Map, parameter.Type); + Assert.AreEqual(0, ((List>)parameter.Value).Count); + } + + private void TestToParaMeter2VMArray() + { + VM.Types.Array item = new VM.Types.Array(); + ContractParameter parameter = VM.Helper.ToParameter(item); + Assert.AreEqual(ContractParameterType.Array, parameter.Type); + Assert.AreEqual(0, ((List)parameter.Value).Count); + } } } From dfe21c1630ab92cc08511347ed12bab66393833a Mon Sep 17 00:00:00 2001 From: Luchuan Date: Thu, 7 Nov 2019 11:26:06 +0800 Subject: [PATCH 121/305] Fix p2p filter unconnected peers (#1160) --- neo/Network/P2P/LocalNode.cs | 3 ++- neo/Network/P2P/Peer.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/neo/Network/P2P/LocalNode.cs b/neo/Network/P2P/LocalNode.cs index 79ad7fb096..0efd8a3287 100644 --- a/neo/Network/P2P/LocalNode.cs +++ b/neo/Network/P2P/LocalNode.cs @@ -20,6 +20,7 @@ internal class RelayDirectly { public IInventory Inventory; } internal class SendDirectly { public IInventory Inventory; } public const uint ProtocolVersion = 0; + private const int MaxCountFromSeedList = 5; private static readonly object lockObj = new object(); private readonly NeoSystem system; @@ -123,7 +124,7 @@ public IEnumerable GetUnconnectedPeers() protected override void NeedMorePeers(int count) { - count = Math.Max(count, 5); + count = Math.Max(count, MaxCountFromSeedList); if (ConnectedPeers.Count > 0) { BroadcastMessage(MessageCommand.GetAddr); diff --git a/neo/Network/P2P/Peer.cs b/neo/Network/P2P/Peer.cs index 0cbd6cbaff..0665567fc8 100644 --- a/neo/Network/P2P/Peer.cs +++ b/neo/Network/P2P/Peer.cs @@ -66,7 +66,7 @@ protected void AddPeers(IEnumerable peers) { if (UnconnectedPeers.Count < UnconnectedMax) { - peers = peers.Where(p => p.Port != ListenerTcpPort || !localAddresses.Contains(p.Address)); + peers = peers.Where(p => (p.Port != ListenerTcpPort || !localAddresses.Contains(p.Address)) && !ConnectedPeers.Values.Contains(p)); ImmutableInterlocked.Update(ref UnconnectedPeers, p => p.Union(peers)); } } From c04de2af7eb0cb819cb90b166344872b8e19f1b0 Mon Sep 17 00:00:00 2001 From: doubiliu Date: Wed, 6 Nov 2019 22:47:07 -0600 Subject: [PATCH 122/305] Remove the case of GetData in ProtocolHandlerMailbox#ShallDrop (#1201) --- neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs | 4 ---- neo/Network/P2P/ProtocolHandler.cs | 1 - 2 files changed, 5 deletions(-) diff --git a/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs b/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs index cb9c4586a8..f41deead2a 100644 --- a/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs +++ b/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs @@ -136,10 +136,6 @@ public void ProtocolHandlerMailbox_Test_ShallDrop() msg = Message.Create(MessageCommand.Inv, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // GetData (drop) - msg = Message.Create(MessageCommand.GetData, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); // NotFound (no drop) msg = Message.Create(MessageCommand.NotFound, s); uut.ShallDrop(msg, emptyQueue).Should().Be(false); diff --git a/neo/Network/P2P/ProtocolHandler.cs b/neo/Network/P2P/ProtocolHandler.cs index ea645b79db..f2262a2db4 100644 --- a/neo/Network/P2P/ProtocolHandler.cs +++ b/neo/Network/P2P/ProtocolHandler.cs @@ -329,7 +329,6 @@ internal protected override bool ShallDrop(object message, IEnumerable queue) { case MessageCommand.GetAddr: case MessageCommand.GetBlocks: - case MessageCommand.GetData: case MessageCommand.GetHeaders: case MessageCommand.Mempool: return queue.OfType().Any(p => p.Command == msg.Command); From 64b8100c7f57da5a604ba5525c4c6515d4ac34d8 Mon Sep 17 00:00:00 2001 From: Erik van den Brink Date: Thu, 7 Nov 2019 15:21:49 +0100 Subject: [PATCH 123/305] Add GetFullBlocks P2P logic (enable fixing #522) (#1138) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add GetFullBlocks P2P logic * add missing new line * allow request genesis block * Optimization * Update MessageCommand.cs * - rename command - fix protocol handler cast - fix payload deserialization for default value * change to ushort per review * remove default count, rename class * typo + failed refactor coverage ¯\_(ツ)_/¯ --- neo/Network/P2P/Message.cs | 3 ++ neo/Network/P2P/MessageCommand.cs | 1 + .../P2P/Payloads/GetBlockDataPayload.cs | 37 +++++++++++++++++++ neo/Network/P2P/ProtocolHandler.cs | 23 ++++++++++++ 4 files changed, 64 insertions(+) create mode 100644 neo/Network/P2P/Payloads/GetBlockDataPayload.cs diff --git a/neo/Network/P2P/Message.cs b/neo/Network/P2P/Message.cs index 61ee4493b4..38bc2bf6c1 100644 --- a/neo/Network/P2P/Message.cs +++ b/neo/Network/P2P/Message.cs @@ -74,6 +74,9 @@ private void DecompressPayload() case MessageCommand.GetData: Payload = decompressed.AsSerializable(); break; + case MessageCommand.GetBlockData: + Payload = decompressed.AsSerializable(); + break; case MessageCommand.Transaction: Payload = decompressed.AsSerializable(); break; diff --git a/neo/Network/P2P/MessageCommand.cs b/neo/Network/P2P/MessageCommand.cs index ed8dc6b96b..d1f2befc67 100644 --- a/neo/Network/P2P/MessageCommand.cs +++ b/neo/Network/P2P/MessageCommand.cs @@ -19,6 +19,7 @@ public enum MessageCommand : byte Mempool = 0x25, Inv = 0x27, GetData = 0x28, + GetBlockData = 0x29, NotFound = 0x2a, Transaction = 0x2b, Block = 0x2c, diff --git a/neo/Network/P2P/Payloads/GetBlockDataPayload.cs b/neo/Network/P2P/Payloads/GetBlockDataPayload.cs new file mode 100644 index 0000000000..9ccd534f34 --- /dev/null +++ b/neo/Network/P2P/Payloads/GetBlockDataPayload.cs @@ -0,0 +1,37 @@ +using Neo.IO; +using System; +using System.IO; + +namespace Neo.Network.P2P.Payloads +{ + public class GetBlockDataPayload : ISerializable + { + private const ushort MaxBlocksCount = 500; + public uint IndexStart; + public ushort Count; + + public int Size => sizeof(uint) + sizeof(ushort); + + public static GetBlockDataPayload Create(uint index_start, ushort count) + { + return new GetBlockDataPayload + { + IndexStart = index_start, + Count = count + }; + } + + void ISerializable.Deserialize(BinaryReader reader) + { + IndexStart = reader.ReadUInt32(); + Count = reader.ReadUInt16(); + if (Count == 0 || Count > MaxBlocksCount) throw new FormatException(); + } + + void ISerializable.Serialize(BinaryWriter writer) + { + writer.Write(IndexStart); + writer.Write(Count); + } + } +} diff --git a/neo/Network/P2P/ProtocolHandler.cs b/neo/Network/P2P/ProtocolHandler.cs index f2262a2db4..1e62e4dfbf 100644 --- a/neo/Network/P2P/ProtocolHandler.cs +++ b/neo/Network/P2P/ProtocolHandler.cs @@ -80,6 +80,9 @@ protected override void OnReceive(object message) case MessageCommand.GetBlocks: OnGetBlocksMessageReceived((GetBlocksPayload)msg.Payload); break; + case MessageCommand.GetBlockData: + OnGetBlockDataMessageReceived((GetBlockDataPayload)msg.Payload); + break; case MessageCommand.GetData: OnGetDataMessageReceived((InvPayload)msg.Payload); break; @@ -175,6 +178,26 @@ private void OnGetBlocksMessageReceived(GetBlocksPayload payload) Context.Parent.Tell(Message.Create(MessageCommand.Inv, InvPayload.Create(InventoryType.Block, hashes.ToArray()))); } + private void OnGetBlockDataMessageReceived(GetBlockDataPayload payload) + { + for (uint i = payload.IndexStart, max = payload.IndexStart + payload.Count; i < max; i++) + { + Block block = Blockchain.Singleton.Store.GetBlock(i); + if (block == null) + break; + + if (bloom_filter == null) + { + Context.Parent.Tell(Message.Create(MessageCommand.Block, block)); + } + else + { + BitArray flags = new BitArray(block.Transactions.Select(p => bloom_filter.Test(p)).ToArray()); + Context.Parent.Tell(Message.Create(MessageCommand.MerkleBlock, MerkleBlockPayload.Create(block, flags))); + } + } + } + private void OnGetDataMessageReceived(InvPayload payload) { UInt256[] hashes = payload.Hashes.Where(p => sentHashes.Add(p)).ToArray(); From e2fb1813c85bd06bbf374a46470500c04d6e2a01 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 7 Nov 2019 17:35:22 +0100 Subject: [PATCH 124/305] Use base64 in JsonSerializer (#1199) * Base64 Json * No format * Json Transaction optimization * Change to Base64 * Revert some changes * Revert * Remove Helper.Base64 * Remove Base64FormattingOptions.None --- neo.UnitTests/Ledger/UT_ContractState.cs | 4 ++-- neo.UnitTests/Network/P2P/Payloads/UT_Block.cs | 2 +- neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs | 2 +- neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs | 4 ++-- .../Network/RPC/Models/UT_RpcNep5Balance.cs | 4 ++-- .../SmartContract/Manifest/UT_ContractManifest.cs | 2 +- .../SmartContract/UT_ContractParameterContext.cs | 6 +++--- neo.UnitTests/SmartContract/UT_JsonSerializer.cs | 4 ++-- neo.UnitTests/SmartContract/UT_Syscalls.cs | 4 ++-- neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs | 4 ++-- neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs | 5 +++-- neo/Ledger/ContractState.cs | 9 +++++---- neo/Network/P2P/Payloads/Transaction.cs | 4 ++-- neo/Network/P2P/Payloads/TransactionAttribute.cs | 4 ++-- neo/Network/P2P/Payloads/Witness.cs | 10 +++++----- neo/Network/RPC/Models/RpcNep5Balances.cs | 2 +- neo/SmartContract/ContractParameter.cs | 4 ++-- neo/SmartContract/ContractParametersContext.cs | 12 ++++++------ neo/SmartContract/JsonSerializer.cs | 2 +- neo/SmartContract/Manifest/ContractGroup.cs | 5 +++-- neo/Wallets/NEP6/NEP6Contract.cs | 5 +++-- 21 files changed, 51 insertions(+), 47 deletions(-) diff --git a/neo.UnitTests/Ledger/UT_ContractState.cs b/neo.UnitTests/Ledger/UT_ContractState.cs index 87f1d7ac88..097e9c3e04 100644 --- a/neo.UnitTests/Ledger/UT_ContractState.cs +++ b/neo.UnitTests/Ledger/UT_ContractState.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.IO.Json; @@ -92,7 +92,7 @@ public void TestToJson() { JObject json = contract.ToJson(); json["hash"].AsString().Should().Be("0x820944cfdc70976602d71b0091445eedbc661bc5"); - json["script"].AsString().Should().Be("01"); + json["script"].AsString().Should().Be("AQ=="); json["manifest"].AsString().Should().Be(manifest.ToJson().AsString()); } } diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index f27421dd74..a26741c732 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -210,7 +210,7 @@ public void ToJson() JObject scObj = ((JArray)jObj["witnesses"])[0]; scObj["invocation"].AsString().Should().Be(""); - scObj["verification"].AsString().Should().Be("51"); + scObj["verification"].AsString().Should().Be("UQ=="); jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index d52867e99e..217e207f83 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -1093,7 +1093,7 @@ public void ToJson() ((JArray)jObj["attributes"]).Count.Should().Be(0); ((JArray)jObj["cosigners"]).Count.Should().Be(0); jObj["net_fee"].AsString().Should().Be("0"); - jObj["script"].AsString().Should().Be("4220202020202020202020202020202020202020202020202020202020202020"); + jObj["script"].AsString().Should().Be("QiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA="); jObj["sys_fee"].AsString().Should().Be("4200000000"); } } diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index 1ef1e2d120..502317e604 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -146,8 +146,8 @@ public void ToJson() JObject json = uut.ToJson(); Assert.IsTrue(json.ContainsProperty("invocation")); Assert.IsTrue(json.ContainsProperty("verification")); - Assert.AreEqual(json["invocation"].AsString(), "2020"); - Assert.AreEqual(json["verification"].AsString(), "202020"); + Assert.AreEqual(json["invocation"].AsString(), "ICA="); + Assert.AreEqual(json["verification"].AsString(), "ICAg"); } } } diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs b/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs index ee32c1998e..9131db811a 100644 --- a/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs +++ b/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs @@ -45,7 +45,7 @@ public void TestToJson() balance.Amount = BigInteger.Zero; balance.LastUpdatedBlock = 0; var json = balance.ToJson(); - json["asset_hash"].AsString().Should().Be("0000000000000000000000000000000000000000"); + json["asset_hash"].AsString().Should().Be("0x0000000000000000000000000000000000000000"); json["amount"].AsNumber().Should().Be(0); json["last_updated_block"].AsNumber().Should().Be(0); } @@ -54,7 +54,7 @@ public void TestToJson() public void TestFromJson() { var json = new JObject(); - json["asset_hash"] = "0000000000000000000000000000000000000000"; + json["asset_hash"] = "0x0000000000000000000000000000000000000000"; json["amount"] = "0"; json["last_updated_block"] = "0"; var rpcNep5Balance = RpcNep5Balance.FromJson(json); diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index 910f30b019..5c2a99ff56 100644 --- a/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -77,7 +77,7 @@ public void ParseFromJson_Trust() [TestMethod] public void ParseFromJson_Groups() { - var json = @"{""groups"":[{""pubKey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141""}],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[]}"; + var json = @"{""groups"":[{""pubKey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[]}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); diff --git a/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index 0b06260b24..0e035349d1 100644 --- a/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; @@ -46,13 +46,13 @@ 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\":\"0000000000f277f1144035a43897f9fa115258eac015adcabe000000000000000000000000000000000000000000000100\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1668747476aa\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}"); + str.Should().Be("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAADyd\\/EUQDWkOJf5\\u002BhFSWOrAFa3KvgAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA==\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}]}}}"); } [TestMethod] public void TestParse() { - var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"0000000000f277f1144035a43897f9fa115258eac015adcabe000000000000000000000000000000000000000000000100\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1668747476aa\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}"); + var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAADyd\\/EUQDWkOJf5\\u002BhFSWOrAFa3KvgAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA==\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}]}}}"); ret.ScriptHashes[0].ToString().Should().Be("0xbecaad15c0ea585211faf99738a4354014f177f2"); ((Transaction)ret.Verifiable).Script.ToHexString().Should().Be(new byte[1].ToHexString()); } diff --git a/neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs index 9863d72b0d..ed9c9cd72b 100644 --- a/neo.UnitTests/SmartContract/UT_JsonSerializer.cs +++ b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs @@ -268,7 +268,7 @@ public void Serialize_Array_Bool_Str_Num() var json = JsonSerializer.Serialize(entry).ToString(); - Assert.AreEqual(json, "[true,\"74657374\",123]"); + Assert.AreEqual(json, "[true,\"dGVzdA==\",123]"); } [TestMethod] @@ -297,7 +297,7 @@ public void Serialize_Array_OfArray() var json = JsonSerializer.Serialize(entry).ToString(); - Assert.AreEqual(json, "[[true,\"7465737431\",123],[true,\"7465737432\",321]]"); + Assert.AreEqual(json, "[[true,\"dGVzdDE=\",123],[true,\"dGVzdDI=\",321]]"); } [TestMethod] diff --git a/neo.UnitTests/SmartContract/UT_Syscalls.cs b/neo.UnitTests/SmartContract/UT_Syscalls.cs index d24850de40..b778f63d94 100644 --- a/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -76,7 +76,7 @@ public void System_Blockchain_GetBlock() Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); Assert.AreEqual(engine.ResultStack.Pop().GetByteArray().ToHexString(), - "5b2235316138373966636161303339626461663437353436306637316334616130383562353964313833616239313764356437363762333865613738323766356266222c332c2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c2230303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030222c322c312c2230303030303030303030303030303030303030303030303030303030303030303030303030303030222c315d"); + "5b22556168355c2f4b6f446d39723064555950636353714346745a30594f726b583164646e7334366e676e3962383d222c332c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c322c312c224141414141414141414141414141414141414141414141414141413d222c315d"); Assert.AreEqual(0, engine.ResultStack.Count); // Clean @@ -127,7 +127,7 @@ public void System_ExecutionEngine_GetScriptContainer() Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); Assert.AreEqual(engine.ResultStack.Pop().GetByteArray().ToHexString(), - @"5b2266613434383036313834373332636138613136363037356430306133643861326531353462333337313333333138303336356561643732373561663132666264222c362c342c2266666666666666666666666666666666666666666666666666666666666666666666666666666666222c332c322c352c223031225d"); + @"5b225c75303032426b53415959527a4c4b69685a676464414b50596f754655737a63544d7867445a6572584a3172784c37303d222c362c342c225c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f383d222c332c322c352c2241513d3d225d"); Assert.AreEqual(0, engine.ResultStack.Count); } } diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index ed84b1bad3..0477b8a595 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -104,7 +104,7 @@ public void TestGetKeyWithString() public void TestToJson() { JObject nep6contract = new JObject(); - nep6contract["script"] = "2103603f3880eb7aea0ad4500893925e4a42fea48a44ee6f898a10b3c7ce05d2a267ac"; + nep6contract["script"] = "IQNgPziA63rqCtRQCJOSXkpC/qSKRO5viYoQs8fOBdKiZ6w="; JObject parameters = new JObject(); parameters["type"] = 0x00; parameters["name"] = "Sig"; @@ -121,7 +121,7 @@ public void TestToJson() json["isDefault"].ToString().Should().Be("false"); json["lock"].ToString().Should().Be("false"); json["key"].Should().BeNull(); - json["contract"]["script"].ToString().Should().Be("\"2103603f3880eb7aea0ad4500893925e4a42fea48a44ee6f898a10b3c7ce05d2a267ac\""); + json["contract"]["script"].ToString().Should().Be("\"IQNgPziA63rqCtRQCJOSXkpC\\/qSKRO5viYoQs8fOBdKiZ6w=\""); json["extra"].Should().BeNull(); _account.Contract = null; diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs index 3f4e7b3a63..1594672946 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs @@ -3,6 +3,7 @@ using Neo.IO.Json; using Neo.SmartContract; using Neo.Wallets.NEP6; +using System; namespace Neo.UnitTests.Wallets.NEP6 { @@ -19,7 +20,7 @@ public void TestFromNullJson() [TestMethod] public void TestFromJson() { - string json = "{\"script\":\"2103ef891df4c0b7eefb937d21ea0fb88cde8e0d82a7ff11872b5e7047969dafb4eb68747476aa\"," + + string json = "{\"script\":\"IQPviR30wLfu+5N9IeoPuIzejg2Cp/8RhytecEeWna+062h0dHaq\"," + "\"parameters\":[{\"name\":\"signature\",\"type\":\"Signature\"}],\"deployed\":false}"; JObject @object = JObject.Parse(json); @@ -45,7 +46,7 @@ public void TestToJson() JObject @object = nep6Contract.ToJson(); JString jString = (JString)@object["script"]; - jString.Value.Should().Be(nep6Contract.Script.ToHexString()); + jString.Value.Should().Be(Convert.ToBase64String(nep6Contract.Script, Base64FormattingOptions.None)); JBoolean jBoolean = (JBoolean)@object["deployed"]; jBoolean.Value.Should().BeFalse(); diff --git a/neo/Ledger/ContractState.cs b/neo/Ledger/ContractState.cs index c369e5d59f..ea1860668e 100644 --- a/neo/Ledger/ContractState.cs +++ b/neo/Ledger/ContractState.cs @@ -4,6 +4,7 @@ using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; +using System; using System.IO; namespace Neo.Ledger @@ -62,7 +63,7 @@ public JObject ToJson() { JObject json = new JObject(); json["hash"] = ScriptHash.ToString(); - json["script"] = Script.ToHexString(); + json["script"] = Convert.ToBase64String(Script); json["manifest"] = Manifest.ToJson(); return json; } @@ -70,7 +71,7 @@ public JObject ToJson() public static ContractState FromJson(JObject json) { ContractState contractState = new ContractState(); - contractState.Script = json["script"].AsString().HexToBytes(); + contractState.Script = Convert.FromBase64String(json["script"].AsString()); contractState.Manifest = ContractManifest.FromJson(json["manifest"]); return contractState; } @@ -82,8 +83,8 @@ public StackItem ToStackItem() new StackItem[] { new ByteArray(Script), - new Boolean(HasStorage), - new Boolean(Payable), + new VM.Types.Boolean(HasStorage), + new VM.Types.Boolean(Payable), } ); } diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index af38ef6dcc..c135185272 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -183,7 +183,7 @@ public JObject ToJson() json["valid_until_block"] = ValidUntilBlock; json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); json["cosigners"] = Cosigners.Select(p => p.ToJson()).ToArray(); - json["script"] = Script.ToHexString(); + json["script"] = Convert.ToBase64String(Script); json["witnesses"] = Witnesses.Select(p => p.ToJson()).ToArray(); return json; } @@ -199,7 +199,7 @@ public static Transaction FromJson(JObject json) tx.ValidUntilBlock = uint.Parse(json["valid_until_block"].AsString()); tx.Attributes = ((JArray)json["attributes"]).Select(p => TransactionAttribute.FromJson(p)).ToArray(); tx.Cosigners = ((JArray)json["cosigners"]).Select(p => Cosigner.FromJson(p)).ToArray(); - tx.Script = json["script"].AsString().HexToBytes(); + tx.Script = Convert.FromBase64String(json["script"].AsString()); tx.Witnesses = ((JArray)json["witnesses"]).Select(p => Witness.FromJson(p)).ToArray(); return tx; } diff --git a/neo/Network/P2P/Payloads/TransactionAttribute.cs b/neo/Network/P2P/Payloads/TransactionAttribute.cs index 5adc34db8e..fa92010004 100644 --- a/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -30,7 +30,7 @@ public JObject ToJson() { JObject json = new JObject(); json["usage"] = Usage; - json["data"] = Data.ToHexString(); + json["data"] = Convert.ToBase64String(Data); return json; } @@ -38,7 +38,7 @@ public static TransactionAttribute FromJson(JObject json) { TransactionAttribute transactionAttribute = new TransactionAttribute(); transactionAttribute.Usage = (TransactionAttributeUsage)(byte.Parse(json["usage"].AsString())); - transactionAttribute.Data = json["data"].AsString().HexToBytes(); + transactionAttribute.Data = Convert.FromBase64String(json["data"].AsString()); return transactionAttribute; } } diff --git a/neo/Network/P2P/Payloads/Witness.cs b/neo/Network/P2P/Payloads/Witness.cs index 227d810a39..55ba4f2564 100644 --- a/neo/Network/P2P/Payloads/Witness.cs +++ b/neo/Network/P2P/Payloads/Witness.cs @@ -1,7 +1,7 @@ using Neo.IO; using Neo.IO.Json; using Neo.SmartContract; -using Neo.VM; +using System; using System.IO; namespace Neo.Network.P2P.Payloads @@ -44,16 +44,16 @@ void ISerializable.Serialize(BinaryWriter writer) public JObject ToJson() { JObject json = new JObject(); - json["invocation"] = InvocationScript.ToHexString(); - json["verification"] = VerificationScript.ToHexString(); + json["invocation"] = Convert.ToBase64String(InvocationScript); + json["verification"] = Convert.ToBase64String(VerificationScript); return json; } public static Witness FromJson(JObject json) { Witness witness = new Witness(); - witness.InvocationScript = json["invocation"].AsString().HexToBytes(); - witness.VerificationScript = json["verification"].AsString().HexToBytes(); + witness.InvocationScript = Convert.FromBase64String(json["invocation"].AsString()); + witness.VerificationScript = Convert.FromBase64String(json["verification"].AsString()); return witness; } } diff --git a/neo/Network/RPC/Models/RpcNep5Balances.cs b/neo/Network/RPC/Models/RpcNep5Balances.cs index b471ab4545..74fe7be729 100644 --- a/neo/Network/RPC/Models/RpcNep5Balances.cs +++ b/neo/Network/RPC/Models/RpcNep5Balances.cs @@ -39,7 +39,7 @@ public class RpcNep5Balance public JObject ToJson() { JObject json = new JObject(); - json["asset_hash"] = AssetHash.ToArray().ToHexString(); + json["asset_hash"] = AssetHash.ToString(); json["amount"] = Amount.ToString(); json["last_updated_block"] = LastUpdatedBlock.ToString(); return json; diff --git a/neo/SmartContract/ContractParameter.cs b/neo/SmartContract/ContractParameter.cs index cae59229b8..5cdc520808 100644 --- a/neo/SmartContract/ContractParameter.cs +++ b/neo/SmartContract/ContractParameter.cs @@ -66,7 +66,7 @@ public static ContractParameter FromJson(JObject json) { case ContractParameterType.Signature: case ContractParameterType.ByteArray: - parameter.Value = json["value"].AsString().HexToBytes(); + parameter.Value = Convert.FromBase64String(json["value"].AsString()); break; case ContractParameterType.Boolean: parameter.Value = json["value"].AsBoolean(); @@ -147,7 +147,7 @@ private static JObject ToJson(ContractParameter parameter, HashSet ContractParameter.FromJson(p)).ToArray(), Signatures = json["signatures"]?.Properties.Select(p => new { PublicKey = ECPoint.Parse(p.Key, ECCurve.Secp256r1), - Signature = p.Value.AsString().HexToBytes() + Signature = Convert.FromBase64String(p.Value.AsString()) }).ToDictionary(p => p.PublicKey, p => p.Signature) }; } @@ -47,13 +47,13 @@ public JObject ToJson() { JObject json = new JObject(); if (Script != null) - json["script"] = Script.ToHexString(); + json["script"] = Convert.ToBase64String(Script); json["parameters"] = new JArray(Parameters.Select(p => p.ToJson())); if (Signatures != null) { json["signatures"] = new JObject(); foreach (var signature in Signatures) - json["signatures"][signature.Key.ToString()] = signature.Value.ToHexString(); + json["signatures"][signature.Key.ToString()] = Convert.ToBase64String(signature.Value); } return json; } @@ -215,7 +215,7 @@ public static ContractParametersContext FromJson(JObject json) if (!typeof(IVerifiable).IsAssignableFrom(type)) throw new FormatException(); var verifiable = (IVerifiable)Activator.CreateInstance(type); - using (MemoryStream ms = new MemoryStream(json["hex"].AsString().HexToBytes(), false)) + using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(json["hex"].AsString()), false)) using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) { verifiable.DeserializeUnsigned(reader); @@ -284,7 +284,7 @@ public JObject ToJson() { Verifiable.SerializeUnsigned(writer); writer.Flush(); - json["hex"] = ms.ToArray().ToHexString(); + json["hex"] = Convert.ToBase64String(ms.ToArray()); } json["items"] = new JObject(); foreach (var item in ContextItems) diff --git a/neo/SmartContract/JsonSerializer.cs b/neo/SmartContract/JsonSerializer.cs index 030585420e..96c525e0b1 100644 --- a/neo/SmartContract/JsonSerializer.cs +++ b/neo/SmartContract/JsonSerializer.cs @@ -26,7 +26,7 @@ public static JObject Serialize(StackItem item) } case ByteArray buffer: { - return buffer.GetByteArray().ToHexString(); + return Convert.ToBase64String(buffer.GetByteArray()); } case Integer num: { diff --git a/neo/SmartContract/Manifest/ContractGroup.cs b/neo/SmartContract/Manifest/ContractGroup.cs index 75359e782a..249c6b8a53 100644 --- a/neo/SmartContract/Manifest/ContractGroup.cs +++ b/neo/SmartContract/Manifest/ContractGroup.cs @@ -1,6 +1,7 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO.Json; +using System; namespace Neo.SmartContract.Manifest { @@ -30,7 +31,7 @@ public static ContractGroup FromJson(JObject json) return new ContractGroup { PubKey = ECPoint.Parse(json["pubKey"].AsString(), ECCurve.Secp256r1), - Signature = json["signature"].AsString().HexToBytes(), + Signature = Convert.FromBase64String(json["signature"].AsString()), }; } @@ -48,7 +49,7 @@ public virtual JObject ToJson() { var json = new JObject(); json["pubKey"] = PubKey.ToString(); - json["signature"] = Signature.ToHexString(); + json["signature"] = Convert.ToBase64String(Signature); return json; } } diff --git a/neo/Wallets/NEP6/NEP6Contract.cs b/neo/Wallets/NEP6/NEP6Contract.cs index 45f0b5e157..af27db6c4c 100644 --- a/neo/Wallets/NEP6/NEP6Contract.cs +++ b/neo/Wallets/NEP6/NEP6Contract.cs @@ -1,5 +1,6 @@ using Neo.IO.Json; using Neo.SmartContract; +using System; using System.Linq; namespace Neo.Wallets.NEP6 @@ -14,7 +15,7 @@ public static NEP6Contract FromJson(JObject json) if (json == null) return null; return new NEP6Contract { - Script = json["script"].AsString().HexToBytes(), + Script = Convert.FromBase64String(json["script"].AsString()), ParameterList = ((JArray)json["parameters"]).Select(p => p["type"].TryGetEnum()).ToArray(), ParameterNames = ((JArray)json["parameters"]).Select(p => p["name"].AsString()).ToArray(), Deployed = json["deployed"].AsBoolean() @@ -24,7 +25,7 @@ public static NEP6Contract FromJson(JObject json) public JObject ToJson() { JObject contract = new JObject(); - contract["script"] = Script.ToHexString(); + contract["script"] = Convert.ToBase64String(Script); contract["parameters"] = new JArray(ParameterList.Zip(ParameterNames, (type, name) => { JObject parameter = new JObject(); From e43bc73322ce0a6a24d727d25a1655f1951f86f1 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 7 Nov 2019 18:36:40 +0100 Subject: [PATCH 125/305] Optimize MerkleTree (3x) (#1203) * Optimize MerkleTree * Update MerkleTree.cs * Update MerkleTree.cs --- neo/Cryptography/MerkleTree.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/neo/Cryptography/MerkleTree.cs b/neo/Cryptography/MerkleTree.cs index 0d8e617f3f..348c439350 100644 --- a/neo/Cryptography/MerkleTree.cs +++ b/neo/Cryptography/MerkleTree.cs @@ -2,12 +2,13 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; namespace Neo.Cryptography { public class MerkleTree { - private MerkleTreeNode root; + private readonly MerkleTreeNode root; public int Depth { get; private set; } @@ -25,6 +26,8 @@ private static MerkleTreeNode Build(MerkleTreeNode[] leaves) { if (leaves.Length == 0) throw new ArgumentException(); if (leaves.Length == 1) return leaves[0]; + + var buffer = new byte[64]; MerkleTreeNode[] parents = new MerkleTreeNode[(leaves.Length + 1) / 2]; for (int i = 0; i < parents.Length; i++) { @@ -40,11 +43,20 @@ private static MerkleTreeNode Build(MerkleTreeNode[] leaves) parents[i].RightChild = leaves[i * 2 + 1]; leaves[i * 2 + 1].Parent = parents[i]; } - parents[i].Hash = new UInt256(Crypto.Default.Hash256(parents[i].LeftChild.Hash.ToArray().Concat(parents[i].RightChild.Hash.ToArray()).ToArray())); + parents[i].Hash = Concat(buffer, parents[i].LeftChild.Hash, parents[i].RightChild.Hash); } return Build(parents); //TailCall } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static UInt256 Concat(byte[] buffer, UInt256 hash1, UInt256 hash2) + { + Buffer.BlockCopy(hash1.ToArray(), 0, buffer, 0, 32); + Buffer.BlockCopy(hash2.ToArray(), 0, buffer, 32, 32); + + return new UInt256(Crypto.Default.Hash256(buffer)); + } + public static UInt256 ComputeRoot(IReadOnlyList hashes) { if (hashes.Count == 0) throw new ArgumentException(); From af8dca4dee2e63f70a24381e5a4ac88448cbf870 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 8 Nov 2019 11:47:54 +0100 Subject: [PATCH 126/305] Added more verbosity to CN logs (#1202) * Add reason log for cn * Update ConsensusService.cs --- neo/Consensus/ConsensusService.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index a65916b5aa..f403f7d3cc 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -615,11 +615,11 @@ private void RequestChangeView(ChangeViewReason reason) ChangeTimer(TimeSpan.FromMilliseconds(Blockchain.MillisecondsPerBlock << (expectedView + 1))); if ((context.CountCommitted + context.CountFailed) > context.F) { - Log($"Skip requesting change view to nv={expectedView} because nc={context.CountCommitted} nf={context.CountFailed}"); + 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}"); + 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); } From 7f13b0f5b283e7e3c745c8b5dc2cdb98c605dddd Mon Sep 17 00:00:00 2001 From: Qiao-Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Sun, 10 Nov 2019 20:21:36 +0800 Subject: [PATCH 127/305] Keep track of sender fee (#1183) * Keep track of sender fee to avoid duplicate computation * Code optimization * Optimize * Optimize * Optimize * Code optimization * Correction * Renaming currentFee to totalSenderFeeFromPool * Renaming on Verify as well * Add consideration for null Transactions * Move sender fee recording systems to class SendersMonitor * Code optimization * Capitalize public items * Code optimization * Code optimization * Optimization * Code optimization --- neo.UnitTests/Ledger/UT_MemoryPool.cs | 5 +- neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs | 56 +++++++++++++++++++ .../Network/P2P/Payloads/UT_Transaction.cs | 2 +- neo/Consensus/ConsensusContext.cs | 13 +++++ neo/Consensus/ConsensusService.cs | 4 +- neo/Ledger/Blockchain.cs | 4 +- neo/Ledger/MemoryPool.cs | 11 +++- neo/Ledger/SendersFeeMonitor.cs | 43 ++++++++++++++ neo/Network/P2P/Payloads/Transaction.cs | 12 ++-- 9 files changed, 136 insertions(+), 14 deletions(-) create mode 100644 neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs create mode 100644 neo/Ledger/SendersFeeMonitor.cs diff --git a/neo.UnitTests/Ledger/UT_MemoryPool.cs b/neo.UnitTests/Ledger/UT_MemoryPool.cs index 27c4d37715..6f9022bef1 100644 --- a/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -13,6 +13,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Numerics; namespace Neo.UnitTests.Ledger { @@ -73,8 +74,8 @@ private Transaction CreateTransactionWithFee(long fee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); - mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny>())).Returns(true); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny>())).Returns(true); + mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(true); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); mock.Object.Script = randomBytes; mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = fee; diff --git a/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs b/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs new file mode 100644 index 0000000000..81fb0d9354 --- /dev/null +++ b/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs @@ -0,0 +1,56 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using System; +using System.Numerics; + +namespace Neo.UnitTests.Ledger +{ + [TestClass] + public class UT_SendersFeeMonitor + { + private Transaction CreateTransactionWithFee(long networkFee, long systemFee) + { + Random random = new Random(); + var randomBytes = new byte[16]; + random.NextBytes(randomBytes); + Mock mock = new Mock(); + mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(true); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); + mock.Object.Script = randomBytes; + mock.Object.Sender = UInt160.Zero; + mock.Object.NetworkFee = networkFee; + mock.Object.SystemFee = systemFee; + mock.Object.Attributes = new TransactionAttribute[0]; + mock.Object.Cosigners = new Cosigner[0]; + mock.Object.Witnesses = new[] + { + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } + }; + return mock.Object; + } + + [TestMethod] + public void TestMemPoolSenderFee() + { + Transaction transaction = CreateTransactionWithFee(1, 2); + SendersFeeMonitor sendersFeeMonitor = new SendersFeeMonitor(); + sendersFeeMonitor.GetSenderFee(transaction.Sender).Should().Be(0); + sendersFeeMonitor.AddSenderFee(transaction); + sendersFeeMonitor.GetSenderFee(transaction.Sender).Should().Be(3); + sendersFeeMonitor.AddSenderFee(transaction); + sendersFeeMonitor.GetSenderFee(transaction.Sender).Should().Be(6); + sendersFeeMonitor.RemoveSenderFee(transaction); + sendersFeeMonitor.GetSenderFee(transaction.Sender).Should().Be(3); + sendersFeeMonitor.RemoveSenderFee(transaction); + sendersFeeMonitor.GetSenderFee(transaction.Sender).Should().Be(0); + } + } +} diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 217e207f83..570151d941 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -798,7 +798,7 @@ public void Transaction_Reverify_Hashes_Length_Unequal_To_Witnesses_Length() }; UInt160[] hashes = txSimple.GetScriptHashesForVerifying(snapshot); Assert.AreEqual(2, hashes.Length); - Assert.IsFalse(txSimple.Reverify(snapshot, new Transaction[0])); + Assert.IsFalse(txSimple.Reverify(snapshot, BigInteger.Zero)); } [TestMethod] diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index 463bdc0407..e7c2649782 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -37,6 +37,11 @@ internal class ConsensusContext : IDisposable, ISerializable // if this node never heard from validator i, LastSeenMessage[i] will be -1. public int[] LastSeenMessage; + /// + /// Store all verified unsorted transactions' senders' fee currently in the consensus context. + /// + public SendersFeeMonitor SendersFeeMonitor = new SendersFeeMonitor(); + public Snapshot Snapshot { get; private set; } private KeyPair keyPair; private int _witnessSize; @@ -110,6 +115,12 @@ public void Deserialize(BinaryReader reader) if (TransactionHashes.Length == 0 && !RequestSentOrReceived) TransactionHashes = null; Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash); + SendersFeeMonitor = new SendersFeeMonitor(); + if (Transactions != null) + { + foreach (Transaction tx in Transactions.Values) + SendersFeeMonitor.AddSenderFee(tx); + } } public void Dispose() @@ -245,6 +256,7 @@ internal void EnsureMaxBlockSize(IEnumerable txs) txs = txs.Take((int)maxTransactionsPerBlock); List hashes = new List(); Transactions = new Dictionary(); + SendersFeeMonitor = new SendersFeeMonitor(); // Expected block size var blockSize = GetExpectedBlockSizeWithoutTransactions(txs.Count()); @@ -258,6 +270,7 @@ internal void EnsureMaxBlockSize(IEnumerable txs) hashes.Add(tx.Hash); Transactions.Add(tx.Hash, tx); + SendersFeeMonitor.AddSenderFee(tx); } TransactionHashes = hashes.ToArray(); diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index f403f7d3cc..574082043a 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -61,7 +61,7 @@ internal ConsensusService(IActorRef localNode, IActorRef taskManager, ConsensusC private bool AddTransaction(Transaction tx, bool verify) { - if (verify && !tx.Verify(context.Snapshot, context.Transactions.Values)) + if (verify && !tx.Verify(context.Snapshot, context.SendersFeeMonitor.GetSenderFee(tx.Sender))) { Log($"Invalid transaction: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning); RequestChangeView(ChangeViewReason.TxInvalid); @@ -74,6 +74,7 @@ private bool AddTransaction(Transaction tx, bool verify) return false; } context.Transactions[tx.Hash] = tx; + context.SendersFeeMonitor.AddSenderFee(tx); return CheckPrepareResponse(); } @@ -423,6 +424,7 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m context.Block.ConsensusData.Nonce = message.Nonce; context.TransactionHashes = message.TransactionHashes; context.Transactions = new Dictionary(); + context.SendersFeeMonitor = new SendersFeeMonitor(); for (int i = 0; i < context.PreparationPayloads.Length; i++) if (context.PreparationPayloads[i] != null) if (!context.PreparationPayloads[i].GetDeserializedMessage().PreparationHash.Equals(payload.Hash)) diff --git a/neo/Ledger/Blockchain.cs b/neo/Ledger/Blockchain.cs index 50393d4c8e..dea46ae3e8 100644 --- a/neo/Ledger/Blockchain.cs +++ b/neo/Ledger/Blockchain.cs @@ -244,7 +244,7 @@ private void OnFillMemoryPool(IEnumerable transactions) // First remove the tx if it is unverified in the pool. MemPool.TryRemoveUnVerified(tx.Hash, out _); // Verify the the transaction - if (!tx.Verify(currentSnapshot, MemPool.GetVerifiedTransactions())) + if (!tx.Verify(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(tx.Sender))) continue; // Add to the memory pool MemPool.TryAdd(tx.Hash, tx); @@ -370,7 +370,7 @@ private RelayResultReason OnNewTransaction(Transaction transaction, bool relay) return RelayResultReason.AlreadyExists; if (!MemPool.CanTransactionFitInPool(transaction)) return RelayResultReason.OutOfMemory; - if (!transaction.Verify(currentSnapshot, MemPool.GetVerifiedTransactions())) + if (!transaction.Verify(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(transaction.Sender))) return RelayResultReason.Invalid; if (!NativeContract.Policy.CheckPolicy(transaction, currentSnapshot)) return RelayResultReason.PolicyFail; diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index f31e07e56c..8f37855a5c 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -69,6 +69,11 @@ public class MemoryPool : IReadOnlyCollection /// public int Capacity { get; } + /// + /// Store all verified unsorted transactions' senders' fee currently in the memory pool. + /// + public SendersFeeMonitor SendersFeeMonitor = new SendersFeeMonitor(); + /// /// Total count of transactions in the pool. /// @@ -268,6 +273,7 @@ internal bool TryAdd(UInt256 hash, Transaction tx) try { _unsortedTransactions.Add(hash, poolItem); + SendersFeeMonitor.AddSenderFee(tx); _sortedTransactions.Add(poolItem); if (Count > Capacity) @@ -310,6 +316,7 @@ private bool TryRemoveVerified(UInt256 hash, out PoolItem item) return false; _unsortedTransactions.Remove(hash); + SendersFeeMonitor.RemoveSenderFee(item.Tx); _sortedTransactions.Remove(item); return true; @@ -337,6 +344,7 @@ internal void InvalidateVerifiedTransactions() // Clear the verified transactions now, since they all must be reverified. _unsortedTransactions.Clear(); + SendersFeeMonitor = new SendersFeeMonitor(); _sortedTransactions.Clear(); } @@ -409,7 +417,7 @@ internal void InvalidateAllTransactions() // Since unverifiedSortedTxPool is ordered in an ascending manner, we take from the end. foreach (PoolItem item in unverifiedSortedTxPool.Reverse().Take(count)) { - if (item.Tx.Reverify(snapshot, _unsortedTransactions.Select(p => p.Value.Tx))) + if (item.Tx.Reverify(snapshot, SendersFeeMonitor.GetSenderFee(item.Tx.Sender))) reverifiedItems.Add(item); else // Transaction no longer valid -- it will be removed from unverifiedTxPool. invalidItems.Add(item); @@ -432,6 +440,7 @@ internal void InvalidateAllTransactions() { if (_unsortedTransactions.TryAdd(item.Tx.Hash, item)) { + SendersFeeMonitor.AddSenderFee(item.Tx); verifiedSortedTxPool.Add(item); if (item.LastBroadcastTimestamp < rebroadcastCutOffTime) diff --git a/neo/Ledger/SendersFeeMonitor.cs b/neo/Ledger/SendersFeeMonitor.cs new file mode 100644 index 0000000000..efe3ac6ebe --- /dev/null +++ b/neo/Ledger/SendersFeeMonitor.cs @@ -0,0 +1,43 @@ +using Neo.Network.P2P.Payloads; +using System.Collections.Generic; +using System.Numerics; +using System.Threading; + +namespace Neo.Ledger +{ + public class SendersFeeMonitor + { + private readonly ReaderWriterLockSlim _senderFeeRwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + + /// + /// Store all verified unsorted transactions' senders' fee currently in the memory pool. + /// + private readonly Dictionary _senderFee = new Dictionary(); + + public BigInteger GetSenderFee(UInt160 sender) + { + _senderFeeRwLock.EnterReadLock(); + if (!_senderFee.TryGetValue(sender, out var value)) + value = BigInteger.Zero; + _senderFeeRwLock.ExitReadLock(); + return value; + } + + public void AddSenderFee(Transaction tx) + { + _senderFeeRwLock.EnterWriteLock(); + if (_senderFee.TryGetValue(tx.Sender, out var value)) + _senderFee[tx.Sender] = value + tx.SystemFee + tx.NetworkFee; + else + _senderFee.Add(tx.Sender, tx.SystemFee + tx.NetworkFee); + _senderFeeRwLock.ExitWriteLock(); + } + + public void RemoveSenderFee(Transaction tx) + { + _senderFeeRwLock.EnterWriteLock(); + if ((_senderFee[tx.Sender] -= tx.SystemFee + tx.NetworkFee) == 0) _senderFee.Remove(tx.Sender); + _senderFeeRwLock.ExitWriteLock(); + } + } +} diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index c135185272..de81275877 100644 --- a/neo/Network/P2P/Payloads/Transaction.cs +++ b/neo/Network/P2P/Payloads/Transaction.cs @@ -130,16 +130,14 @@ public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) return hashes.OrderBy(p => p).ToArray(); } - public virtual bool Reverify(Snapshot snapshot, IEnumerable mempool) + public virtual bool Reverify(Snapshot snapshot, BigInteger totalSenderFeeFromPool) { if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) return false; if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(GetScriptHashesForVerifying(snapshot)).Count() > 0) return false; BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); - BigInteger fee = SystemFee + NetworkFee; - if (balance < fee) return false; - fee += mempool.Where(p => p != this && p.Sender.Equals(Sender)).Select(p => (BigInteger)(p.SystemFee + p.NetworkFee)).Sum(); + BigInteger fee = SystemFee + NetworkFee + totalSenderFeeFromPool; if (balance < fee) return false; UInt160[] hashes = GetScriptHashesForVerifying(snapshot); if (hashes.Length != Witnesses.Length) return false; @@ -206,12 +204,12 @@ public static Transaction FromJson(JObject json) bool IInventory.Verify(Snapshot snapshot) { - return Verify(snapshot, Enumerable.Empty()); + return Verify(snapshot, BigInteger.Zero); } - public virtual bool Verify(Snapshot snapshot, IEnumerable mempool) + public virtual bool Verify(Snapshot snapshot, BigInteger totalSenderFeeFromPool) { - if (!Reverify(snapshot, mempool)) return false; + if (!Reverify(snapshot, totalSenderFeeFromPool)) return false; int size = Size; if (size > MaxTransactionSize) return false; long net_fee = NetworkFee - size * NativeContract.Policy.GetFeePerByte(snapshot); From da505d04484d81d4a27338b72af55785a3139c2c Mon Sep 17 00:00:00 2001 From: Ricardo Prado <38396062+lock9@users.noreply.github.com> Date: Tue, 12 Nov 2019 12:31:54 -0300 Subject: [PATCH 128/305] Using problem description (#1214) Using problem description helps problem grouping (people may propose different solutions for the same problem) --- .github/ISSUE_TEMPLATE/feature-or-enhancement-request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md index a2ddf9047c..9b98d24f99 100644 --- a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md +++ b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md @@ -6,7 +6,7 @@ labels: discussion assignees: '' --- -**Summary** +**Summary or problem description** A summary of the problem you want to solve or metric you want to improve **Do you have any solution you want to propose?** From a3bd55f610f4d53220785aaeb78e219cb9a762f1 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 13 Nov 2019 13:29:43 +0800 Subject: [PATCH 129/305] Improve SYSCALLs: `Neo.Crypto.*` (#1190) --- neo.UnitTests/Ledger/UT_Blockchain.cs | 6 +-- .../Network/P2P/Payloads/UT_Transaction.cs | 31 +++++++------- .../Network/P2P/Payloads/UT_Witness.cs | 4 +- neo.UnitTests/SmartContract/UT_Contract.cs | 42 +++++++++++-------- .../UT_ContractParameterContext.cs | 18 ++++---- .../SmartContract/UT_InteropService.NEO.cs | 22 ++++++---- .../SmartContract/UT_InteropService.cs | 6 +-- .../Wallets/SQLite/UT_VerificationContract.cs | 8 ++-- neo.UnitTests/Wallets/UT_Wallet.cs | 4 +- neo/Network/P2P/Payloads/Witness.cs | 2 +- neo/Network/RPC/TransactionManager.cs | 2 +- neo/SmartContract/Contract.cs | 6 ++- neo/SmartContract/Helper.cs | 12 +++--- neo/SmartContract/InteropService.NEO.cs | 37 ++++++++++------ neo/SmartContract/InteropService.cs | 19 --------- neo/Wallets/Wallet.cs | 4 +- neo/neo.csproj | 2 +- 17 files changed, 117 insertions(+), 108 deletions(-) diff --git a/neo.UnitTests/Ledger/UT_Blockchain.cs b/neo.UnitTests/Ledger/UT_Blockchain.cs index eb17e71c62..af0ec69a79 100644 --- a/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -70,13 +70,13 @@ public void TestContainsTransaction() [TestMethod] public void TestGetCurrentBlockHash() { - Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("5662a113d8fa9532ea9c52046a463e2e3fcfcdd6192d99cad805b376fb643ceb")); + Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0x0d492ce0f38090a65b2b01af50f7a6d685b6b76fbc41672762e96b05d15d742c")); } [TestMethod] public void TestGetCurrentHeaderHash() { - Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("5662a113d8fa9532ea9c52046a463e2e3fcfcdd6192d99cad805b376fb643ceb")); + Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0x0d492ce0f38090a65b2b01af50f7a6d685b6b76fbc41672762e96b05d15d742c")); } [TestMethod] @@ -88,7 +88,7 @@ public void TestGetBlock() [TestMethod] public void TestGetBlockHash() { - Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("5662a113d8fa9532ea9c52046a463e2e3fcfcdd6192d99cad805b376fb643ceb")); + Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0x0d492ce0f38090a65b2b01af50f7a6d685b6b76fbc41672762e96b05d15d742c")); Blockchain.Singleton.GetBlockHash(10).Should().BeNull(); } diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 570151d941..c8c874cee6 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -165,10 +165,9 @@ public void FeeIsMultiSigContract() } var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); - Assert.AreEqual(verificationGas, 2000540); - Assert.AreEqual(sizeGas, 358000); - Assert.AreEqual(verificationGas + sizeGas, 2358540); - Assert.AreEqual(tx.NetworkFee, 2358540); + Assert.AreEqual(verificationGas, 2000570); + Assert.AreEqual(sizeGas, 359000); + Assert.AreEqual(tx.NetworkFee, 2359570); } } @@ -214,7 +213,7 @@ public void FeeIsSignatureContractDetailed() Assert.IsNull(tx.Witnesses); // check pre-computed network fee (already guessing signature sizes) - tx.NetworkFee.Should().Be(1257240); + tx.NetworkFee.Should().Be(1258270); // ---- // Sign @@ -251,12 +250,12 @@ public void FeeIsSignatureContractDetailed() verificationGas += engine.GasConsumed; } } - Assert.AreEqual(verificationGas, 1000240); + Assert.AreEqual(verificationGas, 1000270); // ------------------ // check tx_size cost // ------------------ - Assert.AreEqual(tx.Size, 257); + Assert.AreEqual(tx.Size, 258); // will verify tx size, step by step @@ -272,16 +271,16 @@ public void FeeIsSignatureContractDetailed() // Part III Assert.AreEqual(tx.Script.GetVarSize(), 82); // Part IV - Assert.AreEqual(tx.Witnesses.GetVarSize(), 107); + Assert.AreEqual(tx.Witnesses.GetVarSize(), 108); // I + II + III + IV - Assert.AreEqual(tx.Size, 45 + 23 + 82 + 107); + Assert.AreEqual(tx.Size, 45 + 23 + 82 + 108); Assert.AreEqual(NativeContract.Policy.GetFeePerByte(snapshot), 1000); var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); - Assert.AreEqual(sizeGas, 257000); + Assert.AreEqual(sizeGas, 258000); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1257240); + Assert.AreEqual(verificationGas + sizeGas, 1258270); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -372,7 +371,7 @@ public void FeeIsSignatureContract_TestScope_Global() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1257240); + Assert.AreEqual(verificationGas + sizeGas, 1258270); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -464,7 +463,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1278240); + Assert.AreEqual(verificationGas + sizeGas, 1279270); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -559,7 +558,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1278240); + Assert.AreEqual(verificationGas + sizeGas, 1279270); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -714,7 +713,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1298240); + Assert.AreEqual(verificationGas + sizeGas, 1299270); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -1062,7 +1061,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1257240); + Assert.AreEqual(verificationGas + sizeGas, 1258270); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index 502317e604..87c82124b5 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -82,9 +82,9 @@ public void MaxSize_OK() // Check max size - witness.Size.Should().Be(1003); + witness.Size.Should().Be(1004); witness.InvocationScript.GetVarSize().Should().Be(653); - witness.VerificationScript.GetVarSize().Should().Be(350); + witness.VerificationScript.GetVarSize().Should().Be(351); Assert.IsTrue(witness.Size <= 1024); diff --git a/neo.UnitTests/SmartContract/UT_Contract.cs b/neo.UnitTests/SmartContract/UT_Contract.cs index 458128f569..c0f4a86cf1 100644 --- a/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/neo.UnitTests/SmartContract/UT_Contract.cs @@ -21,11 +21,12 @@ public void TestGetAddress() KeyPair key = new KeyPair(privateKey); Contract contract = Contract.CreateSignatureContract(key.PublicKey); byte[] script = contract.Script; - byte[] expectedArray = new byte[39]; + byte[] expectedArray = new byte[40]; expectedArray[0] = 0x21; Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); - expectedArray[34] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckSig), 0, expectedArray, 35, 4); + expectedArray[34] = 0x50; + expectedArray[35] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 36, 4); Assert.AreEqual(expectedArray.ToScriptHash().ToAddress(), contract.Address); } @@ -38,11 +39,12 @@ public void TestGetScriptHash() KeyPair key = new KeyPair(privateKey); Contract contract = Contract.CreateSignatureContract(key.PublicKey); byte[] script = contract.Script; - byte[] expectedArray = new byte[39]; + byte[] expectedArray = new byte[40]; expectedArray[0] = 0x21; Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); - expectedArray[34] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckSig), 0, expectedArray, 35, 4); + expectedArray[34] = 0x50; + expectedArray[35] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 36, 4); Assert.AreEqual(expectedArray.ToScriptHash(), contract.ScriptHash); } @@ -73,15 +75,16 @@ public void TestCreateMultiSigContract() publicKeys[1] = key2.PublicKey; publicKeys = publicKeys.OrderBy(p => p).ToArray(); Contract contract = Contract.CreateMultiSigContract(2, publicKeys); - byte[] expectedArray = new byte[75]; + byte[] expectedArray = new byte[76]; expectedArray[0] = 0x52; expectedArray[1] = 0x21; Array.Copy(publicKeys[0].EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = 0x21; Array.Copy(publicKeys[1].EncodePoint(true), 0, expectedArray, 36, 33); expectedArray[69] = 0x52; - expectedArray[70] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckMultiSig), 0, expectedArray, 71, 4); + expectedArray[70] = 0x50; + expectedArray[71] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaCheckMultiSig), 0, expectedArray, 72, 4); Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(contract.Script)); Assert.AreEqual(2, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); @@ -106,15 +109,16 @@ public void TestCreateMultiSigRedeemScript() Action action = () => Contract.CreateMultiSigRedeemScript(0, publicKeys); action.Should().Throw(); byte[] script = Contract.CreateMultiSigRedeemScript(2, publicKeys); - byte[] expectedArray = new byte[75]; + byte[] expectedArray = new byte[76]; expectedArray[0] = 0x52; expectedArray[1] = 0x21; Array.Copy(publicKeys[0].EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = 0x21; Array.Copy(publicKeys[1].EncodePoint(true), 0, expectedArray, 36, 33); expectedArray[69] = 0x52; - expectedArray[70] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckMultiSig), 0, expectedArray, 71, 4); + expectedArray[70] = 0x50; + expectedArray[71] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaCheckMultiSig), 0, expectedArray, 72, 4); Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(script)); } @@ -127,11 +131,12 @@ public void TestCreateSignatureContract() KeyPair key = new KeyPair(privateKey); Contract contract = Contract.CreateSignatureContract(key.PublicKey); byte[] script = contract.Script; - byte[] expectedArray = new byte[39]; + byte[] expectedArray = new byte[40]; expectedArray[0] = 0x21; Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); - expectedArray[34] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckSig), 0, expectedArray, 35, 4); + expectedArray[34] = 0x50; + expectedArray[35] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 36, 4); Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(script)); Assert.AreEqual(1, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); @@ -145,11 +150,12 @@ public void TestCreateSignatureRedeemScript() rng.GetBytes(privateKey); KeyPair key = new KeyPair(privateKey); byte[] script = Contract.CreateSignatureRedeemScript(key.PublicKey); - byte[] expectedArray = new byte[39]; + byte[] expectedArray = new byte[40]; expectedArray[0] = 0x21; Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); - expectedArray[34] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_CheckSig), 0, expectedArray, 35, 4); + expectedArray[34] = 0x50; + expectedArray[35] = 0x68; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 36, 4); Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(script)); } } diff --git a/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index 0e035349d1..f5fd304243 100644 --- a/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -33,7 +33,7 @@ public static void ClassSetUp(TestContext context) public void TestGetComplete() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + tx.Sender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); var context = new ContractParametersContext(tx); context.Completed.Should().BeFalse(); } @@ -42,11 +42,11 @@ public void TestGetComplete() public void TestToString() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + tx.Sender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); 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\":\"AAAAAADyd\\/EUQDWkOJf5\\u002BhFSWOrAFa3KvgAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA==\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}]}}}"); + str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""hex"":""AAAAAAB8l3ZIRRctgn08hjdDKTkxppEnGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA=="",""items"":{""0x1a2791a63139294337863c7d822d17454876977c"":{""script"":""IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFlBoCpBq1A=="",""parameters"":[{""type"":""Signature"",""value"":""AQ==""}]}}}"); } [TestMethod] @@ -60,7 +60,7 @@ public void TestParse() [TestMethod] public void TestFromJson() { - Action action = () => ContractParametersContext.Parse("{\"type\":\"wrongType\",\"hex\":\"0000000000f277f1144035a43897f9fa115258eac015adcabe0000000000000000000000000000000000000000000100\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1668747476aa\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}"); + Action action = () => ContractParametersContext.Parse("{\"type\":\"wrongType\",\"hex\":\"00000000007c97764845172d827d3c863743293931a691271a0000000000000000000000000000000000000000000100\",\"items\":{\"0x1a2791a63139294337863c7d822d17454876977c\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1650680a906ad4\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}"); action.Should().Throw(); } @@ -71,7 +71,7 @@ public void TestAdd() var context1 = new ContractParametersContext(tx); context1.Add(contract, 0, new byte[] { 0x01 }).Should().BeFalse(); - tx.Sender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + tx.Sender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); var context2 = new ContractParametersContext(tx); context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); //test repeatlly createItem @@ -82,7 +82,7 @@ public void TestAdd() public void TestGetParameter() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + tx.Sender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); var context = new ContractParametersContext(tx); context.GetParameter(tx.Sender, 0).Should().BeNull(); @@ -95,7 +95,7 @@ public void TestGetParameter() public void TestGetWitnesses() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + tx.Sender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); var context = new ContractParametersContext(tx); context.Add(contract, 0, new byte[] { 0x01 }); Witness[] witnesses = context.GetWitnesses(); @@ -108,7 +108,7 @@ public void TestGetWitnesses() public void TestAddSignature() { Transaction tx = TestUtils.GetTransaction(); - var singleSender = UInt160.Parse("0xbecaad15c0ea585211faf99738a4354014f177f2"); + var singleSender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); tx.Sender = singleSender; //singleSign @@ -138,7 +138,7 @@ public void TestAddSignature() key.PublicKey, key2.PublicKey }); - var multiSender = UInt160.Parse("0xa4712ed1a8d813561b28ec828930d85e6e08ec7a"); + var multiSender = UInt160.Parse("0xfc8b59f1a337dcc17b1a201d327a2081d41fac8d"); tx.Sender = multiSender; context = new ContractParametersContext(tx); context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); diff --git a/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 885f3d3498..703fc6241e 100644 --- a/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -11,6 +11,7 @@ using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; using Neo.SmartContract.Manifest; +using Neo.VM; using Neo.VM.Types; using Neo.Wallets; using System.Linq; @@ -33,12 +34,14 @@ public void TestCheckSig() byte[] signature = Crypto.Default.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); - InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckSig).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(new byte[70]); - InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckSig).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); } @@ -73,13 +76,15 @@ public void TestCrypto_CheckMultiSig() }; engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); - InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckMultiSig).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); pubkeys = new VMArray(); engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); - InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckMultiSig).Should().BeFalse(); + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeFalse(); pubkeys = new VMArray { @@ -89,7 +94,8 @@ public void TestCrypto_CheckMultiSig() signatures = new VMArray(); engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); - InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckMultiSig).Should().BeFalse(); + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeFalse(); pubkeys = new VMArray { @@ -103,7 +109,8 @@ public void TestCrypto_CheckMultiSig() }; engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); - InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckMultiSig).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); pubkeys = new VMArray @@ -118,7 +125,8 @@ public void TestCrypto_CheckMultiSig() }; engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); - InteropService.Invoke(engine, InteropService.Neo_Crypto_CheckMultiSig).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); } diff --git a/neo.UnitTests/SmartContract/UT_InteropService.cs b/neo.UnitTests/SmartContract/UT_InteropService.cs index fe28f1503d..cc6eac738a 100644 --- a/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -365,7 +365,7 @@ public void TestCrypto_Verify() engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); engine.CurrentContext.EvaluationStack.Push(message); - InteropService.Invoke(engine, InteropService.System_Crypto_Verify).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); byte[] wrongkey = pubkey.EncodePoint(false); @@ -373,8 +373,8 @@ public void TestCrypto_Verify() engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(wrongkey); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(engine.ScriptContainer)); - InteropService.Invoke(engine, InteropService.System_Crypto_Verify).Should().BeFalse(); - + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Peek().GetBoolean().Should().BeFalse(); } [TestMethod] diff --git a/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs b/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs index 8f111f2112..70d0ba2e4b 100644 --- a/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs +++ b/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs @@ -120,11 +120,11 @@ public void TestSerialize() byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); byte[] script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey); - byte[] result = new byte[62]; + byte[] result = new byte[63]; result[20] = 0x01; result[21] = 0x00; - result[22] = 0x27; - Array.Copy(script, 0, result, 23, 39); + result[22] = 0x28; + Array.Copy(script, 0, result, 23, 40); Assert.AreEqual(Encoding.Default.GetString(result), Encoding.Default.GetString(byteArray)); } @@ -142,7 +142,7 @@ public void TestGetSize() Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), ParameterList = new[] { ContractParameterType.Signature } }; - Assert.AreEqual(62, contract1.Size); + Assert.AreEqual(63, contract1.Size); } } } diff --git a/neo.UnitTests/Wallets/UT_Wallet.cs b/neo.UnitTests/Wallets/UT_Wallet.cs index ed48a047e5..e6bf40721c 100644 --- a/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/neo.UnitTests/Wallets/UT_Wallet.cs @@ -168,9 +168,9 @@ public void TestGetVersion() public void TestGetAccount1() { MyWallet wallet = new MyWallet(); - wallet.CreateAccount(UInt160.Parse("522a2b818c308c7a2c77cfdda11763fe043bfb40")); + wallet.CreateAccount(UInt160.Parse("0xc43d04da83afcf7df3b2908c169cfbfbf7512d7f")); WalletAccount account = wallet.GetAccount(ECCurve.Secp256r1.G); - account.ScriptHash.Should().Be(UInt160.Parse("0x522a2b818c308c7a2c77cfdda11763fe043bfb40")); + account.ScriptHash.Should().Be(UInt160.Parse("0xc43d04da83afcf7df3b2908c169cfbfbf7512d7f")); } [TestMethod] diff --git a/neo/Network/P2P/Payloads/Witness.cs b/neo/Network/P2P/Payloads/Witness.cs index 55ba4f2564..1cd5bf7ee8 100644 --- a/neo/Network/P2P/Payloads/Witness.cs +++ b/neo/Network/P2P/Payloads/Witness.cs @@ -31,7 +31,7 @@ void ISerializable.Deserialize(BinaryReader reader) // This is designed to allow a MultiSig 10/10 (around 1003 bytes) ~1024 bytes // Invocation = 10 * 64 + 10 = 650 ~ 664 (exact is 653) InvocationScript = reader.ReadVarBytes(664); - // Verification = 10 * 33 + 10 = 340 ~ 360 (exact is 350) + // Verification = 10 * 33 + 10 = 340 ~ 360 (exact is 351) VerificationScript = reader.ReadVarBytes(360); } diff --git a/neo/Network/RPC/TransactionManager.cs b/neo/Network/RPC/TransactionManager.cs index d4a651fd78..ba714a52ef 100644 --- a/neo/Network/RPC/TransactionManager.cs +++ b/neo/Network/RPC/TransactionManager.cs @@ -98,7 +98,7 @@ private long EstimateNetworkFee() foreach (var hash in hashes) { size += 166; - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null); } networkFee += size * new PolicyAPI(rpcClient).GetFeePerByte(); diff --git a/neo/SmartContract/Contract.cs b/neo/SmartContract/Contract.cs index 58e32768d7..35fd0cb95b 100644 --- a/neo/SmartContract/Contract.cs +++ b/neo/SmartContract/Contract.cs @@ -81,7 +81,8 @@ public static byte[] CreateMultiSigRedeemScript(int m, params ECPoint[] publicKe sb.EmitPush(publicKey.EncodePoint(true)); } sb.EmitPush(publicKeys.Length); - sb.EmitSysCall(InteropService.Neo_Crypto_CheckMultiSig); + sb.Emit(OpCode.PUSHNULL); + sb.EmitSysCall(InteropService.Neo_Crypto_ECDsaCheckMultiSig); return sb.ToArray(); } } @@ -100,7 +101,8 @@ public static byte[] CreateSignatureRedeemScript(ECPoint publicKey) using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(publicKey.EncodePoint(true)); - sb.EmitSysCall(InteropService.Neo_Crypto_CheckSig); + sb.Emit(OpCode.PUSHNULL); + sb.EmitSysCall(InteropService.Neo_Crypto_ECDsaVerify); return sb.ToArray(); } } diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index 809ec8345f..4f7e320c37 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -115,7 +115,7 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n) { m = 0; n = 0; int i = 0; - if (script.Length < 41) return false; + if (script.Length < 42) return false; if (script[i] > (byte)OpCode.PUSH16) return false; if (script[i] < (byte)OpCode.PUSH1 && script[i] != 1 && script[i] != 2) return false; switch (script[i]) @@ -154,19 +154,21 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n) if (n != script[i++] - 80) return false; break; } + if (script[i++] != (byte)OpCode.PUSHNULL) return false; if (script[i++] != (byte)OpCode.SYSCALL) return false; if (script.Length != i + 4) return false; - if (BitConverter.ToUInt32(script, i) != InteropService.Neo_Crypto_CheckMultiSig) + if (BitConverter.ToUInt32(script, i) != InteropService.Neo_Crypto_ECDsaCheckMultiSig) return false; return true; } public static bool IsSignatureContract(this byte[] script) { - if (script.Length != 39) return false; + if (script.Length != 40) return false; if (script[0] != (byte)OpCode.PUSHBYTES33 - || script[34] != (byte)OpCode.SYSCALL - || BitConverter.ToUInt32(script, 35) != InteropService.Neo_Crypto_CheckSig) + || script[34] != (byte)OpCode.PUSHNULL + || script[35] != (byte)OpCode.SYSCALL + || BitConverter.ToUInt32(script, 36) != InteropService.Neo_Crypto_ECDsaVerify) return false; return true; } diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index e500eeb235..90a49a4df2 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -2,6 +2,7 @@ using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; using Neo.SmartContract.Manifest; @@ -17,8 +18,8 @@ namespace Neo.SmartContract static partial class InteropService { public static readonly uint Neo_Native_Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application); - public static readonly uint Neo_Crypto_CheckSig = Register("Neo.Crypto.CheckSig", Crypto_CheckSig, 0_01000000, TriggerType.All); - public static readonly uint Neo_Crypto_CheckMultiSig = Register("Neo.Crypto.CheckMultiSig", Crypto_CheckMultiSig, GetCheckMultiSigPrice, TriggerType.All); + public static readonly uint Neo_Crypto_ECDsaVerify = Register("Neo.Crypto.ECDsaVerify", Crypto_ECDsaVerify, 0_01000000, TriggerType.All); + public static readonly uint Neo_Crypto_ECDsaCheckMultiSig = Register("Neo.Crypto.ECDsaCheckMultiSig", Crypto_ECDsaCheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All); public static readonly uint Neo_Account_IsStandard = Register("Neo.Account.IsStandard", Account_IsStandard, 0_00030000, TriggerType.All); public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application); public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application); @@ -41,15 +42,15 @@ static InteropService() Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application); } - private static long GetCheckMultiSigPrice(RandomAccessStack stack) + private static long GetECDsaCheckMultiSigPrice(RandomAccessStack stack) { - if (stack.Count == 0) return 0; - var item = stack.Peek(); + if (stack.Count < 2) return 0; + var item = stack.Peek(1); int n; if (item is VMArray array) n = array.Count; else n = (int)item.GetBigInteger(); if (n < 1) return 0; - return GetPrice(Neo_Crypto_CheckSig, stack) * n; + return GetPrice(Neo_Crypto_ECDsaVerify, stack) * n; } private static long GetDeploymentPrice(RandomAccessStack stack) @@ -73,14 +74,20 @@ private static bool Native_Deploy(ApplicationEngine engine) return true; } - private static bool Crypto_CheckSig(ApplicationEngine engine) + private static bool Crypto_ECDsaVerify(ApplicationEngine engine) { + StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); + byte[] message = item0 switch + { + InteropInterface _interface => _interface.GetInterface().GetHashData(), + Null _ => engine.ScriptContainer.GetHashData(), + _ => item0.GetByteArray() + }; byte[] pubkey = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); byte[] signature = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - try { - engine.CurrentContext.EvaluationStack.Push(Crypto.Default.VerifySignature(engine.ScriptContainer.GetHashData(), signature, pubkey)); + engine.CurrentContext.EvaluationStack.Push(Crypto.Default.VerifySignature(message, signature, pubkey)); } catch (ArgumentException) { @@ -89,12 +96,18 @@ private static bool Crypto_CheckSig(ApplicationEngine engine) return true; } - private static bool Crypto_CheckMultiSig(ApplicationEngine engine) + private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) { + StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); + byte[] message = item0 switch + { + InteropInterface _interface => _interface.GetInterface().GetHashData(), + Null _ => engine.ScriptContainer.GetHashData(), + _ => item0.GetByteArray() + }; int n; byte[][] pubkeys; StackItem item = engine.CurrentContext.EvaluationStack.Pop(); - if (item is VMArray array1) { pubkeys = array1.Select(p => p.GetByteArray()).ToArray(); @@ -109,7 +122,6 @@ private static bool Crypto_CheckMultiSig(ApplicationEngine engine) for (int i = 0; i < n; i++) pubkeys[i] = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); } - int m; byte[][] signatures; item = engine.CurrentContext.EvaluationStack.Pop(); @@ -127,7 +139,6 @@ private static bool Crypto_CheckMultiSig(ApplicationEngine engine) for (int i = 0; i < m; i++) signatures[i] = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); } - byte[] message = engine.ScriptContainer.GetHashData(); bool fSuccess = true; try { diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index eed16fe17a..be80366811 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -2,7 +2,6 @@ using Neo.Cryptography.ECC; using Neo.IO; using Neo.Ledger; -using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; @@ -42,7 +41,6 @@ public static partial class InteropService public static readonly uint System_Runtime_Deserialize = Register("System.Runtime.Deserialize", Runtime_Deserialize, 0_00500000, TriggerType.All); public static readonly uint System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400, TriggerType.All); public static readonly uint System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", Runtime_GetNotifications, 0_00010000, TriggerType.All); - public static readonly uint System_Crypto_Verify = Register("System.Crypto.Verify", Crypto_Verify, 0_01000000, TriggerType.All); public static readonly uint System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); public static readonly uint System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application); public static readonly uint System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application); @@ -329,23 +327,6 @@ private static bool Runtime_Deserialize(ApplicationEngine engine) return true; } - private static bool Crypto_Verify(ApplicationEngine engine) - { - StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); - byte[] message; - if (item0 is InteropInterface _interface) - message = _interface.GetInterface().GetHashData(); - else - message = item0.GetByteArray(); - byte[] pubkey = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - if (pubkey[0] != 2 && pubkey[0] != 3 && pubkey[0] != 4) - return false; - byte[] signature = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - bool result = Crypto.Default.VerifySignature(message, signature, pubkey); - engine.CurrentContext.EvaluationStack.Push(result); - return true; - } - private static bool Blockchain_GetHeight(ApplicationEngine engine) { engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.Height); diff --git a/neo/Wallets/Wallet.cs b/neo/Wallets/Wallet.cs index 5149c2b2fe..bc52b9b368 100644 --- a/neo/Wallets/Wallet.cs +++ b/neo/Wallets/Wallet.cs @@ -353,7 +353,7 @@ public static long CalculateNetWorkFee(byte[] witness_script, ref int size) if (witness_script.IsSignatureContract()) { size += 66 + witness_script.GetVarSize(); - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null); + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null); } else if (witness_script.IsMultiSigContract(out int m, out int n)) { @@ -365,7 +365,7 @@ public static long CalculateNetWorkFee(byte[] witness_script, ref int size) networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] * n; using (ScriptBuilder sb = new ScriptBuilder()) networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; - networkFee += InteropService.GetPrice(InteropService.Neo_Crypto_CheckSig, null) * n; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null) * n; } else { diff --git a/neo/neo.csproj b/neo/neo.csproj index b60b204915..98824eb526 100644 --- a/neo/neo.csproj +++ b/neo/neo.csproj @@ -17,7 +17,7 @@ Neo The Neo Project Neo - latest + preview From a1c2aefac386587299db7aacef6ccf43884e226a Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 13 Nov 2019 08:58:33 +0100 Subject: [PATCH 130/305] Prevent XXE (3x) (#1229) --- neo/Network/UPnP.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/neo/Network/UPnP.cs b/neo/Network/UPnP.cs index 5cac628daa..2d0d1d1cc5 100644 --- a/neo/Network/UPnP.cs +++ b/neo/Network/UPnP.cs @@ -76,7 +76,7 @@ private static string GetServiceUrl(string resp) { try { - XmlDocument desc = new XmlDocument(); + XmlDocument desc = new XmlDocument() { XmlResolver = null }; HttpWebRequest request = WebRequest.CreateHttp(resp); using (WebResponse response = request.GetResponse()) { @@ -155,7 +155,7 @@ private static XmlDocument SOAPRequest(string url, string soap, string function) using (Stream reqs = r.GetRequestStream()) { reqs.Write(b, 0, b.Length); - XmlDocument resp = new XmlDocument(); + XmlDocument resp = new XmlDocument() { XmlResolver = null }; WebResponse wres = r.GetResponse(); using (Stream ress = wres.GetResponseStream()) { From 97d14661bbe322fb58be43050c4d24df9b2c64af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Wed, 13 Nov 2019 12:45:05 -0300 Subject: [PATCH 131/305] Remove unnecessary logic from Mempool (#1216) --- neo/Ledger/MemoryPool.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 8f37855a5c..15c647f163 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -17,8 +17,7 @@ namespace Neo.Ledger public class MemoryPool : IReadOnlyCollection { // Allow a reverified transaction to be rebroadcasted if it has been this many block times since last broadcast. - private const int BlocksTillRebroadcastLowPriorityPoolTx = 30; - private const int BlocksTillRebroadcastHighPriorityPoolTx = 10; + private const int BlocksTillRebroadcast = 10; private int RebroadcastMultiplierThreshold => Capacity / 10; private static readonly double MaxMillisecondsToReverifyTx = (double)Blockchain.MillisecondsPerBlock / 3; @@ -428,9 +427,8 @@ internal void InvalidateAllTransactions() _txRwLock.EnterWriteLock(); try { - int blocksTillRebroadcast = Object.ReferenceEquals(unverifiedSortedTxPool, _sortedTransactions) - ? BlocksTillRebroadcastHighPriorityPoolTx : BlocksTillRebroadcastLowPriorityPoolTx; - + int blocksTillRebroadcast = BlocksTillRebroadcast; + // Increases, proportionally, blocksTillRebroadcast if mempool has more items than threshold bigger RebroadcastMultiplierThreshold if (Count > RebroadcastMultiplierThreshold) blocksTillRebroadcast = blocksTillRebroadcast * Count / RebroadcastMultiplierThreshold; @@ -478,7 +476,7 @@ internal void InvalidateAllTransactions() /// /// Note: this must only be called from a single thread (the Blockchain actor) /// - /// Max transactions to reverify, the value passed cam be >=1 + /// 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, Snapshot snapshot) From 0036a82339f9f9750c54584b3f1d28f88b9d61ee Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 14 Nov 2019 07:45:06 +0100 Subject: [PATCH 132/305] Prevent remove storage flag when there are something stored (#1227) --- neo.UnitTests/SmartContract/UT_InteropService.NEO.cs | 12 ++++++++++++ neo/SmartContract/InteropService.NEO.cs | 1 + 2 files changed, 13 insertions(+) diff --git a/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 703fc6241e..243cfa89ef 100644 --- a/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -240,6 +240,18 @@ public void TestContract_Update() engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeTrue(); + + // Remove Storage flag with something stored + + state.Manifest.Features = ContractFeatures.NoProperty; + mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); + mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); + + engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine.LoadScript(state.Script); + engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); + engine.CurrentContext.EvaluationStack.Push(script); + InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeFalse(); } [TestMethod] diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index 90a49a4df2..212d1091b6 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -236,6 +236,7 @@ private static bool Contract_Update(ApplicationEngine engine) contract = engine.Snapshot.Contracts.GetAndChange(contract.ScriptHash); contract.Manifest = ContractManifest.Parse(manifest); if (!contract.Manifest.IsValid(contract.ScriptHash)) return false; + if (!contract.HasStorage && engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).Any()) return false; } return true; From 4be93ef8dcf06467a4c09724befdb5ac5f2e3510 Mon Sep 17 00:00:00 2001 From: Krain Chen Date: Thu, 14 Nov 2019 15:22:32 +0800 Subject: [PATCH 133/305] Add NeoAPI and update code after testing with NEO3 preview1 (#1150) --- .gitignore | 1 + .../Network/RPC/UT_ContractClient.cs | 11 +- neo.UnitTests/Network/RPC/UT_Helper.cs | 29 +++ neo.UnitTests/Network/RPC/UT_Nep5API.cs | 25 ++- neo.UnitTests/Network/RPC/UT_RpcClient.cs | 50 ++--- .../Network/RPC/UT_TransactionManager.cs | 5 +- neo.UnitTests/Network/RPC/UT_WalletAPI.cs | 116 +++++++++++ neo.UnitTests/UT_Utility.cs | 55 +++++ neo.UnitTests/VM/UT_Helper.cs | 12 +- neo/Network/RPC/ContractClient.cs | 5 +- neo/Network/RPC/Helper.cs | 38 ++++ neo/Network/RPC/Models/RpcBlock.cs | 10 +- neo/Network/RPC/Models/RpcBlockHeader.cs | 10 +- neo/Network/RPC/Models/RpcInvokeResult.cs | 4 - neo/Network/RPC/Models/RpcNep5TokenInfo.cs | 15 ++ neo/Network/RPC/Models/RpcTransaction.cs | 7 +- neo/Network/RPC/Nep5API.cs | 31 ++- neo/Network/RPC/RpcClient.cs | 24 ++- neo/Network/RPC/TransactionManager.cs | 16 +- neo/Network/RPC/WalletAPI.cs | 192 ++++++++++++++++++ neo/Utility.cs | 61 ++++++ 21 files changed, 644 insertions(+), 73 deletions(-) create mode 100644 neo.UnitTests/Network/RPC/UT_Helper.cs create mode 100644 neo.UnitTests/Network/RPC/UT_WalletAPI.cs create mode 100644 neo.UnitTests/UT_Utility.cs create mode 100644 neo/Network/RPC/Helper.cs create mode 100644 neo/Network/RPC/Models/RpcNep5TokenInfo.cs create mode 100644 neo/Network/RPC/WalletAPI.cs create mode 100644 neo/Utility.cs diff --git a/.gitignore b/.gitignore index 88426a8e9b..cb49b769de 100644 --- a/.gitignore +++ b/.gitignore @@ -255,3 +255,4 @@ paket-files/ *.sln.iml PublishProfiles +/.vscode diff --git a/neo.UnitTests/Network/RPC/UT_ContractClient.cs b/neo.UnitTests/Network/RPC/UT_ContractClient.cs index fde8a7747d..13a02b5b9d 100644 --- a/neo.UnitTests/Network/RPC/UT_ContractClient.cs +++ b/neo.UnitTests/Network/RPC/UT_ContractClient.cs @@ -24,15 +24,6 @@ public void TestSetup() rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]); } - [TestMethod] - public void TestMakeScript() - { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - - Assert.AreEqual("14000000000000000000000000000000000000000051c10962616c616e63654f66142582d1b275e86c8f0e93a9b2facd5fdb760976a168627d5b52", - testScript.ToHexString()); - } - [TestMethod] public void TestInvoke() { @@ -60,7 +51,7 @@ public void TestDeployContract() UT_TransactionManager.MockInvokeScript(rpcClientMock, script, new ContractParameter()); ContractClient contractClient = new ContractClient(rpcClientMock.Object); - var result = contractClient.DeployContract(new byte[1], manifest, keyPair1); + var result = contractClient.CreateDeployContractTx(new byte[1], manifest, keyPair1); Assert.IsNotNull(result); } diff --git a/neo.UnitTests/Network/RPC/UT_Helper.cs b/neo.UnitTests/Network/RPC/UT_Helper.cs new file mode 100644 index 0000000000..cb791fa6ce --- /dev/null +++ b/neo.UnitTests/Network/RPC/UT_Helper.cs @@ -0,0 +1,29 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.RPC; +using System; +using System.Numerics; + +namespace Neo.UnitTests.Network.RPC +{ + [TestClass] + public class UT_Helper + { + [TestMethod] + public void TestToBigInteger() + { + decimal amount = 1.23456789m; + uint decimals = 9; + var result = amount.ToBigInteger(decimals); + Assert.AreEqual(1234567890, result); + + amount = 1.23456789m; + decimals = 18; + result = amount.ToBigInteger(decimals); + Assert.AreEqual(BigInteger.Parse("1234567890000000000"), result); + + amount = 1.23456789m; + decimals = 4; + Assert.ThrowsException(() => result = amount.ToBigInteger(decimals)); + } + } +} diff --git a/neo.UnitTests/Network/RPC/UT_Nep5API.cs b/neo.UnitTests/Network/RPC/UT_Nep5API.cs index 72e0c29487..48fd711638 100644 --- a/neo.UnitTests/Network/RPC/UT_Nep5API.cs +++ b/neo.UnitTests/Network/RPC/UT_Nep5API.cs @@ -5,6 +5,7 @@ using Neo.SmartContract.Native; using Neo.VM; using Neo.Wallets; +using System.Linq; using System.Numerics; namespace Neo.UnitTests.Network.RPC @@ -76,13 +77,35 @@ public void TestGetTotalSupply() Assert.AreEqual(1_00000000, (int)result); } + [TestMethod] + public void TestGetTokenInfo() + { + UInt160 scriptHash = NativeContract.GAS.Hash; + byte[] testScript = scriptHash.MakeScript("name") + .Concat(scriptHash.MakeScript("symbol")) + .Concat(scriptHash.MakeScript("decimals")) + .Concat(scriptHash.MakeScript("totalSupply")) + .ToArray(); ; + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, + new ContractParameter { Type = ContractParameterType.String, Value = NativeContract.GAS.Name }, + new ContractParameter { Type = ContractParameterType.String, Value = NativeContract.GAS.Symbol }, + new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(NativeContract.GAS.Decimals) }, + new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_00000000) }); + + var result = nep5API.GetTokenInfo(NativeContract.GAS.Hash); + Assert.AreEqual(NativeContract.GAS.Name, result.Name); + Assert.AreEqual(NativeContract.GAS.Symbol, result.Symbol); + Assert.AreEqual(8, (int)result.Decimals); + Assert.AreEqual(1_00000000, (int)result.TotalSupply); + } + [TestMethod] public void TestTransfer() { byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, new BigInteger(1_00000000)); UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter()); - var result = nep5API.Transfer(NativeContract.GAS.Hash, keyPair1, UInt160.Zero, new BigInteger(1_00000000)); + var result = nep5API.CreateTransferTx(NativeContract.GAS.Hash, keyPair1, UInt160.Zero, new BigInteger(1_00000000)); Assert.IsNotNull(result); } } diff --git a/neo.UnitTests/Network/RPC/UT_RpcClient.cs b/neo.UnitTests/Network/RPC/UT_RpcClient.cs index eb256cc6d1..a8b212fe37 100644 --- a/neo.UnitTests/Network/RPC/UT_RpcClient.cs +++ b/neo.UnitTests/Network/RPC/UT_RpcClient.cs @@ -109,13 +109,9 @@ public void TestGetBlockHex() { JObject response = CreateResponse(1); response["result"] = "000000002deadfa82cbc4682f5800"; - MockResponse(response.ToString()); - var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); - Assert.AreEqual("000000002deadfa82cbc4682f5800", result); - MockResponse(response.ToString()); - result = rpc.GetBlockHex("100"); + var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); Assert.AreEqual("000000002deadfa82cbc4682f5800", result); } @@ -134,20 +130,15 @@ public void TestGetBlock() }; JObject json = block.ToJson(); + json["confirmations"] = 20; JObject response = CreateResponse(1); response["result"] = json; - MockResponse(response.ToString()); - var result = rpc.GetBlock("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); - Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); - Assert.IsNull(result.Confirmations); - Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); - Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); - MockResponse(response.ToString()); - result = rpc.GetBlock("100"); + var result = rpc.GetBlock("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); - Assert.IsNull(result.Confirmations); + Assert.IsNull(result.NextBlockHash); + Assert.AreEqual(20, result.Confirmations); Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); @@ -158,6 +149,7 @@ public void TestGetBlock() result = rpc.GetBlock("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); Assert.AreEqual(20, result.Confirmations); + Assert.AreEqual("0x773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e", result.NextBlockHash.ToString()); Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); } @@ -189,13 +181,9 @@ public void TestGetBlockHeaderHex() { JObject response = CreateResponse(1); response["result"] = "0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; - MockResponse(response.ToString()); - var result = rpc.GetBlockHeaderHex("100"); - Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); - MockResponse(response.ToString()); - result = rpc.GetBlockHeaderHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); + var result = rpc.GetBlockHeaderHex("100"); Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); } @@ -206,18 +194,15 @@ public void TestGetBlockHeader() TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 _, out UInt160 _, out ulong _, out uint _, out Witness _); JObject json = header.ToJson(); + json["confirmations"] = 20; JObject response = CreateResponse(1); response["result"] = json; MockResponse(response.ToString()); var result = rpc.GetBlockHeader("100"); Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); - Assert.IsNull(result.Confirmations); - - MockResponse(response.ToString()); - result = rpc.GetBlockHeader("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); - Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); - Assert.IsNull(result.Confirmations); + Assert.IsNull(result.NextBlockHash); + Assert.AreEqual(20, result.Confirmations); json["confirmations"] = 20; json["nextblockhash"] = "4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; @@ -373,9 +358,18 @@ public void TestGetRawTransaction() Assert.AreEqual(transaction.Hash, result.Transaction.Hash); Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + // make the code compatible with the old version json["blockhash"] = UInt256.Zero.ToString(); json["confirmations"] = 100; json["blocktime"] = 10; + MockResponse(response.ToString()); + + result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); + Assert.AreEqual(transaction.Hash, result.Transaction.Hash); + Assert.AreEqual(100, result.Confirmations); + Assert.AreEqual(null, result.VMState); + Assert.AreEqual(json.ToString(), result.ToJson().ToString()); + json["vmState"] = VMState.HALT; MockResponse(response.ToString()); @@ -472,8 +466,7 @@ public void TestInvokeFunction() ""type"": ""ByteArray"", ""value"": ""262bec084432"" } - ], - ""tx"":""d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"" + ] }"); JObject response = CreateResponse(1); response["result"] = json; @@ -496,8 +489,7 @@ public void TestInvokeScript() ""type"": ""ByteArray"", ""value"": ""262bec084432"" } - ], - ""tx"":""d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"" + ] }"); JObject response = CreateResponse(1); response["result"] = json; diff --git a/neo.UnitTests/Network/RPC/UT_TransactionManager.cs b/neo.UnitTests/Network/RPC/UT_TransactionManager.cs index ee55f81bb9..e1cce967cd 100644 --- a/neo.UnitTests/Network/RPC/UT_TransactionManager.cs +++ b/neo.UnitTests/Network/RPC/UT_TransactionManager.cs @@ -68,11 +68,10 @@ public static void MockInvokeScript(Mock mockClient, byte[] script, p Stack = parameters, GasConsumed = "100", Script = script.ToHexString(), - State = "", - Tx = "" + State = "" }; - mockClient.Setup(p => p.RpcSend("invokescript", It.Is(j => j.AsString() == script.ToHexString()))) + mockClient.Setup(p => p.RpcSend("invokescript", It.Is(j => j[0].AsString() == script.ToHexString()))) .Returns(result.ToJson()) .Verifiable(); } diff --git a/neo.UnitTests/Network/RPC/UT_WalletAPI.cs b/neo.UnitTests/Network/RPC/UT_WalletAPI.cs new file mode 100644 index 0000000000..bba6a787e1 --- /dev/null +++ b/neo.UnitTests/Network/RPC/UT_WalletAPI.cs @@ -0,0 +1,116 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Neo.IO.Json; +using Neo.Network.P2P.Payloads; +using Neo.Network.RPC; +using Neo.Network.RPC.Models; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using System.Numerics; + +namespace Neo.UnitTests.Network.RPC +{ + [TestClass] + public class UT_WalletAPI + { + Mock rpcClientMock; + KeyPair keyPair1; + string address1; + UInt160 sender; + WalletAPI walletAPI; + + [TestInitialize] + public void TestSetup() + { + keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); + sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); + address1 = Neo.Wallets.Helper.ToAddress(sender); + rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]); + walletAPI = new WalletAPI(rpcClientMock.Object); + } + + [TestMethod] + public void TestGetUnclaimedGas() + { + byte[] testScript = NativeContract.NEO.Hash.MakeScript("unclaimedGas", sender, 99); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); + + var balance = walletAPI.GetUnclaimedGas(address1); + Assert.AreEqual(1.1m, balance); + } + + [TestMethod] + public void TestGetNeoBalance() + { + byte[] testScript = NativeContract.NEO.Hash.MakeScript("balanceOf", sender); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_00000000) }); + + var balance = walletAPI.GetNeoBalance(address1); + Assert.AreEqual(1_00000000u, balance); + } + + [TestMethod] + public void TestGetGasBalance() + { + byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", sender); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); + + var balance = walletAPI.GetGasBalance(address1); + Assert.AreEqual(1.1m, balance); + } + + [TestMethod] + public void TestGetTokenBalance() + { + byte[] testScript = UInt160.Zero.MakeScript("balanceOf", sender); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); + + var balance = walletAPI.GetTokenBalance(UInt160.Zero.ToString(), address1); + Assert.AreEqual(1_10000000, balance); + } + + [TestMethod] + public void TestClaimGas() + { + byte[] balanceScript = NativeContract.NEO.Hash.MakeScript("balanceOf", sender); + UT_TransactionManager.MockInvokeScript(rpcClientMock, balanceScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_00000000) }); + + byte[] testScript = NativeContract.NEO.Hash.MakeScript("transfer", sender, sender, new BigInteger(1_00000000)); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); + + rpcClientMock.Setup(p => p.RpcSend("sendrawtransaction", It.IsAny())).Returns(true); + + var tranaction = walletAPI.ClaimGas(keyPair1.Export()); + Assert.AreEqual(testScript.ToHexString(), tranaction.Script.ToHexString()); + } + + [TestMethod] + public void TestTransfer() + { + byte[] decimalsScript = NativeContract.GAS.Hash.MakeScript("decimals"); + UT_TransactionManager.MockInvokeScript(rpcClientMock, decimalsScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(8) }); + + byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, NativeContract.GAS.Factor * 100); + UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); + + rpcClientMock.Setup(p => p.RpcSend("sendrawtransaction", It.IsAny())).Returns(true); + + var tranaction = walletAPI.Transfer(NativeContract.GAS.Hash.ToString(), keyPair1.Export(), UInt160.Zero.ToAddress(), 100, 1.1m); + Assert.AreEqual(testScript.ToHexString(), tranaction.Script.ToHexString()); + } + + [TestMethod] + public void TestWaitTransaction() + { + Transaction transaction = TestUtils.GetTransaction(); + rpcClientMock.Setup(p => p.RpcSend("getrawtransaction", It.Is(j => j[0].AsString() == transaction.Hash.ToString()))) + .Returns(new RpcTransaction { Transaction = transaction, VMState = VMState.HALT, BlockHash = UInt256.Zero, BlockTime = 100, Confirmations = 1 }.ToJson()); + + var tx = walletAPI.WaitTransaction(transaction).Result; + Assert.AreEqual(VMState.HALT, tx.VMState); + Assert.AreEqual(UInt256.Zero, tx.BlockHash); + } + } +} diff --git a/neo.UnitTests/UT_Utility.cs b/neo.UnitTests/UT_Utility.cs new file mode 100644 index 0000000000..dd68da017c --- /dev/null +++ b/neo.UnitTests/UT_Utility.cs @@ -0,0 +1,55 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.Wallets; +using System; + +namespace Neo.UnitTests +{ + [TestClass] + public class UT_Utility + { + private KeyPair keyPair; + private UInt160 scriptHash; + + [TestInitialize] + public void TestSetup() + { + keyPair = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); + scriptHash = Contract.CreateSignatureRedeemScript(keyPair.PublicKey).ToScriptHash(); + } + + [TestMethod] + public void TestGetKeyPair() + { + string nul = null; + Assert.ThrowsException(() => Utility.GetKeyPair(nul)); + + string wif = "KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"; + var result = Utility.GetKeyPair(wif); + Assert.AreEqual(keyPair, result); + + string privateKey = keyPair.PrivateKey.ToHexString(); + result = Utility.GetKeyPair(privateKey); + Assert.AreEqual(keyPair, result); + } + + [TestMethod] + public void TestGetScriptHash() + { + string nul = null; + Assert.ThrowsException(() => Utility.GetScriptHash(nul)); + + string addr = scriptHash.ToAddress(); + var result = Utility.GetScriptHash(addr); + Assert.AreEqual(scriptHash, result); + + string hash = scriptHash.ToString(); + result = Utility.GetScriptHash(hash); + Assert.AreEqual(scriptHash, result); + + string publicKey = keyPair.PublicKey.ToString(); + result = Utility.GetScriptHash(publicKey); + Assert.AreEqual(scriptHash, result); + } + } +} diff --git a/neo.UnitTests/VM/UT_Helper.cs b/neo.UnitTests/VM/UT_Helper.cs index 39f1ec104a..41d6270822 100644 --- a/neo.UnitTests/VM/UT_Helper.cs +++ b/neo.UnitTests/VM/UT_Helper.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.SmartContract; +using Neo.SmartContract.Native; using Neo.VM; using System; using System.Collections.Generic; @@ -9,7 +10,7 @@ using System.Numerics; using System.Text; -namespace Neo.UnitTests.IO +namespace Neo.UnitTests.VMT { [TestClass] public class UT_Helper @@ -84,6 +85,15 @@ public void TestEmitAppCall3() Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(resultArray)); } + [TestMethod] + public void TestMakeScript() + { + byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); + + Assert.AreEqual("14000000000000000000000000000000000000000051c10962616c616e63654f66142582d1b275e86c8f0e93a9b2facd5fdb760976a168627d5b52", + testScript.ToHexString()); + } + [TestMethod] public void TestToParameter() { diff --git a/neo/Network/RPC/ContractClient.cs b/neo/Network/RPC/ContractClient.cs index a329e07674..45fb31d267 100644 --- a/neo/Network/RPC/ContractClient.cs +++ b/neo/Network/RPC/ContractClient.cs @@ -44,7 +44,7 @@ public RpcInvokeResult TestInvoke(UInt160 scriptHash, string operation, params o /// sender KeyPair /// transaction NetworkFee, set to be 0 if you don't need higher priority /// - public Transaction DeployContract(byte[] contractScript, ContractManifest manifest, KeyPair key, long networkFee = 0) + public Transaction CreateDeployContractTx(byte[] contractScript, ContractManifest manifest, KeyPair key, long networkFee = 0) { byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) @@ -53,7 +53,8 @@ public Transaction DeployContract(byte[] contractScript, ContractManifest manife script = sb.ToArray(); } - Transaction tx = new TransactionManager(rpcClient, Contract.CreateSignatureRedeemScript(key.PublicKey).ToScriptHash()) + UInt160 sender = Contract.CreateSignatureRedeemScript(key.PublicKey).ToScriptHash(); + Transaction tx = new TransactionManager(rpcClient, sender) .MakeTransaction(script, null, null, networkFee) .AddSignature(key) .Sign() diff --git a/neo/Network/RPC/Helper.cs b/neo/Network/RPC/Helper.cs new file mode 100644 index 0000000000..5c5c82e329 --- /dev/null +++ b/neo/Network/RPC/Helper.cs @@ -0,0 +1,38 @@ +using System; +using System.Numerics; + +namespace Neo.Network.RPC +{ + internal static class Helper + { + /// + /// Convert decimal amount to BigInteger: amount * 10 ^ decimals + /// + /// float value + /// token decimals + /// + public static BigInteger ToBigInteger(this decimal amount, uint decimals) + { + BigInteger factor = BigInteger.Pow(10, (int)decimals); + var (numerator, denominator) = Fraction(amount); + if (factor < denominator) + { + throw new OverflowException("The decimal places is too long."); + } + + BigInteger res = factor * numerator / denominator; + return res; + } + + private static (BigInteger numerator, BigInteger denominator) Fraction(decimal d) + { + int[] bits = decimal.GetBits(d); + BigInteger numerator = (1 - ((bits[3] >> 30) & 2)) * + unchecked(((BigInteger)(uint)bits[2] << 64) | + ((BigInteger)(uint)bits[1] << 32) | + (uint)bits[0]); + BigInteger denominator = BigInteger.Pow(10, (bits[3] >> 16) & 0xff); + return (numerator, denominator); + } + } +} diff --git a/neo/Network/RPC/Models/RpcBlock.cs b/neo/Network/RPC/Models/RpcBlock.cs index f71af51168..1ff485f96e 100644 --- a/neo/Network/RPC/Models/RpcBlock.cs +++ b/neo/Network/RPC/Models/RpcBlock.cs @@ -7,16 +7,16 @@ public class RpcBlock { public Block Block { get; set; } - public int? Confirmations { get; set; } + public int Confirmations { get; set; } public UInt256 NextBlockHash { get; set; } public JObject ToJson() { JObject json = Block.ToJson(); - if (Confirmations != null) + json["confirmations"] = Confirmations; + if (NextBlockHash != null) { - json["confirmations"] = Confirmations; json["nextblockhash"] = NextBlockHash.ToString(); } return json; @@ -26,9 +26,9 @@ public static RpcBlock FromJson(JObject json) { RpcBlock block = new RpcBlock(); block.Block = Block.FromJson(json); - if (json["confirmations"] != null) + block.Confirmations = (int)json["confirmations"].AsNumber(); + if (json["nextblockhash"] != null) { - block.Confirmations = (int)json["confirmations"].AsNumber(); block.NextBlockHash = UInt256.Parse(json["nextblockhash"].AsString()); } return block; diff --git a/neo/Network/RPC/Models/RpcBlockHeader.cs b/neo/Network/RPC/Models/RpcBlockHeader.cs index 2b9293ecaf..5346dffd91 100644 --- a/neo/Network/RPC/Models/RpcBlockHeader.cs +++ b/neo/Network/RPC/Models/RpcBlockHeader.cs @@ -7,16 +7,16 @@ public class RpcBlockHeader { public Header Header { get; set; } - public int? Confirmations { get; set; } + public int Confirmations { get; set; } public UInt256 NextBlockHash { get; set; } public JObject ToJson() { JObject json = Header.ToJson(); - if (Confirmations != null) + json["confirmations"] = Confirmations; + if (NextBlockHash != null) { - json["confirmations"] = Confirmations; json["nextblockhash"] = NextBlockHash.ToString(); } return json; @@ -26,9 +26,9 @@ public static RpcBlockHeader FromJson(JObject json) { RpcBlockHeader block = new RpcBlockHeader(); block.Header = Header.FromJson(json); - if (json["confirmations"] != null) + block.Confirmations = (int)json["confirmations"].AsNumber(); + if (json["nextblockhash"] != null) { - block.Confirmations = (int)json["confirmations"].AsNumber(); block.NextBlockHash = UInt256.Parse(json["nextblockhash"].AsString()); } return block; diff --git a/neo/Network/RPC/Models/RpcInvokeResult.cs b/neo/Network/RPC/Models/RpcInvokeResult.cs index c56307950a..a9c5f04c48 100644 --- a/neo/Network/RPC/Models/RpcInvokeResult.cs +++ b/neo/Network/RPC/Models/RpcInvokeResult.cs @@ -14,8 +14,6 @@ public class RpcInvokeResult public ContractParameter[] Stack { get; set; } - public string Tx { get; set; } - public JObject ToJson() { JObject json = new JObject(); @@ -23,7 +21,6 @@ public JObject ToJson() json["state"] = State; json["gas_consumed"] = GasConsumed; json["stack"] = new JArray(Stack.Select(p => p.ToJson())); - json["tx"] = Tx; return json; } @@ -33,7 +30,6 @@ public static RpcInvokeResult FromJson(JObject json) invokeScriptResult.Script = json["script"].AsString(); invokeScriptResult.State = json["state"].AsString(); invokeScriptResult.GasConsumed = json["gas_consumed"].AsString(); - invokeScriptResult.Tx = json["tx"].AsString(); invokeScriptResult.Stack = ((JArray)json["stack"]).Select(p => ContractParameter.FromJson(p)).ToArray(); return invokeScriptResult; } diff --git a/neo/Network/RPC/Models/RpcNep5TokenInfo.cs b/neo/Network/RPC/Models/RpcNep5TokenInfo.cs new file mode 100644 index 0000000000..0f251a5a37 --- /dev/null +++ b/neo/Network/RPC/Models/RpcNep5TokenInfo.cs @@ -0,0 +1,15 @@ +using System.Numerics; + +namespace Neo.Network.RPC.Models +{ + public class RpcNep5TokenInfo + { + public string Name { get; set; } + + public string Symbol { get; set; } + + public uint Decimals { get; set; } + + public BigInteger TotalSupply { get; set; } + } +} diff --git a/neo/Network/RPC/Models/RpcTransaction.cs b/neo/Network/RPC/Models/RpcTransaction.cs index f96179c358..48b1e19bd4 100644 --- a/neo/Network/RPC/Models/RpcTransaction.cs +++ b/neo/Network/RPC/Models/RpcTransaction.cs @@ -24,7 +24,10 @@ public JObject ToJson() json["blockhash"] = BlockHash.ToString(); json["confirmations"] = Confirmations; json["blocktime"] = BlockTime; - json["vmState"] = VMState; + if (VMState != null) + { + json["vmState"] = VMState; + } } return json; } @@ -38,7 +41,7 @@ public static RpcTransaction FromJson(JObject json) transaction.BlockHash = UInt256.Parse(json["blockhash"].AsString()); transaction.Confirmations = (int)json["confirmations"].AsNumber(); transaction.BlockTime = (uint)json["blocktime"].AsNumber(); - transaction.VMState = json["vmState"].TryGetEnum(); + transaction.VMState = json["vmState"]?.TryGetEnum(); } return transaction; } diff --git a/neo/Network/RPC/Nep5API.cs b/neo/Network/RPC/Nep5API.cs index abfad6c9ed..26ae041ae0 100644 --- a/neo/Network/RPC/Nep5API.cs +++ b/neo/Network/RPC/Nep5API.cs @@ -1,4 +1,5 @@ using Neo.Network.P2P.Payloads; +using Neo.Network.RPC.Models; using Neo.SmartContract; using Neo.VM; using Neo.Wallets; @@ -71,15 +72,39 @@ public BigInteger TotalSupply(UInt160 scriptHash) } /// - /// Get name of NEP5 token + /// Get token information in one rpc call + /// + /// contract script hash + /// + public RpcNep5TokenInfo GetTokenInfo(UInt160 scriptHash) + { + byte[] script = scriptHash.MakeScript("name") + .Concat(scriptHash.MakeScript("symbol")) + .Concat(scriptHash.MakeScript("decimals")) + .Concat(scriptHash.MakeScript("totalSupply")) + .ToArray(); + + var result = rpcClient.InvokeScript(script).Stack; + + return new RpcNep5TokenInfo + { + Name = result[0].ToStackItem().GetString(), + Symbol = result[1].ToStackItem().GetString(), + Decimals = (uint)result[2].ToStackItem().GetBigInteger(), + TotalSupply = result[3].ToStackItem().GetBigInteger() + }; + } + + /// + /// Create NEP5 token transfer transaction /// /// contract script hash /// from KeyPair /// to account script hash /// transfer amount - /// netwotk fee, set to be 0 if you don't need higher priority + /// netwotk fee, set to be 0 will auto calculate the least fee /// - public Transaction Transfer(UInt160 scriptHash, KeyPair fromKey, UInt160 to, BigInteger amount, long networkFee = 0) + public Transaction CreateTransferTx(UInt160 scriptHash, KeyPair fromKey, UInt160 to, BigInteger amount, long networkFee = 0) { var sender = Contract.CreateSignatureRedeemScript(fromKey.PublicKey).ToScriptHash(); Cosigner[] cosigners = new[] { new Cosigner { Scopes = WitnessScope.CalledByEntry, Account = sender } }; diff --git a/neo/Network/RPC/RpcClient.cs b/neo/Network/RPC/RpcClient.cs index cd0578953e..0721efcb33 100644 --- a/neo/Network/RPC/RpcClient.cs +++ b/neo/Network/RPC/RpcClient.cs @@ -1,7 +1,10 @@ +using Neo.IO; using Neo.IO.Json; using Neo.Ledger; +using Neo.Network.P2P.Payloads; using Neo.Network.RPC.Models; using System; +using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; @@ -257,9 +260,14 @@ public RpcInvokeResult InvokeFunction(string address, string function, RpcStack[ /// Returns the result after passing a script through the VM. /// This RPC call does not affect the blockchain in any way. /// - public RpcInvokeResult InvokeScript(byte[] script) + public RpcInvokeResult InvokeScript(byte[] script, params UInt160[] scriptHashesForVerifying) { - return RpcInvokeResult.FromJson(RpcSend("invokescript", script.ToHexString())); + List parameters = new List + { + script.ToHexString() + }; + parameters.AddRange(scriptHashesForVerifying.Select(p => (JObject)p.ToString())); + return RpcInvokeResult.FromJson(RpcSend("invokescript", parameters.ToArray())); } /// @@ -271,7 +279,7 @@ public RpcPlugin[] ListPlugins() } /// - /// Broadcasts a transaction over the NEO network. + /// Broadcasts a serialized transaction over the NEO network. /// public bool SendRawTransaction(byte[] rawTransaction) { @@ -279,7 +287,15 @@ public bool SendRawTransaction(byte[] rawTransaction) } /// - /// Broadcasts a raw block over the NEO network. + /// Broadcasts a transaction over the NEO network. + /// + public bool SendRawTransaction(Transaction transaction) + { + return SendRawTransaction(transaction.ToArray()); + } + + /// + /// Broadcasts a serialized block over the NEO network. /// public bool SubmitBlock(byte[] block) { diff --git a/neo/Network/RPC/TransactionManager.cs b/neo/Network/RPC/TransactionManager.cs index ba714a52ef..27741a58e3 100644 --- a/neo/Network/RPC/TransactionManager.cs +++ b/neo/Network/RPC/TransactionManager.cs @@ -1,5 +1,6 @@ using Neo.Cryptography.ECC; using Neo.IO; +using Neo.IO.Json; using Neo.Network.P2P.Payloads; using Neo.Network.RPC.Models; using Neo.SmartContract; @@ -7,6 +8,7 @@ using Neo.VM; using Neo.Wallets; using System; +using System.Linq; namespace Neo.Network.RPC { @@ -16,6 +18,8 @@ namespace Neo.Network.RPC public class TransactionManager { private readonly RpcClient rpcClient; + private readonly PolicyAPI policyAPI; + private readonly Nep5API nep5API; private readonly UInt160 sender; /// @@ -36,6 +40,8 @@ public class TransactionManager public TransactionManager(RpcClient rpc, UInt160 sender) { rpcClient = rpc; + policyAPI = new PolicyAPI(rpc); + nep5API = new Nep5API(rpc); this.sender = sender; } @@ -63,7 +69,9 @@ public TransactionManager MakeTransaction(byte[] script, TransactionAttribute[] Witnesses = new Witness[0] }; - RpcInvokeResult result = rpcClient.InvokeScript(script); + // Add witness hashes parameter to pass CheckWitness + UInt160[] hashes = Tx.GetScriptHashesForVerifying(null).ToArray(); + RpcInvokeResult result = rpcClient.InvokeScript(script, hashes); Tx.SystemFee = Math.Max(long.Parse(result.GasConsumed) - ApplicationEngine.GasFree, 0); if (Tx.SystemFee > 0) { @@ -80,7 +88,7 @@ public TransactionManager MakeTransaction(byte[] script, TransactionAttribute[] // set networkfee to estimate value when networkFee is 0 Tx.NetworkFee = networkFee == 0 ? EstimateNetworkFee() : networkFee; - var gasBalance = new Nep5API(rpcClient).BalanceOf(NativeContract.GAS.Hash, sender); + var gasBalance = nep5API.BalanceOf(NativeContract.GAS.Hash, sender); if (gasBalance >= Tx.SystemFee + Tx.NetworkFee) return this; throw new InvalidOperationException($"Insufficient GAS in address: {sender.ToAddress()}"); } @@ -101,7 +109,7 @@ private long EstimateNetworkFee() networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null); } - networkFee += size * new PolicyAPI(rpcClient).GetFeePerByte(); + networkFee += size * policyAPI.GetFeePerByte(); return networkFee; } @@ -129,7 +137,7 @@ private long CalculateNetworkFee() networkFee += Wallet.CalculateNetWorkFee(witness_script, ref size); } - networkFee += size * new PolicyAPI(rpcClient).GetFeePerByte(); + networkFee += size * policyAPI.GetFeePerByte(); return networkFee; } diff --git a/neo/Network/RPC/WalletAPI.cs b/neo/Network/RPC/WalletAPI.cs new file mode 100644 index 0000000000..e1ab3b777f --- /dev/null +++ b/neo/Network/RPC/WalletAPI.cs @@ -0,0 +1,192 @@ +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Network.RPC.Models; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using System; +using System.Linq; +using System.Numerics; +using System.Threading.Tasks; + +namespace Neo.Network.RPC +{ + /// + /// Wallet Common APIs + /// + public class WalletAPI + { + private readonly RpcClient rpcClient; + private readonly Nep5API nep5API; + + /// + /// WalletAPI Constructor + /// + /// the RPC client to call NEO RPC methods + public WalletAPI(RpcClient rpc) + { + rpcClient = rpc; + nep5API = new Nep5API(rpc); + } + + /// + /// Get unclaimed gas with address, scripthash or public key string + /// + /// address, scripthash or public key string + /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") + /// + public decimal GetUnclaimedGas(string account) + { + UInt160 accountHash = Utility.GetScriptHash(account); + return GetUnclaimedGas(accountHash); + } + + /// + /// Get unclaimed gas + /// + /// account scripthash + /// + public decimal GetUnclaimedGas(UInt160 account) + { + UInt160 scriptHash = NativeContract.NEO.Hash; + BigInteger balance = nep5API.TestInvoke(scriptHash, "unclaimedGas", account, rpcClient.GetBlockCount() - 1) + .Stack.Single().ToStackItem().GetBigInteger(); + return ((decimal)balance) / (long)NativeContract.GAS.Factor; + } + + /// + /// Get Neo Balance + /// + /// address, scripthash or public key string + /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") + /// + public uint GetNeoBalance(string account) + { + BigInteger balance = GetTokenBalance(NativeContract.NEO.Hash.ToString(), account); + return (uint)balance; + } + + /// + /// Get Gas Balance + /// + /// address, scripthash or public key string + /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") + /// + public decimal GetGasBalance(string account) + { + BigInteger balance = GetTokenBalance(NativeContract.GAS.Hash.ToString(), account); + return ((decimal)balance) / (long)NativeContract.GAS.Factor; + } + + /// + /// Get token balance with string parameters + /// + /// token script hash, Example: "0x43cf98eddbe047e198a3e5d57006311442a0ca15"(NEO) + /// address, scripthash or public key string + /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") + /// + public BigInteger GetTokenBalance(string tokenHash, string account) + { + UInt160 scriptHash = Utility.GetScriptHash(tokenHash); + UInt160 accountHash = Utility.GetScriptHash(account); + return nep5API.BalanceOf(scriptHash, accountHash); + } + + /// + /// The GAS is claimed when doing NEO transfer + /// This function will transfer NEO balance from account to itself + /// + /// wif or private key + /// Example: WIF ("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"), PrivateKey ("450d6c2a04b5b470339a745427bae6828400cf048400837d73c415063835e005") + /// The transaction sended + public Transaction ClaimGas(string key) + { + KeyPair keyPair = Utility.GetKeyPair(key); + return ClaimGas(keyPair); + } + + /// + /// The GAS is claimed when doing NEO transfer + /// This function will transfer NEO balance from account to itself + /// + /// keyPair + /// The transaction sended + public Transaction ClaimGas(KeyPair keyPair) + { + UInt160 toHash = Contract.CreateSignatureRedeemScript(keyPair.PublicKey).ToScriptHash(); + BigInteger balance = nep5API.BalanceOf(NativeContract.NEO.Hash, toHash); + Transaction transaction = nep5API.CreateTransferTx(NativeContract.NEO.Hash, keyPair, toHash, balance); + rpcClient.SendRawTransaction(transaction); + return transaction; + } + + /// + /// Transfer NEP5 token balance, with common data types + /// + /// nep5 token script hash, Example: scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91") + /// wif or private key + /// Example: WIF ("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"), PrivateKey ("450d6c2a04b5b470339a745427bae6828400cf048400837d73c415063835e005") + /// address or account script hash + /// token amount + /// netwotk fee, set to be 0 will auto calculate the least fee + /// + public Transaction Transfer(string tokenHash, string fromKey, string toAddress, decimal amount, decimal networkFee = 0) + { + UInt160 scriptHash = Utility.GetScriptHash(tokenHash); + var decimals = nep5API.Decimals(scriptHash); + + KeyPair from = Utility.GetKeyPair(fromKey); + UInt160 to = Utility.GetScriptHash(toAddress); + BigInteger amountInteger = amount.ToBigInteger(decimals); + BigInteger networkFeeInteger = networkFee.ToBigInteger(NativeContract.GAS.Decimals); + return Transfer(scriptHash, from, to, amountInteger, (long)networkFeeInteger); + } + + /// + /// Transfer NEP5 token balance + /// + /// contract script hash + /// from KeyPair + /// to account script hash + /// transfer amount + /// netwotk fee, set to be 0 will auto calculate the least fee + /// + public Transaction Transfer(UInt160 scriptHash, KeyPair from, UInt160 to, BigInteger amountInteger, BigInteger networkFeeInteger = default) + { + Transaction transaction = nep5API.CreateTransferTx(scriptHash, from, to, amountInteger, (long)networkFeeInteger); + rpcClient.SendRawTransaction(transaction); + return transaction; + } + + /// + /// Wait until the transaction is observable block chain + /// + /// the transaction to observe + /// TimeoutException throws after "timeout" seconds + /// the Transaction state, including vmState and blockhash + public async Task WaitTransaction(Transaction transaction, int timeout = 60) + { + DateTime deadline = DateTime.UtcNow.AddSeconds(timeout); + RpcTransaction rpcTx = null; + while (rpcTx == null || rpcTx.Confirmations == null) + { + if (deadline < DateTime.UtcNow) + { + throw new TimeoutException(); + } + + try + { + rpcTx = rpcClient.GetRawTransaction(transaction.Hash.ToString()); + if (rpcTx == null || rpcTx.Confirmations == null) + { + await Task.Delay((int)Blockchain.MillisecondsPerBlock / 2); + } + } + catch (Exception) { } + } + return rpcTx; + } + } +} diff --git a/neo/Utility.cs b/neo/Utility.cs new file mode 100644 index 0000000000..56e048a512 --- /dev/null +++ b/neo/Utility.cs @@ -0,0 +1,61 @@ +using Neo.Cryptography.ECC; +using Neo.SmartContract; +using Neo.Wallets; +using System; + +namespace Neo +{ + public static class Utility + { + /// + /// Parse WIF or private key hex string to KeyPair + /// + /// WIF or private key hex string + /// Example: WIF ("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"), PrivateKey ("450d6c2a04b5b470339a745427bae6828400cf048400837d73c415063835e005") + /// + public static KeyPair GetKeyPair(string key) + { + if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key)); } + if (key.StartsWith("0x")) { key = key.Substring(2); } + + if (key.Length == 52) + { + return new KeyPair(Wallet.GetPrivateKeyFromWIF(key)); + } + else if (key.Length == 64) + { + return new KeyPair(key.HexToBytes()); + } + + throw new FormatException(); + } + + /// + /// Parse address, scripthash or public key string to UInt160 + /// + /// account address, scripthash or public key string + /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") + /// + public static UInt160 GetScriptHash(string account) + { + if (string.IsNullOrEmpty(account)) { throw new ArgumentNullException(nameof(account)); } + if (account.StartsWith("0x")) { account = account.Substring(2); } + + if (account.Length == 34) + { + return Wallets.Helper.ToScriptHash(account); + } + else if (account.Length == 40) + { + return UInt160.Parse(account); + } + else if (account.Length == 66) + { + var pubKey = ECPoint.Parse(account, ECCurve.Secp256r1); + return Contract.CreateSignatureRedeemScript(pubKey).ToScriptHash(); + } + + throw new FormatException(); + } + } +} From a670a387ab87219d52326df6f4dbfa6e1e93296a Mon Sep 17 00:00:00 2001 From: Ricardo Prado <38396062+lock9@users.noreply.github.com> Date: Sun, 17 Nov 2019 18:46:27 -0300 Subject: [PATCH 134/305] Adding neo version to feature suggestion (#1240) --- .github/ISSUE_TEMPLATE/feature-or-enhancement-request.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md index 9b98d24f99..4c2e7a8977 100644 --- a/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md +++ b/.github/ISSUE_TEMPLATE/feature-or-enhancement-request.md @@ -12,6 +12,10 @@ A summary of the problem you want to solve or metric you want to improve **Do you have any solution you want to propose?** A clear and concise description of what you expect with this change. +**Neo Version** +- Neo 2 +- Neo 3 + **Where in the software does this update applies to?** - Compiler - Consensus From 957caa1aca1641837aaeecb620486cef7578bd23 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 19 Nov 2019 11:00:06 +0100 Subject: [PATCH 135/305] Fix Base58 issues (3x) (#1224) * Fix base58 * Optimize * Update Base58.cs * Revert some lines * Rename * Update Base58.cs --- neo.UnitTests/Cryptography/UT_Base58.cs | 49 ++++++++++++++------- neo/Cryptography/Base58.cs | 57 ++++++++++++------------- 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/neo.UnitTests/Cryptography/UT_Base58.cs b/neo.UnitTests/Cryptography/UT_Base58.cs index 5f2d750382..fbf29ba888 100644 --- a/neo.UnitTests/Cryptography/UT_Base58.cs +++ b/neo.UnitTests/Cryptography/UT_Base58.cs @@ -2,30 +2,49 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using System; +using System.Collections.Generic; namespace Neo.UnitTests.Cryptography { [TestClass] public class UT_Base58 { - byte[] decoded1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - string encoded1 = "1kA3B2yGe2z4"; - byte[] decoded2 = { 0, 0, 0, 0, 0 }; - string encoded2 = "1111"; - [TestMethod] - public void TestEncode() + public void TestEncodeDecode() { - Base58.Encode(decoded1).Should().Be(encoded1); - } + var bitcoinTest = new Dictionary() + { + // Tests from https://github.com/bitcoin/bitcoin/blob/46fc4d1a24c88e797d6080336e3828e45e39c3fd/src/test/data/base58_encode_decode.json - [TestMethod] - public void TestDecode() - { - Base58.Decode(encoded1).Should().BeEquivalentTo(decoded1); - Base58.Decode(encoded2).Should().BeEquivalentTo(decoded2); - Action action = () => Base58.Decode(encoded1 + "l").Should().BeEquivalentTo(decoded1); - action.Should().Throw(); + {"", ""}, + {"61", "2g"}, + {"626262", "a3gV"}, + {"636363", "aPEr"}, + {"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"}, + {"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"}, + {"516b6fcd0f", "ABnLTmg"}, + {"bf4f89001e670274dd", "3SEo3LWLoPntC"}, + {"572e4794", "3EFU7m"}, + {"ecac89cad93923c02321", "EJDM8drfXA6uyA"}, + {"10c8511e", "Rt5zm"}, + {"00000000000000000000", "1111111111"}, + {"000111d38e5fc9071ffcd20b4a763cc9ae4f252bb4e48fd66a835e252ada93ff480d6dd43dc62a641155a5", "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"}, + {"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "1cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY"}, + + // Extra tests + + {"00", "1"}, + {"00010203040506070809", "1kA3B2yGe2z4"}, + }; + + foreach (var entry in bitcoinTest) + { + Base58.Encode(entry.Key.HexToBytes()).Should().Be(entry.Value); + Base58.Decode(entry.Value).Should().BeEquivalentTo(entry.Key.HexToBytes()); + + Action action = () => Base58.Decode(entry.Value + "l"); + action.Should().Throw(); + } } } } diff --git a/neo/Cryptography/Base58.cs b/neo/Cryptography/Base58.cs index b64f12216b..9ddbea3dc8 100644 --- a/neo/Cryptography/Base58.cs +++ b/neo/Cryptography/Base58.cs @@ -11,45 +11,44 @@ public static class Base58 public static byte[] Decode(string input) { - BigInteger bi = BigInteger.Zero; - for (int i = input.Length - 1; i >= 0; i--) + // Decode Base58 string to BigInteger + var bi = BigInteger.Zero; + for (int i = 0; i < input.Length; i++) { - int index = Alphabet.IndexOf(input[i]); - if (index == -1) - throw new FormatException(); - bi += index * BigInteger.Pow(58, input.Length - 1 - i); + int digit = Alphabet.IndexOf(input[i]); + if (digit < 0) + throw new FormatException($"Invalid Base58 character '{input[i]}' at position {i}"); + bi = bi * Alphabet.Length + digit; } - byte[] bytes = bi.ToByteArray(); - Array.Reverse(bytes); - bool stripSignByte = bytes.Length > 1 && bytes[0] == 0 && bytes[1] >= 0x80; - int leadingZeros = 0; - for (int i = 0; i < input.Length && input[i] == Alphabet[0]; i++) - { - leadingZeros++; - } - byte[] tmp = new byte[bytes.Length - (stripSignByte ? 1 : 0) + leadingZeros]; - Array.Copy(bytes, stripSignByte ? 1 : 0, tmp, leadingZeros, tmp.Length - leadingZeros); - Array.Clear(bytes, 0, bytes.Length); - return tmp; + + // Encode BigInteger to byte[] + // Leading zero bytes get encoded as leading `1` characters + int leadingZeroCount = input.TakeWhile(c => c == Alphabet[0]).Count(); + var leadingZeros = new byte[leadingZeroCount]; + var bytesWithoutLeadingZeros = bi.ToByteArray() + .Reverse()// to big endian + .SkipWhile(b => b == 0);//strip sign byte + return leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray(); } public static string Encode(byte[] input) { + // Decode byte[] to BigInteger BigInteger value = new BigInteger(new byte[1].Concat(input).Reverse().ToArray()); - StringBuilder sb = new StringBuilder(); - while (value >= 58) + + // Encode BigInteger to Base58 string + var sb = new StringBuilder(); + + while (value > 0) { - BigInteger mod = value % 58; - sb.Insert(0, Alphabet[(int)mod]); - value /= 58; + value = BigInteger.DivRem(value, Alphabet.Length, out var remainder); + sb.Insert(0, Alphabet[(int)remainder]); } - sb.Insert(0, Alphabet[(int)value]); - foreach (byte b in input) + + // Append `1` for each leading 0 byte + for (int i = 0; i < input.Length && input[i] == 0; i++) { - if (b == 0) - sb.Insert(0, Alphabet[0]); - else - break; + sb.Insert(0, Alphabet[0]); } return sb.ToString(); } From cd85f98a231e88029d4af97cf6357f457ebb55a7 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 21 Nov 2019 14:23:54 +0100 Subject: [PATCH 136/305] Decrease the block time threshold (3x) (#1233) --- neo.UnitTests/Consensus/UT_Consensus.cs | 1 - neo/Consensus/ConsensusService.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/neo.UnitTests/Consensus/UT_Consensus.cs b/neo.UnitTests/Consensus/UT_Consensus.cs index c8d8fbf6c4..c10e6c94e3 100644 --- a/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/neo.UnitTests/Consensus/UT_Consensus.cs @@ -20,7 +20,6 @@ namespace Neo.UnitTests.Consensus { - [TestClass] public class ConsensusTests : TestKit { diff --git a/neo/Consensus/ConsensusService.cs b/neo/Consensus/ConsensusService.cs index 574082043a..9d0bd5a343 100644 --- a/neo/Consensus/ConsensusService.cs +++ b/neo/Consensus/ConsensusService.cs @@ -405,7 +405,7 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m 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.Timestamp <= context.PrevHeader.Timestamp || message.Timestamp > TimeProvider.Current.UtcNow.AddMinutes(10).ToTimestampMS()) + if (message.Timestamp <= context.PrevHeader.Timestamp || message.Timestamp > TimeProvider.Current.UtcNow.AddMilliseconds(8 * Blockchain.MillisecondsPerBlock).ToTimestampMS()) { Log($"Timestamp incorrect: {message.Timestamp}", LogLevel.Warning); return; From 5eb9432f821c6293a98886dabd38dd72a3af60e9 Mon Sep 17 00:00:00 2001 From: Luchuan Date: Fri, 22 Nov 2019 19:35:13 +0800 Subject: [PATCH 137/305] Fix 1244 Mempool.ReverifyTransactions (#1248) * add internal to DB and WriteBatch * reset db.cs writebatch.cs * fix Nep5Token.Burn method * format * format * reset and fix memorypool.ReverifyTransactions * reset and fix ReverifyTransactions * add ut * remove commit * fix BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered.balance * update comments --- neo.UnitTests/Ledger/UT_MemoryPool.cs | 69 +++++++++++++++++++++++++++ neo/Ledger/MemoryPool.cs | 6 ++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/neo.UnitTests/Ledger/UT_MemoryPool.cs b/neo.UnitTests/Ledger/UT_MemoryPool.cs index 6f9022bef1..631ae06dbc 100644 --- a/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -8,6 +8,7 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.Plugins; +using Neo.SmartContract; using Neo.SmartContract.Native; using System; using System.Collections; @@ -92,6 +93,31 @@ private Transaction CreateTransactionWithFee(long fee) return mock.Object; } + private Transaction CreateTransactionWithFeeAndBalanceVerify(long fee) + { + Random random = new Random(); + var randomBytes = new byte[16]; + random.NextBytes(randomBytes); + Mock mock = new Mock(); + UInt160 sender = UInt160.Zero; + mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(((Snapshot snapshot, BigInteger amount) => NativeContract.GAS.BalanceOf(snapshot, sender) >= amount + fee)); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); + mock.Object.Script = randomBytes; + mock.Object.Sender = sender; + mock.Object.NetworkFee = fee; + mock.Object.Attributes = new TransactionAttribute[0]; + mock.Object.Cosigners = new Cosigner[0]; + mock.Object.Witnesses = new[] + { + new Witness + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + } + }; + return mock.Object; + } + private Transaction CreateTransaction(long fee = -1) { if (fee != -1) @@ -115,6 +141,17 @@ private void AddTransaction(Transaction txToAdd) _unit.TryAdd(txToAdd.Hash, txToAdd); } + private void AddTransactionsWithBalanceVerify(int count, long fee) + { + for (int i = 0; i < count; i++) + { + var txToAdd = CreateTransactionWithFeeAndBalanceVerify(fee); + _unit.TryAdd(txToAdd.Hash, txToAdd); + } + + Console.WriteLine($"created {count} tx"); + } + [TestMethod] public void CapacityTest() { @@ -171,6 +208,38 @@ public void BlockPersistMovesTxToUnverifiedAndReverification() _unit.UnverifiedSortedTxCount.Should().Be(0); } + [TestMethod] + public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() + { + long txFee = 1; + AddTransactionsWithBalanceVerify(70, txFee); + + _unit.SortedTxCount.Should().Be(70); + + var block = new Block + { + Transactions = _unit.GetSortedVerifiedTransactions().Take(10).ToArray() + }; + + // Simulate the transfer process in tx by burning the balance + UInt160 sender = block.Transactions[0].Sender; + Snapshot snapshot = Blockchain.Singleton.GetSnapshot(); + BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, sender); + + ApplicationEngine applicationEngine = new ApplicationEngine(TriggerType.All, block, snapshot, (long)balance); + NativeContract.GAS.Burn(applicationEngine, sender, balance); + NativeContract.GAS.Mint(applicationEngine, sender, txFee * 30); // Set the balance to meet 30 txs only + + // Persist block and reverify all the txs in mempool, but half of the txs will be discarded + _unit.UpdatePoolForBlockPersisted(block, snapshot); + _unit.SortedTxCount.Should().Be(30); + _unit.UnverifiedSortedTxCount.Should().Be(0); + + // Revert the balance + NativeContract.GAS.Burn(applicationEngine, sender, txFee * 30); + NativeContract.GAS.Mint(applicationEngine, sender, balance); + } + private void VerifyTransactionsSortedDescending(IEnumerable transactions) { Transaction lastTransaction = null; diff --git a/neo/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 15c647f163..78df9b97f6 100644 --- a/neo/Ledger/MemoryPool.cs +++ b/neo/Ledger/MemoryPool.cs @@ -417,7 +417,10 @@ internal void InvalidateAllTransactions() foreach (PoolItem item in unverifiedSortedTxPool.Reverse().Take(count)) { if (item.Tx.Reverify(snapshot, SendersFeeMonitor.GetSenderFee(item.Tx.Sender))) + { reverifiedItems.Add(item); + SendersFeeMonitor.AddSenderFee(item.Tx); + } else // Transaction no longer valid -- it will be removed from unverifiedTxPool. invalidItems.Add(item); @@ -438,7 +441,6 @@ internal void InvalidateAllTransactions() { if (_unsortedTransactions.TryAdd(item.Tx.Hash, item)) { - SendersFeeMonitor.AddSenderFee(item.Tx); verifiedSortedTxPool.Add(item); if (item.LastBroadcastTimestamp < rebroadcastCutOffTime) @@ -447,6 +449,8 @@ internal void InvalidateAllTransactions() item.LastBroadcastTimestamp = DateTime.UtcNow; } } + else + SendersFeeMonitor.RemoveSenderFee(item.Tx); _unverifiedTransactions.Remove(item.Tx.Hash); unverifiedSortedTxPool.Remove(item); From 16c122342353f76cd7c0d6ffb83266b6a91172c2 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 22 Nov 2019 20:38:54 +0800 Subject: [PATCH 138/305] Improve p2p message deserialization (#1262) --- .../IO/Caching/UT_ReflectionCache.cs | 51 ++++++------- neo/Consensus/ConsensusMessage.cs | 18 +---- neo/IO/Caching/ReflectionCache.cs | 74 +++++-------------- neo/IO/Caching/ReflectionCacheAttribute.cs | 5 +- neo/Network/P2P/Message.cs | 48 +----------- neo/Network/P2P/MessageCommand.cs | 19 +++++ 6 files changed, 71 insertions(+), 144 deletions(-) diff --git a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs index 4e1fcce634..547c18787d 100644 --- a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs +++ b/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs @@ -1,11 +1,17 @@ +using System.IO; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.IO.Caching; -using System; namespace Neo.UnitTests.IO.Caching { - public class TestItem { } + public class TestItem : ISerializable + { + public int Size => 0; + public void Deserialize(BinaryReader reader) { } + public void Serialize(BinaryWriter writer) { } + } public class TestItem1 : TestItem { } @@ -25,44 +31,35 @@ public enum MyEmptyEnum : byte { } [TestClass] public class UT_ReflectionCache { - ReflectionCache reflectionCache; - - [TestInitialize] - public void SetUp() - { - reflectionCache = ReflectionCache.CreateFromEnum(); - } - [TestMethod] - public void TestCreateFromEnum() + public void TestCreateFromEmptyEnum() { - reflectionCache.Should().NotBeNull(); + ReflectionCache.Count.Should().Be(0); } [TestMethod] - public void TestCreateFromObjectNotEnum() + public void TestCreateInstance() { - Action action = () => ReflectionCache.CreateFromEnum(); - action.Should().Throw(); - } + object item1 = ReflectionCache.CreateInstance(MyTestEnum.Item1, null); + (item1 is TestItem1).Should().BeTrue(); - [TestMethod] - public void TestCreateFromEmptyEnum() - { - reflectionCache = ReflectionCache.CreateFromEnum(); - reflectionCache.Count.Should().Be(0); + object item2 = ReflectionCache.CreateInstance(MyTestEnum.Item2, null); + (item2 is TestItem2).Should().BeTrue(); + + object item3 = ReflectionCache.CreateInstance((MyTestEnum)0x02, null); + item3.Should().BeNull(); } [TestMethod] - public void TestCreateInstance() + public void TestCreateSerializable() { - object item1 = reflectionCache.CreateInstance((byte)MyTestEnum.Item1, null); + object item1 = ReflectionCache.CreateSerializable(MyTestEnum.Item1, new byte[0]); (item1 is TestItem1).Should().BeTrue(); - object item2 = reflectionCache.CreateInstance((byte)MyTestEnum.Item2, null); + object item2 = ReflectionCache.CreateSerializable(MyTestEnum.Item2, new byte[0]); (item2 is TestItem2).Should().BeTrue(); - object item3 = reflectionCache.CreateInstance(0x02, null); + object item3 = ReflectionCache.CreateSerializable((MyTestEnum)0x02, new byte[0]); item3.Should().BeNull(); } @@ -70,10 +67,10 @@ public void TestCreateInstance() public void TestCreateInstance2() { TestItem defaultItem = new TestItem1(); - object item2 = reflectionCache.CreateInstance((byte)MyTestEnum.Item2, defaultItem); + object item2 = ReflectionCache.CreateInstance(MyTestEnum.Item2, defaultItem); (item2 is TestItem2).Should().BeTrue(); - object item1 = reflectionCache.CreateInstance(0x02, new TestItem1()); + object item1 = ReflectionCache.CreateInstance((MyTestEnum)0x02, new TestItem1()); (item1 is TestItem1).Should().BeTrue(); } } diff --git a/neo/Consensus/ConsensusMessage.cs b/neo/Consensus/ConsensusMessage.cs index af16db03e3..0bca5534e1 100644 --- a/neo/Consensus/ConsensusMessage.cs +++ b/neo/Consensus/ConsensusMessage.cs @@ -7,11 +7,6 @@ namespace Neo.Consensus { public abstract class ConsensusMessage : ISerializable { - /// - /// Reflection cache for ConsensusMessageType - /// - private static ReflectionCache ReflectionCache = ReflectionCache.CreateFromEnum(); - public readonly ConsensusMessageType Type; public byte ViewNumber; @@ -31,15 +26,10 @@ public virtual void Deserialize(BinaryReader reader) public static ConsensusMessage DeserializeFrom(byte[] data) { - ConsensusMessage message = ReflectionCache.CreateInstance(data[0]); - if (message == null) throw new FormatException(); - - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader r = new BinaryReader(ms)) - { - message.Deserialize(r); - } - return message; + 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) diff --git a/neo/IO/Caching/ReflectionCache.cs b/neo/IO/Caching/ReflectionCache.cs index 452856698c..e0b92b2a92 100644 --- a/neo/IO/Caching/ReflectionCache.cs +++ b/neo/IO/Caching/ReflectionCache.cs @@ -1,80 +1,44 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; namespace Neo.IO.Caching { - public class ReflectionCache : Dictionary + internal static class ReflectionCache where T : Enum { - /// - /// Constructor - /// - public ReflectionCache() { } - /// - /// Constructor - /// - /// Enum type - public static ReflectionCache CreateFromEnum() where EnumType : struct, IConvertible - { - Type enumType = typeof(EnumType); - - if (!enumType.GetTypeInfo().IsEnum) - throw new ArgumentException("K must be an enumerated type"); + private static readonly Dictionary dictionary = new Dictionary(); - // Cache all types - ReflectionCache r = new ReflectionCache(); + public static int Count => dictionary.Count; - foreach (object t in Enum.GetValues(enumType)) + static ReflectionCache() + { + Type enumType = typeof(T); + foreach (FieldInfo field in enumType.GetFields(BindingFlags.Public | BindingFlags.Static)) { - // Get enumn member - MemberInfo[] memInfo = enumType.GetMember(t.ToString()); - if (memInfo == null || memInfo.Length != 1) - throw (new FormatException()); - // Get attribute - ReflectionCacheAttribute attribute = memInfo[0].GetCustomAttributes(typeof(ReflectionCacheAttribute), false) - .Cast() - .FirstOrDefault(); - - if (attribute == null) - throw (new FormatException()); + ReflectionCacheAttribute attribute = field.GetCustomAttribute(); + if (attribute == null) continue; // Append to cache - r.Add((T)t, attribute.Type); + dictionary.Add((T)field.GetValue(null), attribute.Type); } - return r; } - /// - /// Create object from key - /// - /// Key - /// Default value - public object CreateInstance(T key, object def = null) - { - Type tp; + public static object CreateInstance(T key, object def = null) + { // Get Type from cache - if (TryGetValue(key, out tp)) return Activator.CreateInstance(tp); + if (dictionary.TryGetValue(key, out Type t)) + return Activator.CreateInstance(t); // return null return def; } - /// - /// Create object from key - /// - /// Type - /// Key - /// Default value - public K CreateInstance(T key, K def = default(K)) - { - Type tp; - - // Get Type from cache - if (TryGetValue(key, out tp)) return (K)Activator.CreateInstance(tp); - // return null - return def; + public static ISerializable CreateSerializable(T key, byte[] data) + { + if (dictionary.TryGetValue(key, out Type t)) + return data.AsSerializable(t); + return null; } } } diff --git a/neo/IO/Caching/ReflectionCacheAttribute.cs b/neo/IO/Caching/ReflectionCacheAttribute.cs index b2ac3c3054..2bbbca8568 100644 --- a/neo/IO/Caching/ReflectionCacheAttribute.cs +++ b/neo/IO/Caching/ReflectionCacheAttribute.cs @@ -2,12 +2,13 @@ namespace Neo.IO.Caching { - public class ReflectionCacheAttribute : Attribute + [AttributeUsage(AttributeTargets.Field, AllowMultiple = false)] + internal class ReflectionCacheAttribute : Attribute { /// /// Type /// - public Type Type { get; private set; } + public Type Type { get; } /// /// Constructor diff --git a/neo/Network/P2P/Message.cs b/neo/Network/P2P/Message.cs index 38bc2bf6c1..578259f789 100644 --- a/neo/Network/P2P/Message.cs +++ b/neo/Network/P2P/Message.cs @@ -1,7 +1,7 @@ using Akka.IO; using Neo.Cryptography; using Neo.IO; -using Neo.Network.P2P.Payloads; +using Neo.IO.Caching; using System; using System.IO; @@ -51,51 +51,7 @@ private void DecompressPayload() byte[] decompressed = Flags.HasFlag(MessageFlags.Compressed) ? _payload_compressed.DecompressLz4(PayloadMaxSize) : _payload_compressed; - switch (Command) - { - case MessageCommand.Version: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Addr: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Ping: - case MessageCommand.Pong: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.GetHeaders: - case MessageCommand.GetBlocks: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Headers: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Inv: - case MessageCommand.GetData: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.GetBlockData: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Transaction: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Block: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.Consensus: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.FilterLoad: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.FilterAdd: - Payload = decompressed.AsSerializable(); - break; - case MessageCommand.MerkleBlock: - Payload = decompressed.AsSerializable(); - break; - } + Payload = ReflectionCache.CreateSerializable(Command, decompressed); } void ISerializable.Deserialize(BinaryReader reader) diff --git a/neo/Network/P2P/MessageCommand.cs b/neo/Network/P2P/MessageCommand.cs index d1f2befc67..ccddacfba2 100644 --- a/neo/Network/P2P/MessageCommand.cs +++ b/neo/Network/P2P/MessageCommand.cs @@ -1,35 +1,54 @@ +using Neo.IO.Caching; +using Neo.Network.P2P.Payloads; + namespace Neo.Network.P2P { public enum MessageCommand : byte { //handshaking + [ReflectionCache(typeof(VersionPayload))] Version = 0x00, Verack = 0x01, //connectivity GetAddr = 0x10, + [ReflectionCache(typeof(AddrPayload))] Addr = 0x11, + [ReflectionCache(typeof(PingPayload))] Ping = 0x18, + [ReflectionCache(typeof(PingPayload))] Pong = 0x19, //synchronization + [ReflectionCache(typeof(GetBlocksPayload))] GetHeaders = 0x20, + [ReflectionCache(typeof(HeadersPayload))] Headers = 0x21, + [ReflectionCache(typeof(GetBlocksPayload))] GetBlocks = 0x24, Mempool = 0x25, + [ReflectionCache(typeof(InvPayload))] Inv = 0x27, + [ReflectionCache(typeof(InvPayload))] GetData = 0x28, + [ReflectionCache(typeof(GetBlockDataPayload))] GetBlockData = 0x29, NotFound = 0x2a, + [ReflectionCache(typeof(Transaction))] Transaction = 0x2b, + [ReflectionCache(typeof(Block))] Block = 0x2c, + [ReflectionCache(typeof(ConsensusPayload))] Consensus = 0x2d, Reject = 0x2f, //SPV protocol + [ReflectionCache(typeof(FilterLoadPayload))] FilterLoad = 0x30, + [ReflectionCache(typeof(FilterAddPayload))] FilterAdd = 0x31, FilterClear = 0x32, + [ReflectionCache(typeof(MerkleBlockPayload))] MerkleBlock = 0x38, //others From 840b5ffed7a2958b437b399818429c303421902d Mon Sep 17 00:00:00 2001 From: Shargon Date: Sun, 24 Nov 2019 23:44:44 +0100 Subject: [PATCH 139/305] Ensure that the block is valid before relay it on CheckCommits (#1220) --- neo.UnitTests/Consensus/UT_ConsensusContext.cs | 17 +++-------------- neo/Consensus/ConsensusContext.cs | 1 + 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/neo.UnitTests/Consensus/UT_ConsensusContext.cs index 9a0dd39ba7..821b6f8da5 100644 --- a/neo.UnitTests/Consensus/UT_ConsensusContext.cs +++ b/neo.UnitTests/Consensus/UT_ConsensusContext.cs @@ -4,7 +4,6 @@ using Neo.Consensus; using Neo.IO; using Neo.Network.P2P.Payloads; -using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.Wallets; using System; @@ -119,6 +118,8 @@ private Block SignBlock(ConsensusContext context) { context.Block.MerkleRoot = null; + // Fake commits + for (int x = 0; x < _validatorKeys.Length; x++) { _context.MyIndex = x; @@ -127,19 +128,7 @@ private Block SignBlock(ConsensusContext context) _context.CommitPayloads[_context.MyIndex] = com; } - // Manual block sign - - Contract contract = Contract.CreateMultiSigContract(context.M, context.Validators); - ContractParametersContext sc = new ContractParametersContext(context.Block); - for (int i = 0, j = 0; i < context.Validators.Length && j < context.M; i++) - { - if (context.CommitPayloads[i]?.ConsensusMessage.ViewNumber != context.ViewNumber) continue; - sc.AddSignature(contract, context.Validators[i], context.CommitPayloads[i].GetDeserializedMessage().Signature); - j++; - } - context.Block.Witness = sc.GetWitnesses()[0]; - context.Block.Transactions = context.TransactionHashes.Select(p => context.Transactions[p]).ToArray(); - return context.Block; + return context.CreateBlock(); } private void EnsureContext(ConsensusContext context, params Transaction[] expected) diff --git a/neo/Consensus/ConsensusContext.cs b/neo/Consensus/ConsensusContext.cs index e7c2649782..d05b4a64ea 100644 --- a/neo/Consensus/ConsensusContext.cs +++ b/neo/Consensus/ConsensusContext.cs @@ -82,6 +82,7 @@ public ConsensusContext(Wallet wallet, 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++) From c32473da678e057f7778a47054b082791e4bf61f Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 25 Nov 2019 00:13:02 +0100 Subject: [PATCH 140/305] Upgrade dependencies and target frameworks (#1178) * Upgrade dependencies and target frameworks * Update neo.csproj * Remove BOM * Restore net47 and Neo.VM v3.0.0-CI00042 * Remove net47 * Microsoft.EntityFrameworkCore.Sqlite v3.0.0 * Fix some of the tests * Revert "Microsoft.EntityFrameworkCore.Sqlite v3.0.0" This reverts commit b1c9d09b13490709b4d950218a67765b8df4a240. * Neo.VM v3.0.0-CI00046 * Remove before_script * FormatException * Fixes * Fixes * Fix * Update neo.csproj * Neo.VM v3.0.0-CI00051 * Upgrade dependencies * Disable warnings * Upgrade UT dependencies * Update neo.csproj * Neo.VM v3.0.0-CI00170 --- .travis.yml | 20 +++---- .../Extensions/NativeContractExtensions.cs | 1 + .../Nep5NativeContractExtensions.cs | 9 ++-- .../Network/P2P/Payloads/UT_Transaction.cs | 14 ++--- .../Enumerators/UT_IteratorKeysWrapper.cs | 2 +- .../Enumerators/UT_IteratorValuesWrapper.cs | 2 +- .../SmartContract/Iterators/UT_MapWrapper.cs | 11 ++-- .../Native/Tokens/UT_NeoToken.cs | 52 +++++++++---------- .../Native/Tokens/UT_Nep5Token.cs | 1 + .../SmartContract/Native/UT_PolicyContract.cs | 27 +++++----- .../SmartContract/UT_ApplicationEngine.cs | 3 +- .../SmartContract/UT_ContainerPlaceholder.cs | 10 +--- .../SmartContract/UT_InteropService.NEO.cs | 30 +++++------ .../SmartContract/UT_InteropService.cs | 42 +++++++-------- .../SmartContract/UT_JsonSerializer.cs | 10 ++-- .../SmartContract/UT_NotifyEventArgs.cs | 4 +- .../SmartContract/UT_SmartContractHelper.cs | 7 ++- neo.UnitTests/SmartContract/UT_Syscalls.cs | 8 +-- neo.UnitTests/UT_UInt160.cs | 2 + neo.UnitTests/UT_UInt256.cs | 2 + neo.UnitTests/VM/UT_Helper.cs | 10 ++-- neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs | 2 +- neo.UnitTests/neo.UnitTests.csproj | 10 ++-- neo/Cryptography/RIPEMD160Managed.cs | 2 - neo/Cryptography/SCrypt.cs | 7 --- neo/Helper.cs | 8 +++ neo/IO/Data/LevelDB/Native.cs | 11 ---- neo/IO/Helper.cs | 24 ++++++++- neo/Ledger/Blockchain.ApplicationExecuted.cs | 1 + neo/Network/RPC/PolicyAPI.cs | 2 +- neo/SmartContract/ApplicationEngine.cs | 1 + neo/SmartContract/ContainerPlaceholder.cs | 6 +-- .../Enumerators/ConcatenatedEnumerator.cs | 2 +- neo/SmartContract/Enumerators/IEnumerator.cs | 2 +- .../Enumerators/IteratorKeysWrapper.cs | 2 +- .../Enumerators/IteratorValuesWrapper.cs | 2 +- neo/SmartContract/Helper.cs | 14 ++--- neo/SmartContract/IInteroperable.cs | 2 +- neo/SmartContract/InteropDescriptor.cs | 1 + neo/SmartContract/InteropService.NEO.cs | 24 ++++----- neo/SmartContract/InteropService.cs | 32 ++++++------ neo/SmartContract/Iterators/ArrayWrapper.cs | 4 +- .../Iterators/ConcatenatedIterator.cs | 4 +- neo/SmartContract/Iterators/IIterator.cs | 4 +- neo/SmartContract/Iterators/MapWrapper.cs | 8 +-- .../Iterators/StorageIterator.cs | 4 +- neo/SmartContract/JsonSerializer.cs | 4 +- .../Native/ContractMethodMetadata.cs | 2 +- neo/SmartContract/Native/NativeContract.cs | 1 + neo/SmartContract/Native/PolicyContract.cs | 5 +- neo/SmartContract/Native/Tokens/GasToken.cs | 1 + neo/SmartContract/Native/Tokens/NeoToken.cs | 10 ++-- neo/SmartContract/Native/Tokens/Nep5Token.cs | 7 +-- neo/SmartContract/NotifyEventArgs.cs | 2 +- neo/VM/Helper.cs | 33 ++++++++++-- neo/neo.csproj | 18 +++---- 56 files changed, 279 insertions(+), 250 deletions(-) diff --git a/.travis.yml b/.travis.yml index 59b745ca31..6691c787d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,33 +2,27 @@ language: csharp os: - linux - - osx dist: bionic -osx_image: xcode9.1 - mono: none -dotnet: 2.2.402 +dotnet: 3.0.100 env: - TEST_SUITE="without-cultures" - TEST_SUITE="cultures" -before_install: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ulimit -n 2048; fi install: - - dotnet tool install -g dotnet-format --version 4.0.40103 --add-source https://dotnet.myget.org/F/format/api/v3/index.json + - dotnet tool install -g dotnet-format - export PATH="$PATH:$HOME/.dotnet/tools" - dotnet-format --version -before_script: - - echo "Checking format..." - - dotnet format --check --dry-run -w . -v diagnostic # check C# formatting for neo.sln - - cd neo.UnitTests script: | + echo "Checking format..." + dotnet format --check --dry-run -w . -v diagnostic # check C# formatting for neo.sln + cd neo.UnitTests if [[ "$TEST_SUITE" == "cultures" ]]; then dotnet test -v m --filter FullyQualifiedName=Neo.UnitTests.UT_Culture.All_Tests_Cultures else - if [[ "$TEST_SUITE" == "without-cultures" && "$TRAVIS_OS_NAME" == "linux" ]]; then + if [[ "$TEST_SUITE" == "without-cultures" ]]; then # Test & Calculate coverage find * -name *.csproj | xargs -I % dotnet add % package coverlet.msbuild dotnet test -v m --filter FullyQualifiedName!=Neo.UnitTests.UT_Culture.All_Tests_Cultures /p:CollectCoverage=true /p:CoverletOutputFormat=opencover @@ -38,7 +32,7 @@ script: | fi fi after_success: | - if [[ "$TEST_SUITE" == "without-cultures" && "$TRAVIS_OS_NAME" == "linux" ]]; then + if [[ "$TEST_SUITE" == "without-cultures" ]]; then # Send coverage echo "Test Success - Branch($TRAVIS_BRANCH) Pull Request($TRAVIS_PULL_REQUEST) Tag($TRAVIS_TAG)" bash <(curl -s https://codecov.io/bash) -v diff --git a/neo.UnitTests/Extensions/NativeContractExtensions.cs b/neo.UnitTests/Extensions/NativeContractExtensions.cs index 4d3f7bcb95..9847bddd48 100644 --- a/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -2,6 +2,7 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; +using Neo.VM.Types; using System; namespace Neo.UnitTests.Extensions diff --git a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs index a531591d34..f06a7eb201 100644 --- a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs +++ b/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Numerics; -using System.Text; namespace Neo.UnitTests.Extensions { @@ -65,7 +64,7 @@ public static bool Transfer(this NativeContract contract, Persistence.Snapshot s var result = engine.ResultStack.Pop(); result.Should().BeOfType(typeof(VM.Types.Boolean)); - return (result as VM.Types.Boolean).GetBoolean(); + return result.ToBoolean(); } public static string[] SupportedStandards(this NativeContract contract) @@ -86,7 +85,7 @@ public static string[] SupportedStandards(this NativeContract contract) result.Should().BeOfType(typeof(VM.Types.Array)); return (result as VM.Types.Array).ToArray() - .Select(u => Encoding.ASCII.GetString(u.GetByteArray())) + .Select(u => u.GetString()) .ToArray(); } @@ -168,7 +167,7 @@ public static string Symbol(this NativeContract contract) var result = engine.ResultStack.Pop(); result.Should().BeOfType(typeof(VM.Types.ByteArray)); - return Encoding.UTF8.GetString((result as VM.Types.ByteArray).GetByteArray()); + return result.GetString(); } public static string Name(this NativeContract contract) @@ -188,7 +187,7 @@ public static string Name(this NativeContract contract) var result = engine.ResultStack.Pop(); result.Should().BeOfType(typeof(VM.Types.ByteArray)); - return Encoding.UTF8.GetString((result as VM.Types.ByteArray).GetByteArray()); + return result.GetString(); } } } diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index c8c874cee6..cf5f4504fe 100644 --- a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -159,7 +159,7 @@ public void FeeIsMultiSigContract() engine.LoadScript(witness.InvocationScript); Assert.AreEqual(VMState.HALT, engine.Execute()); Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + Assert.IsTrue(engine.ResultStack.Pop().ToBoolean()); verificationGas += engine.GasConsumed; } } @@ -246,7 +246,7 @@ public void FeeIsSignatureContractDetailed() engine.LoadScript(witness.InvocationScript); Assert.AreEqual(VMState.HALT, engine.Execute()); Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + Assert.IsTrue(engine.ResultStack.Pop().ToBoolean()); verificationGas += engine.GasConsumed; } } @@ -364,7 +364,7 @@ public void FeeIsSignatureContract_TestScope_Global() engine.LoadScript(witness.InvocationScript); Assert.AreEqual(VMState.HALT, engine.Execute()); Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + Assert.IsTrue(engine.ResultStack.Pop().ToBoolean()); verificationGas += engine.GasConsumed; } } @@ -456,7 +456,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() engine.LoadScript(witness.InvocationScript); Assert.AreEqual(VMState.HALT, engine.Execute()); Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + Assert.IsTrue(engine.ResultStack.Pop().ToBoolean()); verificationGas += engine.GasConsumed; } } @@ -551,7 +551,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() engine.LoadScript(witness.InvocationScript); Assert.AreEqual(VMState.HALT, engine.Execute()); Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + Assert.IsTrue(engine.ResultStack.Pop().ToBoolean()); verificationGas += engine.GasConsumed; } } @@ -706,7 +706,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() engine.LoadScript(witness.InvocationScript); Assert.AreEqual(VMState.HALT, engine.Execute()); Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + Assert.IsTrue(engine.ResultStack.Pop().ToBoolean()); verificationGas += engine.GasConsumed; } } @@ -1054,7 +1054,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() engine.LoadScript(witness.InvocationScript); Assert.AreEqual(VMState.HALT, engine.Execute()); Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + Assert.IsTrue(engine.ResultStack.Pop().ToBoolean()); verificationGas += engine.GasConsumed; } } diff --git a/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs b/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs index 2a345893e0..12b82be599 100644 --- a/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs +++ b/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs @@ -2,7 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; -using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; diff --git a/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs b/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs index f236faab1a..2e8bf5709b 100644 --- a/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs +++ b/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs @@ -2,7 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; -using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; diff --git a/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs b/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs index b111b0aa54..7ce7c7e845 100644 --- a/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs +++ b/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Iterators; -using Neo.VM; using Neo.VM.Types; using System; using System.Collections.Generic; @@ -14,7 +13,7 @@ public class UT_MapWrapper [TestMethod] public void TestGeneratorAndDispose() { - MapWrapper mapWrapper = new MapWrapper(new List>()); + MapWrapper mapWrapper = new MapWrapper(new List>()); Assert.IsNotNull(mapWrapper); Action action = () => mapWrapper.Dispose(); action.Should().NotThrow(); @@ -23,10 +22,10 @@ public void TestGeneratorAndDispose() [TestMethod] public void TestKeyAndValue() { - List> list = new List>(); - StackItem stackItem1 = new Integer(0); + List> list = new List>(); + Integer stackItem1 = new Integer(0); StackItem stackItem2 = new Integer(1); - list.Add(new KeyValuePair(stackItem1, stackItem2)); + list.Add(new KeyValuePair(stackItem1, stackItem2)); MapWrapper mapWrapper = new MapWrapper(list); mapWrapper.Next(); Assert.AreEqual(stackItem1, mapWrapper.Key()); @@ -36,7 +35,7 @@ public void TestKeyAndValue() [TestMethod] public void TestNext() { - MapWrapper mapWrapper = new MapWrapper(new List>()); + MapWrapper mapWrapper = new MapWrapper(new List>()); Assert.AreEqual(false, mapWrapper.Next()); } } diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 6df9f7074e..19f7ad2f6a 100644 --- a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -282,13 +282,13 @@ public void TestGetNextBlockValidators1() var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); ((VM.Types.Array)result).Count.Should().Be(7); - ((VM.Types.ByteArray)((VM.Types.Array)result)[0]).GetByteArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[1]).GetByteArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[2]).GetByteArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[3]).GetByteArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[4]).GetByteArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[5]).GetByteArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[6]).GetByteArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + ((VM.Types.Array)result)[0].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + ((VM.Types.Array)result)[1].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); + ((VM.Types.Array)result)[2].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + ((VM.Types.Array)result)[3].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + ((VM.Types.Array)result)[4].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + ((VM.Types.Array)result)[5].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + ((VM.Types.Array)result)[6].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); } } @@ -323,19 +323,19 @@ public void TestGetRegisteredValidators1() var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); ((VM.Types.Array)result).Count.Should().Be(7); - ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[0])[0]).GetByteArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + ((VM.Types.Struct)((VM.Types.Array)result)[0])[0].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); ((VM.Types.Struct)((VM.Types.Array)result)[0])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[1])[0]).GetByteArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + ((VM.Types.Struct)((VM.Types.Array)result)[1])[0].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); ((VM.Types.Struct)((VM.Types.Array)result)[1])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[2])[0]).GetByteArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + ((VM.Types.Struct)((VM.Types.Array)result)[2])[0].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); ((VM.Types.Struct)((VM.Types.Array)result)[2])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[3])[0]).GetByteArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + ((VM.Types.Struct)((VM.Types.Array)result)[3])[0].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); ((VM.Types.Struct)((VM.Types.Array)result)[3])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[4])[0]).GetByteArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); + ((VM.Types.Struct)((VM.Types.Array)result)[4])[0].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); ((VM.Types.Struct)((VM.Types.Array)result)[4])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[5])[0]).GetByteArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + ((VM.Types.Struct)((VM.Types.Array)result)[5])[0].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); ((VM.Types.Struct)((VM.Types.Array)result)[5])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.ByteArray)((VM.Types.Struct)((VM.Types.Array)result)[6])[0]).GetByteArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + ((VM.Types.Struct)((VM.Types.Array)result)[6])[0].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); ((VM.Types.Struct)((VM.Types.Array)result)[6])[1].GetBigInteger().Should().Be(new BigInteger(0)); } } @@ -377,13 +377,13 @@ public void TestGetValidators1() var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); ((VM.Types.Array)result).Count.Should().Be(7); - ((VM.Types.ByteArray)((VM.Types.Array)result)[0]).GetByteArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[1]).GetByteArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[2]).GetByteArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[3]).GetByteArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[4]).GetByteArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[5]).GetByteArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - ((VM.Types.ByteArray)((VM.Types.Array)result)[6]).GetByteArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + ((VM.Types.Array)result)[0].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + ((VM.Types.Array)result)[1].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); + ((VM.Types.Array)result)[2].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + ((VM.Types.Array)result)[3].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + ((VM.Types.Array)result)[4].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + ((VM.Types.Array)result)[5].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + ((VM.Types.Array)result)[6].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); } } @@ -575,7 +575,7 @@ public void TestValidatorState_ToByteArray() engine.Execute(); var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Boolean)); - return (true, (result as VM.Types.Boolean).GetBoolean()); + return (true, result.ToBoolean()); } internal static (bool State, bool Result) Check_Vote(Snapshot snapshot, byte[] account, byte[][] pubkeys, bool signAccount) @@ -605,7 +605,7 @@ internal static (bool State, bool Result) Check_Vote(Snapshot snapshot, byte[] a var result = engine.ResultStack.Pop(); result.Should().BeOfType(typeof(VM.Types.Boolean)); - return (true, (result as VM.Types.Boolean).GetBoolean()); + return (true, result.ToBoolean()); } internal static (bool State, bool Result) Check_RegisterValidator(Snapshot snapshot, byte[] pubkey) @@ -629,7 +629,7 @@ internal static (bool State, bool Result) Check_RegisterValidator(Snapshot snaps var result = engine.ResultStack.Pop(); result.Should().BeOfType(typeof(VM.Types.Boolean)); - return (true, (result as VM.Types.Boolean).GetBoolean()); + return (true, result.ToBoolean()); } internal static ECPoint[] Check_GetValidators(Snapshot snapshot) @@ -649,7 +649,7 @@ internal static ECPoint[] Check_GetValidators(Snapshot snapshot) var result = engine.ResultStack.Pop(); result.Should().BeOfType(typeof(VM.Types.Array)); - return (result as VM.Types.Array).Select(u => u.GetByteArray().AsSerializable()).ToArray(); + return (result as VM.Types.Array).Select(u => u.GetSpan().AsSerializable()).ToArray(); } internal static (BigInteger Value, bool State) Check_UnclaimedGas(Snapshot snapshot, byte[] address) @@ -695,7 +695,7 @@ internal static void CheckBalance(byte[] account, DataCache(Blockchain.MaxValidators)).Should().BeEquivalentTo(votes); // Votes + (st[2].GetSpan().AsSerializableArray(Blockchain.MaxValidators)).Should().BeEquivalentTo(votes); // Votes trackable.Key.Key.Should().BeEquivalentTo(new byte[] { 20 }.Concat(account)); trackable.Item.IsConstant.Should().Be(false); diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs index 0cec84a5a8..a34c7a109f 100644 --- a/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs +++ b/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs @@ -6,6 +6,7 @@ using Neo.SmartContract; using Neo.SmartContract.Native.Tokens; using Neo.VM; +using Neo.VM.Types; using System; using System.Numerics; diff --git a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index e0f87182a6..b75141459f 100644 --- a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -7,6 +7,7 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; +using Neo.VM; using System.Linq; namespace Neo.UnitTests.SmartContract.Native @@ -70,7 +71,7 @@ public void Check_SetMaxBlockSize() var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + ret.ToBoolean().Should().BeFalse(); ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); ret.Should().BeOfType(); @@ -81,7 +82,7 @@ public void Check_SetMaxBlockSize() ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = Neo.Network.P2P.Message.PayloadMaxSize }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + ret.ToBoolean().Should().BeFalse(); ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); ret.Should().BeOfType(); @@ -92,7 +93,7 @@ public void Check_SetMaxBlockSize() ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.ToBoolean().Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); ret.Should().BeOfType(); @@ -116,7 +117,7 @@ public void Check_SetMaxTransactionsPerBlock() var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(), "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + ret.ToBoolean().Should().BeFalse(); ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); ret.Should().BeOfType(); @@ -127,7 +128,7 @@ public void Check_SetMaxTransactionsPerBlock() ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.ToBoolean().Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); ret.Should().BeOfType(); @@ -151,7 +152,7 @@ public void Check_SetFeePerByte() var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(), "setFeePerByte", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + ret.ToBoolean().Should().BeFalse(); ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); @@ -162,7 +163,7 @@ public void Check_SetFeePerByte() ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), "setFeePerByte", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.ToBoolean().Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); @@ -186,7 +187,7 @@ public void Check_Block_UnblockAccount() var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(), "blockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + ret.ToBoolean().Should().BeFalse(); ret = NativeContract.Policy.Call(snapshot, "getBlockedAccounts"); ret.Should().BeOfType(); @@ -197,31 +198,31 @@ public void Check_Block_UnblockAccount() ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), "blockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.ToBoolean().Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getBlockedAccounts"); ret.Should().BeOfType(); ((VM.Types.Array)ret).Count.Should().Be(1); - ((VM.Types.Array)ret)[0].GetByteArray().Should().BeEquivalentTo(UInt160.Zero.ToArray()); + ((VM.Types.Array)ret)[0].GetSpan().ToArray().Should().BeEquivalentTo(UInt160.Zero.ToArray()); // Unblock without signature ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(), "unblockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeFalse(); + ret.ToBoolean().Should().BeFalse(); ret = NativeContract.Policy.Call(snapshot, "getBlockedAccounts"); ret.Should().BeOfType(); ((VM.Types.Array)ret).Count.Should().Be(1); - ((VM.Types.Array)ret)[0].GetByteArray().Should().BeEquivalentTo(UInt160.Zero.ToArray()); + ((VM.Types.Array)ret)[0].GetSpan().ToArray().Should().BeEquivalentTo(UInt160.Zero.ToArray()); // Unblock with signature ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), "unblockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); - ret.GetBoolean().Should().BeTrue(); + ret.ToBoolean().Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getBlockedAccounts"); ret.Should().BeOfType(); diff --git a/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index fae3938a22..d8bdd8a857 100644 --- a/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -7,8 +7,7 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; -using Neo.UnitTests.Ledger; -using Neo.VM; +using Neo.VM.Types; using System; namespace Neo.UnitTests.SmartContract diff --git a/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs b/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs index e7ed55ac42..0ba48e78a9 100644 --- a/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs +++ b/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs @@ -28,15 +28,7 @@ public void TestEquals() public void TestGetBoolean() { ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(); - Action action = () => containerPlaceholder.GetBoolean(); - action.Should().Throw(); - } - - [TestMethod] - public void TestGetByteArray() - { - ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(); - Action action = () => containerPlaceholder.GetByteArray(); + Action action = () => containerPlaceholder.ToBoolean(); action.Should().Throw(); } } diff --git a/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 243cfa89ef..3e60b91591 100644 --- a/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -36,13 +36,13 @@ public void TestCheckSig() engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(new byte[70]); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); } [TestMethod] @@ -78,7 +78,7 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); pubkeys = new VMArray(); engine.CurrentContext.EvaluationStack.Push(signatures); @@ -111,7 +111,7 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); pubkeys = new VMArray { @@ -127,7 +127,7 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); } [TestMethod] @@ -140,7 +140,7 @@ public void TestAccount_IsStandard() 0x01, 0x01, 0x01, 0x01, 0x01 }; engine.CurrentContext.EvaluationStack.Push(hash); InteropService.Invoke(engine, InteropService.Neo_Account_IsStandard).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); var mockSnapshot = new Mock(); var state = TestUtils.GetContract(); @@ -149,7 +149,7 @@ public void TestAccount_IsStandard() engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); InteropService.Invoke(engine, InteropService.Neo_Account_IsStandard).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); } [TestMethod] @@ -286,7 +286,7 @@ public void TestStorage_Find() var iterator = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); iterator.Next(); var ele = iterator.Value(); - ele.GetByteArray().ToHexString().Should().Be(storageItem.Value.ToHexString()); + ele.GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); engine.CurrentContext.EvaluationStack.Push(1); InteropService.Invoke(engine, InteropService.Neo_Storage_Find).Should().BeFalse(); @@ -304,7 +304,7 @@ public void TestEnumerator_Create() InteropService.Invoke(engine, InteropService.Neo_Enumerator_Create).Should().BeTrue(); var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().Next(); - ret.GetInterface().Value().GetByteArray().ToHexString() + ret.GetInterface().Value().GetSpan().ToHexString() .Should().Be(new byte[] { 0x01 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(1); @@ -321,7 +321,7 @@ public void TestEnumerator_Next() }; engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new ArrayWrapper(arr))); InteropService.Invoke(engine, InteropService.Neo_Enumerator_Next).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); engine.CurrentContext.EvaluationStack.Push(1); InteropService.Invoke(engine, InteropService.Neo_Enumerator_Next).Should().BeFalse(); @@ -339,7 +339,7 @@ public void TestEnumerator_Value() wrapper.Next(); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); InteropService.Invoke(engine, InteropService.Neo_Enumerator_Value).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(1); InteropService.Invoke(engine, InteropService.Neo_Enumerator_Value).Should().BeFalse(); @@ -364,7 +364,7 @@ public void TestEnumerator_Concat() InteropService.Invoke(engine, InteropService.Neo_Enumerator_Concat).Should().BeTrue(); var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next().Should().BeTrue(); - ret.Value().GetByteArray().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } [TestMethod] @@ -379,7 +379,7 @@ public void TestIterator_Create() InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeTrue(); var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().Next(); - ret.GetInterface().Value().GetByteArray().ToHexString() + ret.GetInterface().Value().GetSpan().ToHexString() .Should().Be(new byte[] { 0x01 }.ToHexString()); var map = new Map @@ -448,7 +448,7 @@ public void TestIterator_Values() InteropService.Invoke(engine, InteropService.Neo_Iterator_Values).Should().BeTrue(); var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next(); - ret.Value().GetByteArray().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(1); InteropService.Invoke(engine, InteropService.Neo_Iterator_Values).Should().BeFalse(); @@ -473,7 +473,7 @@ public void TestIterator_Concat() InteropService.Invoke(engine, InteropService.Neo_Iterator_Concat).Should().BeTrue(); var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next().Should().BeTrue(); - ret.Value().GetByteArray().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } [TestMethod] diff --git a/neo.UnitTests/SmartContract/UT_InteropService.cs b/neo.UnitTests/SmartContract/UT_InteropService.cs index cc6eac738a..83f4a1485b 100644 --- a/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -188,7 +188,7 @@ private void AssertNotification(StackItem stackItem, UInt160 scriptHash, string var array = (VM.Types.Array)stackItem; Assert.AreEqual(2, array.Count); - CollectionAssert.AreEqual(scriptHash.ToArray(), array[0].GetByteArray()); + CollectionAssert.AreEqual(scriptHash.ToArray(), array[0].GetSpan().ToArray()); Assert.AreEqual(notification, array[1].GetString()); } @@ -198,7 +198,7 @@ private void AssertNotification(StackItem stackItem, UInt160 scriptHash, int not var array = (VM.Types.Array)stackItem; Assert.AreEqual(2, array.Count); - CollectionAssert.AreEqual(scriptHash.ToArray(), array[0].GetByteArray()); + CollectionAssert.AreEqual(scriptHash.ToArray(), array[0].GetSpan().ToArray()); Assert.AreEqual(notification, array[1].GetBigInteger()); } @@ -209,7 +209,7 @@ public void TestExecutionEngine_GetScriptContainer() InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetScriptContainer).Should().BeTrue(); var stackItem = ((VM.Types.Array)engine.CurrentContext.EvaluationStack.Pop()).ToArray(); stackItem.Length.Should().Be(8); - stackItem[0].GetByteArray().ToHexString().Should().Be(TestUtils.GetTransaction().Hash.ToArray().ToHexString()); + stackItem[0].GetSpan().ToHexString().Should().Be(TestUtils.GetTransaction().Hash.ToArray().ToHexString()); } [TestMethod] @@ -217,7 +217,7 @@ public void TestExecutionEngine_GetExecutingScriptHash() { var engine = GetEngine(); InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetExecutingScriptHash).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString() + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() .Should().Be(engine.CurrentScriptHash.ToArray().ToHexString()); } @@ -231,7 +231,7 @@ public void TestExecutionEngine_GetCallingScriptHash() engine = GetEngine(true); engine.LoadScript(new byte[] { 0x01 }); InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetCallingScriptHash).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString() + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() .Should().Be(engine.CallingScriptHash.ToArray().ToHexString()); } @@ -240,7 +240,7 @@ public void TestExecutionEngine_GetEntryScriptHash() { var engine = GetEngine(); InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetEntryScriptHash).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString() + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() .Should().Be(engine.EntryScriptHash.ToArray().ToHexString()); } @@ -249,7 +249,7 @@ public void TestRuntime_Platform() { var engine = GetEngine(); InteropService.Invoke(engine, InteropService.System_Runtime_Platform).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString() + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() .Should().Be(Encoding.ASCII.GetBytes("NEO").ToHexString()); } @@ -276,12 +276,12 @@ public void TestRuntime_CheckWitness() engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(true)); InteropService.Invoke(engine, InteropService.System_Runtime_CheckWitness).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Peek().GetType().Should().Be(typeof(Neo.VM.Types.Boolean)); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().Be(false); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().Be(false); engine.CurrentContext.EvaluationStack.Push(((Transaction)engine.ScriptContainer).Sender.ToArray()); InteropService.Invoke(engine, InteropService.System_Runtime_CheckWitness).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Peek().GetType().Should().Be(typeof(Neo.VM.Types.Boolean)); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().Be(false); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().Be(false); engine.CurrentContext.EvaluationStack.Push(new byte[0]); InteropService.Invoke(engine, InteropService.System_Runtime_CheckWitness).Should().BeFalse(); @@ -317,7 +317,7 @@ public void TestRuntime_Serialize() var engine = GetEngine(); engine.CurrentContext.EvaluationStack.Push(100); InteropService.Invoke(engine, InteropService.System_Runtime_Serialize).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString() + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() .Should().Be(new byte[] { 0x02, 0x01, 0x64 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(new byte[1024 * 1024 * 2]); //Larger than MaxItemSize @@ -366,7 +366,7 @@ public void TestCrypto_Verify() engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); engine.CurrentContext.EvaluationStack.Push(message); InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); byte[] wrongkey = pubkey.EncodePoint(false); wrongkey[0] = 5; @@ -374,7 +374,7 @@ public void TestCrypto_Verify() engine.CurrentContext.EvaluationStack.Push(wrongkey); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(engine.ScriptContainer)); InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Peek().GetBoolean().Should().BeFalse(); + engine.CurrentContext.EvaluationStack.Peek().ToBoolean().Should().BeFalse(); } [TestMethod] @@ -400,7 +400,7 @@ public void TestBlockchain_GetBlock() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; engine.CurrentContext.EvaluationStack.Push(data1); InteropService.Invoke(engine, InteropService.System_Blockchain_GetBlock).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); byte[] data2 = new byte[] { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; engine.CurrentContext.EvaluationStack.Push(data2); @@ -417,7 +417,7 @@ public void TestBlockchain_GetTransaction() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; engine.CurrentContext.EvaluationStack.Push(data1); InteropService.Invoke(engine, InteropService.System_Blockchain_GetTransaction).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBoolean().Should().BeFalse(); + engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); } [TestMethod] @@ -455,9 +455,9 @@ public void TestBlockchain_GetContract() var stackItems = ((VM.Types.Array)engine.CurrentContext.EvaluationStack.Pop()).ToArray(); stackItems.Length.Should().Be(3); stackItems[0].GetType().Should().Be(typeof(ByteArray)); - stackItems[0].GetByteArray().ToHexString().Should().Be(state.Script.ToHexString()); - stackItems[1].GetBoolean().Should().BeFalse(); - stackItems[2].GetBoolean().Should().BeFalse(); + stackItems[0].GetSpan().ToHexString().Should().Be(state.Script.ToHexString()); + stackItems[1].ToBoolean().Should().BeFalse(); + stackItems[2].ToBoolean().Should().BeFalse(); } [TestMethod] @@ -510,7 +510,7 @@ public void TestStorage_Get() IsReadOnly = false })); InteropService.Invoke(engine, InteropService.System_Storage_Get).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString().Should().Be(storageItem.Value.ToHexString()); + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache()); engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); @@ -746,8 +746,8 @@ public void TestContract_Call() engine.CurrentContext.EvaluationStack.Push(method); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); InteropService.Invoke(engine, InteropService.System_Contract_Call).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString().Should().Be(method.ToHexString()); - engine.CurrentContext.EvaluationStack.Pop().GetByteArray().ToHexString().Should().Be(args.ToHexString()); + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(method.ToHexString()); + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(args.ToHexString()); state.Manifest.Permissions[0].Methods = WildCardContainer.Create("a"); engine.CurrentContext.EvaluationStack.Push(args); @@ -838,7 +838,7 @@ private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSn internal class TestInteropInterface : InteropInterface { public override bool Equals(StackItem other) => true; - public override bool GetBoolean() => true; + public override bool ToBoolean() => true; public override T GetInterface() => throw new NotImplementedException(); } } diff --git a/neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs index ed9c9cd72b..788868622c 100644 --- a/neo.UnitTests/SmartContract/UT_JsonSerializer.cs +++ b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs @@ -31,7 +31,7 @@ public void JsonTest_WrongJson() Assert.ThrowsException(() => JObject.Parse(json)); json = @"{""length"":99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999}"; - Assert.ThrowsException(() => JObject.Parse(json)); + Assert.ThrowsException(() => JObject.Parse(json)); json = $"{{\"length\":{long.MaxValue}}}"; Assert.ThrowsException(() => JObject.Parse(json)); @@ -119,7 +119,7 @@ public void JsonTest_String() var json = @" ["""" , ""\b\f\t\n\r\/\\"" ]"; var parsed = JObject.Parse(json); - Assert.AreEqual(@"["""",""\b\f\t\n\r\/\\""]", parsed.ToString()); + Assert.AreEqual(@"["""",""\b\f\t\n\r/\\""]", parsed.ToString()); json = @"[""\uD834\uDD1E""]"; parsed = JObject.Parse(json); @@ -281,7 +281,7 @@ public void Deserialize_Array_Bool_Str_Num() var array = (VM.Types.Array)items; - Assert.IsTrue(array[0].GetBoolean()); + Assert.IsTrue(array[0].ToBoolean()); Assert.AreEqual(array[1].GetString(), "test"); Assert.AreEqual(array[2].GetBigInteger(), 123); } @@ -316,7 +316,7 @@ public void Deserialize_Array_OfArray() array = (VM.Types.Array)array[0]; Assert.AreEqual(array.Count, 3); - Assert.IsTrue(array[0].GetBoolean()); + Assert.IsTrue(array[0].ToBoolean()); Assert.AreEqual(array[1].GetString(), "test1"); Assert.AreEqual(array[2].GetBigInteger(), 123); @@ -324,7 +324,7 @@ public void Deserialize_Array_OfArray() array = (VM.Types.Array)array[1]; Assert.AreEqual(array.Count, 3); - Assert.IsTrue(array[0].GetBoolean()); + Assert.IsTrue(array[0].ToBoolean()); Assert.AreEqual(array[1].GetString(), "test2"); Assert.AreEqual(array[2].GetBigInteger(), 321); } diff --git a/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs b/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs index a1dd95b6ee..1dfd6a6ec5 100644 --- a/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs +++ b/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs @@ -1,8 +1,8 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P.Payloads; using Neo.SmartContract; -using Neo.VM; +using Neo.VM.Types; namespace Neo.UnitTests.SmartContract { diff --git a/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 8379fccd6b..8a85e7a4b4 100644 --- a/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -5,7 +5,6 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; -using Neo.VM; using Neo.VM.Types; using Neo.Wallets; using System; @@ -169,7 +168,7 @@ public void TestSerialize() Assert.AreEqual(Encoding.Default.GetString(expectedArray7), Encoding.Default.GetString(result7)); StackItem stackItem81 = new VM.Types.Integer(1); - Dictionary list8 = new Dictionary + Dictionary list8 = new Dictionary { { new VM.Types.Integer(2), stackItem81 } }; @@ -180,7 +179,7 @@ public void TestSerialize() }; Assert.AreEqual(Encoding.Default.GetString(expectedArray8), Encoding.Default.GetString(result8)); - StackItem stackItem9 = new VM.Types.Integer(1); + Integer stackItem9 = new VM.Types.Integer(1); Map stackItem91 = new VM.Types.Map(); stackItem91.Add(stackItem9, stackItem91); Action action9 = () => Neo.SmartContract.Helper.Serialize(stackItem91); @@ -235,7 +234,7 @@ public void TestDeserializeStackItem() Assert.AreEqual(((VM.Types.Struct)stackItem62).GetEnumerator().Current, ((VM.Types.Struct)result6).GetEnumerator().Current); StackItem stackItem71 = new VM.Types.Integer(1); - Dictionary list7 = new Dictionary(); + Dictionary list7 = new Dictionary(); list7.Add(new VM.Types.Integer(2), stackItem71); StackItem stackItem72 = new VM.Types.Map(list7); byte[] byteArray7 = Neo.SmartContract.Helper.Serialize(stackItem72); diff --git a/neo.UnitTests/SmartContract/UT_Syscalls.cs b/neo.UnitTests/SmartContract/UT_Syscalls.cs index b778f63d94..f54b533106 100644 --- a/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -75,8 +75,8 @@ public void System_Blockchain_GetBlock() Assert.AreEqual(engine.Execute(), VMState.HALT); Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); - Assert.AreEqual(engine.ResultStack.Pop().GetByteArray().ToHexString(), - "5b22556168355c2f4b6f446d39723064555950636353714346745a30594f726b583164646e7334366e676e3962383d222c332c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c322c312c224141414141414141414141414141414141414141414141414141413d222c315d"); + Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(), + "5b22556168352f4b6f446d39723064555950636353714346745a30594f726b583164646e7334366e676e3962383d222c332c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c322c312c224141414141414141414141414141414141414141414141414141413d222c315d"); Assert.AreEqual(0, engine.ResultStack.Count); // Clean @@ -126,8 +126,8 @@ public void System_ExecutionEngine_GetScriptContainer() Assert.AreEqual(engine.Execute(), VMState.HALT); Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); - Assert.AreEqual(engine.ResultStack.Pop().GetByteArray().ToHexString(), - @"5b225c75303032426b53415959527a4c4b69685a676464414b50596f754655737a63544d7867445a6572584a3172784c37303d222c362c342c225c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f383d222c332c322c352c2241513d3d225d"); + Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(), + @"5b225c75303032426b53415959527a4c4b69685a676464414b50596f754655737a63544d7867445a6572584a3172784c37303d222c362c342c222f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f383d222c332c322c352c2241513d3d225d"); Assert.AreEqual(0, engine.ResultStack.Count); } } diff --git a/neo.UnitTests/UT_UInt160.cs b/neo.UnitTests/UT_UInt160.cs index 99cf91a5c3..0a0eb0321e 100644 --- a/neo.UnitTests/UT_UInt160.cs +++ b/neo.UnitTests/UT_UInt160.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1718 + using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; diff --git a/neo.UnitTests/UT_UInt256.cs b/neo.UnitTests/UT_UInt256.cs index 179e991110..d263effdee 100644 --- a/neo.UnitTests/UT_UInt256.cs +++ b/neo.UnitTests/UT_UInt256.cs @@ -1,3 +1,5 @@ +#pragma warning disable CS1718 + using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; diff --git a/neo.UnitTests/VM/UT_Helper.cs b/neo.UnitTests/VM/UT_Helper.cs index 41d6270822..f394b63f0b 100644 --- a/neo.UnitTests/VM/UT_Helper.cs +++ b/neo.UnitTests/VM/UT_Helper.cs @@ -4,11 +4,13 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Text; +using Array = System.Array; namespace Neo.UnitTests.VMT { @@ -112,7 +114,7 @@ public void TestToParameter() StackItem arrayItem = new VM.Types.Array(new[] { byteItem, boolItem, intItem, interopItem }); Assert.AreEqual(1000, (BigInteger)(arrayItem.ToParameter().Value as List)[2].Value); - StackItem mapItem = new VM.Types.Map(new Dictionary(new[] { new KeyValuePair(byteItem, intItem) })); + StackItem mapItem = new Map(new Dictionary { [(PrimitiveType)byteItem] = intItem }); Assert.AreEqual(1000, (BigInteger)(mapItem.ToParameter().Value as List>)[0].Value.Value); } @@ -123,7 +125,7 @@ public void TestToStackItem() Assert.AreEqual(30000000000000L, (long)byteParameter.ToStackItem().GetBigInteger()); ContractParameter boolParameter = new ContractParameter { Type = ContractParameterType.Boolean, Value = false }; - Assert.AreEqual(false, boolParameter.ToStackItem().GetBoolean()); + Assert.AreEqual(false, boolParameter.ToStackItem().ToBoolean()); ContractParameter intParameter = new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1000) }; Assert.AreEqual(1000, intParameter.ToStackItem().GetBigInteger()); @@ -135,10 +137,10 @@ public void TestToStackItem() Assert.AreEqual(0, h256Parameter.ToStackItem().GetBigInteger()); ContractParameter pkParameter = new ContractParameter { Type = ContractParameterType.PublicKey, Value = ECPoint.Parse("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575", ECCurve.Secp256r1) }; - Assert.AreEqual("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575", pkParameter.ToStackItem().GetByteArray().ToHexString()); + Assert.AreEqual("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575", pkParameter.ToStackItem().GetSpan().ToHexString()); ContractParameter strParameter = new ContractParameter { Type = ContractParameterType.String, Value = "test😂👍" }; - Assert.AreEqual("test😂👍", Encoding.UTF8.GetString(strParameter.ToStackItem().GetByteArray())); + Assert.AreEqual("test😂👍", strParameter.ToStackItem().GetString()); ContractParameter interopParameter = new ContractParameter { Type = ContractParameterType.InteropInterface }; Assert.AreEqual(null, interopParameter.ToStackItem()); diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index 0477b8a595..10dd9ba015 100644 --- a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -121,7 +121,7 @@ public void TestToJson() json["isDefault"].ToString().Should().Be("false"); json["lock"].ToString().Should().Be("false"); json["key"].Should().BeNull(); - json["contract"]["script"].ToString().Should().Be("\"IQNgPziA63rqCtRQCJOSXkpC\\/qSKRO5viYoQs8fOBdKiZ6w=\""); + json["contract"]["script"].ToString().Should().Be(@"""IQNgPziA63rqCtRQCJOSXkpC/qSKRO5viYoQs8fOBdKiZ6w="""); json["extra"].Should().BeNull(); _account.Contract = null; diff --git a/neo.UnitTests/neo.UnitTests.csproj b/neo.UnitTests/neo.UnitTests.csproj index 2da1ba4968..da3694e024 100644 --- a/neo.UnitTests/neo.UnitTests.csproj +++ b/neo.UnitTests/neo.UnitTests.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.0 Neo.UnitTests Neo.UnitTests true @@ -16,12 +16,12 @@ - + - - - + + + diff --git a/neo/Cryptography/RIPEMD160Managed.cs b/neo/Cryptography/RIPEMD160Managed.cs index 22ffafb835..89a4de23c8 100644 --- a/neo/Cryptography/RIPEMD160Managed.cs +++ b/neo/Cryptography/RIPEMD160Managed.cs @@ -1,4 +1,3 @@ -#if !NET47 using System; using System.Runtime.InteropServices; using System.Security; @@ -1051,4 +1050,3 @@ private static void DWORDToLittleEndian(byte[] block, uint[] x, int digits) } } } -#endif diff --git a/neo/Cryptography/SCrypt.cs b/neo/Cryptography/SCrypt.cs index e40c2c83c5..4e6f3bc389 100644 --- a/neo/Cryptography/SCrypt.cs +++ b/neo/Cryptography/SCrypt.cs @@ -245,12 +245,6 @@ private unsafe static void SMix(byte* B, int r, int N, uint* V, uint* XY) } } -#if NET47 - public static byte[] DeriveKey(byte[] password, byte[] salt, int N, int r, int p, int derivedKeyLength) - { - return Replicon.Cryptography.SCrypt.SCrypt.DeriveKey(password, salt, (ulong)N, (uint)r, (uint)p, (uint)derivedKeyLength); - } -#else public unsafe static byte[] DeriveKey(byte[] password, byte[] salt, int N, int r, int p, int derivedKeyLength) { var Ba = new byte[128 * r * p + 63]; @@ -280,7 +274,6 @@ public unsafe static byte[] DeriveKey(byte[] password, byte[] salt, int N, int r return buf; } -#endif private static void PBKDF2_SHA256(HMACSHA256 mac, byte[] password, byte[] salt, int saltLength, long iterationCount, byte[] derivedKey, int derivedKeyLength) { diff --git a/neo/Helper.cs b/neo/Helper.cs index fa3b384a35..8fa7005f5e 100644 --- a/neo/Helper.cs +++ b/neo/Helper.cs @@ -186,6 +186,14 @@ public static string ToHexString(this IEnumerable value) return sb.ToString(); } + public static string ToHexString(this ReadOnlySpan value) + { + StringBuilder sb = new StringBuilder(); + foreach (byte b in value) + sb.AppendFormat("{0:x2}", b); + return sb.ToString(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] unsafe internal static int ToInt32(this byte[] value, int startIndex) { diff --git a/neo/IO/Data/LevelDB/Native.cs b/neo/IO/Data/LevelDB/Native.cs index 6a19ef4cfe..c83d3913f8 100644 --- a/neo/IO/Data/LevelDB/Native.cs +++ b/neo/IO/Data/LevelDB/Native.cs @@ -12,17 +12,6 @@ public enum CompressionType : byte public static class Native { -#if NET47 - static Native() - { - string platform = IntPtr.Size == 8 ? "x64" : "x86"; - LoadLibrary(Path.Combine(AppContext.BaseDirectory, platform, "libleveldb")); - } - - [DllImport("kernel32")] - private static extern IntPtr LoadLibrary(string dllToLoad); -#endif - #region Logger [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr leveldb_logger_create(IntPtr /* Action */ logger); diff --git a/neo/IO/Helper.cs b/neo/IO/Helper.cs index c47878579d..adba79fad6 100644 --- a/neo/IO/Helper.cs +++ b/neo/IO/Helper.cs @@ -21,6 +21,17 @@ public static T AsSerializable(this byte[] value, int start = 0) where T : IS } } + public static unsafe T AsSerializable(this ReadOnlySpan value) where T : ISerializable, new() + { + if (value.IsEmpty) throw new FormatException(); + fixed (byte* pointer = value) + { + using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, value.Length); + using BinaryReader reader = new BinaryReader(ms, Encoding.UTF8); + return reader.ReadSerializable(); + } + } + public static ISerializable AsSerializable(this byte[] value, Type type) { if (!typeof(ISerializable).GetTypeInfo().IsAssignableFrom(type)) @@ -43,6 +54,17 @@ public static T[] AsSerializableArray(this byte[] value, int max = 0x1000000) } } + public static unsafe T[] AsSerializableArray(this ReadOnlySpan value, int max = 0x1000000) where T : ISerializable, new() + { + if (value.IsEmpty) throw new FormatException(); + fixed (byte* pointer = value) + { + using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, value.Length); + using BinaryReader reader = new BinaryReader(ms, Encoding.UTF8); + return reader.ReadSerializableArray(max); + } + } + public static int GetVarSize(int value) { if (value < 0xFD) @@ -245,7 +267,7 @@ public static void WriteFixedString(this BinaryWriter writer, string value, int } } - public static void WriteVarBytes(this BinaryWriter writer, byte[] value) + public static void WriteVarBytes(this BinaryWriter writer, ReadOnlySpan value) { writer.WriteVarInt(value.Length); writer.Write(value); diff --git a/neo/Ledger/Blockchain.ApplicationExecuted.cs b/neo/Ledger/Blockchain.ApplicationExecuted.cs index 1bcc65563f..8134a1bd13 100644 --- a/neo/Ledger/Blockchain.ApplicationExecuted.cs +++ b/neo/Ledger/Blockchain.ApplicationExecuted.cs @@ -1,6 +1,7 @@ using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.VM; +using Neo.VM.Types; using System.Linq; namespace Neo.Ledger diff --git a/neo/Network/RPC/PolicyAPI.cs b/neo/Network/RPC/PolicyAPI.cs index 969c1c22f1..1b401cd7e5 100644 --- a/neo/Network/RPC/PolicyAPI.cs +++ b/neo/Network/RPC/PolicyAPI.cs @@ -51,7 +51,7 @@ public long GetFeePerByte() public UInt160[] GetBlockedAccounts() { var result = (VM.Types.Array)TestInvoke(scriptHash, "getBlockedAccounts").Stack.Single().ToStackItem(); - return result.Select(p => new UInt160(p.GetByteArray())).ToArray(); + return result.Select(p => new UInt160(p.GetSpan().ToArray())).ToArray(); } } } diff --git a/neo/SmartContract/ApplicationEngine.cs b/neo/SmartContract/ApplicationEngine.cs index 2eeeef52e0..41c17aebeb 100644 --- a/neo/SmartContract/ApplicationEngine.cs +++ b/neo/SmartContract/ApplicationEngine.cs @@ -2,6 +2,7 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; diff --git a/neo/SmartContract/ContainerPlaceholder.cs b/neo/SmartContract/ContainerPlaceholder.cs index a379ddd9d3..29fa0f6e6e 100644 --- a/neo/SmartContract/ContainerPlaceholder.cs +++ b/neo/SmartContract/ContainerPlaceholder.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM.Types; using System; namespace Neo.SmartContract @@ -10,8 +10,8 @@ internal class ContainerPlaceholder : StackItem public override bool Equals(StackItem other) => throw new NotSupportedException(); - public override bool GetBoolean() => throw new NotImplementedException(); + public override int GetHashCode() => throw new NotSupportedException(); - public override byte[] GetByteArray() => throw new NotSupportedException(); + public override bool ToBoolean() => throw new NotSupportedException(); } } diff --git a/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs b/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs index 2b75b1d46b..05cde03e54 100644 --- a/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs +++ b/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM.Types; namespace Neo.SmartContract.Enumerators { diff --git a/neo/SmartContract/Enumerators/IEnumerator.cs b/neo/SmartContract/Enumerators/IEnumerator.cs index 4d1f11fe65..948bad9371 100644 --- a/neo/SmartContract/Enumerators/IEnumerator.cs +++ b/neo/SmartContract/Enumerators/IEnumerator.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM.Types; using System; namespace Neo.SmartContract.Enumerators diff --git a/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs b/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs index d134eb884c..a7a7f040a4 100644 --- a/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs +++ b/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs @@ -1,5 +1,5 @@ using Neo.SmartContract.Iterators; -using Neo.VM; +using Neo.VM.Types; namespace Neo.SmartContract.Enumerators { diff --git a/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs b/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs index 15e06f2223..5e285938cf 100644 --- a/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs +++ b/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs @@ -1,5 +1,5 @@ using Neo.SmartContract.Iterators; -using Neo.VM; +using Neo.VM.Types; namespace Neo.SmartContract.Enumerators { diff --git a/neo/SmartContract/Helper.cs b/neo/SmartContract/Helper.cs index 4f7e320c37..f8c819dbeb 100644 --- a/neo/SmartContract/Helper.cs +++ b/neo/SmartContract/Helper.cs @@ -100,7 +100,7 @@ private static StackItem DeserializeStackItem(BinaryReader reader, uint maxArray { StackItem key = stack_temp.Pop(); StackItem value = stack_temp.Pop(); - map.Add(key, value); + map.Add((PrimitiveType)key, value); } item = map; break; @@ -199,17 +199,17 @@ private static void SerializeStackItem(StackItem item, BinaryWriter writer) item = unserialized.Pop(); switch (item) { - case ByteArray _: + case ByteArray bytes: writer.Write((byte)StackItemType.ByteArray); - writer.WriteVarBytes(item.GetByteArray()); + writer.WriteVarBytes(bytes.ToByteArray()); break; case VMBoolean _: writer.Write((byte)StackItemType.Boolean); - writer.Write(item.GetBoolean()); + writer.Write(item.ToBoolean()); break; - case Integer _: + case Integer integer: writer.Write((byte)StackItemType.Integer); - writer.WriteVarBytes(item.GetByteArray()); + writer.WriteVarBytes(integer.ToByteArray()); break; case InteropInterface _: throw new NotSupportedException(); @@ -285,7 +285,7 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, Snapshot snaps engine.LoadScript(verification); engine.LoadScript(verifiable.Witnesses[i].InvocationScript); if (engine.Execute().HasFlag(VMState.FAULT)) return false; - if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().GetBoolean()) return false; + if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().ToBoolean()) return false; } } return true; diff --git a/neo/SmartContract/IInteroperable.cs b/neo/SmartContract/IInteroperable.cs index 972147877c..047ce05168 100644 --- a/neo/SmartContract/IInteroperable.cs +++ b/neo/SmartContract/IInteroperable.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM.Types; namespace Neo.SmartContract { diff --git a/neo/SmartContract/InteropDescriptor.cs b/neo/SmartContract/InteropDescriptor.cs index 6db599ea64..f446d4fbfc 100644 --- a/neo/SmartContract/InteropDescriptor.cs +++ b/neo/SmartContract/InteropDescriptor.cs @@ -1,4 +1,5 @@ using Neo.VM; +using Neo.VM.Types; using System; namespace Neo.SmartContract diff --git a/neo/SmartContract/InteropService.NEO.cs b/neo/SmartContract/InteropService.NEO.cs index 212d1091b6..10798c053d 100644 --- a/neo/SmartContract/InteropService.NEO.cs +++ b/neo/SmartContract/InteropService.NEO.cs @@ -81,10 +81,10 @@ private static bool Crypto_ECDsaVerify(ApplicationEngine engine) { InteropInterface _interface => _interface.GetInterface().GetHashData(), Null _ => engine.ScriptContainer.GetHashData(), - _ => item0.GetByteArray() + _ => item0.GetSpan().ToArray() }; - byte[] pubkey = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - byte[] signature = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] pubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + byte[] signature = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); try { engine.CurrentContext.EvaluationStack.Push(Crypto.Default.VerifySignature(message, signature, pubkey)); @@ -103,14 +103,14 @@ private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) { InteropInterface _interface => _interface.GetInterface().GetHashData(), Null _ => engine.ScriptContainer.GetHashData(), - _ => item0.GetByteArray() + _ => item0.GetSpan().ToArray() }; int n; byte[][] pubkeys; StackItem item = engine.CurrentContext.EvaluationStack.Pop(); if (item is VMArray array1) { - pubkeys = array1.Select(p => p.GetByteArray()).ToArray(); + pubkeys = array1.Select(p => p.GetSpan().ToArray()).ToArray(); n = pubkeys.Length; if (n == 0) return false; } @@ -120,14 +120,14 @@ private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) if (n < 1 || n > engine.CurrentContext.EvaluationStack.Count) return false; pubkeys = new byte[n][]; for (int i = 0; i < n; i++) - pubkeys[i] = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + pubkeys[i] = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); } int m; byte[][] signatures; item = engine.CurrentContext.EvaluationStack.Pop(); if (item is VMArray array2) { - signatures = array2.Select(p => p.GetByteArray()).ToArray(); + signatures = array2.Select(p => p.GetSpan().ToArray()).ToArray(); m = signatures.Length; if (m == 0 || m > n) return false; } @@ -137,7 +137,7 @@ private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) if (m < 1 || m > n || m > engine.CurrentContext.EvaluationStack.Count) return false; signatures = new byte[m][]; for (int i = 0; i < m; i++) - signatures[i] = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + signatures[i] = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); } bool fSuccess = true; try @@ -161,7 +161,7 @@ private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) private static bool Account_IsStandard(ApplicationEngine engine) { - UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetByteArray()); + UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray()); ContractState contract = engine.Snapshot.Contracts.TryGet(hash); bool isStandard = contract is null || contract.Script.IsStandardContract(); engine.CurrentContext.EvaluationStack.Push(isStandard); @@ -170,7 +170,7 @@ private static bool Account_IsStandard(ApplicationEngine engine) private static bool Contract_Create(ApplicationEngine engine) { - byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); if (script.Length > 1024 * 1024) return false; var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString(); @@ -194,7 +194,7 @@ private static bool Contract_Create(ApplicationEngine engine) private static bool Contract_Update(ApplicationEngine engine) { - byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); if (script.Length > 1024 * 1024) return false; var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString(); if (manifest.Length > ContractManifest.MaxLength) return false; @@ -248,7 +248,7 @@ private static bool Storage_Find(ApplicationEngine engine) { StorageContext context = _interface.GetInterface(); if (!CheckStorageContext(engine, context)) return false; - byte[] prefix = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] prefix = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); byte[] prefix_key = StorageKey.CreateSearchPrefix(context.ScriptHash, prefix); StorageIterator iterator = engine.AddDisposable(new StorageIterator(engine.Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.Take(prefix.Length).SequenceEqual(prefix)).GetEnumerator())); engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator)); diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index be80366811..012876c96e 100644 --- a/neo/SmartContract/InteropService.cs +++ b/neo/SmartContract/InteropService.cs @@ -229,7 +229,7 @@ private static bool CheckWitness(ApplicationEngine engine, ECPoint pubkey) private static bool Runtime_CheckWitness(ApplicationEngine engine) { - byte[] hashOrPubkey = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] hashOrPubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); bool result; if (hashOrPubkey.Length == 20) result = CheckWitness(engine, new UInt160(hashOrPubkey)); @@ -251,7 +251,7 @@ private static bool Runtime_Notify(ApplicationEngine engine) private static bool Runtime_Log(ApplicationEngine engine) { - byte[] state = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] state = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); if (state.Length > MaxNotificationSize) return false; string message = Encoding.UTF8.GetString(state); engine.SendLog(engine.CurrentScriptHash, message); @@ -288,7 +288,7 @@ private static bool Runtime_GetNotifications(ApplicationEngine engine) IEnumerable notifications = engine.Notifications; if (!item.IsNull) // must filter by scriptHash { - var hash = new UInt160(item.GetByteArray()); + var hash = new UInt160(item.GetSpan().ToArray()); notifications = notifications.Where(p => p.ScriptHash == hash); } @@ -313,7 +313,7 @@ private static bool Runtime_Deserialize(ApplicationEngine engine) StackItem item; try { - item = engine.CurrentContext.EvaluationStack.Pop().GetByteArray().DeserializeStackItem(engine.MaxArraySize, engine.MaxItemSize); + item = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray().DeserializeStackItem(engine.MaxArraySize, engine.MaxItemSize); } catch (FormatException) { @@ -335,7 +335,7 @@ private static bool Blockchain_GetHeight(ApplicationEngine engine) private static bool Blockchain_GetBlock(ApplicationEngine engine) { - byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); UInt256 hash; if (data.Length <= 5) hash = Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); @@ -354,7 +354,7 @@ private static bool Blockchain_GetBlock(ApplicationEngine engine) private static bool Blockchain_GetTransaction(ApplicationEngine engine) { - byte[] hash = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); Transaction tx = engine.Snapshot.GetTransaction(new UInt256(hash)); if (tx == null) engine.CurrentContext.EvaluationStack.Push(StackItem.Null); @@ -365,7 +365,7 @@ private static bool Blockchain_GetTransaction(ApplicationEngine engine) private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine) { - byte[] hash = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); var tx = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); engine.CurrentContext.EvaluationStack.Push(tx != null ? new BigInteger(tx.BlockIndex) : BigInteger.MinusOne); return true; @@ -373,7 +373,7 @@ private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine) private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) { - byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); UInt256 hash; if (data.Length <= 5) hash = Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); @@ -403,7 +403,7 @@ private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) private static bool Blockchain_GetContract(ApplicationEngine engine) { - UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetByteArray()); + UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray()); ContractState contract = engine.Snapshot.Contracts.TryGet(hash); if (contract == null) engine.CurrentContext.EvaluationStack.Push(StackItem.Null); @@ -438,7 +438,7 @@ private static bool Storage_Get(ApplicationEngine engine) { StorageContext context = _interface.GetInterface(); if (!CheckStorageContext(engine, context)) return false; - byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); StorageItem item = engine.Snapshot.Storages.TryGet(new StorageKey { ScriptHash = context.ScriptHash, @@ -471,7 +471,7 @@ private static bool Contract_Call(ApplicationEngine engine) { StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop(); - ContractState contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractHash.GetByteArray())); + ContractState contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractHash.GetSpan().ToArray())); if (contract is null) return false; StackItem method = engine.CurrentContext.EvaluationStack.Pop(); @@ -542,8 +542,8 @@ private static bool Storage_Put(ApplicationEngine engine) if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)) return false; StorageContext context = _interface.GetInterface(); - byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - byte[] value = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + byte[] value = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); return PutEx(engine, context, key, value, StorageFlags.None); } @@ -552,8 +552,8 @@ private static bool Storage_PutEx(ApplicationEngine engine) if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)) return false; StorageContext context = _interface.GetInterface(); - byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); - byte[] value = engine.CurrentContext.EvaluationStack.Pop().GetByteArray(); + byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + byte[] value = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); StorageFlags flags = (StorageFlags)(byte)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger(); return PutEx(engine, context, key, value, flags); } @@ -568,7 +568,7 @@ private static bool Storage_Delete(ApplicationEngine engine) StorageKey key = new StorageKey { ScriptHash = context.ScriptHash, - Key = engine.CurrentContext.EvaluationStack.Pop().GetByteArray() + Key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray() }; if (engine.Snapshot.Storages.TryGet(key)?.IsConstant == true) return false; engine.Snapshot.Storages.Delete(key); diff --git a/neo/SmartContract/Iterators/ArrayWrapper.cs b/neo/SmartContract/Iterators/ArrayWrapper.cs index 883327bee0..84c248c679 100644 --- a/neo/SmartContract/Iterators/ArrayWrapper.cs +++ b/neo/SmartContract/Iterators/ArrayWrapper.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; @@ -18,7 +18,7 @@ public void Dispose() { } - public StackItem Key() + public PrimitiveType Key() { if (index < 0) throw new InvalidOperationException(); diff --git a/neo/SmartContract/Iterators/ConcatenatedIterator.cs b/neo/SmartContract/Iterators/ConcatenatedIterator.cs index 12f2a8cc8e..d8c556f140 100644 --- a/neo/SmartContract/Iterators/ConcatenatedIterator.cs +++ b/neo/SmartContract/Iterators/ConcatenatedIterator.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM.Types; namespace Neo.SmartContract.Iterators { @@ -13,7 +13,7 @@ public ConcatenatedIterator(IIterator first, IIterator second) this.second = second; } - public StackItem Key() => current.Key(); + public PrimitiveType Key() => current.Key(); public StackItem Value() => current.Value(); public bool Next() diff --git a/neo/SmartContract/Iterators/IIterator.cs b/neo/SmartContract/Iterators/IIterator.cs index 3438286615..66b3ef3bc6 100644 --- a/neo/SmartContract/Iterators/IIterator.cs +++ b/neo/SmartContract/Iterators/IIterator.cs @@ -1,10 +1,10 @@ using Neo.SmartContract.Enumerators; -using Neo.VM; +using Neo.VM.Types; namespace Neo.SmartContract.Iterators { internal interface IIterator : IEnumerator { - StackItem Key(); + PrimitiveType Key(); } } diff --git a/neo/SmartContract/Iterators/MapWrapper.cs b/neo/SmartContract/Iterators/MapWrapper.cs index 343b946982..783993a43c 100644 --- a/neo/SmartContract/Iterators/MapWrapper.cs +++ b/neo/SmartContract/Iterators/MapWrapper.cs @@ -1,13 +1,13 @@ -using Neo.VM; +using Neo.VM.Types; using System.Collections.Generic; namespace Neo.SmartContract.Iterators { internal class MapWrapper : IIterator { - private readonly IEnumerator> enumerator; + private readonly IEnumerator> enumerator; - public MapWrapper(IEnumerable> map) + public MapWrapper(IEnumerable> map) { this.enumerator = map.GetEnumerator(); } @@ -17,7 +17,7 @@ public void Dispose() enumerator.Dispose(); } - public StackItem Key() + public PrimitiveType Key() { return enumerator.Current.Key; } diff --git a/neo/SmartContract/Iterators/StorageIterator.cs b/neo/SmartContract/Iterators/StorageIterator.cs index 45c9d21ebd..f35d70d890 100644 --- a/neo/SmartContract/Iterators/StorageIterator.cs +++ b/neo/SmartContract/Iterators/StorageIterator.cs @@ -1,5 +1,5 @@ using Neo.Ledger; -using Neo.VM; +using Neo.VM.Types; using System.Collections.Generic; namespace Neo.SmartContract.Iterators @@ -18,7 +18,7 @@ public void Dispose() enumerator.Dispose(); } - public StackItem Key() + public PrimitiveType Key() { return enumerator.Current.Key.Key; } diff --git a/neo/SmartContract/JsonSerializer.cs b/neo/SmartContract/JsonSerializer.cs index 96c525e0b1..14edaa5caa 100644 --- a/neo/SmartContract/JsonSerializer.cs +++ b/neo/SmartContract/JsonSerializer.cs @@ -26,7 +26,7 @@ public static JObject Serialize(StackItem item) } case ByteArray buffer: { - return Convert.ToBase64String(buffer.GetByteArray()); + return Convert.ToBase64String(buffer.GetSpan()); } case Integer num: { @@ -37,7 +37,7 @@ public static JObject Serialize(StackItem item) } case VMBoolean boolean: { - return boolean.GetBoolean(); + return boolean.ToBoolean(); } case Map map: { diff --git a/neo/SmartContract/Native/ContractMethodMetadata.cs b/neo/SmartContract/Native/ContractMethodMetadata.cs index 9267bb7db9..87dfba4a40 100644 --- a/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -1,4 +1,4 @@ -using Neo.VM; +using Neo.VM.Types; using System; using VMArray = Neo.VM.Types.Array; diff --git a/neo/SmartContract/Native/NativeContract.cs b/neo/SmartContract/Native/NativeContract.cs index 546743fc49..b9d3c9ecba 100644 --- a/neo/SmartContract/Native/NativeContract.cs +++ b/neo/SmartContract/Native/NativeContract.cs @@ -5,6 +5,7 @@ using Neo.SmartContract.Manifest; using Neo.SmartContract.Native.Tokens; using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; using System.Linq; diff --git a/neo/SmartContract/Native/PolicyContract.cs b/neo/SmartContract/Native/PolicyContract.cs index 3ffe8199e5..797104924e 100644 --- a/neo/SmartContract/Native/PolicyContract.cs +++ b/neo/SmartContract/Native/PolicyContract.cs @@ -7,6 +7,7 @@ using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; using System.Linq; @@ -144,7 +145,7 @@ private StackItem SetFeePerByte(ApplicationEngine engine, VMArray args) private StackItem BlockAccount(ApplicationEngine engine, VMArray args) { if (!CheckValidators(engine)) return false; - UInt160 account = new UInt160(args[0].GetByteArray()); + UInt160 account = new UInt160(args[0].GetSpan().ToArray()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; HashSet accounts = new HashSet(storage.Value.AsSerializableArray()); @@ -158,7 +159,7 @@ private StackItem BlockAccount(ApplicationEngine engine, VMArray args) private StackItem UnblockAccount(ApplicationEngine engine, VMArray args) { if (!CheckValidators(engine)) return false; - UInt160 account = new UInt160(args[0].GetByteArray()); + UInt160 account = new UInt160(args[0].GetSpan().ToArray()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; HashSet accounts = new HashSet(storage.Value.AsSerializableArray()); diff --git a/neo/SmartContract/Native/Tokens/GasToken.cs b/neo/SmartContract/Native/Tokens/GasToken.cs index 7ef522dab7..4266494a4f 100644 --- a/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/neo/SmartContract/Native/Tokens/GasToken.cs @@ -5,6 +5,7 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.VM; +using Neo.VM.Types; using System; using System.Linq; using System.Numerics; diff --git a/neo/SmartContract/Native/Tokens/NeoToken.cs b/neo/SmartContract/Native/Tokens/NeoToken.cs index 7b28bd0923..ba059aff4d 100644 --- a/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -119,7 +119,7 @@ protected override bool OnPersist(ApplicationEngine engine) [ContractMethod(0_03000000, ContractParameterType.Integer, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "account", "end" }, SafeMethod = true)] private StackItem UnclaimedGas(ApplicationEngine engine, VMArray args) { - UInt160 account = new UInt160(args[0].GetByteArray()); + UInt160 account = new UInt160(args[0].GetSpan().ToArray()); uint end = (uint)args[1].GetBigInteger(); return UnclaimedGas(engine.Snapshot, account, end); } @@ -135,7 +135,7 @@ public BigInteger UnclaimedGas(Snapshot snapshot, UInt160 account, uint end) [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] private StackItem RegisterValidator(ApplicationEngine engine, VMArray args) { - ECPoint pubkey = args[0].GetByteArray().AsSerializable(); + ECPoint pubkey = args[0].GetSpan().AsSerializable(); return RegisterValidator(engine.Snapshot, pubkey); } @@ -153,8 +153,8 @@ private bool RegisterValidator(Snapshot snapshot, ECPoint pubkey) [ContractMethod(5_00000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" })] private StackItem Vote(ApplicationEngine engine, VMArray args) { - UInt160 account = new UInt160(args[0].GetByteArray()); - ECPoint[] pubkeys = ((VMArray)args[1]).Select(p => p.GetByteArray().AsSerializable()).ToArray(); + UInt160 account = new UInt160(args[0].GetSpan().ToArray()); + ECPoint[] pubkeys = ((VMArray)args[1]).Select(p => p.GetSpan().AsSerializable()).ToArray(); if (!InteropService.CheckWitness(engine, account)) return false; StorageKey key_account = CreateAccountKey(account); if (engine.Snapshot.Storages.TryGet(key_account) is null) return false; @@ -266,7 +266,7 @@ protected override void FromStruct(Struct @struct) { base.FromStruct(@struct); BalanceHeight = (uint)@struct[1].GetBigInteger(); - Votes = @struct[2].GetByteArray().AsSerializableArray(Blockchain.MaxValidators); + Votes = @struct[2].GetSpan().AsSerializableArray(Blockchain.MaxValidators); } protected override Struct ToStruct() diff --git a/neo/SmartContract/Native/Tokens/Nep5Token.cs b/neo/SmartContract/Native/Tokens/Nep5Token.cs index a95885a665..6d35d00410 100644 --- a/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -4,6 +4,7 @@ using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.VM; +using Neo.VM.Types; using System; using System.Collections.Generic; using System.Numerics; @@ -147,7 +148,7 @@ public virtual BigInteger TotalSupply(Snapshot snapshot) [ContractMethod(0_01000000, ContractParameterType.Integer, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" }, SafeMethod = true)] protected StackItem BalanceOf(ApplicationEngine engine, VMArray args) { - return BalanceOf(engine.Snapshot, new UInt160(args[0].GetByteArray())); + return BalanceOf(engine.Snapshot, new UInt160(args[0].GetSpan().ToArray())); } public virtual BigInteger BalanceOf(Snapshot snapshot, UInt160 account) @@ -161,8 +162,8 @@ public virtual BigInteger BalanceOf(Snapshot snapshot, UInt160 account) [ContractMethod(0_08000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" })] protected StackItem Transfer(ApplicationEngine engine, VMArray args) { - UInt160 from = new UInt160(args[0].GetByteArray()); - UInt160 to = new UInt160(args[1].GetByteArray()); + UInt160 from = new UInt160(args[0].GetSpan().ToArray()); + UInt160 to = new UInt160(args[1].GetSpan().ToArray()); BigInteger amount = args[2].GetBigInteger(); return Transfer(engine, from, to, amount); } diff --git a/neo/SmartContract/NotifyEventArgs.cs b/neo/SmartContract/NotifyEventArgs.cs index 8fab961408..e1d3fd8c1e 100644 --- a/neo/SmartContract/NotifyEventArgs.cs +++ b/neo/SmartContract/NotifyEventArgs.cs @@ -1,5 +1,5 @@ using Neo.Network.P2P.Payloads; -using Neo.VM; +using Neo.VM.Types; using System; namespace Neo.SmartContract diff --git a/neo/VM/Helper.cs b/neo/VM/Helper.cs index 5d2a2aa485..37a4d964c2 100644 --- a/neo/VM/Helper.cs +++ b/neo/VM/Helper.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Text; using VMArray = Neo.VM.Types.Array; using VMBoolean = Neo.VM.Types.Boolean; @@ -165,6 +166,32 @@ public static ScriptBuilder EmitSysCall(this ScriptBuilder sb, uint method, para return sb.EmitSysCall(method); } + public static BigInteger GetBigInteger(this StackItem item) + { + if (!(item is PrimitiveType primitive)) + throw new ArgumentException(); + return primitive.ToBigInteger(); + } + + public static int GetByteLength(this StackItem item) + { + if (!(item is PrimitiveType primitive)) + throw new ArgumentException(); + return primitive.GetByteLength(); + } + + public static ReadOnlySpan GetSpan(this StackItem item) + { + if (!(item is PrimitiveType primitive)) + throw new ArgumentException(); + return primitive.ToByteArray(); + } + + public static string GetString(this StackItem item) + { + return Encoding.UTF8.GetString(item.GetSpan()); + } + /// /// Generate scripts to call a specific method from a specific contract. /// @@ -222,14 +249,14 @@ private static ContractParameter ToParameter(StackItem item, List ReferenceEquals(p.Item2, parameter))?.Item1; if (stackItem is null) { - stackItem = new Map(((IList>)parameter.Value).ToDictionary(p => ToStackItem(p.Key, context), p => ToStackItem(p.Value, context))); + stackItem = new Map(((IList>)parameter.Value).ToDictionary(p => (PrimitiveType)ToStackItem(p.Key, context), p => ToStackItem(p.Value, context))); context.Add(new Tuple(stackItem, parameter)); } break; diff --git a/neo/neo.csproj b/neo/neo.csproj index 98824eb526..5556e8c660 100644 --- a/neo/neo.csproj +++ b/neo/neo.csproj @@ -5,7 +5,7 @@ Neo 3.0.0-preview1 The Neo Project - netstandard2.0;net47 + netstandard2.1 true Neo Neo @@ -17,25 +17,19 @@ Neo The Neo Project Neo - preview - + - - - - - - - - - + + + + From 2c80501d40a0fcff32576bf8d3acb84df9289000 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 25 Nov 2019 23:53:47 +0800 Subject: [PATCH 141/305] Move projects into folders (#1269) --- .travis.yml | 2 +- neo.sln | 20 ++++++++++++++----- {neo => src/neo}/BigDecimal.cs | 0 {neo => src/neo}/Consensus/ChangeView.cs | 0 .../neo}/Consensus/ChangeViewReason.cs | 0 {neo => src/neo}/Consensus/Commit.cs | 0 .../neo}/Consensus/ConsensusContext.cs | 0 .../neo}/Consensus/ConsensusMessage.cs | 0 .../neo}/Consensus/ConsensusMessageType.cs | 0 .../neo}/Consensus/ConsensusService.cs | 0 {neo => src/neo}/Consensus/PrepareRequest.cs | 0 {neo => src/neo}/Consensus/PrepareResponse.cs | 0 ...ecoveryMessage.ChangeViewPayloadCompact.cs | 0 .../RecoveryMessage.CommitPayloadCompact.cs | 0 ...coveryMessage.PreparationPayloadCompact.cs | 0 {neo => src/neo}/Consensus/RecoveryMessage.cs | 0 {neo => src/neo}/Consensus/RecoveryRequest.cs | 0 {neo => src/neo}/Cryptography/Base58.cs | 0 {neo => src/neo}/Cryptography/BloomFilter.cs | 0 {neo => src/neo}/Cryptography/Crypto.cs | 0 {neo => src/neo}/Cryptography/ECC/ECCurve.cs | 0 {neo => src/neo}/Cryptography/ECC/ECDsa.cs | 0 .../neo}/Cryptography/ECC/ECFieldElement.cs | 0 {neo => src/neo}/Cryptography/ECC/ECPoint.cs | 0 {neo => src/neo}/Cryptography/Helper.cs | 0 {neo => src/neo}/Cryptography/MerkleTree.cs | 0 .../neo}/Cryptography/MerkleTreeNode.cs | 0 {neo => src/neo}/Cryptography/Murmur3.cs | 0 .../neo}/Cryptography/RIPEMD160Managed.cs | 0 {neo => src/neo}/Cryptography/SCrypt.cs | 0 {neo => src/neo}/Helper.cs | 0 {neo => src/neo}/IO/Actors/Idle.cs | 0 {neo => src/neo}/IO/Actors/PriorityMailbox.cs | 0 .../neo}/IO/Actors/PriorityMessageQueue.cs | 0 {neo => src/neo}/IO/ByteArrayComparer.cs | 0 {neo => src/neo}/IO/Caching/Cache.cs | 0 {neo => src/neo}/IO/Caching/CloneCache.cs | 0 {neo => src/neo}/IO/Caching/CloneMetaCache.cs | 0 {neo => src/neo}/IO/Caching/DataCache.cs | 0 {neo => src/neo}/IO/Caching/FIFOCache.cs | 0 {neo => src/neo}/IO/Caching/FIFOSet.cs | 0 {neo => src/neo}/IO/Caching/MetaDataCache.cs | 0 .../neo}/IO/Caching/OrderedDictionary.cs | 0 .../neo}/IO/Caching/ReflectionCache.cs | 0 .../IO/Caching/ReflectionCacheAttribute.cs | 0 {neo => src/neo}/IO/Caching/RelayCache.cs | 0 {neo => src/neo}/IO/Caching/TrackState.cs | 0 {neo => src/neo}/IO/Data/LevelDB/DB.cs | 0 {neo => src/neo}/IO/Data/LevelDB/Helper.cs | 0 {neo => src/neo}/IO/Data/LevelDB/Iterator.cs | 0 .../neo}/IO/Data/LevelDB/LevelDBException.cs | 0 {neo => src/neo}/IO/Data/LevelDB/Native.cs | 0 {neo => src/neo}/IO/Data/LevelDB/Options.cs | 0 .../neo}/IO/Data/LevelDB/ReadOptions.cs | 0 {neo => src/neo}/IO/Data/LevelDB/Slice.cs | 0 .../neo}/IO/Data/LevelDB/SliceBuilder.cs | 0 {neo => src/neo}/IO/Data/LevelDB/Snapshot.cs | 0 .../neo}/IO/Data/LevelDB/WriteBatch.cs | 0 .../neo}/IO/Data/LevelDB/WriteOptions.cs | 0 {neo => src/neo}/IO/Helper.cs | 0 {neo => src/neo}/IO/ICloneable.cs | 0 {neo => src/neo}/IO/ISerializable.cs | 0 {neo => src/neo}/IO/Json/JArray.cs | 0 {neo => src/neo}/IO/Json/JBoolean.cs | 0 {neo => src/neo}/IO/Json/JNumber.cs | 0 {neo => src/neo}/IO/Json/JObject.cs | 0 {neo => src/neo}/IO/Json/JString.cs | 0 .../neo}/IO/Wrappers/SerializableWrapper.cs | 0 {neo => src/neo}/IO/Wrappers/UInt32Wrapper.cs | 0 .../Ledger/Blockchain.ApplicationExecuted.cs | 0 {neo => src/neo}/Ledger/Blockchain.cs | 0 {neo => src/neo}/Ledger/ContractState.cs | 0 {neo => src/neo}/Ledger/HashIndexState.cs | 0 {neo => src/neo}/Ledger/HeaderHashList.cs | 0 {neo => src/neo}/Ledger/MemoryPool.cs | 0 {neo => src/neo}/Ledger/PoolItem.cs | 0 {neo => src/neo}/Ledger/RelayResultReason.cs | 0 {neo => src/neo}/Ledger/SendersFeeMonitor.cs | 0 {neo => src/neo}/Ledger/StorageFlags.cs | 0 {neo => src/neo}/Ledger/StorageItem.cs | 0 {neo => src/neo}/Ledger/StorageKey.cs | 0 {neo => src/neo}/Ledger/TransactionState.cs | 0 {neo => src/neo}/Ledger/TrimmedBlock.cs | 0 {neo => src/neo}/NeoSystem.cs | 0 .../P2P/Capabilities/FullNodeCapability.cs | 0 .../P2P/Capabilities/NodeCapability.cs | 0 .../P2P/Capabilities/NodeCapabilityType.cs | 0 .../P2P/Capabilities/ServerCapability.cs | 0 .../neo}/Network/P2P/ChannelsConfig.cs | 0 {neo => src/neo}/Network/P2P/Connection.cs | 0 {neo => src/neo}/Network/P2P/Helper.cs | 0 {neo => src/neo}/Network/P2P/LocalNode.cs | 0 {neo => src/neo}/Network/P2P/Message.cs | 0 .../neo}/Network/P2P/MessageCommand.cs | 0 {neo => src/neo}/Network/P2P/MessageFlags.cs | 0 .../neo}/Network/P2P/Payloads/AddrPayload.cs | 0 .../neo}/Network/P2P/Payloads/Block.cs | 0 .../neo}/Network/P2P/Payloads/BlockBase.cs | 0 .../Network/P2P/Payloads/ConsensusData.cs | 0 .../Network/P2P/Payloads/ConsensusPayload.cs | 0 .../neo}/Network/P2P/Payloads/Cosigner.cs | 0 .../Network/P2P/Payloads/FilterAddPayload.cs | 0 .../Network/P2P/Payloads/FilterLoadPayload.cs | 0 .../P2P/Payloads/GetBlockDataPayload.cs | 0 .../Network/P2P/Payloads/GetBlocksPayload.cs | 0 .../neo}/Network/P2P/Payloads/Header.cs | 0 .../Network/P2P/Payloads/HeadersPayload.cs | 0 .../neo}/Network/P2P/Payloads/IInventory.cs | 0 .../neo}/Network/P2P/Payloads/IVerifiable.cs | 0 .../neo}/Network/P2P/Payloads/InvPayload.cs | 0 .../Network/P2P/Payloads/InventoryType.cs | 0 .../P2P/Payloads/MerkleBlockPayload.cs | 0 .../P2P/Payloads/NetworkAddressWithTime.cs | 0 .../neo}/Network/P2P/Payloads/PingPayload.cs | 0 .../neo}/Network/P2P/Payloads/Transaction.cs | 0 .../P2P/Payloads/TransactionAttribute.cs | 0 .../P2P/Payloads/TransactionAttributeUsage.cs | 0 .../Network/P2P/Payloads/VersionPayload.cs | 0 .../neo}/Network/P2P/Payloads/Witness.cs | 0 .../neo}/Network/P2P/Payloads/WitnessScope.cs | 0 {neo => src/neo}/Network/P2P/Peer.cs | 0 .../neo}/Network/P2P/ProtocolHandler.cs | 0 {neo => src/neo}/Network/P2P/RemoteNode.cs | 0 {neo => src/neo}/Network/P2P/TaskManager.cs | 0 {neo => src/neo}/Network/P2P/TaskSession.cs | 0 .../neo}/Network/RPC/ContractClient.cs | 0 {neo => src/neo}/Network/RPC/Helper.cs | 0 .../neo}/Network/RPC/Models/RpcBlock.cs | 0 .../neo}/Network/RPC/Models/RpcBlockHeader.cs | 0 .../Network/RPC/Models/RpcInvokeResult.cs | 0 .../Network/RPC/Models/RpcNep5Balances.cs | 0 .../Network/RPC/Models/RpcNep5TokenInfo.cs | 0 .../neo}/Network/RPC/Models/RpcPeers.cs | 0 .../neo}/Network/RPC/Models/RpcPlugin.cs | 0 .../neo}/Network/RPC/Models/RpcRawMemPool.cs | 0 .../neo}/Network/RPC/Models/RpcRequest.cs | 0 .../neo}/Network/RPC/Models/RpcResponse.cs | 0 .../neo}/Network/RPC/Models/RpcTransaction.cs | 0 .../RPC/Models/RpcValidateAddressResult.cs | 0 .../neo}/Network/RPC/Models/RpcValidator.cs | 0 .../neo}/Network/RPC/Models/RpcVersion.cs | 0 {neo => src/neo}/Network/RPC/Nep5API.cs | 0 {neo => src/neo}/Network/RPC/PolicyAPI.cs | 0 {neo => src/neo}/Network/RPC/RpcClient.cs | 0 {neo => src/neo}/Network/RPC/RpcException.cs | 0 {neo => src/neo}/Network/RPC/RpcServer.cs | 0 .../neo}/Network/RPC/TransactionManager.cs | 0 {neo => src/neo}/Network/RPC/WalletAPI.cs | 0 {neo => src/neo}/Network/UPnP.cs | 0 {neo => src/neo}/Persistence/CloneSnapshot.cs | 0 {neo => src/neo}/Persistence/Helper.cs | 0 {neo => src/neo}/Persistence/IPersistence.cs | 0 .../neo}/Persistence/LevelDB/DbCache.cs | 0 .../Persistence/LevelDB/DbMetaDataCache.cs | 0 .../neo}/Persistence/LevelDB/DbSnapshot.cs | 0 .../neo}/Persistence/LevelDB/LevelDBStore.cs | 0 .../neo}/Persistence/LevelDB/Prefixes.cs | 0 {neo => src/neo}/Persistence/Snapshot.cs | 0 {neo => src/neo}/Persistence/Store.cs | 0 {neo => src/neo}/Plugins/ILogPlugin.cs | 0 .../Plugins/IMemoryPoolTxObserverPlugin.cs | 0 {neo => src/neo}/Plugins/IP2PPlugin.cs | 0 .../neo}/Plugins/IPersistencePlugin.cs | 0 {neo => src/neo}/Plugins/IRpcPlugin.cs | 0 {neo => src/neo}/Plugins/LogLevel.cs | 0 .../neo}/Plugins/MemoryPoolTxRemovalReason.cs | 0 {neo => src/neo}/Plugins/Plugin.cs | 0 {neo => src/neo}/Properties/AssemblyInfo.cs | 0 {neo => src/neo}/ProtocolSettings.cs | 0 .../ApplicationEngine.OpCodePrices.cs | 0 .../neo}/SmartContract/ApplicationEngine.cs | 0 .../SmartContract/ContainerPlaceholder.cs | 0 {neo => src/neo}/SmartContract/Contract.cs | 0 .../neo}/SmartContract/ContractParameter.cs | 0 .../SmartContract/ContractParameterType.cs | 0 .../ContractParametersContext.cs | 0 .../Enumerators/ConcatenatedEnumerator.cs | 0 .../SmartContract/Enumerators/IEnumerator.cs | 0 .../Enumerators/IteratorKeysWrapper.cs | 0 .../Enumerators/IteratorValuesWrapper.cs | 0 .../SmartContract/ExecutionContextState.cs | 0 {neo => src/neo}/SmartContract/Helper.cs | 0 .../neo}/SmartContract/IInteroperable.cs | 0 .../neo}/SmartContract/InteropDescriptor.cs | 0 .../neo}/SmartContract/InteropService.NEO.cs | 0 .../neo}/SmartContract/InteropService.cs | 0 .../SmartContract/Iterators/ArrayWrapper.cs | 0 .../Iterators/ConcatenatedIterator.cs | 0 .../neo}/SmartContract/Iterators/IIterator.cs | 0 .../SmartContract/Iterators/MapWrapper.cs | 0 .../Iterators/StorageIterator.cs | 0 .../neo}/SmartContract/JsonSerializer.cs | 0 .../neo}/SmartContract/LogEventArgs.cs | 0 .../SmartContract/Manifest/ContractAbi.cs | 0 .../Manifest/ContractEventDescriptor.cs | 0 .../Manifest/ContractFeatures.cs | 0 .../SmartContract/Manifest/ContractGroup.cs | 0 .../Manifest/ContractManifest.cs | 0 .../Manifest/ContractMethodDescriptor.cs | 0 .../Manifest/ContractParameterDefinition.cs | 0 .../Manifest/ContractPermission.cs | 0 .../Manifest/ContractPermissionDescriptor.cs | 0 .../Manifest/WildCardContainer.cs | 0 .../Native/ContractMethodAttribute.cs | 0 .../Native/ContractMethodMetadata.cs | 0 .../SmartContract/Native/NativeContract.cs | 0 .../SmartContract/Native/PolicyContract.cs | 0 .../SmartContract/Native/Tokens/GasToken.cs | 0 .../SmartContract/Native/Tokens/NeoToken.cs | 0 .../Native/Tokens/Nep5AccountState.cs | 0 .../SmartContract/Native/Tokens/Nep5Token.cs | 0 {neo => src/neo}/SmartContract/NefFile.cs | 0 .../neo}/SmartContract/NotifyEventArgs.cs | 0 .../neo}/SmartContract/StackItemType.cs | 0 .../neo}/SmartContract/StorageContext.cs | 0 {neo => src/neo}/SmartContract/TriggerType.cs | 0 {neo => src/neo}/TimeProvider.cs | 0 {neo => src/neo}/UInt160.cs | 0 {neo => src/neo}/UInt256.cs | 0 {neo => src/neo}/UIntBase.cs | 0 {neo => src/neo}/Utility.cs | 0 {neo => src/neo}/VM/Helper.cs | 0 {neo => src/neo}/Wallets/AssetDescriptor.cs | 0 {neo => src/neo}/Wallets/Helper.cs | 0 {neo => src/neo}/Wallets/KeyPair.cs | 0 {neo => src/neo}/Wallets/NEP6/NEP6Account.cs | 0 {neo => src/neo}/Wallets/NEP6/NEP6Contract.cs | 0 {neo => src/neo}/Wallets/NEP6/NEP6Wallet.cs | 0 .../neo}/Wallets/NEP6/ScryptParameters.cs | 0 {neo => src/neo}/Wallets/NEP6/WalletLocker.cs | 0 {neo => src/neo}/Wallets/SQLite/Account.cs | 0 {neo => src/neo}/Wallets/SQLite/Address.cs | 0 {neo => src/neo}/Wallets/SQLite/Contract.cs | 0 {neo => src/neo}/Wallets/SQLite/Key.cs | 0 {neo => src/neo}/Wallets/SQLite/UserWallet.cs | 0 .../neo}/Wallets/SQLite/UserWalletAccount.cs | 0 .../Wallets/SQLite/VerificationContract.cs | 0 .../neo}/Wallets/SQLite/WalletDataContext.cs | 0 {neo => src/neo}/Wallets/TransferOutput.cs | 0 {neo => src/neo}/Wallets/Wallet.cs | 0 {neo => src/neo}/Wallets/WalletAccount.cs | 0 {neo => src/neo}/neo.csproj | 0 .../neo.UnitTests}/Consensus/UT_Consensus.cs | 0 .../Consensus/UT_ConsensusContext.cs | 0 .../Consensus/UT_ConsensusServiceMailbox.cs | 0 .../Cryptography/ECC/UT_ECDsa.cs | 0 .../Cryptography/ECC/UT_ECFieldElement.cs | 0 .../Cryptography/ECC/UT_ECPoint.cs | 0 .../neo.UnitTests}/Cryptography/UT_Base58.cs | 0 .../Cryptography/UT_BloomFilter.cs | 0 .../neo.UnitTests}/Cryptography/UT_Crypto.cs | 0 .../Cryptography/UT_Cryptography_Helper.cs | 0 .../Cryptography/UT_MerkleTree.cs | 0 .../Cryptography/UT_MerkleTreeNode.cs | 0 .../neo.UnitTests}/Cryptography/UT_Murmur3.cs | 0 .../neo.UnitTests}/Cryptography/UT_SCrypt.cs | 0 .../Extensions/NativeContractExtensions.cs | 0 .../Nep5NativeContractExtensions.cs | 0 .../neo.UnitTests}/IO/Caching/UT_Cache.cs | 0 .../IO/Caching/UT_CloneCache.cs | 0 .../IO/Caching/UT_CloneMetaCache.cs | 0 .../neo.UnitTests}/IO/Caching/UT_DataCache.cs | 0 .../neo.UnitTests}/IO/Caching/UT_FIFOSet.cs | 0 .../IO/Caching/UT_MetaDataCache.cs | 0 .../IO/Caching/UT_OrderedDictionary.cs | 0 .../IO/Caching/UT_ReflectionCache.cs | 0 .../IO/Caching/UT_RelayCache.cs | 0 .../IO/Data/LevelDb/UT_Slice.cs | 0 .../neo.UnitTests}/IO/Json/UT_JArray.cs | 0 .../neo.UnitTests}/IO/Json/UT_JBoolean.cs | 0 .../neo.UnitTests}/IO/Json/UT_JNumber.cs | 0 .../neo.UnitTests}/IO/Json/UT_JObject.cs | 0 .../neo.UnitTests}/IO/Json/UT_JString.cs | 0 .../neo.UnitTests}/IO/UT_ByteArrayComparer.cs | 0 .../neo.UnitTests}/IO/UT_IOHelper.cs | 0 .../IO/Wrappers/UT_SerializableWrapper.cs | 0 .../IO/Wrappers/UT_UInt32Wrapper.cs | 0 .../neo.UnitTests}/Ledger/UT_Blockchain.cs | 0 .../neo.UnitTests}/Ledger/UT_ContractState.cs | 0 .../Ledger/UT_HashIndexState.cs | 0 .../Ledger/UT_HeaderHashList.cs | 0 .../neo.UnitTests}/Ledger/UT_MemoryPool.cs | 0 .../neo.UnitTests}/Ledger/UT_PoolItem.cs | 0 .../Ledger/UT_SendersFeeMonitor.cs | 0 .../neo.UnitTests}/Ledger/UT_StorageItem.cs | 0 .../neo.UnitTests}/Ledger/UT_StorageKey.cs | 0 .../Ledger/UT_TransactionState.cs | 0 .../neo.UnitTests}/Ledger/UT_TrimmedBlock.cs | 0 .../Network/P2P/Payloads/UT_Block.cs | 0 .../Network/P2P/Payloads/UT_Cosigner.cs | 0 .../Network/P2P/Payloads/UT_Header.cs | 0 .../Network/P2P/Payloads/UT_Transaction.cs | 0 .../Network/P2P/Payloads/UT_Witness.cs | 0 .../neo.UnitTests}/Network/P2P/UT_Message.cs | 0 .../Network/P2P/UT_ProtocolHandler.cs | 0 .../Network/P2P/UT_ProtocolHandlerMailbox.cs | 0 .../Network/P2P/UT_RemoteNode.cs | 0 .../Network/P2P/UT_RemoteNodeMailbox.cs | 0 .../Network/P2P/UT_TaskManagerMailbox.cs | 0 .../Network/RPC/Models/UT_RpcBlock.cs | 0 .../Network/RPC/Models/UT_RpcBlockHeader.cs | 0 .../Network/RPC/Models/UT_RpcNep5Balance.cs | 0 .../Network/RPC/Models/UT_RpcNep5Balances.cs | 0 .../Network/RPC/Models/UT_RpcPeer.cs | 0 .../Network/RPC/Models/UT_RpcPeers.cs | 0 .../Network/RPC/Models/UT_RpcRawMemPool.cs | 0 .../Network/RPC/Models/UT_RpcRequest.cs | 0 .../Network/RPC/Models/UT_RpcResponse.cs | 0 .../Network/RPC/Models/UT_RpcVersion.cs | 0 .../Network/RPC/UT_ContractClient.cs | 0 .../neo.UnitTests}/Network/RPC/UT_Helper.cs | 0 .../neo.UnitTests}/Network/RPC/UT_Nep5API.cs | 0 .../Network/RPC/UT_PolicyAPI.cs | 0 .../Network/RPC/UT_RpcClient.cs | 0 .../Network/RPC/UT_RpcServer.cs | 0 .../Network/RPC/UT_TransactionManager.cs | 0 .../Network/RPC/UT_WalletAPI.cs | 0 .../neo.UnitTests}/Plugins/TestLogPlugin.cs | 0 .../neo.UnitTests}/Plugins/UT_Plugin.cs | 0 .../neo.UnitTests}/README.md | 0 .../Enumerators/UT_ConcatenatedEnumerator.cs | 0 .../Enumerators/UT_IteratorKeysWrapper.cs | 0 .../Enumerators/UT_IteratorValuesWrapper.cs | 0 .../Iterators/UT_ArrayWrapper.cs | 0 .../Iterators/UT_ConcatenatedIterator.cs | 0 .../SmartContract/Iterators/UT_MapWrapper.cs | 0 .../Iterators/UT_StorageIterator.cs | 0 .../Manifest/UT_ContractEventDescriptor.cs | 0 .../Manifest/UT_ContractGroup.cs | 0 .../Manifest/UT_ContractManifest.cs | 0 .../Manifest/UT_ContractPermission.cs | 0 .../UT_ContractPermissionDescriptor.cs | 0 .../Manifest/UT_WildCardContainer.cs | 0 .../Native/Tokens/UT_GasToken.cs | 0 .../Native/Tokens/UT_NeoToken.cs | 0 .../Native/Tokens/UT_Nep5Token.cs | 0 .../SmartContract/Native/UT_NativeContract.cs | 0 .../SmartContract/Native/UT_PolicyContract.cs | 0 .../SmartContract/UT_ApplicationEngine.cs | 0 .../SmartContract/UT_ContainerPlaceholder.cs | 0 .../SmartContract/UT_Contract.cs | 0 .../SmartContract/UT_ContractParameter.cs | 0 .../UT_ContractParameterContext.cs | 0 .../SmartContract/UT_InteropDescriptor.cs | 0 .../SmartContract/UT_InteropPrices.cs | 0 .../SmartContract/UT_InteropService.NEO.cs | 0 .../SmartContract/UT_InteropService.cs | 0 .../SmartContract/UT_JsonSerializer.cs | 0 .../SmartContract/UT_LogEventArgs.cs | 0 .../SmartContract/UT_NefFile.cs | 0 .../SmartContract/UT_NotifyEventArgs.cs | 0 .../SmartContract/UT_OpCodePrices.cs | 0 .../SmartContract/UT_SmartContractHelper.cs | 0 .../SmartContract/UT_StorageContext.cs | 0 .../SmartContract/UT_Syscalls.cs | 0 .../neo.UnitTests}/TestBlockchain.cs | 0 .../neo.UnitTests}/TestDataCache.cs | 0 .../neo.UnitTests}/TestMetaDataCache.cs | 0 .../neo.UnitTests}/TestUtils.cs | 0 .../neo.UnitTests}/TestVerifiable.cs | 0 .../neo.UnitTests}/TestWalletAccount.cs | 0 .../neo.UnitTests}/UT_BigDecimal.cs | 0 .../neo.UnitTests}/UT_Culture.cs | 0 .../neo.UnitTests}/UT_DataCache.cs | 0 .../neo.UnitTests}/UT_Helper.cs | 0 .../neo.UnitTests}/UT_NeoSystem.cs | 0 .../neo.UnitTests}/UT_ProtocolSettings.cs | 0 .../neo.UnitTests}/UT_UInt160.cs | 0 .../neo.UnitTests}/UT_UInt256.cs | 0 .../neo.UnitTests}/UT_UIntBase.cs | 0 .../neo.UnitTests}/UT_UIntBenchmarks.cs | 0 .../neo.UnitTests}/UT_Utility.cs | 0 .../neo.UnitTests}/VM/UT_Helper.cs | 0 .../Wallets/NEP6/UT_NEP6Account.cs | 0 .../Wallets/NEP6/UT_NEP6Contract.cs | 0 .../Wallets/NEP6/UT_NEP6Wallet.cs | 0 .../Wallets/NEP6/UT_ScryptParameters.cs | 0 .../Wallets/SQLite/UT_Account.cs | 0 .../Wallets/SQLite/UT_Address.cs | 0 .../Wallets/SQLite/UT_Contract.cs | 0 .../neo.UnitTests}/Wallets/SQLite/UT_Key.cs | 0 .../Wallets/SQLite/UT_UserWallet.cs | 0 .../Wallets/SQLite/UT_UserWalletAccount.cs | 0 .../Wallets/SQLite/UT_VerificationContract.cs | 0 .../Wallets/UT_AssetDescriptor.cs | 0 .../neo.UnitTests}/Wallets/UT_KeyPair.cs | 0 .../neo.UnitTests}/Wallets/UT_Wallet.cs | 0 .../Wallets/UT_WalletAccount.cs | 0 .../Wallets/UT_Wallets_Helper.cs | 0 .../neo.UnitTests}/neo.UnitTests.csproj | 2 +- .../neo.UnitTests}/protocol.json | 0 391 files changed, 17 insertions(+), 7 deletions(-) rename {neo => src/neo}/BigDecimal.cs (100%) rename {neo => src/neo}/Consensus/ChangeView.cs (100%) rename {neo => src/neo}/Consensus/ChangeViewReason.cs (100%) rename {neo => src/neo}/Consensus/Commit.cs (100%) rename {neo => src/neo}/Consensus/ConsensusContext.cs (100%) rename {neo => src/neo}/Consensus/ConsensusMessage.cs (100%) rename {neo => src/neo}/Consensus/ConsensusMessageType.cs (100%) rename {neo => src/neo}/Consensus/ConsensusService.cs (100%) rename {neo => src/neo}/Consensus/PrepareRequest.cs (100%) rename {neo => src/neo}/Consensus/PrepareResponse.cs (100%) rename {neo => src/neo}/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs (100%) rename {neo => src/neo}/Consensus/RecoveryMessage.CommitPayloadCompact.cs (100%) rename {neo => src/neo}/Consensus/RecoveryMessage.PreparationPayloadCompact.cs (100%) rename {neo => src/neo}/Consensus/RecoveryMessage.cs (100%) rename {neo => src/neo}/Consensus/RecoveryRequest.cs (100%) rename {neo => src/neo}/Cryptography/Base58.cs (100%) rename {neo => src/neo}/Cryptography/BloomFilter.cs (100%) rename {neo => src/neo}/Cryptography/Crypto.cs (100%) rename {neo => src/neo}/Cryptography/ECC/ECCurve.cs (100%) rename {neo => src/neo}/Cryptography/ECC/ECDsa.cs (100%) rename {neo => src/neo}/Cryptography/ECC/ECFieldElement.cs (100%) rename {neo => src/neo}/Cryptography/ECC/ECPoint.cs (100%) rename {neo => src/neo}/Cryptography/Helper.cs (100%) rename {neo => src/neo}/Cryptography/MerkleTree.cs (100%) rename {neo => src/neo}/Cryptography/MerkleTreeNode.cs (100%) rename {neo => src/neo}/Cryptography/Murmur3.cs (100%) rename {neo => src/neo}/Cryptography/RIPEMD160Managed.cs (100%) rename {neo => src/neo}/Cryptography/SCrypt.cs (100%) rename {neo => src/neo}/Helper.cs (100%) rename {neo => src/neo}/IO/Actors/Idle.cs (100%) rename {neo => src/neo}/IO/Actors/PriorityMailbox.cs (100%) rename {neo => src/neo}/IO/Actors/PriorityMessageQueue.cs (100%) rename {neo => src/neo}/IO/ByteArrayComparer.cs (100%) rename {neo => src/neo}/IO/Caching/Cache.cs (100%) rename {neo => src/neo}/IO/Caching/CloneCache.cs (100%) rename {neo => src/neo}/IO/Caching/CloneMetaCache.cs (100%) rename {neo => src/neo}/IO/Caching/DataCache.cs (100%) rename {neo => src/neo}/IO/Caching/FIFOCache.cs (100%) rename {neo => src/neo}/IO/Caching/FIFOSet.cs (100%) rename {neo => src/neo}/IO/Caching/MetaDataCache.cs (100%) rename {neo => src/neo}/IO/Caching/OrderedDictionary.cs (100%) rename {neo => src/neo}/IO/Caching/ReflectionCache.cs (100%) rename {neo => src/neo}/IO/Caching/ReflectionCacheAttribute.cs (100%) rename {neo => src/neo}/IO/Caching/RelayCache.cs (100%) rename {neo => src/neo}/IO/Caching/TrackState.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/DB.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/Helper.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/Iterator.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/LevelDBException.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/Native.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/Options.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/ReadOptions.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/Slice.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/SliceBuilder.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/Snapshot.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/WriteBatch.cs (100%) rename {neo => src/neo}/IO/Data/LevelDB/WriteOptions.cs (100%) rename {neo => src/neo}/IO/Helper.cs (100%) rename {neo => src/neo}/IO/ICloneable.cs (100%) rename {neo => src/neo}/IO/ISerializable.cs (100%) rename {neo => src/neo}/IO/Json/JArray.cs (100%) rename {neo => src/neo}/IO/Json/JBoolean.cs (100%) rename {neo => src/neo}/IO/Json/JNumber.cs (100%) rename {neo => src/neo}/IO/Json/JObject.cs (100%) rename {neo => src/neo}/IO/Json/JString.cs (100%) rename {neo => src/neo}/IO/Wrappers/SerializableWrapper.cs (100%) rename {neo => src/neo}/IO/Wrappers/UInt32Wrapper.cs (100%) rename {neo => src/neo}/Ledger/Blockchain.ApplicationExecuted.cs (100%) rename {neo => src/neo}/Ledger/Blockchain.cs (100%) rename {neo => src/neo}/Ledger/ContractState.cs (100%) rename {neo => src/neo}/Ledger/HashIndexState.cs (100%) rename {neo => src/neo}/Ledger/HeaderHashList.cs (100%) rename {neo => src/neo}/Ledger/MemoryPool.cs (100%) rename {neo => src/neo}/Ledger/PoolItem.cs (100%) rename {neo => src/neo}/Ledger/RelayResultReason.cs (100%) rename {neo => src/neo}/Ledger/SendersFeeMonitor.cs (100%) rename {neo => src/neo}/Ledger/StorageFlags.cs (100%) rename {neo => src/neo}/Ledger/StorageItem.cs (100%) rename {neo => src/neo}/Ledger/StorageKey.cs (100%) rename {neo => src/neo}/Ledger/TransactionState.cs (100%) rename {neo => src/neo}/Ledger/TrimmedBlock.cs (100%) rename {neo => src/neo}/NeoSystem.cs (100%) rename {neo => src/neo}/Network/P2P/Capabilities/FullNodeCapability.cs (100%) rename {neo => src/neo}/Network/P2P/Capabilities/NodeCapability.cs (100%) rename {neo => src/neo}/Network/P2P/Capabilities/NodeCapabilityType.cs (100%) rename {neo => src/neo}/Network/P2P/Capabilities/ServerCapability.cs (100%) rename {neo => src/neo}/Network/P2P/ChannelsConfig.cs (100%) rename {neo => src/neo}/Network/P2P/Connection.cs (100%) rename {neo => src/neo}/Network/P2P/Helper.cs (100%) rename {neo => src/neo}/Network/P2P/LocalNode.cs (100%) rename {neo => src/neo}/Network/P2P/Message.cs (100%) rename {neo => src/neo}/Network/P2P/MessageCommand.cs (100%) rename {neo => src/neo}/Network/P2P/MessageFlags.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/AddrPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/Block.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/BlockBase.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/ConsensusData.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/ConsensusPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/Cosigner.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/FilterAddPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/FilterLoadPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/GetBlockDataPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/GetBlocksPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/Header.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/HeadersPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/IInventory.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/IVerifiable.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/InvPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/InventoryType.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/MerkleBlockPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/NetworkAddressWithTime.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/PingPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/Transaction.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/TransactionAttribute.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/TransactionAttributeUsage.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/VersionPayload.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/Witness.cs (100%) rename {neo => src/neo}/Network/P2P/Payloads/WitnessScope.cs (100%) rename {neo => src/neo}/Network/P2P/Peer.cs (100%) rename {neo => src/neo}/Network/P2P/ProtocolHandler.cs (100%) rename {neo => src/neo}/Network/P2P/RemoteNode.cs (100%) rename {neo => src/neo}/Network/P2P/TaskManager.cs (100%) rename {neo => src/neo}/Network/P2P/TaskSession.cs (100%) rename {neo => src/neo}/Network/RPC/ContractClient.cs (100%) rename {neo => src/neo}/Network/RPC/Helper.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcBlock.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcBlockHeader.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcInvokeResult.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcNep5Balances.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcNep5TokenInfo.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcPeers.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcPlugin.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcRawMemPool.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcRequest.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcResponse.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcTransaction.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcValidateAddressResult.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcValidator.cs (100%) rename {neo => src/neo}/Network/RPC/Models/RpcVersion.cs (100%) rename {neo => src/neo}/Network/RPC/Nep5API.cs (100%) rename {neo => src/neo}/Network/RPC/PolicyAPI.cs (100%) rename {neo => src/neo}/Network/RPC/RpcClient.cs (100%) rename {neo => src/neo}/Network/RPC/RpcException.cs (100%) rename {neo => src/neo}/Network/RPC/RpcServer.cs (100%) rename {neo => src/neo}/Network/RPC/TransactionManager.cs (100%) rename {neo => src/neo}/Network/RPC/WalletAPI.cs (100%) rename {neo => src/neo}/Network/UPnP.cs (100%) rename {neo => src/neo}/Persistence/CloneSnapshot.cs (100%) rename {neo => src/neo}/Persistence/Helper.cs (100%) rename {neo => src/neo}/Persistence/IPersistence.cs (100%) rename {neo => src/neo}/Persistence/LevelDB/DbCache.cs (100%) rename {neo => src/neo}/Persistence/LevelDB/DbMetaDataCache.cs (100%) rename {neo => src/neo}/Persistence/LevelDB/DbSnapshot.cs (100%) rename {neo => src/neo}/Persistence/LevelDB/LevelDBStore.cs (100%) rename {neo => src/neo}/Persistence/LevelDB/Prefixes.cs (100%) rename {neo => src/neo}/Persistence/Snapshot.cs (100%) rename {neo => src/neo}/Persistence/Store.cs (100%) rename {neo => src/neo}/Plugins/ILogPlugin.cs (100%) rename {neo => src/neo}/Plugins/IMemoryPoolTxObserverPlugin.cs (100%) rename {neo => src/neo}/Plugins/IP2PPlugin.cs (100%) rename {neo => src/neo}/Plugins/IPersistencePlugin.cs (100%) rename {neo => src/neo}/Plugins/IRpcPlugin.cs (100%) rename {neo => src/neo}/Plugins/LogLevel.cs (100%) rename {neo => src/neo}/Plugins/MemoryPoolTxRemovalReason.cs (100%) rename {neo => src/neo}/Plugins/Plugin.cs (100%) rename {neo => src/neo}/Properties/AssemblyInfo.cs (100%) rename {neo => src/neo}/ProtocolSettings.cs (100%) rename {neo => src/neo}/SmartContract/ApplicationEngine.OpCodePrices.cs (100%) rename {neo => src/neo}/SmartContract/ApplicationEngine.cs (100%) rename {neo => src/neo}/SmartContract/ContainerPlaceholder.cs (100%) rename {neo => src/neo}/SmartContract/Contract.cs (100%) rename {neo => src/neo}/SmartContract/ContractParameter.cs (100%) rename {neo => src/neo}/SmartContract/ContractParameterType.cs (100%) rename {neo => src/neo}/SmartContract/ContractParametersContext.cs (100%) rename {neo => src/neo}/SmartContract/Enumerators/ConcatenatedEnumerator.cs (100%) rename {neo => src/neo}/SmartContract/Enumerators/IEnumerator.cs (100%) rename {neo => src/neo}/SmartContract/Enumerators/IteratorKeysWrapper.cs (100%) rename {neo => src/neo}/SmartContract/Enumerators/IteratorValuesWrapper.cs (100%) rename {neo => src/neo}/SmartContract/ExecutionContextState.cs (100%) rename {neo => src/neo}/SmartContract/Helper.cs (100%) rename {neo => src/neo}/SmartContract/IInteroperable.cs (100%) rename {neo => src/neo}/SmartContract/InteropDescriptor.cs (100%) rename {neo => src/neo}/SmartContract/InteropService.NEO.cs (100%) rename {neo => src/neo}/SmartContract/InteropService.cs (100%) rename {neo => src/neo}/SmartContract/Iterators/ArrayWrapper.cs (100%) rename {neo => src/neo}/SmartContract/Iterators/ConcatenatedIterator.cs (100%) rename {neo => src/neo}/SmartContract/Iterators/IIterator.cs (100%) rename {neo => src/neo}/SmartContract/Iterators/MapWrapper.cs (100%) rename {neo => src/neo}/SmartContract/Iterators/StorageIterator.cs (100%) rename {neo => src/neo}/SmartContract/JsonSerializer.cs (100%) rename {neo => src/neo}/SmartContract/LogEventArgs.cs (100%) rename {neo => src/neo}/SmartContract/Manifest/ContractAbi.cs (100%) rename {neo => src/neo}/SmartContract/Manifest/ContractEventDescriptor.cs (100%) rename {neo => src/neo}/SmartContract/Manifest/ContractFeatures.cs (100%) rename {neo => src/neo}/SmartContract/Manifest/ContractGroup.cs (100%) rename {neo => src/neo}/SmartContract/Manifest/ContractManifest.cs (100%) rename {neo => src/neo}/SmartContract/Manifest/ContractMethodDescriptor.cs (100%) rename {neo => src/neo}/SmartContract/Manifest/ContractParameterDefinition.cs (100%) rename {neo => src/neo}/SmartContract/Manifest/ContractPermission.cs (100%) rename {neo => src/neo}/SmartContract/Manifest/ContractPermissionDescriptor.cs (100%) rename {neo => src/neo}/SmartContract/Manifest/WildCardContainer.cs (100%) rename {neo => src/neo}/SmartContract/Native/ContractMethodAttribute.cs (100%) rename {neo => src/neo}/SmartContract/Native/ContractMethodMetadata.cs (100%) rename {neo => src/neo}/SmartContract/Native/NativeContract.cs (100%) rename {neo => src/neo}/SmartContract/Native/PolicyContract.cs (100%) rename {neo => src/neo}/SmartContract/Native/Tokens/GasToken.cs (100%) rename {neo => src/neo}/SmartContract/Native/Tokens/NeoToken.cs (100%) rename {neo => src/neo}/SmartContract/Native/Tokens/Nep5AccountState.cs (100%) rename {neo => src/neo}/SmartContract/Native/Tokens/Nep5Token.cs (100%) rename {neo => src/neo}/SmartContract/NefFile.cs (100%) rename {neo => src/neo}/SmartContract/NotifyEventArgs.cs (100%) rename {neo => src/neo}/SmartContract/StackItemType.cs (100%) rename {neo => src/neo}/SmartContract/StorageContext.cs (100%) rename {neo => src/neo}/SmartContract/TriggerType.cs (100%) rename {neo => src/neo}/TimeProvider.cs (100%) rename {neo => src/neo}/UInt160.cs (100%) rename {neo => src/neo}/UInt256.cs (100%) rename {neo => src/neo}/UIntBase.cs (100%) rename {neo => src/neo}/Utility.cs (100%) rename {neo => src/neo}/VM/Helper.cs (100%) rename {neo => src/neo}/Wallets/AssetDescriptor.cs (100%) rename {neo => src/neo}/Wallets/Helper.cs (100%) rename {neo => src/neo}/Wallets/KeyPair.cs (100%) rename {neo => src/neo}/Wallets/NEP6/NEP6Account.cs (100%) rename {neo => src/neo}/Wallets/NEP6/NEP6Contract.cs (100%) rename {neo => src/neo}/Wallets/NEP6/NEP6Wallet.cs (100%) rename {neo => src/neo}/Wallets/NEP6/ScryptParameters.cs (100%) rename {neo => src/neo}/Wallets/NEP6/WalletLocker.cs (100%) rename {neo => src/neo}/Wallets/SQLite/Account.cs (100%) rename {neo => src/neo}/Wallets/SQLite/Address.cs (100%) rename {neo => src/neo}/Wallets/SQLite/Contract.cs (100%) rename {neo => src/neo}/Wallets/SQLite/Key.cs (100%) rename {neo => src/neo}/Wallets/SQLite/UserWallet.cs (100%) rename {neo => src/neo}/Wallets/SQLite/UserWalletAccount.cs (100%) rename {neo => src/neo}/Wallets/SQLite/VerificationContract.cs (100%) rename {neo => src/neo}/Wallets/SQLite/WalletDataContext.cs (100%) rename {neo => src/neo}/Wallets/TransferOutput.cs (100%) rename {neo => src/neo}/Wallets/Wallet.cs (100%) rename {neo => src/neo}/Wallets/WalletAccount.cs (100%) rename {neo => src/neo}/neo.csproj (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Consensus/UT_Consensus.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Consensus/UT_ConsensusContext.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Consensus/UT_ConsensusServiceMailbox.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/ECC/UT_ECDsa.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/ECC/UT_ECFieldElement.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/ECC/UT_ECPoint.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/UT_Base58.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/UT_BloomFilter.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/UT_Crypto.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/UT_Cryptography_Helper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/UT_MerkleTree.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/UT_MerkleTreeNode.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/UT_Murmur3.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Cryptography/UT_SCrypt.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Extensions/NativeContractExtensions.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Extensions/Nep5NativeContractExtensions.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Caching/UT_Cache.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Caching/UT_CloneCache.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Caching/UT_CloneMetaCache.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Caching/UT_DataCache.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Caching/UT_FIFOSet.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Caching/UT_MetaDataCache.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Caching/UT_OrderedDictionary.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Caching/UT_ReflectionCache.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Caching/UT_RelayCache.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Data/LevelDb/UT_Slice.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Json/UT_JArray.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Json/UT_JBoolean.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Json/UT_JNumber.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Json/UT_JObject.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Json/UT_JString.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/UT_ByteArrayComparer.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/UT_IOHelper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Wrappers/UT_SerializableWrapper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/IO/Wrappers/UT_UInt32Wrapper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_Blockchain.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_ContractState.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_HashIndexState.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_HeaderHashList.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_MemoryPool.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_PoolItem.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_SendersFeeMonitor.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_StorageItem.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_StorageKey.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_TransactionState.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Ledger/UT_TrimmedBlock.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/Payloads/UT_Block.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/Payloads/UT_Cosigner.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/Payloads/UT_Header.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/Payloads/UT_Transaction.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/Payloads/UT_Witness.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/UT_Message.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/UT_ProtocolHandler.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/UT_ProtocolHandlerMailbox.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/UT_RemoteNode.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/UT_RemoteNodeMailbox.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/P2P/UT_TaskManagerMailbox.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/Models/UT_RpcBlock.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/Models/UT_RpcBlockHeader.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/Models/UT_RpcNep5Balance.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/Models/UT_RpcNep5Balances.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/Models/UT_RpcPeer.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/Models/UT_RpcPeers.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/Models/UT_RpcRawMemPool.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/Models/UT_RpcRequest.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/Models/UT_RpcResponse.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/Models/UT_RpcVersion.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/UT_ContractClient.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/UT_Helper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/UT_Nep5API.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/UT_PolicyAPI.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/UT_RpcClient.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/UT_RpcServer.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/UT_TransactionManager.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Network/RPC/UT_WalletAPI.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Plugins/TestLogPlugin.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Plugins/UT_Plugin.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/README.md (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Iterators/UT_ArrayWrapper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Iterators/UT_ConcatenatedIterator.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Iterators/UT_MapWrapper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Iterators/UT_StorageIterator.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Manifest/UT_ContractEventDescriptor.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Manifest/UT_ContractGroup.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Manifest/UT_ContractManifest.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Manifest/UT_ContractPermission.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Manifest/UT_WildCardContainer.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Native/Tokens/UT_GasToken.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Native/Tokens/UT_NeoToken.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Native/Tokens/UT_Nep5Token.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Native/UT_NativeContract.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/Native/UT_PolicyContract.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_ApplicationEngine.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_ContainerPlaceholder.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_Contract.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_ContractParameter.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_ContractParameterContext.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_InteropDescriptor.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_InteropPrices.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_InteropService.NEO.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_InteropService.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_JsonSerializer.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_LogEventArgs.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_NefFile.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_NotifyEventArgs.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_OpCodePrices.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_SmartContractHelper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_StorageContext.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/SmartContract/UT_Syscalls.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/TestBlockchain.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/TestDataCache.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/TestMetaDataCache.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/TestUtils.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/TestVerifiable.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/TestWalletAccount.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_BigDecimal.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_Culture.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_DataCache.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_Helper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_NeoSystem.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_ProtocolSettings.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_UInt160.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_UInt256.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_UIntBase.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_UIntBenchmarks.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/UT_Utility.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/VM/UT_Helper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/NEP6/UT_NEP6Account.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/NEP6/UT_NEP6Contract.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/NEP6/UT_NEP6Wallet.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/NEP6/UT_ScryptParameters.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/SQLite/UT_Account.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/SQLite/UT_Address.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/SQLite/UT_Contract.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/SQLite/UT_Key.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/SQLite/UT_UserWallet.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/SQLite/UT_UserWalletAccount.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/SQLite/UT_VerificationContract.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/UT_AssetDescriptor.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/UT_KeyPair.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/UT_Wallet.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/UT_WalletAccount.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/Wallets/UT_Wallets_Helper.cs (100%) rename {neo.UnitTests => tests/neo.UnitTests}/neo.UnitTests.csproj (94%) rename {neo.UnitTests => tests/neo.UnitTests}/protocol.json (100%) diff --git a/.travis.yml b/.travis.yml index 6691c787d3..f13cecc403 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ install: script: | echo "Checking format..." dotnet format --check --dry-run -w . -v diagnostic # check C# formatting for neo.sln - cd neo.UnitTests + cd tests/neo.UnitTests if [[ "$TEST_SUITE" == "cultures" ]]; then dotnet test -v m --filter FullyQualifiedName=Neo.UnitTests.UT_Culture.All_Tests_Cultures else diff --git a/neo.sln b/neo.sln index fd72762fc2..79e13f0185 100644 --- a/neo.sln +++ b/neo.sln @@ -1,11 +1,14 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.15 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.87 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "neo", "neo\neo.csproj", "{36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "neo", "src\neo\neo.csproj", "{36447A9B-0311-4D4D-A3D5-AECBE9C15BBC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "neo.UnitTests", "neo.UnitTests\neo.UnitTests.csproj", "{5B783B30-B422-4C2F-AC22-187A8D1993F4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "neo.UnitTests", "tests\neo.UnitTests\neo.UnitTests.csproj", "{5B783B30-B422-4C2F-AC22-187A8D1993F4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B5339DF7-5D1D-43BA-B332-74B825E1770E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{EDE05FA8-8E73-4924-BC63-DD117127EEE1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -25,4 +28,11 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {36447A9B-0311-4D4D-A3D5-AECBE9C15BBC} = {B5339DF7-5D1D-43BA-B332-74B825E1770E} + {5B783B30-B422-4C2F-AC22-187A8D1993F4} = {EDE05FA8-8E73-4924-BC63-DD117127EEE1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BCBA19D9-F868-4C6D-8061-A2B91E06E3EC} + EndGlobalSection EndGlobal diff --git a/neo/BigDecimal.cs b/src/neo/BigDecimal.cs similarity index 100% rename from neo/BigDecimal.cs rename to src/neo/BigDecimal.cs diff --git a/neo/Consensus/ChangeView.cs b/src/neo/Consensus/ChangeView.cs similarity index 100% rename from neo/Consensus/ChangeView.cs rename to src/neo/Consensus/ChangeView.cs diff --git a/neo/Consensus/ChangeViewReason.cs b/src/neo/Consensus/ChangeViewReason.cs similarity index 100% rename from neo/Consensus/ChangeViewReason.cs rename to src/neo/Consensus/ChangeViewReason.cs diff --git a/neo/Consensus/Commit.cs b/src/neo/Consensus/Commit.cs similarity index 100% rename from neo/Consensus/Commit.cs rename to src/neo/Consensus/Commit.cs diff --git a/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs similarity index 100% rename from neo/Consensus/ConsensusContext.cs rename to src/neo/Consensus/ConsensusContext.cs diff --git a/neo/Consensus/ConsensusMessage.cs b/src/neo/Consensus/ConsensusMessage.cs similarity index 100% rename from neo/Consensus/ConsensusMessage.cs rename to src/neo/Consensus/ConsensusMessage.cs diff --git a/neo/Consensus/ConsensusMessageType.cs b/src/neo/Consensus/ConsensusMessageType.cs similarity index 100% rename from neo/Consensus/ConsensusMessageType.cs rename to src/neo/Consensus/ConsensusMessageType.cs diff --git a/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs similarity index 100% rename from neo/Consensus/ConsensusService.cs rename to src/neo/Consensus/ConsensusService.cs diff --git a/neo/Consensus/PrepareRequest.cs b/src/neo/Consensus/PrepareRequest.cs similarity index 100% rename from neo/Consensus/PrepareRequest.cs rename to src/neo/Consensus/PrepareRequest.cs diff --git a/neo/Consensus/PrepareResponse.cs b/src/neo/Consensus/PrepareResponse.cs similarity index 100% rename from neo/Consensus/PrepareResponse.cs rename to src/neo/Consensus/PrepareResponse.cs diff --git a/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs similarity index 100% rename from neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs rename to src/neo/Consensus/RecoveryMessage.ChangeViewPayloadCompact.cs diff --git a/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs similarity index 100% rename from neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs rename to src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs diff --git a/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs similarity index 100% rename from neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs rename to src/neo/Consensus/RecoveryMessage.PreparationPayloadCompact.cs diff --git a/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs similarity index 100% rename from neo/Consensus/RecoveryMessage.cs rename to src/neo/Consensus/RecoveryMessage.cs diff --git a/neo/Consensus/RecoveryRequest.cs b/src/neo/Consensus/RecoveryRequest.cs similarity index 100% rename from neo/Consensus/RecoveryRequest.cs rename to src/neo/Consensus/RecoveryRequest.cs diff --git a/neo/Cryptography/Base58.cs b/src/neo/Cryptography/Base58.cs similarity index 100% rename from neo/Cryptography/Base58.cs rename to src/neo/Cryptography/Base58.cs diff --git a/neo/Cryptography/BloomFilter.cs b/src/neo/Cryptography/BloomFilter.cs similarity index 100% rename from neo/Cryptography/BloomFilter.cs rename to src/neo/Cryptography/BloomFilter.cs diff --git a/neo/Cryptography/Crypto.cs b/src/neo/Cryptography/Crypto.cs similarity index 100% rename from neo/Cryptography/Crypto.cs rename to src/neo/Cryptography/Crypto.cs diff --git a/neo/Cryptography/ECC/ECCurve.cs b/src/neo/Cryptography/ECC/ECCurve.cs similarity index 100% rename from neo/Cryptography/ECC/ECCurve.cs rename to src/neo/Cryptography/ECC/ECCurve.cs diff --git a/neo/Cryptography/ECC/ECDsa.cs b/src/neo/Cryptography/ECC/ECDsa.cs similarity index 100% rename from neo/Cryptography/ECC/ECDsa.cs rename to src/neo/Cryptography/ECC/ECDsa.cs diff --git a/neo/Cryptography/ECC/ECFieldElement.cs b/src/neo/Cryptography/ECC/ECFieldElement.cs similarity index 100% rename from neo/Cryptography/ECC/ECFieldElement.cs rename to src/neo/Cryptography/ECC/ECFieldElement.cs diff --git a/neo/Cryptography/ECC/ECPoint.cs b/src/neo/Cryptography/ECC/ECPoint.cs similarity index 100% rename from neo/Cryptography/ECC/ECPoint.cs rename to src/neo/Cryptography/ECC/ECPoint.cs diff --git a/neo/Cryptography/Helper.cs b/src/neo/Cryptography/Helper.cs similarity index 100% rename from neo/Cryptography/Helper.cs rename to src/neo/Cryptography/Helper.cs diff --git a/neo/Cryptography/MerkleTree.cs b/src/neo/Cryptography/MerkleTree.cs similarity index 100% rename from neo/Cryptography/MerkleTree.cs rename to src/neo/Cryptography/MerkleTree.cs diff --git a/neo/Cryptography/MerkleTreeNode.cs b/src/neo/Cryptography/MerkleTreeNode.cs similarity index 100% rename from neo/Cryptography/MerkleTreeNode.cs rename to src/neo/Cryptography/MerkleTreeNode.cs diff --git a/neo/Cryptography/Murmur3.cs b/src/neo/Cryptography/Murmur3.cs similarity index 100% rename from neo/Cryptography/Murmur3.cs rename to src/neo/Cryptography/Murmur3.cs diff --git a/neo/Cryptography/RIPEMD160Managed.cs b/src/neo/Cryptography/RIPEMD160Managed.cs similarity index 100% rename from neo/Cryptography/RIPEMD160Managed.cs rename to src/neo/Cryptography/RIPEMD160Managed.cs diff --git a/neo/Cryptography/SCrypt.cs b/src/neo/Cryptography/SCrypt.cs similarity index 100% rename from neo/Cryptography/SCrypt.cs rename to src/neo/Cryptography/SCrypt.cs diff --git a/neo/Helper.cs b/src/neo/Helper.cs similarity index 100% rename from neo/Helper.cs rename to src/neo/Helper.cs diff --git a/neo/IO/Actors/Idle.cs b/src/neo/IO/Actors/Idle.cs similarity index 100% rename from neo/IO/Actors/Idle.cs rename to src/neo/IO/Actors/Idle.cs diff --git a/neo/IO/Actors/PriorityMailbox.cs b/src/neo/IO/Actors/PriorityMailbox.cs similarity index 100% rename from neo/IO/Actors/PriorityMailbox.cs rename to src/neo/IO/Actors/PriorityMailbox.cs diff --git a/neo/IO/Actors/PriorityMessageQueue.cs b/src/neo/IO/Actors/PriorityMessageQueue.cs similarity index 100% rename from neo/IO/Actors/PriorityMessageQueue.cs rename to src/neo/IO/Actors/PriorityMessageQueue.cs diff --git a/neo/IO/ByteArrayComparer.cs b/src/neo/IO/ByteArrayComparer.cs similarity index 100% rename from neo/IO/ByteArrayComparer.cs rename to src/neo/IO/ByteArrayComparer.cs diff --git a/neo/IO/Caching/Cache.cs b/src/neo/IO/Caching/Cache.cs similarity index 100% rename from neo/IO/Caching/Cache.cs rename to src/neo/IO/Caching/Cache.cs diff --git a/neo/IO/Caching/CloneCache.cs b/src/neo/IO/Caching/CloneCache.cs similarity index 100% rename from neo/IO/Caching/CloneCache.cs rename to src/neo/IO/Caching/CloneCache.cs diff --git a/neo/IO/Caching/CloneMetaCache.cs b/src/neo/IO/Caching/CloneMetaCache.cs similarity index 100% rename from neo/IO/Caching/CloneMetaCache.cs rename to src/neo/IO/Caching/CloneMetaCache.cs diff --git a/neo/IO/Caching/DataCache.cs b/src/neo/IO/Caching/DataCache.cs similarity index 100% rename from neo/IO/Caching/DataCache.cs rename to src/neo/IO/Caching/DataCache.cs diff --git a/neo/IO/Caching/FIFOCache.cs b/src/neo/IO/Caching/FIFOCache.cs similarity index 100% rename from neo/IO/Caching/FIFOCache.cs rename to src/neo/IO/Caching/FIFOCache.cs diff --git a/neo/IO/Caching/FIFOSet.cs b/src/neo/IO/Caching/FIFOSet.cs similarity index 100% rename from neo/IO/Caching/FIFOSet.cs rename to src/neo/IO/Caching/FIFOSet.cs diff --git a/neo/IO/Caching/MetaDataCache.cs b/src/neo/IO/Caching/MetaDataCache.cs similarity index 100% rename from neo/IO/Caching/MetaDataCache.cs rename to src/neo/IO/Caching/MetaDataCache.cs diff --git a/neo/IO/Caching/OrderedDictionary.cs b/src/neo/IO/Caching/OrderedDictionary.cs similarity index 100% rename from neo/IO/Caching/OrderedDictionary.cs rename to src/neo/IO/Caching/OrderedDictionary.cs diff --git a/neo/IO/Caching/ReflectionCache.cs b/src/neo/IO/Caching/ReflectionCache.cs similarity index 100% rename from neo/IO/Caching/ReflectionCache.cs rename to src/neo/IO/Caching/ReflectionCache.cs diff --git a/neo/IO/Caching/ReflectionCacheAttribute.cs b/src/neo/IO/Caching/ReflectionCacheAttribute.cs similarity index 100% rename from neo/IO/Caching/ReflectionCacheAttribute.cs rename to src/neo/IO/Caching/ReflectionCacheAttribute.cs diff --git a/neo/IO/Caching/RelayCache.cs b/src/neo/IO/Caching/RelayCache.cs similarity index 100% rename from neo/IO/Caching/RelayCache.cs rename to src/neo/IO/Caching/RelayCache.cs diff --git a/neo/IO/Caching/TrackState.cs b/src/neo/IO/Caching/TrackState.cs similarity index 100% rename from neo/IO/Caching/TrackState.cs rename to src/neo/IO/Caching/TrackState.cs diff --git a/neo/IO/Data/LevelDB/DB.cs b/src/neo/IO/Data/LevelDB/DB.cs similarity index 100% rename from neo/IO/Data/LevelDB/DB.cs rename to src/neo/IO/Data/LevelDB/DB.cs diff --git a/neo/IO/Data/LevelDB/Helper.cs b/src/neo/IO/Data/LevelDB/Helper.cs similarity index 100% rename from neo/IO/Data/LevelDB/Helper.cs rename to src/neo/IO/Data/LevelDB/Helper.cs diff --git a/neo/IO/Data/LevelDB/Iterator.cs b/src/neo/IO/Data/LevelDB/Iterator.cs similarity index 100% rename from neo/IO/Data/LevelDB/Iterator.cs rename to src/neo/IO/Data/LevelDB/Iterator.cs diff --git a/neo/IO/Data/LevelDB/LevelDBException.cs b/src/neo/IO/Data/LevelDB/LevelDBException.cs similarity index 100% rename from neo/IO/Data/LevelDB/LevelDBException.cs rename to src/neo/IO/Data/LevelDB/LevelDBException.cs diff --git a/neo/IO/Data/LevelDB/Native.cs b/src/neo/IO/Data/LevelDB/Native.cs similarity index 100% rename from neo/IO/Data/LevelDB/Native.cs rename to src/neo/IO/Data/LevelDB/Native.cs diff --git a/neo/IO/Data/LevelDB/Options.cs b/src/neo/IO/Data/LevelDB/Options.cs similarity index 100% rename from neo/IO/Data/LevelDB/Options.cs rename to src/neo/IO/Data/LevelDB/Options.cs diff --git a/neo/IO/Data/LevelDB/ReadOptions.cs b/src/neo/IO/Data/LevelDB/ReadOptions.cs similarity index 100% rename from neo/IO/Data/LevelDB/ReadOptions.cs rename to src/neo/IO/Data/LevelDB/ReadOptions.cs diff --git a/neo/IO/Data/LevelDB/Slice.cs b/src/neo/IO/Data/LevelDB/Slice.cs similarity index 100% rename from neo/IO/Data/LevelDB/Slice.cs rename to src/neo/IO/Data/LevelDB/Slice.cs diff --git a/neo/IO/Data/LevelDB/SliceBuilder.cs b/src/neo/IO/Data/LevelDB/SliceBuilder.cs similarity index 100% rename from neo/IO/Data/LevelDB/SliceBuilder.cs rename to src/neo/IO/Data/LevelDB/SliceBuilder.cs diff --git a/neo/IO/Data/LevelDB/Snapshot.cs b/src/neo/IO/Data/LevelDB/Snapshot.cs similarity index 100% rename from neo/IO/Data/LevelDB/Snapshot.cs rename to src/neo/IO/Data/LevelDB/Snapshot.cs diff --git a/neo/IO/Data/LevelDB/WriteBatch.cs b/src/neo/IO/Data/LevelDB/WriteBatch.cs similarity index 100% rename from neo/IO/Data/LevelDB/WriteBatch.cs rename to src/neo/IO/Data/LevelDB/WriteBatch.cs diff --git a/neo/IO/Data/LevelDB/WriteOptions.cs b/src/neo/IO/Data/LevelDB/WriteOptions.cs similarity index 100% rename from neo/IO/Data/LevelDB/WriteOptions.cs rename to src/neo/IO/Data/LevelDB/WriteOptions.cs diff --git a/neo/IO/Helper.cs b/src/neo/IO/Helper.cs similarity index 100% rename from neo/IO/Helper.cs rename to src/neo/IO/Helper.cs diff --git a/neo/IO/ICloneable.cs b/src/neo/IO/ICloneable.cs similarity index 100% rename from neo/IO/ICloneable.cs rename to src/neo/IO/ICloneable.cs diff --git a/neo/IO/ISerializable.cs b/src/neo/IO/ISerializable.cs similarity index 100% rename from neo/IO/ISerializable.cs rename to src/neo/IO/ISerializable.cs diff --git a/neo/IO/Json/JArray.cs b/src/neo/IO/Json/JArray.cs similarity index 100% rename from neo/IO/Json/JArray.cs rename to src/neo/IO/Json/JArray.cs diff --git a/neo/IO/Json/JBoolean.cs b/src/neo/IO/Json/JBoolean.cs similarity index 100% rename from neo/IO/Json/JBoolean.cs rename to src/neo/IO/Json/JBoolean.cs diff --git a/neo/IO/Json/JNumber.cs b/src/neo/IO/Json/JNumber.cs similarity index 100% rename from neo/IO/Json/JNumber.cs rename to src/neo/IO/Json/JNumber.cs diff --git a/neo/IO/Json/JObject.cs b/src/neo/IO/Json/JObject.cs similarity index 100% rename from neo/IO/Json/JObject.cs rename to src/neo/IO/Json/JObject.cs diff --git a/neo/IO/Json/JString.cs b/src/neo/IO/Json/JString.cs similarity index 100% rename from neo/IO/Json/JString.cs rename to src/neo/IO/Json/JString.cs diff --git a/neo/IO/Wrappers/SerializableWrapper.cs b/src/neo/IO/Wrappers/SerializableWrapper.cs similarity index 100% rename from neo/IO/Wrappers/SerializableWrapper.cs rename to src/neo/IO/Wrappers/SerializableWrapper.cs diff --git a/neo/IO/Wrappers/UInt32Wrapper.cs b/src/neo/IO/Wrappers/UInt32Wrapper.cs similarity index 100% rename from neo/IO/Wrappers/UInt32Wrapper.cs rename to src/neo/IO/Wrappers/UInt32Wrapper.cs diff --git a/neo/Ledger/Blockchain.ApplicationExecuted.cs b/src/neo/Ledger/Blockchain.ApplicationExecuted.cs similarity index 100% rename from neo/Ledger/Blockchain.ApplicationExecuted.cs rename to src/neo/Ledger/Blockchain.ApplicationExecuted.cs diff --git a/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs similarity index 100% rename from neo/Ledger/Blockchain.cs rename to src/neo/Ledger/Blockchain.cs diff --git a/neo/Ledger/ContractState.cs b/src/neo/Ledger/ContractState.cs similarity index 100% rename from neo/Ledger/ContractState.cs rename to src/neo/Ledger/ContractState.cs diff --git a/neo/Ledger/HashIndexState.cs b/src/neo/Ledger/HashIndexState.cs similarity index 100% rename from neo/Ledger/HashIndexState.cs rename to src/neo/Ledger/HashIndexState.cs diff --git a/neo/Ledger/HeaderHashList.cs b/src/neo/Ledger/HeaderHashList.cs similarity index 100% rename from neo/Ledger/HeaderHashList.cs rename to src/neo/Ledger/HeaderHashList.cs diff --git a/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs similarity index 100% rename from neo/Ledger/MemoryPool.cs rename to src/neo/Ledger/MemoryPool.cs diff --git a/neo/Ledger/PoolItem.cs b/src/neo/Ledger/PoolItem.cs similarity index 100% rename from neo/Ledger/PoolItem.cs rename to src/neo/Ledger/PoolItem.cs diff --git a/neo/Ledger/RelayResultReason.cs b/src/neo/Ledger/RelayResultReason.cs similarity index 100% rename from neo/Ledger/RelayResultReason.cs rename to src/neo/Ledger/RelayResultReason.cs diff --git a/neo/Ledger/SendersFeeMonitor.cs b/src/neo/Ledger/SendersFeeMonitor.cs similarity index 100% rename from neo/Ledger/SendersFeeMonitor.cs rename to src/neo/Ledger/SendersFeeMonitor.cs diff --git a/neo/Ledger/StorageFlags.cs b/src/neo/Ledger/StorageFlags.cs similarity index 100% rename from neo/Ledger/StorageFlags.cs rename to src/neo/Ledger/StorageFlags.cs diff --git a/neo/Ledger/StorageItem.cs b/src/neo/Ledger/StorageItem.cs similarity index 100% rename from neo/Ledger/StorageItem.cs rename to src/neo/Ledger/StorageItem.cs diff --git a/neo/Ledger/StorageKey.cs b/src/neo/Ledger/StorageKey.cs similarity index 100% rename from neo/Ledger/StorageKey.cs rename to src/neo/Ledger/StorageKey.cs diff --git a/neo/Ledger/TransactionState.cs b/src/neo/Ledger/TransactionState.cs similarity index 100% rename from neo/Ledger/TransactionState.cs rename to src/neo/Ledger/TransactionState.cs diff --git a/neo/Ledger/TrimmedBlock.cs b/src/neo/Ledger/TrimmedBlock.cs similarity index 100% rename from neo/Ledger/TrimmedBlock.cs rename to src/neo/Ledger/TrimmedBlock.cs diff --git a/neo/NeoSystem.cs b/src/neo/NeoSystem.cs similarity index 100% rename from neo/NeoSystem.cs rename to src/neo/NeoSystem.cs diff --git a/neo/Network/P2P/Capabilities/FullNodeCapability.cs b/src/neo/Network/P2P/Capabilities/FullNodeCapability.cs similarity index 100% rename from neo/Network/P2P/Capabilities/FullNodeCapability.cs rename to src/neo/Network/P2P/Capabilities/FullNodeCapability.cs diff --git a/neo/Network/P2P/Capabilities/NodeCapability.cs b/src/neo/Network/P2P/Capabilities/NodeCapability.cs similarity index 100% rename from neo/Network/P2P/Capabilities/NodeCapability.cs rename to src/neo/Network/P2P/Capabilities/NodeCapability.cs diff --git a/neo/Network/P2P/Capabilities/NodeCapabilityType.cs b/src/neo/Network/P2P/Capabilities/NodeCapabilityType.cs similarity index 100% rename from neo/Network/P2P/Capabilities/NodeCapabilityType.cs rename to src/neo/Network/P2P/Capabilities/NodeCapabilityType.cs diff --git a/neo/Network/P2P/Capabilities/ServerCapability.cs b/src/neo/Network/P2P/Capabilities/ServerCapability.cs similarity index 100% rename from neo/Network/P2P/Capabilities/ServerCapability.cs rename to src/neo/Network/P2P/Capabilities/ServerCapability.cs diff --git a/neo/Network/P2P/ChannelsConfig.cs b/src/neo/Network/P2P/ChannelsConfig.cs similarity index 100% rename from neo/Network/P2P/ChannelsConfig.cs rename to src/neo/Network/P2P/ChannelsConfig.cs diff --git a/neo/Network/P2P/Connection.cs b/src/neo/Network/P2P/Connection.cs similarity index 100% rename from neo/Network/P2P/Connection.cs rename to src/neo/Network/P2P/Connection.cs diff --git a/neo/Network/P2P/Helper.cs b/src/neo/Network/P2P/Helper.cs similarity index 100% rename from neo/Network/P2P/Helper.cs rename to src/neo/Network/P2P/Helper.cs diff --git a/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs similarity index 100% rename from neo/Network/P2P/LocalNode.cs rename to src/neo/Network/P2P/LocalNode.cs diff --git a/neo/Network/P2P/Message.cs b/src/neo/Network/P2P/Message.cs similarity index 100% rename from neo/Network/P2P/Message.cs rename to src/neo/Network/P2P/Message.cs diff --git a/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs similarity index 100% rename from neo/Network/P2P/MessageCommand.cs rename to src/neo/Network/P2P/MessageCommand.cs diff --git a/neo/Network/P2P/MessageFlags.cs b/src/neo/Network/P2P/MessageFlags.cs similarity index 100% rename from neo/Network/P2P/MessageFlags.cs rename to src/neo/Network/P2P/MessageFlags.cs diff --git a/neo/Network/P2P/Payloads/AddrPayload.cs b/src/neo/Network/P2P/Payloads/AddrPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/AddrPayload.cs rename to src/neo/Network/P2P/Payloads/AddrPayload.cs diff --git a/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs similarity index 100% rename from neo/Network/P2P/Payloads/Block.cs rename to src/neo/Network/P2P/Payloads/Block.cs diff --git a/neo/Network/P2P/Payloads/BlockBase.cs b/src/neo/Network/P2P/Payloads/BlockBase.cs similarity index 100% rename from neo/Network/P2P/Payloads/BlockBase.cs rename to src/neo/Network/P2P/Payloads/BlockBase.cs diff --git a/neo/Network/P2P/Payloads/ConsensusData.cs b/src/neo/Network/P2P/Payloads/ConsensusData.cs similarity index 100% rename from neo/Network/P2P/Payloads/ConsensusData.cs rename to src/neo/Network/P2P/Payloads/ConsensusData.cs diff --git a/neo/Network/P2P/Payloads/ConsensusPayload.cs b/src/neo/Network/P2P/Payloads/ConsensusPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/ConsensusPayload.cs rename to src/neo/Network/P2P/Payloads/ConsensusPayload.cs diff --git a/neo/Network/P2P/Payloads/Cosigner.cs b/src/neo/Network/P2P/Payloads/Cosigner.cs similarity index 100% rename from neo/Network/P2P/Payloads/Cosigner.cs rename to src/neo/Network/P2P/Payloads/Cosigner.cs diff --git a/neo/Network/P2P/Payloads/FilterAddPayload.cs b/src/neo/Network/P2P/Payloads/FilterAddPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/FilterAddPayload.cs rename to src/neo/Network/P2P/Payloads/FilterAddPayload.cs diff --git a/neo/Network/P2P/Payloads/FilterLoadPayload.cs b/src/neo/Network/P2P/Payloads/FilterLoadPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/FilterLoadPayload.cs rename to src/neo/Network/P2P/Payloads/FilterLoadPayload.cs diff --git a/neo/Network/P2P/Payloads/GetBlockDataPayload.cs b/src/neo/Network/P2P/Payloads/GetBlockDataPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/GetBlockDataPayload.cs rename to src/neo/Network/P2P/Payloads/GetBlockDataPayload.cs diff --git a/neo/Network/P2P/Payloads/GetBlocksPayload.cs b/src/neo/Network/P2P/Payloads/GetBlocksPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/GetBlocksPayload.cs rename to src/neo/Network/P2P/Payloads/GetBlocksPayload.cs diff --git a/neo/Network/P2P/Payloads/Header.cs b/src/neo/Network/P2P/Payloads/Header.cs similarity index 100% rename from neo/Network/P2P/Payloads/Header.cs rename to src/neo/Network/P2P/Payloads/Header.cs diff --git a/neo/Network/P2P/Payloads/HeadersPayload.cs b/src/neo/Network/P2P/Payloads/HeadersPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/HeadersPayload.cs rename to src/neo/Network/P2P/Payloads/HeadersPayload.cs diff --git a/neo/Network/P2P/Payloads/IInventory.cs b/src/neo/Network/P2P/Payloads/IInventory.cs similarity index 100% rename from neo/Network/P2P/Payloads/IInventory.cs rename to src/neo/Network/P2P/Payloads/IInventory.cs diff --git a/neo/Network/P2P/Payloads/IVerifiable.cs b/src/neo/Network/P2P/Payloads/IVerifiable.cs similarity index 100% rename from neo/Network/P2P/Payloads/IVerifiable.cs rename to src/neo/Network/P2P/Payloads/IVerifiable.cs diff --git a/neo/Network/P2P/Payloads/InvPayload.cs b/src/neo/Network/P2P/Payloads/InvPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/InvPayload.cs rename to src/neo/Network/P2P/Payloads/InvPayload.cs diff --git a/neo/Network/P2P/Payloads/InventoryType.cs b/src/neo/Network/P2P/Payloads/InventoryType.cs similarity index 100% rename from neo/Network/P2P/Payloads/InventoryType.cs rename to src/neo/Network/P2P/Payloads/InventoryType.cs diff --git a/neo/Network/P2P/Payloads/MerkleBlockPayload.cs b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/MerkleBlockPayload.cs rename to src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs diff --git a/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs b/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs similarity index 100% rename from neo/Network/P2P/Payloads/NetworkAddressWithTime.cs rename to src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs diff --git a/neo/Network/P2P/Payloads/PingPayload.cs b/src/neo/Network/P2P/Payloads/PingPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/PingPayload.cs rename to src/neo/Network/P2P/Payloads/PingPayload.cs diff --git a/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs similarity index 100% rename from neo/Network/P2P/Payloads/Transaction.cs rename to src/neo/Network/P2P/Payloads/Transaction.cs diff --git a/neo/Network/P2P/Payloads/TransactionAttribute.cs b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs similarity index 100% rename from neo/Network/P2P/Payloads/TransactionAttribute.cs rename to src/neo/Network/P2P/Payloads/TransactionAttribute.cs diff --git a/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs b/src/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs similarity index 100% rename from neo/Network/P2P/Payloads/TransactionAttributeUsage.cs rename to src/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs diff --git a/neo/Network/P2P/Payloads/VersionPayload.cs b/src/neo/Network/P2P/Payloads/VersionPayload.cs similarity index 100% rename from neo/Network/P2P/Payloads/VersionPayload.cs rename to src/neo/Network/P2P/Payloads/VersionPayload.cs diff --git a/neo/Network/P2P/Payloads/Witness.cs b/src/neo/Network/P2P/Payloads/Witness.cs similarity index 100% rename from neo/Network/P2P/Payloads/Witness.cs rename to src/neo/Network/P2P/Payloads/Witness.cs diff --git a/neo/Network/P2P/Payloads/WitnessScope.cs b/src/neo/Network/P2P/Payloads/WitnessScope.cs similarity index 100% rename from neo/Network/P2P/Payloads/WitnessScope.cs rename to src/neo/Network/P2P/Payloads/WitnessScope.cs diff --git a/neo/Network/P2P/Peer.cs b/src/neo/Network/P2P/Peer.cs similarity index 100% rename from neo/Network/P2P/Peer.cs rename to src/neo/Network/P2P/Peer.cs diff --git a/neo/Network/P2P/ProtocolHandler.cs b/src/neo/Network/P2P/ProtocolHandler.cs similarity index 100% rename from neo/Network/P2P/ProtocolHandler.cs rename to src/neo/Network/P2P/ProtocolHandler.cs diff --git a/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs similarity index 100% rename from neo/Network/P2P/RemoteNode.cs rename to src/neo/Network/P2P/RemoteNode.cs diff --git a/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs similarity index 100% rename from neo/Network/P2P/TaskManager.cs rename to src/neo/Network/P2P/TaskManager.cs diff --git a/neo/Network/P2P/TaskSession.cs b/src/neo/Network/P2P/TaskSession.cs similarity index 100% rename from neo/Network/P2P/TaskSession.cs rename to src/neo/Network/P2P/TaskSession.cs diff --git a/neo/Network/RPC/ContractClient.cs b/src/neo/Network/RPC/ContractClient.cs similarity index 100% rename from neo/Network/RPC/ContractClient.cs rename to src/neo/Network/RPC/ContractClient.cs diff --git a/neo/Network/RPC/Helper.cs b/src/neo/Network/RPC/Helper.cs similarity index 100% rename from neo/Network/RPC/Helper.cs rename to src/neo/Network/RPC/Helper.cs diff --git a/neo/Network/RPC/Models/RpcBlock.cs b/src/neo/Network/RPC/Models/RpcBlock.cs similarity index 100% rename from neo/Network/RPC/Models/RpcBlock.cs rename to src/neo/Network/RPC/Models/RpcBlock.cs diff --git a/neo/Network/RPC/Models/RpcBlockHeader.cs b/src/neo/Network/RPC/Models/RpcBlockHeader.cs similarity index 100% rename from neo/Network/RPC/Models/RpcBlockHeader.cs rename to src/neo/Network/RPC/Models/RpcBlockHeader.cs diff --git a/neo/Network/RPC/Models/RpcInvokeResult.cs b/src/neo/Network/RPC/Models/RpcInvokeResult.cs similarity index 100% rename from neo/Network/RPC/Models/RpcInvokeResult.cs rename to src/neo/Network/RPC/Models/RpcInvokeResult.cs diff --git a/neo/Network/RPC/Models/RpcNep5Balances.cs b/src/neo/Network/RPC/Models/RpcNep5Balances.cs similarity index 100% rename from neo/Network/RPC/Models/RpcNep5Balances.cs rename to src/neo/Network/RPC/Models/RpcNep5Balances.cs diff --git a/neo/Network/RPC/Models/RpcNep5TokenInfo.cs b/src/neo/Network/RPC/Models/RpcNep5TokenInfo.cs similarity index 100% rename from neo/Network/RPC/Models/RpcNep5TokenInfo.cs rename to src/neo/Network/RPC/Models/RpcNep5TokenInfo.cs diff --git a/neo/Network/RPC/Models/RpcPeers.cs b/src/neo/Network/RPC/Models/RpcPeers.cs similarity index 100% rename from neo/Network/RPC/Models/RpcPeers.cs rename to src/neo/Network/RPC/Models/RpcPeers.cs diff --git a/neo/Network/RPC/Models/RpcPlugin.cs b/src/neo/Network/RPC/Models/RpcPlugin.cs similarity index 100% rename from neo/Network/RPC/Models/RpcPlugin.cs rename to src/neo/Network/RPC/Models/RpcPlugin.cs diff --git a/neo/Network/RPC/Models/RpcRawMemPool.cs b/src/neo/Network/RPC/Models/RpcRawMemPool.cs similarity index 100% rename from neo/Network/RPC/Models/RpcRawMemPool.cs rename to src/neo/Network/RPC/Models/RpcRawMemPool.cs diff --git a/neo/Network/RPC/Models/RpcRequest.cs b/src/neo/Network/RPC/Models/RpcRequest.cs similarity index 100% rename from neo/Network/RPC/Models/RpcRequest.cs rename to src/neo/Network/RPC/Models/RpcRequest.cs diff --git a/neo/Network/RPC/Models/RpcResponse.cs b/src/neo/Network/RPC/Models/RpcResponse.cs similarity index 100% rename from neo/Network/RPC/Models/RpcResponse.cs rename to src/neo/Network/RPC/Models/RpcResponse.cs diff --git a/neo/Network/RPC/Models/RpcTransaction.cs b/src/neo/Network/RPC/Models/RpcTransaction.cs similarity index 100% rename from neo/Network/RPC/Models/RpcTransaction.cs rename to src/neo/Network/RPC/Models/RpcTransaction.cs diff --git a/neo/Network/RPC/Models/RpcValidateAddressResult.cs b/src/neo/Network/RPC/Models/RpcValidateAddressResult.cs similarity index 100% rename from neo/Network/RPC/Models/RpcValidateAddressResult.cs rename to src/neo/Network/RPC/Models/RpcValidateAddressResult.cs diff --git a/neo/Network/RPC/Models/RpcValidator.cs b/src/neo/Network/RPC/Models/RpcValidator.cs similarity index 100% rename from neo/Network/RPC/Models/RpcValidator.cs rename to src/neo/Network/RPC/Models/RpcValidator.cs diff --git a/neo/Network/RPC/Models/RpcVersion.cs b/src/neo/Network/RPC/Models/RpcVersion.cs similarity index 100% rename from neo/Network/RPC/Models/RpcVersion.cs rename to src/neo/Network/RPC/Models/RpcVersion.cs diff --git a/neo/Network/RPC/Nep5API.cs b/src/neo/Network/RPC/Nep5API.cs similarity index 100% rename from neo/Network/RPC/Nep5API.cs rename to src/neo/Network/RPC/Nep5API.cs diff --git a/neo/Network/RPC/PolicyAPI.cs b/src/neo/Network/RPC/PolicyAPI.cs similarity index 100% rename from neo/Network/RPC/PolicyAPI.cs rename to src/neo/Network/RPC/PolicyAPI.cs diff --git a/neo/Network/RPC/RpcClient.cs b/src/neo/Network/RPC/RpcClient.cs similarity index 100% rename from neo/Network/RPC/RpcClient.cs rename to src/neo/Network/RPC/RpcClient.cs diff --git a/neo/Network/RPC/RpcException.cs b/src/neo/Network/RPC/RpcException.cs similarity index 100% rename from neo/Network/RPC/RpcException.cs rename to src/neo/Network/RPC/RpcException.cs diff --git a/neo/Network/RPC/RpcServer.cs b/src/neo/Network/RPC/RpcServer.cs similarity index 100% rename from neo/Network/RPC/RpcServer.cs rename to src/neo/Network/RPC/RpcServer.cs diff --git a/neo/Network/RPC/TransactionManager.cs b/src/neo/Network/RPC/TransactionManager.cs similarity index 100% rename from neo/Network/RPC/TransactionManager.cs rename to src/neo/Network/RPC/TransactionManager.cs diff --git a/neo/Network/RPC/WalletAPI.cs b/src/neo/Network/RPC/WalletAPI.cs similarity index 100% rename from neo/Network/RPC/WalletAPI.cs rename to src/neo/Network/RPC/WalletAPI.cs diff --git a/neo/Network/UPnP.cs b/src/neo/Network/UPnP.cs similarity index 100% rename from neo/Network/UPnP.cs rename to src/neo/Network/UPnP.cs diff --git a/neo/Persistence/CloneSnapshot.cs b/src/neo/Persistence/CloneSnapshot.cs similarity index 100% rename from neo/Persistence/CloneSnapshot.cs rename to src/neo/Persistence/CloneSnapshot.cs diff --git a/neo/Persistence/Helper.cs b/src/neo/Persistence/Helper.cs similarity index 100% rename from neo/Persistence/Helper.cs rename to src/neo/Persistence/Helper.cs diff --git a/neo/Persistence/IPersistence.cs b/src/neo/Persistence/IPersistence.cs similarity index 100% rename from neo/Persistence/IPersistence.cs rename to src/neo/Persistence/IPersistence.cs diff --git a/neo/Persistence/LevelDB/DbCache.cs b/src/neo/Persistence/LevelDB/DbCache.cs similarity index 100% rename from neo/Persistence/LevelDB/DbCache.cs rename to src/neo/Persistence/LevelDB/DbCache.cs diff --git a/neo/Persistence/LevelDB/DbMetaDataCache.cs b/src/neo/Persistence/LevelDB/DbMetaDataCache.cs similarity index 100% rename from neo/Persistence/LevelDB/DbMetaDataCache.cs rename to src/neo/Persistence/LevelDB/DbMetaDataCache.cs diff --git a/neo/Persistence/LevelDB/DbSnapshot.cs b/src/neo/Persistence/LevelDB/DbSnapshot.cs similarity index 100% rename from neo/Persistence/LevelDB/DbSnapshot.cs rename to src/neo/Persistence/LevelDB/DbSnapshot.cs diff --git a/neo/Persistence/LevelDB/LevelDBStore.cs b/src/neo/Persistence/LevelDB/LevelDBStore.cs similarity index 100% rename from neo/Persistence/LevelDB/LevelDBStore.cs rename to src/neo/Persistence/LevelDB/LevelDBStore.cs diff --git a/neo/Persistence/LevelDB/Prefixes.cs b/src/neo/Persistence/LevelDB/Prefixes.cs similarity index 100% rename from neo/Persistence/LevelDB/Prefixes.cs rename to src/neo/Persistence/LevelDB/Prefixes.cs diff --git a/neo/Persistence/Snapshot.cs b/src/neo/Persistence/Snapshot.cs similarity index 100% rename from neo/Persistence/Snapshot.cs rename to src/neo/Persistence/Snapshot.cs diff --git a/neo/Persistence/Store.cs b/src/neo/Persistence/Store.cs similarity index 100% rename from neo/Persistence/Store.cs rename to src/neo/Persistence/Store.cs diff --git a/neo/Plugins/ILogPlugin.cs b/src/neo/Plugins/ILogPlugin.cs similarity index 100% rename from neo/Plugins/ILogPlugin.cs rename to src/neo/Plugins/ILogPlugin.cs diff --git a/neo/Plugins/IMemoryPoolTxObserverPlugin.cs b/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs similarity index 100% rename from neo/Plugins/IMemoryPoolTxObserverPlugin.cs rename to src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs diff --git a/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs similarity index 100% rename from neo/Plugins/IP2PPlugin.cs rename to src/neo/Plugins/IP2PPlugin.cs diff --git a/neo/Plugins/IPersistencePlugin.cs b/src/neo/Plugins/IPersistencePlugin.cs similarity index 100% rename from neo/Plugins/IPersistencePlugin.cs rename to src/neo/Plugins/IPersistencePlugin.cs diff --git a/neo/Plugins/IRpcPlugin.cs b/src/neo/Plugins/IRpcPlugin.cs similarity index 100% rename from neo/Plugins/IRpcPlugin.cs rename to src/neo/Plugins/IRpcPlugin.cs diff --git a/neo/Plugins/LogLevel.cs b/src/neo/Plugins/LogLevel.cs similarity index 100% rename from neo/Plugins/LogLevel.cs rename to src/neo/Plugins/LogLevel.cs diff --git a/neo/Plugins/MemoryPoolTxRemovalReason.cs b/src/neo/Plugins/MemoryPoolTxRemovalReason.cs similarity index 100% rename from neo/Plugins/MemoryPoolTxRemovalReason.cs rename to src/neo/Plugins/MemoryPoolTxRemovalReason.cs diff --git a/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs similarity index 100% rename from neo/Plugins/Plugin.cs rename to src/neo/Plugins/Plugin.cs diff --git a/neo/Properties/AssemblyInfo.cs b/src/neo/Properties/AssemblyInfo.cs similarity index 100% rename from neo/Properties/AssemblyInfo.cs rename to src/neo/Properties/AssemblyInfo.cs diff --git a/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs similarity index 100% rename from neo/ProtocolSettings.cs rename to src/neo/ProtocolSettings.cs diff --git a/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs similarity index 100% rename from neo/SmartContract/ApplicationEngine.OpCodePrices.cs rename to src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs diff --git a/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs similarity index 100% rename from neo/SmartContract/ApplicationEngine.cs rename to src/neo/SmartContract/ApplicationEngine.cs diff --git a/neo/SmartContract/ContainerPlaceholder.cs b/src/neo/SmartContract/ContainerPlaceholder.cs similarity index 100% rename from neo/SmartContract/ContainerPlaceholder.cs rename to src/neo/SmartContract/ContainerPlaceholder.cs diff --git a/neo/SmartContract/Contract.cs b/src/neo/SmartContract/Contract.cs similarity index 100% rename from neo/SmartContract/Contract.cs rename to src/neo/SmartContract/Contract.cs diff --git a/neo/SmartContract/ContractParameter.cs b/src/neo/SmartContract/ContractParameter.cs similarity index 100% rename from neo/SmartContract/ContractParameter.cs rename to src/neo/SmartContract/ContractParameter.cs diff --git a/neo/SmartContract/ContractParameterType.cs b/src/neo/SmartContract/ContractParameterType.cs similarity index 100% rename from neo/SmartContract/ContractParameterType.cs rename to src/neo/SmartContract/ContractParameterType.cs diff --git a/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs similarity index 100% rename from neo/SmartContract/ContractParametersContext.cs rename to src/neo/SmartContract/ContractParametersContext.cs diff --git a/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs b/src/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs similarity index 100% rename from neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs rename to src/neo/SmartContract/Enumerators/ConcatenatedEnumerator.cs diff --git a/neo/SmartContract/Enumerators/IEnumerator.cs b/src/neo/SmartContract/Enumerators/IEnumerator.cs similarity index 100% rename from neo/SmartContract/Enumerators/IEnumerator.cs rename to src/neo/SmartContract/Enumerators/IEnumerator.cs diff --git a/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs b/src/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs similarity index 100% rename from neo/SmartContract/Enumerators/IteratorKeysWrapper.cs rename to src/neo/SmartContract/Enumerators/IteratorKeysWrapper.cs diff --git a/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs b/src/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs similarity index 100% rename from neo/SmartContract/Enumerators/IteratorValuesWrapper.cs rename to src/neo/SmartContract/Enumerators/IteratorValuesWrapper.cs diff --git a/neo/SmartContract/ExecutionContextState.cs b/src/neo/SmartContract/ExecutionContextState.cs similarity index 100% rename from neo/SmartContract/ExecutionContextState.cs rename to src/neo/SmartContract/ExecutionContextState.cs diff --git a/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs similarity index 100% rename from neo/SmartContract/Helper.cs rename to src/neo/SmartContract/Helper.cs diff --git a/neo/SmartContract/IInteroperable.cs b/src/neo/SmartContract/IInteroperable.cs similarity index 100% rename from neo/SmartContract/IInteroperable.cs rename to src/neo/SmartContract/IInteroperable.cs diff --git a/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs similarity index 100% rename from neo/SmartContract/InteropDescriptor.cs rename to src/neo/SmartContract/InteropDescriptor.cs diff --git a/neo/SmartContract/InteropService.NEO.cs b/src/neo/SmartContract/InteropService.NEO.cs similarity index 100% rename from neo/SmartContract/InteropService.NEO.cs rename to src/neo/SmartContract/InteropService.NEO.cs diff --git a/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs similarity index 100% rename from neo/SmartContract/InteropService.cs rename to src/neo/SmartContract/InteropService.cs diff --git a/neo/SmartContract/Iterators/ArrayWrapper.cs b/src/neo/SmartContract/Iterators/ArrayWrapper.cs similarity index 100% rename from neo/SmartContract/Iterators/ArrayWrapper.cs rename to src/neo/SmartContract/Iterators/ArrayWrapper.cs diff --git a/neo/SmartContract/Iterators/ConcatenatedIterator.cs b/src/neo/SmartContract/Iterators/ConcatenatedIterator.cs similarity index 100% rename from neo/SmartContract/Iterators/ConcatenatedIterator.cs rename to src/neo/SmartContract/Iterators/ConcatenatedIterator.cs diff --git a/neo/SmartContract/Iterators/IIterator.cs b/src/neo/SmartContract/Iterators/IIterator.cs similarity index 100% rename from neo/SmartContract/Iterators/IIterator.cs rename to src/neo/SmartContract/Iterators/IIterator.cs diff --git a/neo/SmartContract/Iterators/MapWrapper.cs b/src/neo/SmartContract/Iterators/MapWrapper.cs similarity index 100% rename from neo/SmartContract/Iterators/MapWrapper.cs rename to src/neo/SmartContract/Iterators/MapWrapper.cs diff --git a/neo/SmartContract/Iterators/StorageIterator.cs b/src/neo/SmartContract/Iterators/StorageIterator.cs similarity index 100% rename from neo/SmartContract/Iterators/StorageIterator.cs rename to src/neo/SmartContract/Iterators/StorageIterator.cs diff --git a/neo/SmartContract/JsonSerializer.cs b/src/neo/SmartContract/JsonSerializer.cs similarity index 100% rename from neo/SmartContract/JsonSerializer.cs rename to src/neo/SmartContract/JsonSerializer.cs diff --git a/neo/SmartContract/LogEventArgs.cs b/src/neo/SmartContract/LogEventArgs.cs similarity index 100% rename from neo/SmartContract/LogEventArgs.cs rename to src/neo/SmartContract/LogEventArgs.cs diff --git a/neo/SmartContract/Manifest/ContractAbi.cs b/src/neo/SmartContract/Manifest/ContractAbi.cs similarity index 100% rename from neo/SmartContract/Manifest/ContractAbi.cs rename to src/neo/SmartContract/Manifest/ContractAbi.cs diff --git a/neo/SmartContract/Manifest/ContractEventDescriptor.cs b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs similarity index 100% rename from neo/SmartContract/Manifest/ContractEventDescriptor.cs rename to src/neo/SmartContract/Manifest/ContractEventDescriptor.cs diff --git a/neo/SmartContract/Manifest/ContractFeatures.cs b/src/neo/SmartContract/Manifest/ContractFeatures.cs similarity index 100% rename from neo/SmartContract/Manifest/ContractFeatures.cs rename to src/neo/SmartContract/Manifest/ContractFeatures.cs diff --git a/neo/SmartContract/Manifest/ContractGroup.cs b/src/neo/SmartContract/Manifest/ContractGroup.cs similarity index 100% rename from neo/SmartContract/Manifest/ContractGroup.cs rename to src/neo/SmartContract/Manifest/ContractGroup.cs diff --git a/neo/SmartContract/Manifest/ContractManifest.cs b/src/neo/SmartContract/Manifest/ContractManifest.cs similarity index 100% rename from neo/SmartContract/Manifest/ContractManifest.cs rename to src/neo/SmartContract/Manifest/ContractManifest.cs diff --git a/neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs similarity index 100% rename from neo/SmartContract/Manifest/ContractMethodDescriptor.cs rename to src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs diff --git a/neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs similarity index 100% rename from neo/SmartContract/Manifest/ContractParameterDefinition.cs rename to src/neo/SmartContract/Manifest/ContractParameterDefinition.cs diff --git a/neo/SmartContract/Manifest/ContractPermission.cs b/src/neo/SmartContract/Manifest/ContractPermission.cs similarity index 100% rename from neo/SmartContract/Manifest/ContractPermission.cs rename to src/neo/SmartContract/Manifest/ContractPermission.cs diff --git a/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs b/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs similarity index 100% rename from neo/SmartContract/Manifest/ContractPermissionDescriptor.cs rename to src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs diff --git a/neo/SmartContract/Manifest/WildCardContainer.cs b/src/neo/SmartContract/Manifest/WildCardContainer.cs similarity index 100% rename from neo/SmartContract/Manifest/WildCardContainer.cs rename to src/neo/SmartContract/Manifest/WildCardContainer.cs diff --git a/neo/SmartContract/Native/ContractMethodAttribute.cs b/src/neo/SmartContract/Native/ContractMethodAttribute.cs similarity index 100% rename from neo/SmartContract/Native/ContractMethodAttribute.cs rename to src/neo/SmartContract/Native/ContractMethodAttribute.cs diff --git a/neo/SmartContract/Native/ContractMethodMetadata.cs b/src/neo/SmartContract/Native/ContractMethodMetadata.cs similarity index 100% rename from neo/SmartContract/Native/ContractMethodMetadata.cs rename to src/neo/SmartContract/Native/ContractMethodMetadata.cs diff --git a/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs similarity index 100% rename from neo/SmartContract/Native/NativeContract.cs rename to src/neo/SmartContract/Native/NativeContract.cs diff --git a/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs similarity index 100% rename from neo/SmartContract/Native/PolicyContract.cs rename to src/neo/SmartContract/Native/PolicyContract.cs diff --git a/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs similarity index 100% rename from neo/SmartContract/Native/Tokens/GasToken.cs rename to src/neo/SmartContract/Native/Tokens/GasToken.cs diff --git a/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs similarity index 100% rename from neo/SmartContract/Native/Tokens/NeoToken.cs rename to src/neo/SmartContract/Native/Tokens/NeoToken.cs diff --git a/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs similarity index 100% rename from neo/SmartContract/Native/Tokens/Nep5AccountState.cs rename to src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs diff --git a/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs similarity index 100% rename from neo/SmartContract/Native/Tokens/Nep5Token.cs rename to src/neo/SmartContract/Native/Tokens/Nep5Token.cs diff --git a/neo/SmartContract/NefFile.cs b/src/neo/SmartContract/NefFile.cs similarity index 100% rename from neo/SmartContract/NefFile.cs rename to src/neo/SmartContract/NefFile.cs diff --git a/neo/SmartContract/NotifyEventArgs.cs b/src/neo/SmartContract/NotifyEventArgs.cs similarity index 100% rename from neo/SmartContract/NotifyEventArgs.cs rename to src/neo/SmartContract/NotifyEventArgs.cs diff --git a/neo/SmartContract/StackItemType.cs b/src/neo/SmartContract/StackItemType.cs similarity index 100% rename from neo/SmartContract/StackItemType.cs rename to src/neo/SmartContract/StackItemType.cs diff --git a/neo/SmartContract/StorageContext.cs b/src/neo/SmartContract/StorageContext.cs similarity index 100% rename from neo/SmartContract/StorageContext.cs rename to src/neo/SmartContract/StorageContext.cs diff --git a/neo/SmartContract/TriggerType.cs b/src/neo/SmartContract/TriggerType.cs similarity index 100% rename from neo/SmartContract/TriggerType.cs rename to src/neo/SmartContract/TriggerType.cs diff --git a/neo/TimeProvider.cs b/src/neo/TimeProvider.cs similarity index 100% rename from neo/TimeProvider.cs rename to src/neo/TimeProvider.cs diff --git a/neo/UInt160.cs b/src/neo/UInt160.cs similarity index 100% rename from neo/UInt160.cs rename to src/neo/UInt160.cs diff --git a/neo/UInt256.cs b/src/neo/UInt256.cs similarity index 100% rename from neo/UInt256.cs rename to src/neo/UInt256.cs diff --git a/neo/UIntBase.cs b/src/neo/UIntBase.cs similarity index 100% rename from neo/UIntBase.cs rename to src/neo/UIntBase.cs diff --git a/neo/Utility.cs b/src/neo/Utility.cs similarity index 100% rename from neo/Utility.cs rename to src/neo/Utility.cs diff --git a/neo/VM/Helper.cs b/src/neo/VM/Helper.cs similarity index 100% rename from neo/VM/Helper.cs rename to src/neo/VM/Helper.cs diff --git a/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs similarity index 100% rename from neo/Wallets/AssetDescriptor.cs rename to src/neo/Wallets/AssetDescriptor.cs diff --git a/neo/Wallets/Helper.cs b/src/neo/Wallets/Helper.cs similarity index 100% rename from neo/Wallets/Helper.cs rename to src/neo/Wallets/Helper.cs diff --git a/neo/Wallets/KeyPair.cs b/src/neo/Wallets/KeyPair.cs similarity index 100% rename from neo/Wallets/KeyPair.cs rename to src/neo/Wallets/KeyPair.cs diff --git a/neo/Wallets/NEP6/NEP6Account.cs b/src/neo/Wallets/NEP6/NEP6Account.cs similarity index 100% rename from neo/Wallets/NEP6/NEP6Account.cs rename to src/neo/Wallets/NEP6/NEP6Account.cs diff --git a/neo/Wallets/NEP6/NEP6Contract.cs b/src/neo/Wallets/NEP6/NEP6Contract.cs similarity index 100% rename from neo/Wallets/NEP6/NEP6Contract.cs rename to src/neo/Wallets/NEP6/NEP6Contract.cs diff --git a/neo/Wallets/NEP6/NEP6Wallet.cs b/src/neo/Wallets/NEP6/NEP6Wallet.cs similarity index 100% rename from neo/Wallets/NEP6/NEP6Wallet.cs rename to src/neo/Wallets/NEP6/NEP6Wallet.cs diff --git a/neo/Wallets/NEP6/ScryptParameters.cs b/src/neo/Wallets/NEP6/ScryptParameters.cs similarity index 100% rename from neo/Wallets/NEP6/ScryptParameters.cs rename to src/neo/Wallets/NEP6/ScryptParameters.cs diff --git a/neo/Wallets/NEP6/WalletLocker.cs b/src/neo/Wallets/NEP6/WalletLocker.cs similarity index 100% rename from neo/Wallets/NEP6/WalletLocker.cs rename to src/neo/Wallets/NEP6/WalletLocker.cs diff --git a/neo/Wallets/SQLite/Account.cs b/src/neo/Wallets/SQLite/Account.cs similarity index 100% rename from neo/Wallets/SQLite/Account.cs rename to src/neo/Wallets/SQLite/Account.cs diff --git a/neo/Wallets/SQLite/Address.cs b/src/neo/Wallets/SQLite/Address.cs similarity index 100% rename from neo/Wallets/SQLite/Address.cs rename to src/neo/Wallets/SQLite/Address.cs diff --git a/neo/Wallets/SQLite/Contract.cs b/src/neo/Wallets/SQLite/Contract.cs similarity index 100% rename from neo/Wallets/SQLite/Contract.cs rename to src/neo/Wallets/SQLite/Contract.cs diff --git a/neo/Wallets/SQLite/Key.cs b/src/neo/Wallets/SQLite/Key.cs similarity index 100% rename from neo/Wallets/SQLite/Key.cs rename to src/neo/Wallets/SQLite/Key.cs diff --git a/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs similarity index 100% rename from neo/Wallets/SQLite/UserWallet.cs rename to src/neo/Wallets/SQLite/UserWallet.cs diff --git a/neo/Wallets/SQLite/UserWalletAccount.cs b/src/neo/Wallets/SQLite/UserWalletAccount.cs similarity index 100% rename from neo/Wallets/SQLite/UserWalletAccount.cs rename to src/neo/Wallets/SQLite/UserWalletAccount.cs diff --git a/neo/Wallets/SQLite/VerificationContract.cs b/src/neo/Wallets/SQLite/VerificationContract.cs similarity index 100% rename from neo/Wallets/SQLite/VerificationContract.cs rename to src/neo/Wallets/SQLite/VerificationContract.cs diff --git a/neo/Wallets/SQLite/WalletDataContext.cs b/src/neo/Wallets/SQLite/WalletDataContext.cs similarity index 100% rename from neo/Wallets/SQLite/WalletDataContext.cs rename to src/neo/Wallets/SQLite/WalletDataContext.cs diff --git a/neo/Wallets/TransferOutput.cs b/src/neo/Wallets/TransferOutput.cs similarity index 100% rename from neo/Wallets/TransferOutput.cs rename to src/neo/Wallets/TransferOutput.cs diff --git a/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs similarity index 100% rename from neo/Wallets/Wallet.cs rename to src/neo/Wallets/Wallet.cs diff --git a/neo/Wallets/WalletAccount.cs b/src/neo/Wallets/WalletAccount.cs similarity index 100% rename from neo/Wallets/WalletAccount.cs rename to src/neo/Wallets/WalletAccount.cs diff --git a/neo/neo.csproj b/src/neo/neo.csproj similarity index 100% rename from neo/neo.csproj rename to src/neo/neo.csproj diff --git a/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs similarity index 100% rename from neo.UnitTests/Consensus/UT_Consensus.cs rename to tests/neo.UnitTests/Consensus/UT_Consensus.cs diff --git a/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs similarity index 100% rename from neo.UnitTests/Consensus/UT_ConsensusContext.cs rename to tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs diff --git a/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs similarity index 100% rename from neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs rename to tests/neo.UnitTests/Consensus/UT_ConsensusServiceMailbox.cs diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs b/tests/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs similarity index 100% rename from neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs rename to tests/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs b/tests/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs similarity index 100% rename from neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs rename to tests/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs diff --git a/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs b/tests/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs similarity index 100% rename from neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs rename to tests/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs diff --git a/neo.UnitTests/Cryptography/UT_Base58.cs b/tests/neo.UnitTests/Cryptography/UT_Base58.cs similarity index 100% rename from neo.UnitTests/Cryptography/UT_Base58.cs rename to tests/neo.UnitTests/Cryptography/UT_Base58.cs diff --git a/neo.UnitTests/Cryptography/UT_BloomFilter.cs b/tests/neo.UnitTests/Cryptography/UT_BloomFilter.cs similarity index 100% rename from neo.UnitTests/Cryptography/UT_BloomFilter.cs rename to tests/neo.UnitTests/Cryptography/UT_BloomFilter.cs diff --git a/neo.UnitTests/Cryptography/UT_Crypto.cs b/tests/neo.UnitTests/Cryptography/UT_Crypto.cs similarity index 100% rename from neo.UnitTests/Cryptography/UT_Crypto.cs rename to tests/neo.UnitTests/Cryptography/UT_Crypto.cs diff --git a/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs similarity index 100% rename from neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs rename to tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs diff --git a/neo.UnitTests/Cryptography/UT_MerkleTree.cs b/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs similarity index 100% rename from neo.UnitTests/Cryptography/UT_MerkleTree.cs rename to tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs diff --git a/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs b/tests/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs similarity index 100% rename from neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs rename to tests/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs diff --git a/neo.UnitTests/Cryptography/UT_Murmur3.cs b/tests/neo.UnitTests/Cryptography/UT_Murmur3.cs similarity index 100% rename from neo.UnitTests/Cryptography/UT_Murmur3.cs rename to tests/neo.UnitTests/Cryptography/UT_Murmur3.cs diff --git a/neo.UnitTests/Cryptography/UT_SCrypt.cs b/tests/neo.UnitTests/Cryptography/UT_SCrypt.cs similarity index 100% rename from neo.UnitTests/Cryptography/UT_SCrypt.cs rename to tests/neo.UnitTests/Cryptography/UT_SCrypt.cs diff --git a/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs similarity index 100% rename from neo.UnitTests/Extensions/NativeContractExtensions.cs rename to tests/neo.UnitTests/Extensions/NativeContractExtensions.cs diff --git a/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs similarity index 100% rename from neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs rename to tests/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs diff --git a/neo.UnitTests/IO/Caching/UT_Cache.cs b/tests/neo.UnitTests/IO/Caching/UT_Cache.cs similarity index 100% rename from neo.UnitTests/IO/Caching/UT_Cache.cs rename to tests/neo.UnitTests/IO/Caching/UT_Cache.cs diff --git a/neo.UnitTests/IO/Caching/UT_CloneCache.cs b/tests/neo.UnitTests/IO/Caching/UT_CloneCache.cs similarity index 100% rename from neo.UnitTests/IO/Caching/UT_CloneCache.cs rename to tests/neo.UnitTests/IO/Caching/UT_CloneCache.cs diff --git a/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs b/tests/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs similarity index 100% rename from neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs rename to tests/neo.UnitTests/IO/Caching/UT_CloneMetaCache.cs diff --git a/neo.UnitTests/IO/Caching/UT_DataCache.cs b/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs similarity index 100% rename from neo.UnitTests/IO/Caching/UT_DataCache.cs rename to tests/neo.UnitTests/IO/Caching/UT_DataCache.cs diff --git a/neo.UnitTests/IO/Caching/UT_FIFOSet.cs b/tests/neo.UnitTests/IO/Caching/UT_FIFOSet.cs similarity index 100% rename from neo.UnitTests/IO/Caching/UT_FIFOSet.cs rename to tests/neo.UnitTests/IO/Caching/UT_FIFOSet.cs diff --git a/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs b/tests/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs similarity index 100% rename from neo.UnitTests/IO/Caching/UT_MetaDataCache.cs rename to tests/neo.UnitTests/IO/Caching/UT_MetaDataCache.cs diff --git a/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs b/tests/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs similarity index 100% rename from neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs rename to tests/neo.UnitTests/IO/Caching/UT_OrderedDictionary.cs diff --git a/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs b/tests/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs similarity index 100% rename from neo.UnitTests/IO/Caching/UT_ReflectionCache.cs rename to tests/neo.UnitTests/IO/Caching/UT_ReflectionCache.cs diff --git a/neo.UnitTests/IO/Caching/UT_RelayCache.cs b/tests/neo.UnitTests/IO/Caching/UT_RelayCache.cs similarity index 100% rename from neo.UnitTests/IO/Caching/UT_RelayCache.cs rename to tests/neo.UnitTests/IO/Caching/UT_RelayCache.cs diff --git a/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs b/tests/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs similarity index 100% rename from neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs rename to tests/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs diff --git a/neo.UnitTests/IO/Json/UT_JArray.cs b/tests/neo.UnitTests/IO/Json/UT_JArray.cs similarity index 100% rename from neo.UnitTests/IO/Json/UT_JArray.cs rename to tests/neo.UnitTests/IO/Json/UT_JArray.cs diff --git a/neo.UnitTests/IO/Json/UT_JBoolean.cs b/tests/neo.UnitTests/IO/Json/UT_JBoolean.cs similarity index 100% rename from neo.UnitTests/IO/Json/UT_JBoolean.cs rename to tests/neo.UnitTests/IO/Json/UT_JBoolean.cs diff --git a/neo.UnitTests/IO/Json/UT_JNumber.cs b/tests/neo.UnitTests/IO/Json/UT_JNumber.cs similarity index 100% rename from neo.UnitTests/IO/Json/UT_JNumber.cs rename to tests/neo.UnitTests/IO/Json/UT_JNumber.cs diff --git a/neo.UnitTests/IO/Json/UT_JObject.cs b/tests/neo.UnitTests/IO/Json/UT_JObject.cs similarity index 100% rename from neo.UnitTests/IO/Json/UT_JObject.cs rename to tests/neo.UnitTests/IO/Json/UT_JObject.cs diff --git a/neo.UnitTests/IO/Json/UT_JString.cs b/tests/neo.UnitTests/IO/Json/UT_JString.cs similarity index 100% rename from neo.UnitTests/IO/Json/UT_JString.cs rename to tests/neo.UnitTests/IO/Json/UT_JString.cs diff --git a/neo.UnitTests/IO/UT_ByteArrayComparer.cs b/tests/neo.UnitTests/IO/UT_ByteArrayComparer.cs similarity index 100% rename from neo.UnitTests/IO/UT_ByteArrayComparer.cs rename to tests/neo.UnitTests/IO/UT_ByteArrayComparer.cs diff --git a/neo.UnitTests/IO/UT_IOHelper.cs b/tests/neo.UnitTests/IO/UT_IOHelper.cs similarity index 100% rename from neo.UnitTests/IO/UT_IOHelper.cs rename to tests/neo.UnitTests/IO/UT_IOHelper.cs diff --git a/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs b/tests/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs similarity index 100% rename from neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs rename to tests/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs diff --git a/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs b/tests/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs similarity index 100% rename from neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs rename to tests/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs diff --git a/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_Blockchain.cs rename to tests/neo.UnitTests/Ledger/UT_Blockchain.cs diff --git a/neo.UnitTests/Ledger/UT_ContractState.cs b/tests/neo.UnitTests/Ledger/UT_ContractState.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_ContractState.cs rename to tests/neo.UnitTests/Ledger/UT_ContractState.cs diff --git a/neo.UnitTests/Ledger/UT_HashIndexState.cs b/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_HashIndexState.cs rename to tests/neo.UnitTests/Ledger/UT_HashIndexState.cs diff --git a/neo.UnitTests/Ledger/UT_HeaderHashList.cs b/tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_HeaderHashList.cs rename to tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs diff --git a/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_MemoryPool.cs rename to tests/neo.UnitTests/Ledger/UT_MemoryPool.cs diff --git a/neo.UnitTests/Ledger/UT_PoolItem.cs b/tests/neo.UnitTests/Ledger/UT_PoolItem.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_PoolItem.cs rename to tests/neo.UnitTests/Ledger/UT_PoolItem.cs diff --git a/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs b/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs rename to tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs diff --git a/neo.UnitTests/Ledger/UT_StorageItem.cs b/tests/neo.UnitTests/Ledger/UT_StorageItem.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_StorageItem.cs rename to tests/neo.UnitTests/Ledger/UT_StorageItem.cs diff --git a/neo.UnitTests/Ledger/UT_StorageKey.cs b/tests/neo.UnitTests/Ledger/UT_StorageKey.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_StorageKey.cs rename to tests/neo.UnitTests/Ledger/UT_StorageKey.cs diff --git a/neo.UnitTests/Ledger/UT_TransactionState.cs b/tests/neo.UnitTests/Ledger/UT_TransactionState.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_TransactionState.cs rename to tests/neo.UnitTests/Ledger/UT_TransactionState.cs diff --git a/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs similarity index 100% rename from neo.UnitTests/Ledger/UT_TrimmedBlock.cs rename to tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs similarity index 100% rename from neo.UnitTests/Network/P2P/Payloads/UT_Block.cs rename to tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs similarity index 100% rename from neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs rename to tests/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs similarity index 100% rename from neo.UnitTests/Network/P2P/Payloads/UT_Header.cs rename to tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs similarity index 100% rename from neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs rename to tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs diff --git a/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs similarity index 100% rename from neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs rename to tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs diff --git a/neo.UnitTests/Network/P2P/UT_Message.cs b/tests/neo.UnitTests/Network/P2P/UT_Message.cs similarity index 100% rename from neo.UnitTests/Network/P2P/UT_Message.cs rename to tests/neo.UnitTests/Network/P2P/UT_Message.cs diff --git a/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs b/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs similarity index 100% rename from neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs rename to tests/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs diff --git a/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs b/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs similarity index 100% rename from neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs rename to tests/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs diff --git a/neo.UnitTests/Network/P2P/UT_RemoteNode.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs similarity index 100% rename from neo.UnitTests/Network/P2P/UT_RemoteNode.cs rename to tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs diff --git a/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs similarity index 100% rename from neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs rename to tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs diff --git a/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs b/tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs similarity index 100% rename from neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs rename to tests/neo.UnitTests/Network/P2P/UT_TaskManagerMailbox.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs similarity index 100% rename from neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs rename to tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs similarity index 100% rename from neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs rename to tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs similarity index 100% rename from neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs rename to tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs similarity index 100% rename from neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs rename to tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs similarity index 100% rename from neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs rename to tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs similarity index 100% rename from neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs rename to tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs similarity index 100% rename from neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs rename to tests/neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs similarity index 100% rename from neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs rename to tests/neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs similarity index 100% rename from neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs rename to tests/neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs diff --git a/neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs similarity index 100% rename from neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs rename to tests/neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs diff --git a/neo.UnitTests/Network/RPC/UT_ContractClient.cs b/tests/neo.UnitTests/Network/RPC/UT_ContractClient.cs similarity index 100% rename from neo.UnitTests/Network/RPC/UT_ContractClient.cs rename to tests/neo.UnitTests/Network/RPC/UT_ContractClient.cs diff --git a/neo.UnitTests/Network/RPC/UT_Helper.cs b/tests/neo.UnitTests/Network/RPC/UT_Helper.cs similarity index 100% rename from neo.UnitTests/Network/RPC/UT_Helper.cs rename to tests/neo.UnitTests/Network/RPC/UT_Helper.cs diff --git a/neo.UnitTests/Network/RPC/UT_Nep5API.cs b/tests/neo.UnitTests/Network/RPC/UT_Nep5API.cs similarity index 100% rename from neo.UnitTests/Network/RPC/UT_Nep5API.cs rename to tests/neo.UnitTests/Network/RPC/UT_Nep5API.cs diff --git a/neo.UnitTests/Network/RPC/UT_PolicyAPI.cs b/tests/neo.UnitTests/Network/RPC/UT_PolicyAPI.cs similarity index 100% rename from neo.UnitTests/Network/RPC/UT_PolicyAPI.cs rename to tests/neo.UnitTests/Network/RPC/UT_PolicyAPI.cs diff --git a/neo.UnitTests/Network/RPC/UT_RpcClient.cs b/tests/neo.UnitTests/Network/RPC/UT_RpcClient.cs similarity index 100% rename from neo.UnitTests/Network/RPC/UT_RpcClient.cs rename to tests/neo.UnitTests/Network/RPC/UT_RpcClient.cs diff --git a/neo.UnitTests/Network/RPC/UT_RpcServer.cs b/tests/neo.UnitTests/Network/RPC/UT_RpcServer.cs similarity index 100% rename from neo.UnitTests/Network/RPC/UT_RpcServer.cs rename to tests/neo.UnitTests/Network/RPC/UT_RpcServer.cs diff --git a/neo.UnitTests/Network/RPC/UT_TransactionManager.cs b/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs similarity index 100% rename from neo.UnitTests/Network/RPC/UT_TransactionManager.cs rename to tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs diff --git a/neo.UnitTests/Network/RPC/UT_WalletAPI.cs b/tests/neo.UnitTests/Network/RPC/UT_WalletAPI.cs similarity index 100% rename from neo.UnitTests/Network/RPC/UT_WalletAPI.cs rename to tests/neo.UnitTests/Network/RPC/UT_WalletAPI.cs diff --git a/neo.UnitTests/Plugins/TestLogPlugin.cs b/tests/neo.UnitTests/Plugins/TestLogPlugin.cs similarity index 100% rename from neo.UnitTests/Plugins/TestLogPlugin.cs rename to tests/neo.UnitTests/Plugins/TestLogPlugin.cs diff --git a/neo.UnitTests/Plugins/UT_Plugin.cs b/tests/neo.UnitTests/Plugins/UT_Plugin.cs similarity index 100% rename from neo.UnitTests/Plugins/UT_Plugin.cs rename to tests/neo.UnitTests/Plugins/UT_Plugin.cs diff --git a/neo.UnitTests/README.md b/tests/neo.UnitTests/README.md similarity index 100% rename from neo.UnitTests/README.md rename to tests/neo.UnitTests/README.md diff --git a/neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs b/tests/neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs similarity index 100% rename from neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs rename to tests/neo.UnitTests/SmartContract/Enumerators/UT_ConcatenatedEnumerator.cs diff --git a/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs b/tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs similarity index 100% rename from neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs rename to tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorKeysWrapper.cs diff --git a/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs b/tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs similarity index 100% rename from neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs rename to tests/neo.UnitTests/SmartContract/Enumerators/UT_IteratorValuesWrapper.cs diff --git a/neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs similarity index 100% rename from neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs rename to tests/neo.UnitTests/SmartContract/Iterators/UT_ArrayWrapper.cs diff --git a/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs similarity index 100% rename from neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs rename to tests/neo.UnitTests/SmartContract/Iterators/UT_ConcatenatedIterator.cs diff --git a/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs similarity index 100% rename from neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs rename to tests/neo.UnitTests/SmartContract/Iterators/UT_MapWrapper.cs diff --git a/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs similarity index 100% rename from neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs rename to tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs similarity index 100% rename from neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs rename to tests/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs similarity index 100% rename from neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs rename to tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs similarity index 100% rename from neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs rename to tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs similarity index 100% rename from neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs rename to tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs diff --git a/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs similarity index 100% rename from neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs rename to tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs diff --git a/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs similarity index 100% rename from neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs rename to tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs similarity index 100% rename from neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs rename to tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs similarity index 100% rename from neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs rename to tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs diff --git a/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs similarity index 100% rename from neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs rename to tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs diff --git a/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs similarity index 100% rename from neo.UnitTests/SmartContract/Native/UT_NativeContract.cs rename to tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs diff --git a/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs similarity index 100% rename from neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs rename to tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs diff --git a/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_ApplicationEngine.cs rename to tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs diff --git a/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs b/tests/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs rename to tests/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs diff --git a/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/neo.UnitTests/SmartContract/UT_Contract.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_Contract.cs rename to tests/neo.UnitTests/SmartContract/UT_Contract.cs diff --git a/neo.UnitTests/SmartContract/UT_ContractParameter.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameter.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_ContractParameter.cs rename to tests/neo.UnitTests/SmartContract/UT_ContractParameter.cs diff --git a/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_ContractParameterContext.cs rename to tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs diff --git a/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs b/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_InteropDescriptor.cs rename to tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs diff --git a/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_InteropPrices.cs rename to tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs diff --git a/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_InteropService.NEO.cs rename to tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs diff --git a/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_InteropService.cs rename to tests/neo.UnitTests/SmartContract/UT_InteropService.cs diff --git a/neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_JsonSerializer.cs rename to tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs diff --git a/neo.UnitTests/SmartContract/UT_LogEventArgs.cs b/tests/neo.UnitTests/SmartContract/UT_LogEventArgs.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_LogEventArgs.cs rename to tests/neo.UnitTests/SmartContract/UT_LogEventArgs.cs diff --git a/neo.UnitTests/SmartContract/UT_NefFile.cs b/tests/neo.UnitTests/SmartContract/UT_NefFile.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_NefFile.cs rename to tests/neo.UnitTests/SmartContract/UT_NefFile.cs diff --git a/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs b/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs rename to tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs diff --git a/neo.UnitTests/SmartContract/UT_OpCodePrices.cs b/tests/neo.UnitTests/SmartContract/UT_OpCodePrices.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_OpCodePrices.cs rename to tests/neo.UnitTests/SmartContract/UT_OpCodePrices.cs diff --git a/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_SmartContractHelper.cs rename to tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs diff --git a/neo.UnitTests/SmartContract/UT_StorageContext.cs b/tests/neo.UnitTests/SmartContract/UT_StorageContext.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_StorageContext.cs rename to tests/neo.UnitTests/SmartContract/UT_StorageContext.cs diff --git a/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs similarity index 100% rename from neo.UnitTests/SmartContract/UT_Syscalls.cs rename to tests/neo.UnitTests/SmartContract/UT_Syscalls.cs diff --git a/neo.UnitTests/TestBlockchain.cs b/tests/neo.UnitTests/TestBlockchain.cs similarity index 100% rename from neo.UnitTests/TestBlockchain.cs rename to tests/neo.UnitTests/TestBlockchain.cs diff --git a/neo.UnitTests/TestDataCache.cs b/tests/neo.UnitTests/TestDataCache.cs similarity index 100% rename from neo.UnitTests/TestDataCache.cs rename to tests/neo.UnitTests/TestDataCache.cs diff --git a/neo.UnitTests/TestMetaDataCache.cs b/tests/neo.UnitTests/TestMetaDataCache.cs similarity index 100% rename from neo.UnitTests/TestMetaDataCache.cs rename to tests/neo.UnitTests/TestMetaDataCache.cs diff --git a/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs similarity index 100% rename from neo.UnitTests/TestUtils.cs rename to tests/neo.UnitTests/TestUtils.cs diff --git a/neo.UnitTests/TestVerifiable.cs b/tests/neo.UnitTests/TestVerifiable.cs similarity index 100% rename from neo.UnitTests/TestVerifiable.cs rename to tests/neo.UnitTests/TestVerifiable.cs diff --git a/neo.UnitTests/TestWalletAccount.cs b/tests/neo.UnitTests/TestWalletAccount.cs similarity index 100% rename from neo.UnitTests/TestWalletAccount.cs rename to tests/neo.UnitTests/TestWalletAccount.cs diff --git a/neo.UnitTests/UT_BigDecimal.cs b/tests/neo.UnitTests/UT_BigDecimal.cs similarity index 100% rename from neo.UnitTests/UT_BigDecimal.cs rename to tests/neo.UnitTests/UT_BigDecimal.cs diff --git a/neo.UnitTests/UT_Culture.cs b/tests/neo.UnitTests/UT_Culture.cs similarity index 100% rename from neo.UnitTests/UT_Culture.cs rename to tests/neo.UnitTests/UT_Culture.cs diff --git a/neo.UnitTests/UT_DataCache.cs b/tests/neo.UnitTests/UT_DataCache.cs similarity index 100% rename from neo.UnitTests/UT_DataCache.cs rename to tests/neo.UnitTests/UT_DataCache.cs diff --git a/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs similarity index 100% rename from neo.UnitTests/UT_Helper.cs rename to tests/neo.UnitTests/UT_Helper.cs diff --git a/neo.UnitTests/UT_NeoSystem.cs b/tests/neo.UnitTests/UT_NeoSystem.cs similarity index 100% rename from neo.UnitTests/UT_NeoSystem.cs rename to tests/neo.UnitTests/UT_NeoSystem.cs diff --git a/neo.UnitTests/UT_ProtocolSettings.cs b/tests/neo.UnitTests/UT_ProtocolSettings.cs similarity index 100% rename from neo.UnitTests/UT_ProtocolSettings.cs rename to tests/neo.UnitTests/UT_ProtocolSettings.cs diff --git a/neo.UnitTests/UT_UInt160.cs b/tests/neo.UnitTests/UT_UInt160.cs similarity index 100% rename from neo.UnitTests/UT_UInt160.cs rename to tests/neo.UnitTests/UT_UInt160.cs diff --git a/neo.UnitTests/UT_UInt256.cs b/tests/neo.UnitTests/UT_UInt256.cs similarity index 100% rename from neo.UnitTests/UT_UInt256.cs rename to tests/neo.UnitTests/UT_UInt256.cs diff --git a/neo.UnitTests/UT_UIntBase.cs b/tests/neo.UnitTests/UT_UIntBase.cs similarity index 100% rename from neo.UnitTests/UT_UIntBase.cs rename to tests/neo.UnitTests/UT_UIntBase.cs diff --git a/neo.UnitTests/UT_UIntBenchmarks.cs b/tests/neo.UnitTests/UT_UIntBenchmarks.cs similarity index 100% rename from neo.UnitTests/UT_UIntBenchmarks.cs rename to tests/neo.UnitTests/UT_UIntBenchmarks.cs diff --git a/neo.UnitTests/UT_Utility.cs b/tests/neo.UnitTests/UT_Utility.cs similarity index 100% rename from neo.UnitTests/UT_Utility.cs rename to tests/neo.UnitTests/UT_Utility.cs diff --git a/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs similarity index 100% rename from neo.UnitTests/VM/UT_Helper.cs rename to tests/neo.UnitTests/VM/UT_Helper.cs diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs similarity index 100% rename from neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs rename to tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs similarity index 100% rename from neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs rename to tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Contract.cs diff --git a/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs similarity index 100% rename from neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs rename to tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs diff --git a/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs similarity index 100% rename from neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs rename to tests/neo.UnitTests/Wallets/NEP6/UT_ScryptParameters.cs diff --git a/neo.UnitTests/Wallets/SQLite/UT_Account.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_Account.cs similarity index 100% rename from neo.UnitTests/Wallets/SQLite/UT_Account.cs rename to tests/neo.UnitTests/Wallets/SQLite/UT_Account.cs diff --git a/neo.UnitTests/Wallets/SQLite/UT_Address.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_Address.cs similarity index 100% rename from neo.UnitTests/Wallets/SQLite/UT_Address.cs rename to tests/neo.UnitTests/Wallets/SQLite/UT_Address.cs diff --git a/neo.UnitTests/Wallets/SQLite/UT_Contract.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_Contract.cs similarity index 100% rename from neo.UnitTests/Wallets/SQLite/UT_Contract.cs rename to tests/neo.UnitTests/Wallets/SQLite/UT_Contract.cs diff --git a/neo.UnitTests/Wallets/SQLite/UT_Key.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_Key.cs similarity index 100% rename from neo.UnitTests/Wallets/SQLite/UT_Key.cs rename to tests/neo.UnitTests/Wallets/SQLite/UT_Key.cs diff --git a/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs similarity index 100% rename from neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs rename to tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs diff --git a/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs similarity index 100% rename from neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs rename to tests/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs diff --git a/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs similarity index 100% rename from neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs rename to tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs diff --git a/neo.UnitTests/Wallets/UT_AssetDescriptor.cs b/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs similarity index 100% rename from neo.UnitTests/Wallets/UT_AssetDescriptor.cs rename to tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs diff --git a/neo.UnitTests/Wallets/UT_KeyPair.cs b/tests/neo.UnitTests/Wallets/UT_KeyPair.cs similarity index 100% rename from neo.UnitTests/Wallets/UT_KeyPair.cs rename to tests/neo.UnitTests/Wallets/UT_KeyPair.cs diff --git a/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs similarity index 100% rename from neo.UnitTests/Wallets/UT_Wallet.cs rename to tests/neo.UnitTests/Wallets/UT_Wallet.cs diff --git a/neo.UnitTests/Wallets/UT_WalletAccount.cs b/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs similarity index 100% rename from neo.UnitTests/Wallets/UT_WalletAccount.cs rename to tests/neo.UnitTests/Wallets/UT_WalletAccount.cs diff --git a/neo.UnitTests/Wallets/UT_Wallets_Helper.cs b/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs similarity index 100% rename from neo.UnitTests/Wallets/UT_Wallets_Helper.cs rename to tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs diff --git a/neo.UnitTests/neo.UnitTests.csproj b/tests/neo.UnitTests/neo.UnitTests.csproj similarity index 94% rename from neo.UnitTests/neo.UnitTests.csproj rename to tests/neo.UnitTests/neo.UnitTests.csproj index da3694e024..5857ea504f 100644 --- a/neo.UnitTests/neo.UnitTests.csproj +++ b/tests/neo.UnitTests/neo.UnitTests.csproj @@ -25,7 +25,7 @@ - + diff --git a/neo.UnitTests/protocol.json b/tests/neo.UnitTests/protocol.json similarity index 100% rename from neo.UnitTests/protocol.json rename to tests/neo.UnitTests/protocol.json From c6629b77e0bbfed80c751dea59976fc26a9524b4 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 26 Nov 2019 00:34:09 +0800 Subject: [PATCH 142/305] Remove Travis and use Github Actions (#1267) --- .github/workflows/dotnetcore.yml | 73 ++++++++++++++++++++++++++++++++ .travis.yml | 41 ------------------ README.md | 4 +- src/neo/neo.csproj | 3 +- 4 files changed, 77 insertions(+), 44 deletions(-) create mode 100644 .github/workflows/dotnetcore.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml new file mode 100644 index 0000000000..2aae8920e8 --- /dev/null +++ b/.github/workflows/dotnetcore.yml @@ -0,0 +1,73 @@ +name: .NET Core Test and Publish + +on: + push: + branches: master + pull_request: + +env: + DOTNET_VERSION: 3.0.100 + +jobs: + + Test: + runs-on: ubuntu-latest + steps: + - name: Chectout + uses: actions/checkout@v1 + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + - name: Check format + run: | + dotnet tool install --tool-path ./ dotnet-format + ./dotnet-format --check --dry-run -v diagnostic + - name: Test + run: | + find tests -name *.csproj | xargs -I % dotnet add % package coverlet.msbuild + dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=${GITHUB_WORKSPACE}/coverage/lcov + - name: Coveralls + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + PublishGithub: + if: github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/') + needs: Test + runs-on: ubuntu-latest + steps: + - name: Chectout + uses: actions/checkout@v1 + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + - name: Setup NuGet.exe for use with actions + uses: NuGet/setup-nuget@v1 + - name: Pack with dotnet + run: git rev-list --count HEAD |xargs printf "CI%05d" |xargs dotnet pack -c Debug -o out --include-source --version-suffix + - name: Publish to Github Packages + run: | + nuget source Add -Name "GitHub" -Source "https://nuget.pkg.github.com/neo-project/index.json" -UserName neo-project -Password ${GITHUB_TOKEN} + nuget push out/*.nupkg -Source "GitHub" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + PublishMyGet: + if: github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/') + needs: Test + runs-on: ubuntu-latest + steps: + - name: Chectout + uses: actions/checkout@v1 + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + - name: Pack with dotnet + run: git rev-list --count HEAD |xargs printf "CI%05d" |xargs dotnet pack -c Debug -o out --include-source --version-suffix + - name: Publish to MyGet + run: dotnet nuget push out/*.nupkg -s https://www.myget.org/F/neo/api/v2/package -k ${MYGET_TOKEN} -ss https://www.myget.org/F/neo/symbols/api/v2/package -sk ${MYGET_TOKEN} + env: + MYGET_TOKEN: ${{ secrets.MYGET_TOKEN }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f13cecc403..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,41 +0,0 @@ -language: csharp - -os: - - linux - -dist: bionic -mono: none -dotnet: 3.0.100 - -env: - - TEST_SUITE="without-cultures" - - TEST_SUITE="cultures" - -install: - - dotnet tool install -g dotnet-format - - export PATH="$PATH:$HOME/.dotnet/tools" - - dotnet-format --version -script: | - echo "Checking format..." - dotnet format --check --dry-run -w . -v diagnostic # check C# formatting for neo.sln - cd tests/neo.UnitTests - if [[ "$TEST_SUITE" == "cultures" ]]; then - dotnet test -v m --filter FullyQualifiedName=Neo.UnitTests.UT_Culture.All_Tests_Cultures - else - if [[ "$TEST_SUITE" == "without-cultures" ]]; then - # Test & Calculate coverage - find * -name *.csproj | xargs -I % dotnet add % package coverlet.msbuild - dotnet test -v m --filter FullyQualifiedName!=Neo.UnitTests.UT_Culture.All_Tests_Cultures /p:CollectCoverage=true /p:CoverletOutputFormat=opencover - else - # Only test - dotnet test -v m --filter FullyQualifiedName!=Neo.UnitTests.UT_Culture.All_Tests_Cultures - fi - fi -after_success: | - if [[ "$TEST_SUITE" == "without-cultures" ]]; then - # Send coverage - echo "Test Success - Branch($TRAVIS_BRANCH) Pull Request($TRAVIS_PULL_REQUEST) Tag($TRAVIS_TAG)" - bash <(curl -s https://codecov.io/bash) -v - fi - - diff --git a/README.md b/README.md index ac251a7a77..d567717e71 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ Current neo version. - - Current Coverage Status. + + Coverage Status License. diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 5556e8c660..5897121579 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -3,7 +3,8 @@ 2015-2019 The Neo Project Neo - 3.0.0-preview1 + 3.0.0 + preview1 The Neo Project netstandard2.1 true From 0be3125756392c58af25a47f7ba80ccaf312415b Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 26 Nov 2019 11:34:58 +0800 Subject: [PATCH 143/305] Optimize BigInteger (#1280) --- src/neo/Cryptography/Base58.cs | 7 +++---- src/neo/Cryptography/ECC/ECDsa.cs | 5 ++--- src/neo/Cryptography/ECC/ECFieldElement.cs | 8 +++----- src/neo/Cryptography/ECC/ECPoint.cs | 12 ++++++------ 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/neo/Cryptography/Base58.cs b/src/neo/Cryptography/Base58.cs index 9ddbea3dc8..1920bed10d 100644 --- a/src/neo/Cryptography/Base58.cs +++ b/src/neo/Cryptography/Base58.cs @@ -25,16 +25,15 @@ public static byte[] Decode(string input) // Leading zero bytes get encoded as leading `1` characters int leadingZeroCount = input.TakeWhile(c => c == Alphabet[0]).Count(); var leadingZeros = new byte[leadingZeroCount]; - var bytesWithoutLeadingZeros = bi.ToByteArray() - .Reverse()// to big endian - .SkipWhile(b => b == 0);//strip sign byte + if (bi.IsZero) return leadingZeros; + var bytesWithoutLeadingZeros = bi.ToByteArray(isUnsigned: true, isBigEndian: true); return leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray(); } public static string Encode(byte[] input) { // Decode byte[] to BigInteger - BigInteger value = new BigInteger(new byte[1].Concat(input).Reverse().ToArray()); + BigInteger value = new BigInteger(input, isUnsigned: true, isBigEndian: true); // Encode BigInteger to Base58 string var sb = new StringBuilder(); diff --git a/src/neo/Cryptography/ECC/ECDsa.cs b/src/neo/Cryptography/ECC/ECDsa.cs index 07373a7844..cae8dc4e7c 100644 --- a/src/neo/Cryptography/ECC/ECDsa.cs +++ b/src/neo/Cryptography/ECC/ECDsa.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Numerics; using System.Security.Cryptography; @@ -26,7 +25,7 @@ public ECDsa(ECPoint publicKey) private BigInteger CalculateE(BigInteger n, byte[] message) { int messageBitLength = message.Length * 8; - BigInteger trunc = new BigInteger(message.Reverse().Concat(new byte[1]).ToArray()); + BigInteger trunc = new BigInteger(message, isUnsigned: true, isBigEndian: true); if (n.GetBitLength() < messageBitLength) { trunc >>= messageBitLength - n.GetBitLength(); @@ -38,7 +37,7 @@ public BigInteger[] GenerateSignature(byte[] message) { if (privateKey == null) throw new InvalidOperationException(); BigInteger e = CalculateE(curve.N, message); - BigInteger d = new BigInteger(privateKey.Reverse().Concat(new byte[1]).ToArray()); + BigInteger d = new BigInteger(privateKey, isUnsigned: true, isBigEndian: true); BigInteger r, s; using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) { diff --git a/src/neo/Cryptography/ECC/ECFieldElement.cs b/src/neo/Cryptography/ECC/ECFieldElement.cs index 331e1e4408..33d593b4cf 100644 --- a/src/neo/Cryptography/ECC/ECFieldElement.cs +++ b/src/neo/Cryptography/ECC/ECFieldElement.cs @@ -142,12 +142,10 @@ public ECFieldElement Square() public byte[] ToByteArray() { - byte[] data = Value.ToByteArray(); + byte[] data = Value.ToByteArray(isUnsigned: true, isBigEndian: true); if (data.Length == 32) - return data.Reverse().ToArray(); - if (data.Length > 32) - return data.Take(32).Reverse().ToArray(); - return Enumerable.Repeat(0, 32 - data.Length).Concat(data.Reverse()).ToArray(); + return data; + return Enumerable.Repeat(0, 32 - data.Length).Concat(data).ToArray(); } public static ECFieldElement operator -(ECFieldElement x) diff --git a/src/neo/Cryptography/ECC/ECPoint.cs b/src/neo/Cryptography/ECC/ECPoint.cs index 803aab9987..d597c4a8c5 100644 --- a/src/neo/Cryptography/ECC/ECPoint.cs +++ b/src/neo/Cryptography/ECC/ECPoint.cs @@ -52,7 +52,7 @@ public static ECPoint DecodePoint(byte[] encoded, ECCurve curve) if (encoded.Length != (expectedLength + 1)) throw new FormatException("Incorrect length for compressed encoding"); int yTilde = encoded[0] & 1; - BigInteger X1 = new BigInteger(encoded.Skip(1).Reverse().Concat(new byte[1]).ToArray()); + BigInteger X1 = new BigInteger(encoded.AsSpan(1), isUnsigned: true, isBigEndian: true); p = DecompressPoint(yTilde, X1, curve); break; } @@ -60,8 +60,8 @@ public static ECPoint DecodePoint(byte[] encoded, ECCurve curve) { if (encoded.Length != (2 * expectedLength + 1)) throw new FormatException("Incorrect length for uncompressed/hybrid encoding"); - BigInteger X1 = new BigInteger(encoded.Skip(1).Take(expectedLength).Reverse().Concat(new byte[1]).ToArray()); - BigInteger Y1 = new BigInteger(encoded.Skip(1 + expectedLength).Reverse().Concat(new byte[1]).ToArray()); + BigInteger X1 = new BigInteger(encoded.AsSpan(1, expectedLength), isUnsigned: true, isBigEndian: true); + BigInteger Y1 = new BigInteger(encoded.AsSpan(1 + expectedLength), isUnsigned: true, isBigEndian: true); p = new ECPoint(new ECFieldElement(X1, curve), new ECFieldElement(Y1, curve), curve); break; } @@ -143,10 +143,10 @@ public byte[] EncodePoint(bool commpressed) else { data = new byte[65]; - byte[] yBytes = Y.Value.ToByteArray().Reverse().ToArray(); + byte[] yBytes = Y.Value.ToByteArray(isUnsigned: true, isBigEndian: true); Buffer.BlockCopy(yBytes, 0, data, 65 - yBytes.Length, yBytes.Length); } - byte[] xBytes = X.Value.ToByteArray().Reverse().ToArray(); + byte[] xBytes = X.Value.ToByteArray(isUnsigned: true, isBigEndian: true); Buffer.BlockCopy(xBytes, 0, data, 33 - xBytes.Length, xBytes.Length); data[0] = commpressed ? Y.Value.IsEven ? (byte)0x02 : (byte)0x03 : (byte)0x04; return data; @@ -379,7 +379,7 @@ private static sbyte[] WindowNaf(sbyte width, BigInteger k) throw new ArgumentException(); if (p.IsInfinity) return p; - BigInteger k = new BigInteger(n.Reverse().Concat(new byte[1]).ToArray()); + BigInteger k = new BigInteger(n, isUnsigned: true, isBigEndian: true); if (k.Sign == 0) return p.Curve.Infinity; return Multiply(p, k); From 1c326ca37fb514ed1afb3c57862d131ae976fe03 Mon Sep 17 00:00:00 2001 From: Erik van den Brink Date: Tue, 26 Nov 2019 04:58:50 +0100 Subject: [PATCH 144/305] apply strict enum checking (#1254) --- src/neo/Consensus/ConsensusMessage.cs | 2 ++ src/neo/Network/P2P/Payloads/TransactionAttribute.cs | 4 +++- src/neo/Wallets/SQLite/VerificationContract.cs | 8 +++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/neo/Consensus/ConsensusMessage.cs b/src/neo/Consensus/ConsensusMessage.cs index 0bca5534e1..856ccdc72b 100644 --- a/src/neo/Consensus/ConsensusMessage.cs +++ b/src/neo/Consensus/ConsensusMessage.cs @@ -14,6 +14,8 @@ public abstract class ConsensusMessage : ISerializable protected ConsensusMessage(ConsensusMessageType type) { + if (!Enum.IsDefined(typeof(ConsensusMessageType), type)) + throw new ArgumentOutOfRangeException(nameof(type)); this.Type = type; } diff --git a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs index fa92010004..d4c31b2e49 100644 --- a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -37,7 +37,9 @@ public JObject ToJson() public static TransactionAttribute FromJson(JObject json) { TransactionAttribute transactionAttribute = new TransactionAttribute(); - transactionAttribute.Usage = (TransactionAttributeUsage)(byte.Parse(json["usage"].AsString())); + transactionAttribute.Usage = (TransactionAttributeUsage)byte.Parse(json["usage"].AsString()); + if (!Enum.IsDefined(typeof(TransactionAttributeUsage), transactionAttribute.Usage)) + throw new ArgumentException(); transactionAttribute.Data = Convert.FromBase64String(json["data"].AsString()); return transactionAttribute; } diff --git a/src/neo/Wallets/SQLite/VerificationContract.cs b/src/neo/Wallets/SQLite/VerificationContract.cs index 587a0fe468..b195f50ca5 100644 --- a/src/neo/Wallets/SQLite/VerificationContract.cs +++ b/src/neo/Wallets/SQLite/VerificationContract.cs @@ -14,7 +14,13 @@ public class VerificationContract : SmartContract.Contract, IEquatable(); - ParameterList = reader.ReadVarBytes().Select(p => (ContractParameterType)p).ToArray(); + ParameterList = reader.ReadVarBytes().Select(p => + { + var ret = (ContractParameterType)p; + if (!Enum.IsDefined(typeof(ContractParameterType), ret)) + throw new FormatException(); + return ret; + }).ToArray(); Script = reader.ReadVarBytes(); } From 497840828051292bc8a9287dd32cd85b013404a4 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 26 Nov 2019 05:23:38 +0100 Subject: [PATCH 145/305] Small optimizations using cached VM Script (#1215) --- src/neo/Ledger/Blockchain.cs | 5 ++--- src/neo/Network/P2P/Payloads/Header.cs | 2 -- src/neo/SmartContract/Helper.cs | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index dea46ae3e8..07575b895e 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -53,7 +53,7 @@ public class FillCompleted { } Transactions = new[] { DeployNativeContracts() } }; - private readonly static byte[] onPersistNativeContractScript; + private readonly static Script onPersistNativeContractScript; private const int MaxTxToReverifyPerIdle = 10; private static readonly object lockObj = new object(); private readonly NeoSystem system; @@ -323,8 +323,7 @@ private RelayResultReason OnNewBlock(Block block) using (Snapshot snapshot = GetSnapshot()) { snapshot.Blocks.Add(block.Hash, block.Header.Trim()); - snapshot.HeaderHashIndex.GetAndChange().Hash = block.Hash; - snapshot.HeaderHashIndex.GetAndChange().Index = block.Index; + snapshot.HeaderHashIndex.GetAndChange().Set(block); SaveHeaderHashList(snapshot); snapshot.Commit(); } diff --git a/src/neo/Network/P2P/Payloads/Header.cs b/src/neo/Network/P2P/Payloads/Header.cs index eaef0dc160..193b6b977e 100644 --- a/src/neo/Network/P2P/Payloads/Header.cs +++ b/src/neo/Network/P2P/Payloads/Header.cs @@ -1,9 +1,7 @@ using Neo.IO.Json; using Neo.Ledger; -using Neo.Wallets; using System; using System.IO; -using System.Linq; namespace Neo.Network.P2P.Payloads { diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index f8c819dbeb..03e543a5b4 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -284,8 +284,8 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, Snapshot snaps { engine.LoadScript(verification); engine.LoadScript(verifiable.Witnesses[i].InvocationScript); - if (engine.Execute().HasFlag(VMState.FAULT)) return false; - if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().ToBoolean()) return false; + if (engine.Execute() == VMState.FAULT) return false; + if (!engine.ResultStack.TryPop(out var result) || !result.ToBoolean()) return false; } } return true; From 284b593900d4d2021974b343404d28576145d9e6 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 27 Nov 2019 11:45:40 +0800 Subject: [PATCH 146/305] Storage abstract (#1249) --- src/neo/Consensus/ConsensusContext.cs | 12 +-- src/neo/Consensus/ConsensusService.cs | 4 +- src/neo/IO/Caching/CloneCache.cs | 8 +- src/neo/IO/Caching/DataCache.cs | 10 +- src/neo/IO/Data/LevelDB/SliceBuilder.cs | 9 +- src/neo/Ledger/Blockchain.cs | 70 ++++++++++---- src/neo/Ledger/MemoryPool.cs | 8 +- src/neo/NeoSystem.cs | 6 +- src/neo/Network/P2P/Payloads/BlockBase.cs | 4 +- .../Network/P2P/Payloads/ConsensusPayload.cs | 4 +- src/neo/Network/P2P/Payloads/IInventory.cs | 2 +- src/neo/Network/P2P/Payloads/IVerifiable.cs | 2 +- src/neo/Network/P2P/Payloads/Transaction.cs | 8 +- src/neo/Network/P2P/ProtocolHandler.cs | 10 +- src/neo/Network/RPC/RpcServer.cs | 26 ++--- .../{CloneSnapshot.cs => ClonedView.cs} | 20 ++-- src/neo/Persistence/Helper.cs | 62 ------------ src/neo/Persistence/IPersistence.cs | 17 ---- src/neo/Persistence/IReadOnlyStore.cs | 13 +++ src/neo/Persistence/ISnapshot.cs | 14 +++ src/neo/Persistence/IStore.cs | 15 +++ src/neo/Persistence/LevelDB/DbCache.cs | 56 ----------- .../Persistence/LevelDB/DbMetaDataCache.cs | 42 --------- src/neo/Persistence/LevelDB/DbSnapshot.cs | 49 ---------- src/neo/Persistence/LevelDB/LevelDBStore.cs | 94 ------------------- src/neo/Persistence/LevelDB/Snapshot.cs | 55 +++++++++++ src/neo/Persistence/LevelDB/Store.cs | 69 ++++++++++++++ .../Memory/ByteArrayEqualityComparer.cs | 47 ++++++++++ src/neo/Persistence/Memory/Helper.cs | 12 +++ src/neo/Persistence/Memory/Snapshot.cs | 63 +++++++++++++ src/neo/Persistence/Memory/Store.cs | 60 ++++++++++++ src/neo/Persistence/{LevelDB => }/Prefixes.cs | 6 +- src/neo/Persistence/ReadOnlyView.cs | 33 +++++++ src/neo/Persistence/SnapshotView.cs | 46 +++++++++ src/neo/Persistence/Store.cs | 30 ------ src/neo/Persistence/StoreDataCache.cs | 54 +++++++++++ src/neo/Persistence/StoreMetaDataCache.cs | 37 ++++++++ .../Persistence/{Snapshot.cs => StoreView.cs} | 39 +++++++- src/neo/Plugins/IPersistencePlugin.cs | 4 +- src/neo/SmartContract/ApplicationEngine.cs | 10 +- .../ContractParametersContext.cs | 2 +- src/neo/SmartContract/Helper.cs | 2 +- .../Iterators/StorageIterator.cs | 4 +- .../SmartContract/Native/PolicyContract.cs | 10 +- .../SmartContract/Native/Tokens/GasToken.cs | 2 +- .../SmartContract/Native/Tokens/NeoToken.cs | 14 +-- .../SmartContract/Native/Tokens/Nep5Token.cs | 4 +- src/neo/Wallets/Wallet.cs | 6 +- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 2 +- .../Consensus/UT_ConsensusContext.cs | 2 +- .../Extensions/NativeContractExtensions.cs | 5 +- .../Nep5NativeContractExtensions.cs | 9 +- .../neo.UnitTests/IO/Caching/UT_DataCache.cs | 6 +- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 10 +- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 79 +++------------- .../Ledger/UT_SendersFeeMonitor.cs | 4 +- tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs | 10 +- .../Network/P2P/Payloads/UT_Transaction.cs | 39 +++++--- .../Network/P2P/UT_ProtocolHandler.cs | 10 +- .../Network/P2P/UT_RemoteNode.cs | 2 +- .../neo.UnitTests/Network/RPC/UT_RpcServer.cs | 3 +- .../Network/RPC/UT_TransactionManager.cs | 4 +- tests/neo.UnitTests/Plugins/UT_Plugin.cs | 3 +- .../Iterators/UT_StorageIterator.cs | 6 +- .../Native/Tokens/UT_GasToken.cs | 10 +- .../Native/Tokens/UT_NeoToken.cs | 45 +++++---- .../Native/Tokens/UT_Nep5Token.cs | 18 ++-- .../SmartContract/Native/UT_NativeContract.cs | 15 ++- .../SmartContract/Native/UT_PolicyContract.cs | 17 ++-- .../SmartContract/UT_ApplicationEngine.cs | 37 +++----- .../SmartContract/UT_InteropService.NEO.cs | 36 ++++--- .../SmartContract/UT_InteropService.cs | 68 +++++++------- .../SmartContract/UT_SmartContractHelper.cs | 38 +++----- .../SmartContract/UT_Syscalls.cs | 12 +-- tests/neo.UnitTests/TestBlockchain.cs | 60 +++--------- tests/neo.UnitTests/TestDataCache.cs | 53 ----------- tests/neo.UnitTests/TestMetaDataCache.cs | 26 ----- tests/neo.UnitTests/TestVerifiable.cs | 2 +- tests/neo.UnitTests/UT_DataCache.cs | 6 +- tests/neo.UnitTests/UT_NeoSystem.cs | 4 +- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 36 ++++--- 81 files changed, 943 insertions(+), 908 deletions(-) rename src/neo/Persistence/{CloneSnapshot.cs => ClonedView.cs} (52%) delete mode 100644 src/neo/Persistence/Helper.cs delete mode 100644 src/neo/Persistence/IPersistence.cs create mode 100644 src/neo/Persistence/IReadOnlyStore.cs create mode 100644 src/neo/Persistence/ISnapshot.cs create mode 100644 src/neo/Persistence/IStore.cs delete mode 100644 src/neo/Persistence/LevelDB/DbCache.cs delete mode 100644 src/neo/Persistence/LevelDB/DbMetaDataCache.cs delete mode 100644 src/neo/Persistence/LevelDB/DbSnapshot.cs delete mode 100644 src/neo/Persistence/LevelDB/LevelDBStore.cs create mode 100644 src/neo/Persistence/LevelDB/Snapshot.cs create mode 100644 src/neo/Persistence/LevelDB/Store.cs create mode 100644 src/neo/Persistence/Memory/ByteArrayEqualityComparer.cs create mode 100644 src/neo/Persistence/Memory/Helper.cs create mode 100644 src/neo/Persistence/Memory/Snapshot.cs create mode 100644 src/neo/Persistence/Memory/Store.cs rename src/neo/Persistence/{LevelDB => }/Prefixes.cs (76%) create mode 100644 src/neo/Persistence/ReadOnlyView.cs create mode 100644 src/neo/Persistence/SnapshotView.cs delete mode 100644 src/neo/Persistence/Store.cs create mode 100644 src/neo/Persistence/StoreDataCache.cs create mode 100644 src/neo/Persistence/StoreMetaDataCache.cs rename src/neo/Persistence/{Snapshot.cs => StoreView.cs} (53%) delete mode 100644 tests/neo.UnitTests/TestDataCache.cs delete mode 100644 tests/neo.UnitTests/TestMetaDataCache.cs diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index d05b4a64ea..cab0ce8370 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -21,7 +21,7 @@ internal class ConsensusContext : IDisposable, ISerializable /// /// Key for saving consensus state. /// - private static readonly byte[] ConsensusStateKey = { 0xf4 }; + private const byte ConsensusStatePrefix = 0xf4; public Block Block; public byte ViewNumber; @@ -42,11 +42,11 @@ internal class ConsensusContext : IDisposable, ISerializable /// public SendersFeeMonitor SendersFeeMonitor = new SendersFeeMonitor(); - public Snapshot Snapshot { get; private set; } + public SnapshotView Snapshot { get; private set; } private KeyPair keyPair; private int _witnessSize; private readonly Wallet wallet; - private readonly Store store; + private readonly IStore store; public int F => (Validators.Length - 1) / 3; public int M => Validators.Length - F; @@ -74,7 +74,7 @@ internal class ConsensusContext : IDisposable, ISerializable public int Size => throw new NotImplementedException(); - public ConsensusContext(Wallet wallet, Store store) + public ConsensusContext(Wallet wallet, IStore store) { this.wallet = wallet; this.store = store; @@ -146,7 +146,7 @@ public uint GetPrimaryIndex(byte viewNumber) public bool Load() { - byte[] data = store.Get(ConsensusStateKey); + 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)) @@ -409,7 +409,7 @@ public void Reset(byte viewNumber) public void Save() { - store.PutSync(ConsensusStateKey, this.ToArray()); + store.PutSync(ConsensusStatePrefix, null, this.ToArray()); } public void Serialize(BinaryWriter writer) diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 9d0bd5a343..3c5c334e11 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -46,7 +46,7 @@ internal class Timer { public uint Height; public byte ViewNumber; } /// private bool isRecovering = false; - public ConsensusService(IActorRef localNode, IActorRef taskManager, Store store, Wallet wallet) + public ConsensusService(IActorRef localNode, IActorRef taskManager, IStore store, Wallet wallet) : this(localNode, taskManager, new ConsensusContext(wallet, store)) { } @@ -601,7 +601,7 @@ protected override void PostStop() base.PostStop(); } - public static Props Props(IActorRef localNode, IActorRef taskManager, Store store, Wallet wallet) + public static Props Props(IActorRef localNode, IActorRef taskManager, IStore store, Wallet wallet) { return Akka.Actor.Props.Create(() => new ConsensusService(localNode, taskManager, store, wallet)).WithMailbox("consensus-service-mailbox"); } diff --git a/src/neo/IO/Caching/CloneCache.cs b/src/neo/IO/Caching/CloneCache.cs index 0dd030fcb6..559ee3d279 100644 --- a/src/neo/IO/Caching/CloneCache.cs +++ b/src/neo/IO/Caching/CloneCache.cs @@ -19,15 +19,15 @@ protected override void AddInternal(TKey key, TValue value) innerCache.Add(key, value); } - public override void DeleteInternal(TKey key) + protected override void DeleteInternal(TKey key) { innerCache.Delete(key); } - protected override IEnumerable> FindInternal(byte[] key_prefix) + protected override IEnumerable<(TKey, TValue)> FindInternal(byte[] key_prefix) { - foreach (KeyValuePair pair in innerCache.Find(key_prefix)) - yield return new KeyValuePair(pair.Key, pair.Value.Clone()); + foreach (var (key, value) in innerCache.Find(key_prefix)) + yield return (key, value.Clone()); } protected override TValue GetInternal(TKey key) diff --git a/src/neo/IO/Caching/DataCache.cs b/src/neo/IO/Caching/DataCache.cs index dc97031d10..373c4f2757 100644 --- a/src/neo/IO/Caching/DataCache.cs +++ b/src/neo/IO/Caching/DataCache.cs @@ -107,7 +107,7 @@ public void Delete(TKey key) } } - public abstract void DeleteInternal(TKey key); + protected abstract void DeleteInternal(TKey key); public void DeleteWhere(Func predicate) { @@ -123,7 +123,7 @@ public void DeleteWhere(Func predicate) /// /// Must maintain the deserialized format of TKey /// Entries found with the desired prefix - public IEnumerable> Find(byte[] key_prefix = null) + public IEnumerable<(TKey Key, TValue Value)> Find(byte[] key_prefix = null) { IEnumerable<(byte[], TKey, TValue)> cached; lock (dictionary) @@ -159,13 +159,13 @@ public void DeleteWhere(Func predicate) { if (!c2 || (c1 && ByteArrayComparer.Default.Compare(i1.KeyBytes, i2.KeyBytes) < 0)) { - yield return new KeyValuePair(i1.Key, i1.Item); + yield return (i1.Key, i1.Item); c1 = e1.MoveNext(); i1 = c1 ? e1.Current : default; } else { - yield return new KeyValuePair(i2.Key, i2.Item); + yield return (i2.Key, i2.Item); c2 = e2.MoveNext(); i2 = c2 ? e2.Current : default; } @@ -173,7 +173,7 @@ public void DeleteWhere(Func predicate) } } - protected abstract IEnumerable> FindInternal(byte[] key_prefix); + protected abstract IEnumerable<(TKey Key, TValue Value)> FindInternal(byte[] key_prefix); public IEnumerable GetChangeSet() { diff --git a/src/neo/IO/Data/LevelDB/SliceBuilder.cs b/src/neo/IO/Data/LevelDB/SliceBuilder.cs index d5888c6b5e..cf1cff0264 100644 --- a/src/neo/IO/Data/LevelDB/SliceBuilder.cs +++ b/src/neo/IO/Data/LevelDB/SliceBuilder.cs @@ -38,19 +38,22 @@ public SliceBuilder Add(long value) public SliceBuilder Add(IEnumerable value) { - data.AddRange(value); + if (value != null) + data.AddRange(value); return this; } public SliceBuilder Add(string value) { - data.AddRange(Encoding.UTF8.GetBytes(value)); + if (value != null) + data.AddRange(Encoding.UTF8.GetBytes(value)); return this; } public SliceBuilder Add(ISerializable value) { - data.AddRange(value.ToArray()); + if (value != null) + data.AddRange(value.ToArray()); return this; } diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 07575b895e..14b38860cf 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -62,9 +62,10 @@ public class FillCompleted { } private readonly Dictionary block_cache = new Dictionary(); private readonly Dictionary> block_cache_unverified = new Dictionary>(); internal readonly RelayCache ConsensusRelayCache = new RelayCache(100); - private Snapshot currentSnapshot; + private SnapshotView currentSnapshot; - public Store Store { get; } + public IStore Store { get; } + public ReadOnlyView View { get; } public MemoryPool MemPool { get; } public uint Height => currentSnapshot.Height; public uint HeaderHeight => currentSnapshot.HeaderHeight; @@ -95,27 +96,28 @@ static Blockchain() } } - public Blockchain(NeoSystem system, Store store) + public Blockchain(NeoSystem system, IStore store) { this.system = 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(store.GetHeaderHashList().Find().OrderBy(p => (uint)p.Key).SelectMany(p => p.Value.Hashes)); + 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(store.GetBlocks().Find().OrderBy(p => p.Value.Index).Select(p => p.Key)); + header_index.AddRange(View.Blocks.Find().OrderBy(p => p.Value.Index).Select(p => p.Key)); } else { - HashIndexState hashIndex = store.GetHeaderHashIndex().Get(); + HashIndexState hashIndex = View.HeaderHashIndex.Get(); if (hashIndex.Index >= stored_header_count) { - DataCache cache = store.GetBlocks(); + 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); @@ -139,13 +141,13 @@ public Blockchain(NeoSystem system, Store store) public bool ContainsBlock(UInt256 hash) { if (block_cache.ContainsKey(hash)) return true; - return Store.ContainsBlock(hash); + return View.ContainsBlock(hash); } public bool ContainsTransaction(UInt256 hash) { if (MemPool.ContainsKey(hash)) return true; - return Store.ContainsTransaction(hash); + return View.ContainsTransaction(hash); } private static Transaction DeployNativeContracts() @@ -175,11 +177,19 @@ private static Transaction DeployNativeContracts() }; } + public Block GetBlock(uint index) + { + if (index == 0) return GenesisBlock; + UInt256 hash = GetBlockHash(index); + if (hash == null) return null; + return GetBlock(hash); + } + public Block GetBlock(UInt256 hash) { if (block_cache.TryGetValue(hash, out Block block)) return block; - return Store.GetBlock(hash); + return View.GetBlock(hash); } public UInt256 GetBlockHash(uint index) @@ -193,16 +203,38 @@ public static UInt160 GetConsensusAddress(ECPoint[] validators) return Contract.CreateMultiSigRedeemScript(validators.Length - (validators.Length - 1) / 3, validators).ToScriptHash(); } - public Snapshot GetSnapshot() + 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 Store.GetSnapshot(); + return new SnapshotView(Store); } public Transaction GetTransaction(UInt256 hash) { if (MemPool.TryGetValue(hash, out Transaction transaction)) return transaction; - return Store.GetTransaction(hash); + return View.GetTransaction(hash); } private void OnImport(IEnumerable blocks) @@ -237,7 +269,7 @@ private void OnFillMemoryPool(IEnumerable transactions) // Add the transactions to the memory pool foreach (var tx in transactions) { - if (Store.ContainsTransaction(tx.Hash)) + if (View.ContainsTransaction(tx.Hash)) continue; if (!NativeContract.Policy.CheckPolicy(tx, currentSnapshot)) continue; @@ -320,7 +352,7 @@ private RelayResultReason OnNewBlock(Block block) if (block.Index == header_index.Count) { header_index.Add(block.Hash); - using (Snapshot snapshot = GetSnapshot()) + using (SnapshotView snapshot = GetSnapshot()) { snapshot.Blocks.Add(block.Hash, block.Header.Trim()); snapshot.HeaderHashIndex.GetAndChange().Set(block); @@ -344,7 +376,7 @@ private RelayResultReason OnNewConsensus(ConsensusPayload payload) private void OnNewHeaders(Header[] headers) { - using (Snapshot snapshot = GetSnapshot()) + using (SnapshotView snapshot = GetSnapshot()) { foreach (Header header in headers) { @@ -425,7 +457,7 @@ protected override void OnReceive(object message) private void Persist(Block block) { - using (Snapshot snapshot = GetSnapshot()) + using (SnapshotView snapshot = GetSnapshot()) { List all_application_executed = new List(); snapshot.PersistingBlock = block; @@ -503,12 +535,12 @@ protected override void PostStop() currentSnapshot?.Dispose(); } - public static Props Props(NeoSystem system, Store store) + public static Props Props(NeoSystem system, IStore store) { return Akka.Actor.Props.Create(() => new Blockchain(system, store)).WithMailbox("blockchain-mailbox"); } - private void SaveHeaderHashList(Snapshot snapshot = null) + private void SaveHeaderHashList(SnapshotView snapshot = null) { if ((header_index.Count - stored_header_count < 2000)) return; diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index 78df9b97f6..4b9f1b8139 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -105,7 +105,7 @@ public MemoryPool(NeoSystem system, int capacity) Capacity = capacity; } - internal bool LoadPolicy(Snapshot snapshot) + internal bool LoadPolicy(StoreView snapshot) { _maxTxPerBlock = (int)NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); long newFeePerByte = NativeContract.Policy.GetFeePerByte(snapshot); @@ -348,7 +348,7 @@ internal void InvalidateVerifiedTransactions() } // Note: this must only be called from a single thread (the Blockchain actor) - internal void UpdatePoolForBlockPersisted(Block block, Snapshot snapshot) + internal void UpdatePoolForBlockPersisted(Block block, StoreView snapshot) { bool policyChanged = LoadPolicy(snapshot); @@ -407,7 +407,7 @@ internal void InvalidateAllTransactions() } private int ReverifyTransactions(SortedSet verifiedSortedTxPool, - SortedSet unverifiedSortedTxPool, int count, double millisecondsTimeout, Snapshot snapshot) + SortedSet unverifiedSortedTxPool, int count, double millisecondsTimeout, StoreView snapshot) { DateTime reverifyCutOffTimeStamp = DateTime.UtcNow.AddMilliseconds(millisecondsTimeout); List reverifiedItems = new List(count); @@ -483,7 +483,7 @@ internal void InvalidateAllTransactions() /// 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, Snapshot snapshot) + internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, StoreView snapshot) { if (Blockchain.Singleton.Height < Blockchain.Singleton.HeaderHeight) return false; diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 9265f47645..b1ef9b6ec8 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -26,11 +26,11 @@ public class NeoSystem : IDisposable public IActorRef Consensus { get; private set; } public RpcServer RpcServer { get; private set; } - private readonly Store store; + private readonly IStore store; private ChannelsConfig start_message = null; private bool suspend = false; - public NeoSystem(Store store) + public NeoSystem(IStore store) { this.store = store; Plugin.LoadPlugins(this); @@ -69,7 +69,7 @@ internal void ResumeNodeStartup() } } - public void StartConsensus(Wallet wallet, Store consensus_store = null, bool ignoreRecoveryLogs = false) + public void StartConsensus(Wallet wallet, IStore consensus_store = null, bool ignoreRecoveryLogs = false) { Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, consensus_store ?? store, wallet)); Consensus.Tell(new ConsensusService.Start { IgnoreRecoveryLogs = ignoreRecoveryLogs }, Blockchain); diff --git a/src/neo/Network/P2P/Payloads/BlockBase.cs b/src/neo/Network/P2P/Payloads/BlockBase.cs index 4274cb990a..37ab7abe66 100644 --- a/src/neo/Network/P2P/Payloads/BlockBase.cs +++ b/src/neo/Network/P2P/Payloads/BlockBase.cs @@ -74,7 +74,7 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) NextConsensus = reader.ReadSerializable(); } - UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) + UInt160[] IVerifiable.GetScriptHashesForVerifying(StoreView snapshot) { if (PrevHash == UInt256.Zero) return new[] { Witness.ScriptHash }; Header prev_header = snapshot.GetHeader(PrevHash); @@ -124,7 +124,7 @@ public void FromJson(JObject json) Witness = ((JArray)json["witnesses"]).Select(p => Witness.FromJson(p)).FirstOrDefault(); } - public virtual bool Verify(Snapshot snapshot) + public virtual bool Verify(StoreView snapshot) { Header prev_header = snapshot.GetHeader(PrevHash); if (prev_header == null) return false; diff --git a/src/neo/Network/P2P/Payloads/ConsensusPayload.cs b/src/neo/Network/P2P/Payloads/ConsensusPayload.cs index 062e5d9830..21730a3470 100644 --- a/src/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ b/src/neo/Network/P2P/Payloads/ConsensusPayload.cs @@ -95,7 +95,7 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) Data = reader.ReadVarBytes(); } - UInt160[] IVerifiable.GetScriptHashesForVerifying(Snapshot snapshot) + UInt160[] IVerifiable.GetScriptHashesForVerifying(StoreView snapshot) { ECPoint[] validators = NativeContract.NEO.GetNextBlockValidators(snapshot); if (validators.Length <= ValidatorIndex) @@ -118,7 +118,7 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.WriteVarBytes(Data); } - public bool Verify(Snapshot snapshot) + public bool Verify(StoreView snapshot) { if (BlockIndex <= snapshot.Height) return false; diff --git a/src/neo/Network/P2P/Payloads/IInventory.cs b/src/neo/Network/P2P/Payloads/IInventory.cs index 26b62346d9..2b175647c5 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(Snapshot snapshot); + bool Verify(StoreView snapshot); } } diff --git a/src/neo/Network/P2P/Payloads/IVerifiable.cs b/src/neo/Network/P2P/Payloads/IVerifiable.cs index 8540d8a626..cecd0570da 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(Snapshot snapshot); + UInt160[] GetScriptHashesForVerifying(StoreView snapshot); void SerializeUnsigned(BinaryWriter writer); } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index de81275877..6bda3f8c6b 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -123,14 +123,14 @@ public override int GetHashCode() return Hash.GetHashCode(); } - public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) + public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) { var hashes = new HashSet { Sender }; hashes.UnionWith(Cosigners.Select(p => p.Account)); return hashes.OrderBy(p => p).ToArray(); } - public virtual bool Reverify(Snapshot snapshot, BigInteger totalSenderFeeFromPool) + public virtual bool Reverify(StoreView snapshot, BigInteger totalSenderFeeFromPool) { if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) return false; @@ -202,12 +202,12 @@ public static Transaction FromJson(JObject json) return tx; } - bool IInventory.Verify(Snapshot snapshot) + bool IInventory.Verify(StoreView snapshot) { return Verify(snapshot, BigInteger.Zero); } - public virtual bool Verify(Snapshot snapshot, BigInteger totalSenderFeeFromPool) + public virtual bool Verify(StoreView snapshot, BigInteger totalSenderFeeFromPool) { if (!Reverify(snapshot, totalSenderFeeFromPool)) return false; int size = Size; diff --git a/src/neo/Network/P2P/ProtocolHandler.cs b/src/neo/Network/P2P/ProtocolHandler.cs index 1e62e4dfbf..e16d7f238c 100644 --- a/src/neo/Network/P2P/ProtocolHandler.cs +++ b/src/neo/Network/P2P/ProtocolHandler.cs @@ -162,7 +162,7 @@ private void OnGetBlocksMessageReceived(GetBlocksPayload payload) { UInt256 hash = payload.HashStart; int count = payload.Count < 0 || payload.Count > InvPayload.MaxHashesCount ? InvPayload.MaxHashesCount : payload.Count; - TrimmedBlock state = Blockchain.Singleton.Store.GetBlocks().TryGet(hash); + TrimmedBlock state = Blockchain.Singleton.View.Blocks.TryGet(hash); if (state == null) return; List hashes = new List(); for (uint i = 1; i <= count; i++) @@ -182,7 +182,7 @@ private void OnGetBlockDataMessageReceived(GetBlockDataPayload payload) { for (uint i = payload.IndexStart, max = payload.IndexStart + payload.Count; i < max; i++) { - Block block = Blockchain.Singleton.Store.GetBlock(i); + Block block = Blockchain.Singleton.GetBlock(i); if (block == null) break; @@ -237,7 +237,7 @@ private void OnGetHeadersMessageReceived(GetBlocksPayload payload) { UInt256 hash = payload.HashStart; int count = payload.Count < 0 || payload.Count > HeadersPayload.MaxHeadersCount ? HeadersPayload.MaxHeadersCount : payload.Count; - DataCache cache = Blockchain.Singleton.Store.GetBlocks(); + DataCache cache = Blockchain.Singleton.View.Blocks; TrimmedBlock state = cache.TryGet(hash); if (state == null) return; List
headers = new List
(); @@ -273,11 +273,11 @@ private void OnInvMessageReceived(InvPayload payload) switch (payload.Type) { case InventoryType.Block: - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) hashes = hashes.Where(p => !snapshot.ContainsBlock(p)).ToArray(); break; case InventoryType.TX: - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) hashes = hashes.Where(p => !snapshot.ContainsTransaction(p)).ToArray(); break; } diff --git a/src/neo/Network/RPC/RpcServer.cs b/src/neo/Network/RPC/RpcServer.cs index 7414d54d10..953783b5ed 100644 --- a/src/neo/Network/RPC/RpcServer.cs +++ b/src/neo/Network/RPC/RpcServer.cs @@ -57,7 +57,7 @@ public void DeserializeUnsigned(BinaryReader reader) throw new NotImplementedException(); } - public UInt160[] GetScriptHashesForVerifying(Snapshot snapshot) + public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) { return _scriptHashesForVerifying; } @@ -435,12 +435,12 @@ private JObject GetBlock(JObject key, bool verbose) if (key is JNumber) { uint index = uint.Parse(key.AsString()); - block = Blockchain.Singleton.Store.GetBlock(index); + block = Blockchain.Singleton.GetBlock(index); } else { UInt256 hash = UInt256.Parse(key.AsString()); - block = Blockchain.Singleton.Store.GetBlock(hash); + block = Blockchain.Singleton.View.GetBlock(hash); } if (block == null) throw new RpcException(-100, "Unknown block"); @@ -448,7 +448,7 @@ private JObject GetBlock(JObject key, bool verbose) { JObject json = block.ToJson(); json["confirmations"] = Blockchain.Singleton.Height - block.Index + 1; - UInt256 hash = Blockchain.Singleton.Store.GetNextBlockHash(block.Hash); + UInt256 hash = Blockchain.Singleton.GetNextBlockHash(block.Hash); if (hash != null) json["nextblockhash"] = hash.ToString(); return json; @@ -476,12 +476,12 @@ private JObject GetBlockHeader(JObject key, bool verbose) if (key is JNumber) { uint height = uint.Parse(key.AsString()); - header = Blockchain.Singleton.Store.GetHeader(height); + header = Blockchain.Singleton.GetHeader(height); } else { UInt256 hash = UInt256.Parse(key.AsString()); - header = Blockchain.Singleton.Store.GetHeader(hash); + header = Blockchain.Singleton.View.GetHeader(hash); } if (header == null) throw new RpcException(-100, "Unknown block"); @@ -490,7 +490,7 @@ private JObject GetBlockHeader(JObject key, bool verbose) { JObject json = header.ToJson(); json["confirmations"] = Blockchain.Singleton.Height - header.Index + 1; - UInt256 hash = Blockchain.Singleton.Store.GetNextBlockHash(header.Hash); + UInt256 hash = Blockchain.Singleton.GetNextBlockHash(header.Hash); if (hash != null) json["nextblockhash"] = hash.ToString(); return json; @@ -516,7 +516,7 @@ private JObject GetConnectionCount() private JObject GetContractState(UInt160 script_hash) { - ContractState contract = Blockchain.Singleton.Store.GetContracts().TryGet(script_hash); + ContractState contract = Blockchain.Singleton.View.Contracts.TryGet(script_hash); return contract?.ToJson() ?? throw new RpcException(-100, "Unknown contract"); } @@ -564,10 +564,10 @@ private JObject GetRawTransaction(UInt256 hash, bool verbose) if (verbose) { JObject json = tx.ToJson(); - TransactionState txState = Blockchain.Singleton.Store.GetTransactions().TryGet(hash); + TransactionState txState = Blockchain.Singleton.View.Transactions.TryGet(hash); if (txState != null) { - Header header = Blockchain.Singleton.Store.GetHeader(txState.BlockIndex); + Header header = Blockchain.Singleton.GetHeader(txState.BlockIndex); json["blockhash"] = header.Hash.ToString(); json["confirmations"] = Blockchain.Singleton.Height - header.Index + 1; json["blocktime"] = header.Timestamp; @@ -580,7 +580,7 @@ private JObject GetRawTransaction(UInt256 hash, bool verbose) private JObject GetStorage(UInt160 script_hash, byte[] key) { - StorageItem item = Blockchain.Singleton.Store.GetStorages().TryGet(new StorageKey + StorageItem item = Blockchain.Singleton.View.Storages.TryGet(new StorageKey { ScriptHash = script_hash, Key = key @@ -590,14 +590,14 @@ private JObject GetStorage(UInt160 script_hash, byte[] key) private JObject GetTransactionHeight(UInt256 hash) { - uint? height = Blockchain.Singleton.Store.GetTransactions().TryGet(hash)?.BlockIndex; + uint? height = Blockchain.Singleton.View.Transactions.TryGet(hash)?.BlockIndex; if (height.HasValue) return height.Value; throw new RpcException(-100, "Unknown transaction"); } private JObject GetValidators() { - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { var validators = NativeContract.NEO.GetValidators(snapshot); return NativeContract.NEO.GetRegisteredValidators(snapshot).Select(p => diff --git a/src/neo/Persistence/CloneSnapshot.cs b/src/neo/Persistence/ClonedView.cs similarity index 52% rename from src/neo/Persistence/CloneSnapshot.cs rename to src/neo/Persistence/ClonedView.cs index 0b79bd2739..927207d6a1 100644 --- a/src/neo/Persistence/CloneSnapshot.cs +++ b/src/neo/Persistence/ClonedView.cs @@ -4,7 +4,7 @@ namespace Neo.Persistence { - internal class CloneSnapshot : Snapshot + internal class ClonedView : StoreView { public override DataCache Blocks { get; } public override DataCache Transactions { get; } @@ -14,16 +14,16 @@ internal class CloneSnapshot : Snapshot public override MetaDataCache BlockHashIndex { get; } public override MetaDataCache HeaderHashIndex { get; } - public CloneSnapshot(Snapshot snapshot) + public ClonedView(StoreView view) { - this.PersistingBlock = snapshot.PersistingBlock; - this.Blocks = snapshot.Blocks.CreateSnapshot(); - this.Transactions = snapshot.Transactions.CreateSnapshot(); - this.Contracts = snapshot.Contracts.CreateSnapshot(); - this.Storages = snapshot.Storages.CreateSnapshot(); - this.HeaderHashList = snapshot.HeaderHashList.CreateSnapshot(); - this.BlockHashIndex = snapshot.BlockHashIndex.CreateSnapshot(); - this.HeaderHashIndex = snapshot.HeaderHashIndex.CreateSnapshot(); + this.PersistingBlock = view.PersistingBlock; + this.Blocks = view.Blocks.CreateSnapshot(); + this.Transactions = view.Transactions.CreateSnapshot(); + this.Contracts = view.Contracts.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/Persistence/Helper.cs b/src/neo/Persistence/Helper.cs deleted file mode 100644 index 7f412e47d3..0000000000 --- a/src/neo/Persistence/Helper.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Neo.Ledger; -using Neo.Network.P2P.Payloads; - -namespace Neo.Persistence -{ - public static class Helper - { - public static bool ContainsBlock(this IPersistence persistence, UInt256 hash) - { - TrimmedBlock state = persistence.Blocks.TryGet(hash); - if (state == null) return false; - return state.IsBlock; - } - - public static bool ContainsTransaction(this IPersistence persistence, UInt256 hash) - { - TransactionState state = persistence.Transactions.TryGet(hash); - return state != null; - } - - public static Block GetBlock(this IPersistence persistence, uint index) - { - if (index == 0) return Blockchain.GenesisBlock; - UInt256 hash = Blockchain.Singleton.GetBlockHash(index); - if (hash == null) return null; - return persistence.GetBlock(hash); - } - - public static Block GetBlock(this IPersistence persistence, UInt256 hash) - { - TrimmedBlock state = persistence.Blocks.TryGet(hash); - if (state == null) return null; - if (!state.IsBlock) return null; - return state.GetBlock(persistence.Transactions); - } - - public static Header GetHeader(this IPersistence persistence, uint index) - { - if (index == 0) return Blockchain.GenesisBlock.Header; - UInt256 hash = Blockchain.Singleton.GetBlockHash(index); - if (hash == null) return null; - return persistence.GetHeader(hash); - } - - public static Header GetHeader(this IPersistence persistence, UInt256 hash) - { - return persistence.Blocks.TryGet(hash)?.Header; - } - - public static UInt256 GetNextBlockHash(this IPersistence persistence, UInt256 hash) - { - TrimmedBlock state = persistence.Blocks.TryGet(hash); - if (state == null) return null; - return Blockchain.Singleton.GetBlockHash(state.Index + 1); - } - - public static Transaction GetTransaction(this IPersistence persistence, UInt256 hash) - { - return persistence.Transactions.TryGet(hash)?.Transaction; - } - } -} diff --git a/src/neo/Persistence/IPersistence.cs b/src/neo/Persistence/IPersistence.cs deleted file mode 100644 index 0632b5d0fa..0000000000 --- a/src/neo/Persistence/IPersistence.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Neo.IO.Caching; -using Neo.IO.Wrappers; -using Neo.Ledger; - -namespace Neo.Persistence -{ - public interface IPersistence - { - DataCache Blocks { get; } - DataCache Transactions { get; } - DataCache Contracts { get; } - DataCache Storages { get; } - DataCache HeaderHashList { get; } - MetaDataCache BlockHashIndex { get; } - MetaDataCache HeaderHashIndex { get; } - } -} diff --git a/src/neo/Persistence/IReadOnlyStore.cs b/src/neo/Persistence/IReadOnlyStore.cs new file mode 100644 index 0000000000..7a23bd4c80 --- /dev/null +++ b/src/neo/Persistence/IReadOnlyStore.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Neo.Persistence +{ + /// + /// This interface provides methods to read from the database. + /// + public interface IReadOnlyStore + { + IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix); + byte[] TryGet(byte table, byte[] key); + } +} diff --git a/src/neo/Persistence/ISnapshot.cs b/src/neo/Persistence/ISnapshot.cs new file mode 100644 index 0000000000..335089d4bc --- /dev/null +++ b/src/neo/Persistence/ISnapshot.cs @@ -0,0 +1,14 @@ +using System; + +namespace Neo.Persistence +{ + /// + /// This interface provides methods for reading, writing, and committing from/to snapshot. + /// + public interface ISnapshot : IDisposable, IReadOnlyStore + { + void Commit(); + void Delete(byte table, byte[] key); + void Put(byte table, byte[] key, byte[] value); + } +} diff --git a/src/neo/Persistence/IStore.cs b/src/neo/Persistence/IStore.cs new file mode 100644 index 0000000000..e91e0b9386 --- /dev/null +++ b/src/neo/Persistence/IStore.cs @@ -0,0 +1,15 @@ +using System; + +namespace Neo.Persistence +{ + /// + /// This interface provides methods for reading, writing from/to database. Developers should implement this interface to provide new storage engines for NEO. + /// + public interface IStore : IDisposable, IReadOnlyStore + { + void Delete(byte table, byte[] key); + ISnapshot GetSnapshot(); + void Put(byte table, byte[] key, byte[] value); + void PutSync(byte table, byte[] key, byte[] value); + } +} diff --git a/src/neo/Persistence/LevelDB/DbCache.cs b/src/neo/Persistence/LevelDB/DbCache.cs deleted file mode 100644 index ae55b31326..0000000000 --- a/src/neo/Persistence/LevelDB/DbCache.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; -using Neo.IO.Data.LevelDB; -using System; -using System.Collections.Generic; - -namespace Neo.Persistence.LevelDB -{ - public class DbCache : DataCache - where TKey : IEquatable, ISerializable, new() - where TValue : class, ICloneable, ISerializable, new() - { - private readonly DB db; - private readonly ReadOptions options; - private readonly WriteBatch batch; - private readonly byte prefix; - - public DbCache(DB db, ReadOptions options, WriteBatch batch, byte prefix) - { - this.db = db; - this.options = options ?? ReadOptions.Default; - this.batch = batch; - this.prefix = prefix; - } - - protected override void AddInternal(TKey key, TValue value) - { - batch?.Put(prefix, key, value); - } - - public override void DeleteInternal(TKey key) - { - batch?.Delete(prefix, key); - } - - protected override IEnumerable> FindInternal(byte[] key_prefix) - { - return db.Find(options, SliceBuilder.Begin(prefix).Add(key_prefix), (k, v) => new KeyValuePair(k.ToArray().AsSerializable(1), v.ToArray().AsSerializable())); - } - - protected override TValue GetInternal(TKey key) - { - return db.Get(options, prefix, key); - } - - protected override TValue TryGetInternal(TKey key) - { - return db.TryGet(options, prefix, key); - } - - protected override void UpdateInternal(TKey key, TValue value) - { - batch?.Put(prefix, key, value); - } - } -} diff --git a/src/neo/Persistence/LevelDB/DbMetaDataCache.cs b/src/neo/Persistence/LevelDB/DbMetaDataCache.cs deleted file mode 100644 index 0163d84e39..0000000000 --- a/src/neo/Persistence/LevelDB/DbMetaDataCache.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; -using Neo.IO.Data.LevelDB; -using System; - -namespace Neo.Persistence.LevelDB -{ - internal class DbMetaDataCache : MetaDataCache - where T : class, ICloneable, ISerializable, new() - { - private readonly DB db; - private readonly ReadOptions options; - private readonly WriteBatch batch; - private readonly byte prefix; - - public DbMetaDataCache(DB db, ReadOptions options, WriteBatch batch, byte prefix, Func factory = null) - : base(factory) - { - this.db = db; - this.options = options ?? ReadOptions.Default; - this.batch = batch; - this.prefix = prefix; - } - - protected override void AddInternal(T item) - { - batch?.Put(prefix, item.ToArray()); - } - - protected override T TryGetInternal() - { - if (!db.TryGet(options, prefix, out Slice slice)) - return null; - return slice.ToArray().AsSerializable(); - } - - protected override void UpdateInternal(T item) - { - batch?.Put(prefix, item.ToArray()); - } - } -} diff --git a/src/neo/Persistence/LevelDB/DbSnapshot.cs b/src/neo/Persistence/LevelDB/DbSnapshot.cs deleted file mode 100644 index 365584fa7c..0000000000 --- a/src/neo/Persistence/LevelDB/DbSnapshot.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Neo.IO.Caching; -using Neo.IO.Data.LevelDB; -using Neo.IO.Wrappers; -using Neo.Ledger; -using LSnapshot = Neo.IO.Data.LevelDB.Snapshot; - -namespace Neo.Persistence.LevelDB -{ - internal class DbSnapshot : Snapshot - { - private readonly DB db; - private readonly LSnapshot snapshot; - private readonly WriteBatch batch; - - public override DataCache Blocks { get; } - public override DataCache Transactions { get; } - public override DataCache Contracts { get; } - public override DataCache Storages { get; } - public override DataCache HeaderHashList { get; } - public override MetaDataCache BlockHashIndex { get; } - public override MetaDataCache HeaderHashIndex { get; } - - public DbSnapshot(DB db) - { - this.db = db; - this.snapshot = db.GetSnapshot(); - this.batch = new WriteBatch(); - ReadOptions options = new ReadOptions { FillCache = false, Snapshot = snapshot }; - Blocks = new DbCache(db, options, batch, Prefixes.DATA_Block); - Transactions = new DbCache(db, options, batch, Prefixes.DATA_Transaction); - Contracts = new DbCache(db, options, batch, Prefixes.ST_Contract); - Storages = new DbCache(db, options, batch, Prefixes.ST_Storage); - HeaderHashList = new DbCache(db, options, batch, Prefixes.IX_HeaderHashList); - BlockHashIndex = new DbMetaDataCache(db, options, batch, Prefixes.IX_CurrentBlock); - HeaderHashIndex = new DbMetaDataCache(db, options, batch, Prefixes.IX_CurrentHeader); - } - - public override void Commit() - { - base.Commit(); - db.Write(WriteOptions.Default, batch); - } - - public override void Dispose() - { - snapshot.Dispose(); - } - } -} diff --git a/src/neo/Persistence/LevelDB/LevelDBStore.cs b/src/neo/Persistence/LevelDB/LevelDBStore.cs deleted file mode 100644 index 805c1fe915..0000000000 --- a/src/neo/Persistence/LevelDB/LevelDBStore.cs +++ /dev/null @@ -1,94 +0,0 @@ -using Neo.IO.Caching; -using Neo.IO.Data.LevelDB; -using Neo.IO.Wrappers; -using Neo.Ledger; -using System; -using System.Reflection; - -namespace Neo.Persistence.LevelDB -{ - public class LevelDBStore : Store, IDisposable - { - private readonly DB db; - - public LevelDBStore(string path) - { - this.db = DB.Open(path, new Options { CreateIfMissing = true }); - if (db.TryGet(ReadOptions.Default, SliceBuilder.Begin(Prefixes.SYS_Version), out Slice value) && Version.TryParse(value.ToString(), out Version version) && version >= Version.Parse("2.9.1")) - return; - WriteBatch batch = new WriteBatch(); - ReadOptions options = new ReadOptions { FillCache = false }; - using (Iterator it = db.NewIterator(options)) - { - for (it.SeekToFirst(); it.Valid(); it.Next()) - { - batch.Delete(it.Key()); - } - } - db.Put(WriteOptions.Default, SliceBuilder.Begin(Prefixes.SYS_Version), Assembly.GetExecutingAssembly().GetName().Version.ToString()); - db.Write(WriteOptions.Default, batch); - } - - public void Dispose() - { - db.Dispose(); - } - - public override byte[] Get(byte[] key) - { - if (!db.TryGet(ReadOptions.Default, key, out Slice slice)) - return null; - return slice.ToArray(); - } - - public override DataCache GetBlocks() - { - return new DbCache(db, null, null, Prefixes.DATA_Block); - } - - public override DataCache GetContracts() - { - return new DbCache(db, null, null, Prefixes.ST_Contract); - } - - public override Snapshot GetSnapshot() - { - return new DbSnapshot(db); - } - - public override DataCache GetStorages() - { - return new DbCache(db, null, null, Prefixes.ST_Storage); - } - - public override DataCache GetTransactions() - { - return new DbCache(db, null, null, Prefixes.DATA_Transaction); - } - - public override DataCache GetHeaderHashList() - { - return new DbCache(db, null, null, Prefixes.IX_HeaderHashList); - } - - public override MetaDataCache GetBlockHashIndex() - { - return new DbMetaDataCache(db, null, null, Prefixes.IX_CurrentBlock); - } - - public override MetaDataCache GetHeaderHashIndex() - { - return new DbMetaDataCache(db, null, null, Prefixes.IX_CurrentHeader); - } - - public override void Put(byte[] key, byte[] value) - { - db.Put(WriteOptions.Default, key, value); - } - - public override void PutSync(byte[] key, byte[] value) - { - db.Put(new WriteOptions { Sync = true }, key, value); - } - } -} diff --git a/src/neo/Persistence/LevelDB/Snapshot.cs b/src/neo/Persistence/LevelDB/Snapshot.cs new file mode 100644 index 0000000000..dbf5cbe278 --- /dev/null +++ b/src/neo/Persistence/LevelDB/Snapshot.cs @@ -0,0 +1,55 @@ +using Neo.IO.Data.LevelDB; +using System.Collections.Generic; +using System.Linq; +using LSnapshot = Neo.IO.Data.LevelDB.Snapshot; + +namespace Neo.Persistence.LevelDB +{ + internal class Snapshot : ISnapshot + { + private readonly DB db; + private readonly LSnapshot snapshot; + private readonly ReadOptions options; + private readonly WriteBatch batch; + + public Snapshot(DB db) + { + this.db = db; + this.snapshot = db.GetSnapshot(); + this.options = new ReadOptions { FillCache = false, Snapshot = snapshot }; + this.batch = new WriteBatch(); + } + + public void Commit() + { + db.Write(WriteOptions.Default, batch); + } + + public void Delete(byte table, byte[] key) + { + batch.Delete(SliceBuilder.Begin(table).Add(key)); + } + + public void Dispose() + { + snapshot.Dispose(); + } + + public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) + { + return db.Find(options, SliceBuilder.Begin(table).Add(prefix), (k, v) => (k.ToArray().Skip(1).ToArray(), v.ToArray())); + } + + public void Put(byte table, byte[] key, byte[] value) + { + batch.Put(SliceBuilder.Begin(table).Add(key), value); + } + + public byte[] TryGet(byte table, byte[] key) + { + if (!db.TryGet(options, SliceBuilder.Begin(table).Add(key), out Slice slice)) + return null; + return slice.ToArray(); + } + } +} diff --git a/src/neo/Persistence/LevelDB/Store.cs b/src/neo/Persistence/LevelDB/Store.cs new file mode 100644 index 0000000000..fbded76391 --- /dev/null +++ b/src/neo/Persistence/LevelDB/Store.cs @@ -0,0 +1,69 @@ +using Neo.IO.Data.LevelDB; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Neo.Persistence.LevelDB +{ + public class Store : IStore + { + private const byte SYS_Version = 0xf0; + private readonly DB db; + + public Store(string path) + { + this.db = DB.Open(path, new Options { CreateIfMissing = true }); + if (db.TryGet(ReadOptions.Default, SliceBuilder.Begin(SYS_Version), out Slice value) && Version.TryParse(value.ToString(), out Version version) && version >= Version.Parse("2.9.1")) + return; + WriteBatch batch = new WriteBatch(); + ReadOptions options = new ReadOptions { FillCache = false }; + using (Iterator it = db.NewIterator(options)) + { + for (it.SeekToFirst(); it.Valid(); it.Next()) + { + batch.Delete(it.Key()); + } + } + db.Put(WriteOptions.Default, SliceBuilder.Begin(SYS_Version), Assembly.GetExecutingAssembly().GetName().Version.ToString()); + db.Write(WriteOptions.Default, batch); + } + + public void Delete(byte table, byte[] key) + { + db.Delete(WriteOptions.Default, SliceBuilder.Begin(table).Add(key)); + } + + public void Dispose() + { + db.Dispose(); + } + + public IEnumerable<(byte[], byte[])> Find(byte table, byte[] prefix) + { + return db.Find(ReadOptions.Default, SliceBuilder.Begin(table).Add(prefix), (k, v) => (k.ToArray().Skip(1).ToArray(), v.ToArray())); + } + + public ISnapshot GetSnapshot() + { + return new Snapshot(db); + } + + public void Put(byte table, byte[] key, byte[] value) + { + db.Put(WriteOptions.Default, SliceBuilder.Begin(table).Add(key), value); + } + + public void PutSync(byte table, byte[] key, byte[] value) + { + db.Put(new WriteOptions { Sync = true }, SliceBuilder.Begin(table).Add(key), value); + } + + public byte[] TryGet(byte table, byte[] key) + { + if (!db.TryGet(ReadOptions.Default, SliceBuilder.Begin(table).Add(key), out Slice slice)) + return null; + return slice.ToArray(); + } + } +} diff --git a/src/neo/Persistence/Memory/ByteArrayEqualityComparer.cs b/src/neo/Persistence/Memory/ByteArrayEqualityComparer.cs new file mode 100644 index 0000000000..97096498aa --- /dev/null +++ b/src/neo/Persistence/Memory/ByteArrayEqualityComparer.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; + +namespace Neo.Persistence.Memory +{ + internal class ByteArrayEqualityComparer : IEqualityComparer + { + public static readonly ByteArrayEqualityComparer Default = new ByteArrayEqualityComparer(); + + public unsafe bool Equals(byte[] x, byte[] y) + { + if (ReferenceEquals(x, y)) return true; + if (x is null || y is null) return false; + int len = x.Length; + if (len != y.Length) return false; + if (len == 0) return true; + fixed (byte* xp = x, yp = y) + { + long* xlp = (long*)xp, ylp = (long*)yp; + for (; len >= 8; len -= 8) + { + if (*xlp != *ylp) return false; + xlp++; + ylp++; + } + byte* xbp = (byte*)xlp, ybp = (byte*)ylp; + for (; len > 0; len--) + { + if (*xbp != *ybp) return false; + xbp++; + ybp++; + } + } + return true; + } + + public int GetHashCode(byte[] obj) + { + unchecked + { + int hash = 17; + foreach (byte element in obj) + hash = hash * 31 + element; + return hash; + } + } + } +} diff --git a/src/neo/Persistence/Memory/Helper.cs b/src/neo/Persistence/Memory/Helper.cs new file mode 100644 index 0000000000..c3535d897d --- /dev/null +++ b/src/neo/Persistence/Memory/Helper.cs @@ -0,0 +1,12 @@ +namespace Neo.Persistence.Memory +{ + internal static class Helper + { + private static readonly byte[] EmptyBytes = new byte[0]; + + public static byte[] EnsureNotNull(this byte[] source) + { + return source ?? EmptyBytes; + } + } +} diff --git a/src/neo/Persistence/Memory/Snapshot.cs b/src/neo/Persistence/Memory/Snapshot.cs new file mode 100644 index 0000000000..82455ddea8 --- /dev/null +++ b/src/neo/Persistence/Memory/Snapshot.cs @@ -0,0 +1,63 @@ +using Neo.IO; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; + +namespace Neo.Persistence.Memory +{ + internal class Snapshot : ISnapshot + { + private readonly ConcurrentDictionary[] innerData; + private readonly ImmutableDictionary[] immutableData; + private readonly ConcurrentDictionary[] writeBatch; + + public Snapshot(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); + } + + 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; + } + + public void Delete(byte table, byte[] key) + { + writeBatch[table][key.EnsureNotNull()] = null; + } + + public void Dispose() + { + } + + public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) + { + IEnumerable> records = immutableData[table]; + if (prefix?.Length > 0) + records = records.Where(p => p.Key.Length >= prefix.Length && p.Key.Take(prefix.Length).SequenceEqual(prefix)); + records = records.OrderBy(p => p.Key, ByteArrayComparer.Default); + return records.Select(p => (p.Key, p.Value)); + } + + public void Put(byte table, byte[] key, byte[] value) + { + writeBatch[table][key.EnsureNotNull()] = value; + } + + public byte[] TryGet(byte table, byte[] key) + { + immutableData[table].TryGetValue(key.EnsureNotNull(), out byte[] value); + return value; + } + } +} diff --git a/src/neo/Persistence/Memory/Store.cs b/src/neo/Persistence/Memory/Store.cs new file mode 100644 index 0000000000..5e2d11eeb9 --- /dev/null +++ b/src/neo/Persistence/Memory/Store.cs @@ -0,0 +1,60 @@ +using Neo.IO; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.Persistence.Memory +{ + public class Store : IStore + { + private readonly ConcurrentDictionary[] innerData; + + public Store() + { + 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 _); + } + + public void Dispose() + { + } + + public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) + { + IEnumerable> records = innerData[table]; + if (prefix?.Length > 0) + records = records.Where(p => p.Key.Length >= prefix.Length && p.Key.Take(prefix.Length).SequenceEqual(prefix)); + records = records.OrderBy(p => p.Key, ByteArrayComparer.Default); + foreach (var pair in records) + yield return (pair.Key, pair.Value); + } + + public ISnapshot GetSnapshot() + { + return new Snapshot(innerData); + } + + public void Put(byte table, byte[] key, byte[] value) + { + PutSync(table, key, value); + } + + public void PutSync(byte table, byte[] key, byte[] value) + { + innerData[table][key.EnsureNotNull()] = value; + } + + public byte[] TryGet(byte table, byte[] key) + { + innerData[table].TryGetValue(key.EnsureNotNull(), out byte[] value); + return value; + } + } +} diff --git a/src/neo/Persistence/LevelDB/Prefixes.cs b/src/neo/Persistence/Prefixes.cs similarity index 76% rename from src/neo/Persistence/LevelDB/Prefixes.cs rename to src/neo/Persistence/Prefixes.cs index f40359a55a..dad8540787 100644 --- a/src/neo/Persistence/LevelDB/Prefixes.cs +++ b/src/neo/Persistence/Prefixes.cs @@ -1,4 +1,4 @@ -namespace Neo.Persistence.LevelDB +namespace Neo.Persistence { internal static class Prefixes { @@ -12,9 +12,7 @@ internal static class Prefixes public const byte IX_CurrentBlock = 0xc0; public const byte IX_CurrentHeader = 0xc1; - public const byte SYS_Version = 0xf0; - - /* Prefixes 0xf1 to 0xff are reserved for external use. + /* Prefixes 0xf0 to 0xff are reserved for external use. * * Note: The saved consensus state uses the Prefix 0xf4 */ diff --git a/src/neo/Persistence/ReadOnlyView.cs b/src/neo/Persistence/ReadOnlyView.cs new file mode 100644 index 0000000000..f138b616c1 --- /dev/null +++ b/src/neo/Persistence/ReadOnlyView.cs @@ -0,0 +1,33 @@ +using Neo.IO.Caching; +using Neo.IO.Wrappers; +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 Contracts => new StoreDataCache(store, Prefixes.ST_Contract); + public override DataCache Storages => new StoreDataCache(store, Prefixes.ST_Storage); + public override DataCache HeaderHashList => new StoreDataCache(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/Persistence/SnapshotView.cs b/src/neo/Persistence/SnapshotView.cs new file mode 100644 index 0000000000..5eb9071fe3 --- /dev/null +++ b/src/neo/Persistence/SnapshotView.cs @@ -0,0 +1,46 @@ +using Neo.IO.Caching; +using Neo.IO.Wrappers; +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 Contracts { get; } + public override DataCache Storages { get; } + public override DataCache 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); + Contracts = new StoreDataCache(snapshot, Prefixes.ST_Contract); + Storages = new StoreDataCache(snapshot, Prefixes.ST_Storage); + HeaderHashList = new StoreDataCache(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/Store.cs b/src/neo/Persistence/Store.cs deleted file mode 100644 index eab5c182b9..0000000000 --- a/src/neo/Persistence/Store.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Neo.IO.Caching; -using Neo.IO.Wrappers; -using Neo.Ledger; - -namespace Neo.Persistence -{ - public abstract class Store : IPersistence - { - DataCache IPersistence.Blocks => GetBlocks(); - DataCache IPersistence.Transactions => GetTransactions(); - DataCache IPersistence.Contracts => GetContracts(); - DataCache IPersistence.Storages => GetStorages(); - DataCache IPersistence.HeaderHashList => GetHeaderHashList(); - MetaDataCache IPersistence.BlockHashIndex => GetBlockHashIndex(); - MetaDataCache IPersistence.HeaderHashIndex => GetHeaderHashIndex(); - - public abstract byte[] Get(byte[] key); - public abstract DataCache GetBlocks(); - public abstract DataCache GetTransactions(); - public abstract DataCache GetContracts(); - public abstract DataCache GetStorages(); - public abstract DataCache GetHeaderHashList(); - public abstract MetaDataCache GetBlockHashIndex(); - public abstract MetaDataCache GetHeaderHashIndex(); - public abstract void Put(byte[] key, byte[] value); - public abstract void PutSync(byte[] key, byte[] value); - - public abstract Snapshot GetSnapshot(); - } -} diff --git a/src/neo/Persistence/StoreDataCache.cs b/src/neo/Persistence/StoreDataCache.cs new file mode 100644 index 0000000000..995a34290f --- /dev/null +++ b/src/neo/Persistence/StoreDataCache.cs @@ -0,0 +1,54 @@ +using Neo.IO; +using Neo.IO.Caching; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Neo.Persistence +{ + internal 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 IEnumerable<(TKey, TValue)> FindInternal(byte[] key_prefix) + { + return store.Find(prefix, key_prefix).Select(p => (p.Key.AsSerializable(), p.Value.AsSerializable())); + } + + protected override TValue GetInternal(TKey key) + { + return store.TryGet(prefix, key.ToArray()).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 new file mode 100644 index 0000000000..152a315e03 --- /dev/null +++ b/src/neo/Persistence/StoreMetaDataCache.cs @@ -0,0 +1,37 @@ +using Neo.IO; +using Neo.IO.Caching; +using System; + +namespace Neo.Persistence +{ + internal 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/Snapshot.cs b/src/neo/Persistence/StoreView.cs similarity index 53% rename from src/neo/Persistence/Snapshot.cs rename to src/neo/Persistence/StoreView.cs index 1e900800b0..c23e000ff2 100644 --- a/src/neo/Persistence/Snapshot.cs +++ b/src/neo/Persistence/StoreView.cs @@ -2,11 +2,13 @@ using Neo.IO.Wrappers; using Neo.Ledger; using Neo.Network.P2P.Payloads; -using System; namespace Neo.Persistence { - public abstract class Snapshot : IDisposable, IPersistence + /// + /// It provides a set of properties and methods for reading formatted data from the underlying storage. Such as and . + /// + public abstract class StoreView { public Block PersistingBlock { get; internal set; } public abstract DataCache Blocks { get; } @@ -22,9 +24,9 @@ public abstract class Snapshot : IDisposable, IPersistence public UInt256 CurrentBlockHash => BlockHashIndex.Get().Hash; public UInt256 CurrentHeaderHash => HeaderHashIndex.Get().Hash; - public Snapshot Clone() + public StoreView Clone() { - return new CloneSnapshot(this); + return new ClonedView(this); } public virtual void Commit() @@ -38,8 +40,35 @@ public virtual void Commit() HeaderHashIndex.Commit(); } - public virtual void Dispose() + public bool ContainsBlock(UInt256 hash) { + TrimmedBlock state = Blocks.TryGet(hash); + if (state == null) return false; + return state.IsBlock; + } + + public bool ContainsTransaction(UInt256 hash) + { + TransactionState state = Transactions.TryGet(hash); + return state != null; + } + + 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/Plugins/IPersistencePlugin.cs b/src/neo/Plugins/IPersistencePlugin.cs index 0f06ae51eb..14a3316115 100644 --- a/src/neo/Plugins/IPersistencePlugin.cs +++ b/src/neo/Plugins/IPersistencePlugin.cs @@ -7,8 +7,8 @@ namespace Neo.Plugins { public interface IPersistencePlugin { - void OnPersist(Snapshot snapshot, IReadOnlyList applicationExecutedList); - void OnCommit(Snapshot snapshot); + void OnPersist(StoreView snapshot, IReadOnlyList applicationExecutedList); + void OnCommit(StoreView snapshot); bool ShouldThrowExceptionFromCommit(Exception ex); } } diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 41c17aebeb..eb4eb5e7c2 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -21,7 +21,7 @@ public partial class ApplicationEngine : ExecutionEngine public TriggerType Trigger { get; } public IVerifiable ScriptContainer { get; } - public Snapshot Snapshot { get; } + public StoreView Snapshot { get; } public long GasConsumed { get; private set; } = 0; public UInt160 CurrentScriptHash => CurrentContext?.GetState().ScriptHash; public UInt160 CallingScriptHash => InvocationStack.Count > 1 ? InvocationStack.Peek(1).GetState().ScriptHash : null; @@ -29,7 +29,7 @@ public partial class ApplicationEngine : ExecutionEngine public IReadOnlyList Notifications => notifications; internal Dictionary InvocationCounter { get; } = new Dictionary(); - public ApplicationEngine(TriggerType trigger, IVerifiable container, Snapshot snapshot, long gas, bool testMode = false) + public ApplicationEngine(TriggerType trigger, IVerifiable container, StoreView snapshot, long gas, bool testMode = false) { this.gas_amount = GasFree + gas; this.testMode = testMode; @@ -84,7 +84,7 @@ protected override bool PreExecuteInstruction() return AddGas(OpCodePrices[CurrentContext.CurrentInstruction.OpCode]); } - private static Block CreateDummyBlock(Snapshot snapshot) + private static Block CreateDummyBlock(StoreView snapshot) { var currentBlock = snapshot.Blocks[snapshot.CurrentBlockHash]; return new Block @@ -105,7 +105,7 @@ private static Block CreateDummyBlock(Snapshot snapshot) }; } - public static ApplicationEngine Run(byte[] script, Snapshot snapshot, + public static ApplicationEngine Run(byte[] script, StoreView snapshot, IVerifiable container = null, Block persistingBlock = null, bool testMode = false, long extraGAS = default) { snapshot.PersistingBlock = persistingBlock ?? snapshot.PersistingBlock ?? CreateDummyBlock(snapshot); @@ -117,7 +117,7 @@ private static Block CreateDummyBlock(Snapshot snapshot) public static ApplicationEngine Run(byte[] script, IVerifiable container = null, Block persistingBlock = null, bool testMode = false, long extraGAS = default) { - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { return Run(script, snapshot, container, persistingBlock, testMode, extraGAS); } diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index 327c8b1ef8..4465e7831e 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -94,7 +94,7 @@ public IReadOnlyList ScriptHashes return _ScriptHashes; } - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { _ScriptHashes = Verifiable.GetScriptHashesForVerifying(snapshot); } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 03e543a5b4..4ddaf5fca5 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -254,7 +254,7 @@ public static UInt160 ToScriptHash(this byte[] script) return new UInt160(Crypto.Default.Hash160(script)); } - internal static bool VerifyWitnesses(this IVerifiable verifiable, Snapshot snapshot, long gas) + internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snapshot, long gas) { if (gas < 0) return false; diff --git a/src/neo/SmartContract/Iterators/StorageIterator.cs b/src/neo/SmartContract/Iterators/StorageIterator.cs index f35d70d890..a42875e1aa 100644 --- a/src/neo/SmartContract/Iterators/StorageIterator.cs +++ b/src/neo/SmartContract/Iterators/StorageIterator.cs @@ -6,9 +6,9 @@ namespace Neo.SmartContract.Iterators { internal class StorageIterator : IIterator { - private readonly IEnumerator> enumerator; + private readonly IEnumerator<(StorageKey Key, StorageItem Value)> enumerator; - public StorageIterator(IEnumerator> enumerator) + public StorageIterator(IEnumerator<(StorageKey, StorageItem)> enumerator) { this.enumerator = enumerator; } diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 797104924e..3726cf49fb 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -29,7 +29,7 @@ public PolicyContract() Manifest.Features = ContractFeatures.HasStorage; } - internal bool CheckPolicy(Transaction tx, Snapshot snapshot) + internal bool CheckPolicy(Transaction tx, StoreView snapshot) { UInt160[] blockedAccounts = GetBlockedAccounts(snapshot); if (blockedAccounts.Intersect(tx.GetScriptHashesForVerifying(snapshot)).Any()) @@ -72,7 +72,7 @@ private StackItem GetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray a return GetMaxTransactionsPerBlock(engine.Snapshot); } - public uint GetMaxTransactionsPerBlock(Snapshot snapshot) + public uint GetMaxTransactionsPerBlock(StoreView snapshot) { return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxTransactionsPerBlock)].Value, 0); } @@ -83,7 +83,7 @@ private StackItem GetMaxBlockSize(ApplicationEngine engine, VMArray args) return GetMaxBlockSize(engine.Snapshot); } - public uint GetMaxBlockSize(Snapshot snapshot) + public uint GetMaxBlockSize(StoreView snapshot) { return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxBlockSize)].Value, 0); } @@ -94,7 +94,7 @@ private StackItem GetFeePerByte(ApplicationEngine engine, VMArray args) return GetFeePerByte(engine.Snapshot); } - public long GetFeePerByte(Snapshot snapshot) + public long GetFeePerByte(StoreView snapshot) { return BitConverter.ToInt64(snapshot.Storages[CreateStorageKey(Prefix_FeePerByte)].Value, 0); } @@ -105,7 +105,7 @@ private StackItem GetBlockedAccounts(ApplicationEngine engine, VMArray args) return GetBlockedAccounts(engine.Snapshot).Select(p => (StackItem)p.ToArray()).ToList(); } - public UInt160[] GetBlockedAccounts(Snapshot snapshot) + public UInt160[] GetBlockedAccounts(StoreView snapshot) { return snapshot.Storages[CreateStorageKey(Prefix_BlockedAccounts)].Value.AsSerializableArray(); } diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index 4266494a4f..be46335940 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -60,7 +60,7 @@ private StackItem GetSysFeeAmount(ApplicationEngine engine, VMArray args) return GetSysFeeAmount(engine.Snapshot, index); } - public BigInteger GetSysFeeAmount(Snapshot snapshot, uint index) + public BigInteger GetSysFeeAmount(StoreView snapshot, uint index) { if (index == 0) return Blockchain.GenesisBlock.Transactions.Sum(p => p.SystemFee); StorageKey key = CreateStorageKey(Prefix_SystemFeeAmount, BitConverter.GetBytes(index)); diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index ba059aff4d..e0023b3841 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -33,7 +33,7 @@ internal NeoToken() this.TotalAmount = 100000000 * Factor; } - public override BigInteger TotalSupply(Snapshot snapshot) + public override BigInteger TotalSupply(StoreView snapshot) { return TotalAmount; } @@ -64,7 +64,7 @@ private void DistributeGas(ApplicationEngine engine, UInt160 account, AccountSta engine.Snapshot.Storages.GetAndChange(CreateAccountKey(account)).Value = state.ToByteArray(); } - private BigInteger CalculateBonus(Snapshot snapshot, BigInteger value, uint start, uint end) + private BigInteger CalculateBonus(StoreView snapshot, BigInteger value, uint start, uint end) { if (value.IsZero || start >= end) return BigInteger.Zero; if (value.Sign < 0) throw new ArgumentOutOfRangeException(nameof(value)); @@ -124,7 +124,7 @@ private StackItem UnclaimedGas(ApplicationEngine engine, VMArray args) return UnclaimedGas(engine.Snapshot, account, end); } - public BigInteger UnclaimedGas(Snapshot snapshot, UInt160 account, uint end) + public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) { StorageItem storage = snapshot.Storages.TryGet(CreateAccountKey(account)); if (storage is null) return BigInteger.Zero; @@ -139,7 +139,7 @@ private StackItem RegisterValidator(ApplicationEngine engine, VMArray args) return RegisterValidator(engine.Snapshot, pubkey); } - private bool RegisterValidator(Snapshot snapshot, ECPoint pubkey) + private bool RegisterValidator(StoreView snapshot, ECPoint pubkey) { StorageKey key = CreateStorageKey(Prefix_Validator, pubkey); if (snapshot.Storages.TryGet(key) != null) return false; @@ -199,7 +199,7 @@ private StackItem GetRegisteredValidators(ApplicationEngine engine, VMArray args return GetRegisteredValidators(engine.Snapshot).Select(p => new Struct(new StackItem[] { p.PublicKey.ToArray(), p.Votes })).ToArray(); } - public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetRegisteredValidators(Snapshot snapshot) + public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetRegisteredValidators(StoreView snapshot) { byte[] prefix_key = StorageKey.CreateSearchPrefix(Hash, new[] { Prefix_Validator }); return snapshot.Storages.Find(prefix_key).Select(p => @@ -215,7 +215,7 @@ private StackItem GetValidators(ApplicationEngine engine, VMArray args) return GetValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray()).ToList(); } - public ECPoint[] GetValidators(Snapshot snapshot) + public ECPoint[] GetValidators(StoreView snapshot) { StorageItem storage_count = snapshot.Storages.TryGet(CreateStorageKey(Prefix_ValidatorsCount)); if (storage_count is null) return Blockchain.StandbyValidators; @@ -240,7 +240,7 @@ private StackItem GetNextBlockValidators(ApplicationEngine engine, VMArray args) return GetNextBlockValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray()).ToList(); } - public ECPoint[] GetNextBlockValidators(Snapshot snapshot) + public ECPoint[] GetNextBlockValidators(StoreView snapshot) { StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_NextValidators)); if (storage is null) return Blockchain.StandbyValidators; diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index 6d35d00410..5728c2d8be 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -138,7 +138,7 @@ protected StackItem TotalSupply(ApplicationEngine engine, VMArray args) return TotalSupply(engine.Snapshot); } - public virtual BigInteger TotalSupply(Snapshot snapshot) + public virtual BigInteger TotalSupply(StoreView snapshot) { StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_TotalSupply)); if (storage is null) return BigInteger.Zero; @@ -151,7 +151,7 @@ protected StackItem BalanceOf(ApplicationEngine engine, VMArray args) return BalanceOf(engine.Snapshot, new UInt160(args[0].GetSpan().ToArray())); } - public virtual BigInteger BalanceOf(Snapshot snapshot, UInt160 account) + public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) { StorageItem storage = snapshot.Storages.TryGet(CreateAccountKey(account)); if (storage is null) return BigInteger.Zero; diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index bc52b9b368..f2cf92e7b2 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -221,7 +221,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null throw new ArgumentException($"The address {from.ToString()} was not found in the wallet"); accounts = new[] { from }; } - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { HashSet cosignerList = new HashSet(); byte[] script; @@ -290,14 +290,14 @@ public Transaction MakeTransaction(byte[] script, UInt160 sender = null, Transac throw new ArgumentException($"The address {sender.ToString()} was not found in the wallet"); accounts = new[] { sender }; } - using (Snapshot snapshot = Blockchain.Singleton.GetSnapshot()) + using (SnapshotView 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, attributes ?? new TransactionAttribute[0], cosigners ?? new Cosigner[0], balances_gas); } } - private Transaction MakeTransaction(Snapshot snapshot, byte[] script, TransactionAttribute[] attributes, Cosigner[] cosigners, List<(UInt160 Account, BigInteger Value)> balances_gas) + private Transaction MakeTransaction(StoreView snapshot, byte[] script, TransactionAttribute[] attributes, Cosigner[] cosigners, List<(UInt160 Account, BigInteger Value)> balances_gas) { Random rand = new Random(); foreach (var (account, value) in balances_gas) diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index c10e6c94e3..bc4c2277a0 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -41,7 +41,7 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() TestProbe subscriber = CreateTestProbe(); var mockWallet = new Mock(); mockWallet.Setup(p => p.GetAccount(It.IsAny())).Returns(p => new TestWalletAccount(p)); - ConsensusContext context = new ConsensusContext(mockWallet.Object, TestBlockchain.GetStore()); + ConsensusContext context = new ConsensusContext(mockWallet.Object, TestBlockchain.Store); int timeIndex = 0; var timeValues = new[] { diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs index 821b6f8da5..ee5eb5f9d1 100644 --- a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs +++ b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs @@ -38,7 +38,7 @@ public void TestSetup() _validatorKeys[x] = new KeyPair(pk); } - _context = new ConsensusContext(mockWallet.Object, TestBlockchain.GetStore()) + _context = new ConsensusContext(mockWallet.Object, TestBlockchain.Store) { Validators = _validatorKeys.Select(u => u.PublicKey).ToArray() }; diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index 9847bddd48..cbf4a109d9 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -1,4 +1,5 @@ using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -9,12 +10,12 @@ namespace Neo.UnitTests.Extensions { public static class NativeContractExtensions { - public static StackItem Call(this NativeContract contract, Neo.Persistence.Snapshot snapshot, string method, params ContractParameter[] args) + public static StackItem Call(this NativeContract contract, StoreView snapshot, string method, params ContractParameter[] args) { return Call(contract, snapshot, null, method, args); } - public static StackItem Call(this NativeContract contract, Neo.Persistence.Snapshot snapshot, IVerifiable container, string method, params ContractParameter[] args) + public static StackItem Call(this NativeContract contract, StoreView snapshot, IVerifiable container, string method, params ContractParameter[] args) { var engine = new ApplicationEngine(TriggerType.Application, container, snapshot, 0, true); diff --git a/tests/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs index f06a7eb201..4a04d97c84 100644 --- a/tests/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs @@ -1,5 +1,6 @@ using FluentAssertions; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -33,14 +34,14 @@ public ManualWitness(params UInt160[] hashForVerify) public void DeserializeUnsigned(BinaryReader reader) { } - public UInt160[] GetScriptHashesForVerifying(Persistence.Snapshot snapshot) => _hashForVerify; + public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) => _hashForVerify; public void Serialize(BinaryWriter writer) { } public void SerializeUnsigned(BinaryWriter writer) { } } - public static bool Transfer(this NativeContract contract, Persistence.Snapshot 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) { var engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(signFrom ? new UInt160(from) : null), snapshot, 0, true); @@ -89,7 +90,7 @@ public static string[] SupportedStandards(this NativeContract contract) .ToArray(); } - public static BigInteger TotalSupply(this NativeContract contract, Persistence.Snapshot snapshot) + public static BigInteger TotalSupply(this NativeContract contract, StoreView snapshot) { var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); @@ -109,7 +110,7 @@ public static BigInteger TotalSupply(this NativeContract contract, Persistence.S return (result as VM.Types.Integer).GetBigInteger(); } - public static BigInteger BalanceOf(this NativeContract contract, Persistence.Snapshot snapshot, byte[] account) + public static BigInteger BalanceOf(this NativeContract contract, StoreView snapshot, byte[] account) { var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); diff --git a/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs b/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs index 437f2b92f1..cd9752c819 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs +++ b/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs @@ -105,7 +105,7 @@ class MyDataCache : DataCache { public Dictionary InnerDict = new Dictionary(); - public override void DeleteInternal(TKey key) + protected override void DeleteInternal(TKey key) { InnerDict.Remove(key); } @@ -115,9 +115,9 @@ protected override void AddInternal(TKey key, TValue value) InnerDict.Add(key, value); } - protected override IEnumerable> FindInternal(byte[] key_prefix) + protected override IEnumerable<(TKey, TValue)> FindInternal(byte[] key_prefix) { - return InnerDict.Where(kvp => kvp.Key.ToArray().Take(key_prefix.Length).SequenceEqual(key_prefix)); + return InnerDict.Where(kvp => kvp.Key.ToArray().Take(key_prefix.Length).SequenceEqual(key_prefix)).Select(p => (p.Key, p.Value)); } protected override TValue GetInternal(TKey key) diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index af0ec69a79..27dd15b6aa 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -9,7 +9,7 @@ namespace Neo.UnitTests.Ledger { internal class TestBlock : Block { - public override bool Verify(Snapshot snapshot) + public override bool Verify(StoreView snapshot) { return true; } @@ -22,7 +22,7 @@ public static TestBlock Cast(Block input) internal class TestHeader : Header { - public override bool Verify(Snapshot snapshot) + public override bool Verify(StoreView snapshot) { return true; } @@ -37,14 +37,14 @@ public static TestHeader Cast(Header input) public class UT_Blockchain { private NeoSystem system; - private Store store; + private IStore store; Transaction txSample = Blockchain.GenesisBlock.Transactions[0]; [TestInitialize] public void Initialize() { - system = TestBlockchain.InitializeMockNeoSystem(); - store = TestBlockchain.GetStore(); + system = TestBlockchain.TheNeoSystem; + store = TestBlockchain.Store; Blockchain.Singleton.MemPool.TryAdd(txSample.Hash, txSample); } diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 631ae06dbc..c68504f4c6 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -3,7 +3,6 @@ using Moq; using Neo.Cryptography; using Neo.IO; -using Neo.IO.Caching; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -40,11 +39,11 @@ public void TestSetup() // protect against external changes on TimeProvider TimeProvider.ResetToDefault(); - NeoSystem TheNeoSystem = TestBlockchain.InitializeMockNeoSystem(); + TestBlockchain.InitializeMockNeoSystem(); // Create a MemoryPool with capacity of 100 - _unit = new MemoryPool(TheNeoSystem, 100); - _unit.LoadPolicy(TestBlockchain.GetStore().GetSnapshot()); + _unit = new MemoryPool(TestBlockchain.TheNeoSystem, 100); + _unit.LoadPolicy(Blockchain.Singleton.GetSnapshot()); // Verify capacity equals the amount specified _unit.Capacity.Should().Be(100); @@ -52,7 +51,7 @@ public void TestSetup() _unit.VerifiedCount.Should().Be(0); _unit.UnVerifiedCount.Should().Be(0); _unit.Count.Should().Be(0); - _unit2 = new MemoryPool(TheNeoSystem, 0); + _unit2 = new MemoryPool(TestBlockchain.TheNeoSystem, 0); plugin = new TestIMemoryPoolTxObserverPlugin(); } @@ -75,8 +74,8 @@ private Transaction CreateTransactionWithFee(long fee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); - mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(true); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); + mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(true); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); mock.Object.Script = randomBytes; mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = fee; @@ -100,8 +99,8 @@ private Transaction CreateTransactionWithFeeAndBalanceVerify(long fee) random.NextBytes(randomBytes); Mock mock = new Mock(); UInt160 sender = UInt160.Zero; - mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(((Snapshot snapshot, BigInteger amount) => NativeContract.GAS.BalanceOf(snapshot, sender) >= amount + fee)); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); + mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(((StoreView snapshot, BigInteger amount) => NativeContract.GAS.BalanceOf(snapshot, sender) >= amount + fee)); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); mock.Object.Script = randomBytes; mock.Object.Sender = sender; mock.Object.NetworkFee = fee; @@ -223,7 +222,7 @@ public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() // Simulate the transfer process in tx by burning the balance UInt160 sender = block.Transactions[0].Sender; - Snapshot snapshot = Blockchain.Singleton.GetSnapshot(); + SnapshotView snapshot = Blockchain.Singleton.GetSnapshot(); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, sender); ApplicationEngine applicationEngine = new ApplicationEngine(TriggerType.All, block, snapshot, (long)balance); @@ -412,10 +411,8 @@ public void TestGetVerifiedTransactions() [TestMethod] public void TestReVerifyTopUnverifiedTransactionsIfNeeded() { - NeoSystem TheNeoSystem = TestBlockchain.InitializeMockNeoSystem(); - var s = Blockchain.Singleton.Height; - _unit = new MemoryPool(TheNeoSystem, 600); - _unit.LoadPolicy(TestBlockchain.GetStore().GetSnapshot()); + _unit = new MemoryPool(TestBlockchain.TheNeoSystem, 600); + _unit.LoadPolicy(Blockchain.Singleton.GetSnapshot()); AddTransaction(CreateTransaction(100000001)); AddTransaction(CreateTransaction(100000001)); AddTransaction(CreateTransaction(100000001)); @@ -475,7 +472,7 @@ public void TestTryGetValue() [TestMethod] public void TestUpdatePoolForBlockPersisted() { - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); byte[] transactionsPerBlock = { 0x18, 0x00, 0x00, 0x00 }; // 24 byte[] feePerByte = { 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 }; // 1048576 StorageItem item1 = new StorageItem @@ -486,14 +483,12 @@ public void TestUpdatePoolForBlockPersisted() { Value = feePerByte }; - var myDataCache = new MyDataCache(); var key1 = CreateStorageKey(Prefix_MaxTransactionsPerBlock); var key2 = CreateStorageKey(Prefix_FeePerByte); key1.ScriptHash = NativeContract.Policy.Hash; key2.ScriptHash = NativeContract.Policy.Hash; - myDataCache.Add(key1, item1); - myDataCache.Add(key2, item2); - mockSnapshot.SetupGet(p => p.Storages).Returns(myDataCache); + snapshot.Storages.Add(key1, item1); + snapshot.Storages.Add(key2, item2); var tx1 = CreateTransaction(); var tx2 = CreateTransaction(); @@ -505,7 +500,7 @@ public void TestUpdatePoolForBlockPersisted() _unit.UnVerifiedCount.Should().Be(0); _unit.VerifiedCount.Should().Be(1); - _unit.UpdatePoolForBlockPersisted(block, mockSnapshot.Object); + _unit.UpdatePoolForBlockPersisted(block, snapshot); _unit.UnVerifiedCount.Should().Be(0); _unit.VerifiedCount.Should().Be(0); @@ -524,48 +519,4 @@ public StorageKey CreateStorageKey(byte prefix, byte[] key = null) return storageKey; } } - - public class MyDataCache : DataCache - where TKey : IEquatable, ISerializable - where TValue : class, ICloneable, ISerializable, new() - { - private readonly TValue _defaultValue; - - public MyDataCache() - { - _defaultValue = null; - } - - public MyDataCache(TValue defaultValue) - { - this._defaultValue = defaultValue; - } - public override void DeleteInternal(TKey key) - { - } - - protected override void AddInternal(TKey key, TValue value) - { - Add(key, value); - } - - protected override IEnumerable> FindInternal(byte[] key_prefix) - { - return Enumerable.Empty>(); - } - - protected override TValue GetInternal(TKey key) - { - return TryGet(key); - } - - protected override TValue TryGetInternal(TKey key) - { - return _defaultValue; - } - - protected override void UpdateInternal(TKey key, TValue value) - { - } - } } diff --git a/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs b/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs index 81fb0d9354..ac5cf57b7c 100644 --- a/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs +++ b/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs @@ -18,8 +18,8 @@ private Transaction CreateTransactionWithFee(long networkFee, long systemFee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); - mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(true); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); + mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(true); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); mock.Object.Script = randomBytes; mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = networkFee; diff --git a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs index 3704279701..482297f7ab 100644 --- a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs +++ b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Ledger; @@ -42,7 +42,7 @@ public void TestGetIsBlock() [TestMethod] public void TestGetBlock() { - var cache = new TestDataCache(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var tx1 = TestUtils.GetTransaction(); tx1.Script = new byte[] { 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, @@ -63,12 +63,12 @@ public void TestGetBlock() Transaction = tx2, BlockIndex = 1 }; - cache.Add(tx1.Hash, state1); - cache.Add(tx2.Hash, state2); + snapshot.Transactions.Add(tx1.Hash, state1); + snapshot.Transactions.Add(tx2.Hash, state2); TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); tblock.Hashes = new UInt256[] { tx1.Hash, tx2.Hash }; - Block block = tblock.GetBlock(cache); + Block block = tblock.GetBlock(snapshot.Transactions); block.Index.Should().Be(1); block.MerkleRoot.Should().Be(UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02")); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index cf5f4504fe..af12fe148e 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -5,7 +5,6 @@ using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.SmartContract.Native.Tokens; @@ -20,13 +19,12 @@ namespace Neo.UnitTests.Network.P2P.Payloads public class UT_Transaction { Transaction uut; - Store store; [TestInitialize] public void TestSetup() { + TestBlockchain.InitializeMockNeoSystem(); uut = new Transaction(); - store = TestBlockchain.GetStore(); } [TestMethod] @@ -86,10 +84,9 @@ public void Size_Get() [TestMethod] public void FeeIsMultiSigContract() { - var store = TestBlockchain.GetStore(); var walletA = TestUtils.GenerateTestWallet(); var walletB = TestUtils.GenerateTestWallet(); - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); using (var unlockA = walletA.Unlock("123")) using (var unlockB = walletB.Unlock("123")) @@ -121,6 +118,8 @@ public void FeeIsMultiSigContract() } .ToByteArray(); + snapshot.Commit(); + // Make transaction var tx = walletA.MakeTransaction(new TransferOutput[] @@ -175,7 +174,7 @@ public void FeeIsMultiSigContract() public void FeeIsSignatureContractDetailed() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); using (var unlock = wallet.Unlock("123")) { @@ -196,6 +195,8 @@ public void FeeIsSignatureContractDetailed() } .ToByteArray(); + snapshot.Commit(); + // Make transaction // self-transfer of 1e-8 GAS @@ -290,7 +291,7 @@ public void FeeIsSignatureContractDetailed() public void FeeIsSignatureContract_TestScope_Global() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -312,6 +313,8 @@ public void FeeIsSignatureContract_TestScope_Global() } .ToByteArray(); + snapshot.Commit(); + // Make transaction // Manually creating script @@ -381,7 +384,7 @@ public void FeeIsSignatureContract_TestScope_Global() public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -403,6 +406,8 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() } .ToByteArray(); + snapshot.Commit(); + // Make transaction // Manually creating script @@ -473,7 +478,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -495,6 +500,8 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() } .ToByteArray(); + snapshot.Commit(); + // Make transaction // Manually creating script @@ -568,7 +575,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -626,7 +633,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -648,6 +655,8 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() } .ToByteArray(); + snapshot.Commit(); + // Make transaction // Manually creating script @@ -723,7 +732,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() public void FeeIsSignatureContract_TestScope_NoScopeFAULT() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -775,7 +784,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() [TestMethod] public void Transaction_Reverify_Hashes_Length_Unequal_To_Witnesses_Length() { - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); Transaction txSimple = new Transaction { Version = 0x00, @@ -981,7 +990,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() cosigner.Scopes.Should().Be(WitnessScope.Global); var wallet = TestUtils.GenerateTestWallet(); - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -1003,6 +1012,8 @@ public void FeeIsSignatureContract_TestScope_Global_Default() } .ToByteArray(); + snapshot.Commit(); + // Make transaction // Manually creating script diff --git a/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs b/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs index f8b932c4f2..1f62a77580 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs @@ -9,26 +9,18 @@ namespace Neo.UnitTests.Network.P2P [TestClass] public class UT_ProtocolHandler : TestKit { - private NeoSystem testBlockchain; - [TestCleanup] public void Cleanup() { Shutdown(); } - [TestInitialize] - public void TestSetup() - { - testBlockchain = TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void ProtocolHandler_Test_SendVersion_TellParent() { var senderProbe = CreateTestProbe(); var parent = CreateTestProbe(); - var protocolActor = ActorOfAsTestActorRef(() => new ProtocolHandler(testBlockchain), parent); + var protocolActor = ActorOfAsTestActorRef(() => new ProtocolHandler(TestBlockchain.TheNeoSystem), parent); var payload = new VersionPayload() { diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs index 570cc904da..a700902f6c 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs @@ -23,7 +23,7 @@ public UT_RemoteNode() [ClassInitialize] public static void TestSetup(TestContext ctx) { - testBlockchain = TestBlockchain.InitializeMockNeoSystem(); + testBlockchain = TestBlockchain.TheNeoSystem; } [TestMethod] diff --git a/tests/neo.UnitTests/Network/RPC/UT_RpcServer.cs b/tests/neo.UnitTests/Network/RPC/UT_RpcServer.cs index fae945a21a..cfdfe7fbd8 100644 --- a/tests/neo.UnitTests/Network/RPC/UT_RpcServer.cs +++ b/tests/neo.UnitTests/Network/RPC/UT_RpcServer.cs @@ -14,8 +14,7 @@ public class UT_RpcServer [TestInitialize] public void Setup() { - var system = TestBlockchain.InitializeMockNeoSystem(); - server = new RpcServer(system); + server = new RpcServer(TestBlockchain.TheNeoSystem); } [TestCleanup] diff --git a/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs b/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs index e1cce967cd..e11c551a9a 100644 --- a/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs +++ b/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs @@ -3,6 +3,7 @@ using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; +using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.Network.RPC; @@ -156,8 +157,7 @@ public void TestSignMulti() .AddSignature(keyPair1) .Sign(); - var store = TestBlockchain.GetStore(); - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var tx = txManager.Tx; Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); diff --git a/tests/neo.UnitTests/Plugins/UT_Plugin.cs b/tests/neo.UnitTests/Plugins/UT_Plugin.cs index 3c6098e47a..7c3224c82a 100644 --- a/tests/neo.UnitTests/Plugins/UT_Plugin.cs +++ b/tests/neo.UnitTests/Plugins/UT_Plugin.cs @@ -65,8 +65,7 @@ public void TestNotifyPluginsLoadedAfterSystemConstructed() [TestMethod] public void TestResumeNodeStartupAndSuspendNodeStartup() { - var system = TestBlockchain.InitializeMockNeoSystem(); - TestLogPlugin.TestLoadPlugins(system); + TestLogPlugin.TestLoadPlugins(TestBlockchain.TheNeoSystem); TestLogPlugin.TestSuspendNodeStartup(); TestLogPlugin.TestSuspendNodeStartup(); TestLogPlugin.TestResumeNodeStartup().Should().BeFalse(); diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs index aad833b04e..a0126b3b03 100644 --- a/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs +++ b/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs @@ -14,7 +14,7 @@ public class UT_StorageIterator [TestMethod] public void TestGeneratorAndDispose() { - StorageIterator storageIterator = new StorageIterator(new List>().GetEnumerator()); + StorageIterator storageIterator = new StorageIterator(new List<(StorageKey, StorageItem)>().GetEnumerator()); Assert.IsNotNull(storageIterator); Action action = () => storageIterator.Dispose(); action.Should().NotThrow(); @@ -23,12 +23,12 @@ public void TestGeneratorAndDispose() [TestMethod] public void TestKeyAndValueAndNext() { - List> list = new List>(); + List<(StorageKey, StorageItem)> list = new List<(StorageKey, StorageItem)>(); StorageKey storageKey = new StorageKey(); storageKey.Key = new byte[1]; StorageItem storageItem = new StorageItem(); storageItem.Value = new byte[1]; - list.Add(new KeyValuePair(storageKey, storageItem)); + list.Add((storageKey, storageItem)); StorageIterator storageIterator = new StorageIterator(list.GetEnumerator()); storageIterator.Next(); Assert.AreEqual(new ByteArray(new byte[1]), storageIterator.Key()); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index 7e811d3eda..96beff3cbd 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; @@ -17,13 +16,10 @@ namespace Neo.UnitTests.SmartContract.Native.Tokens [TestClass] public class UT_GasToken { - Store Store; - [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - Store = TestBlockchain.GetStore(); } [TestMethod] @@ -41,7 +37,7 @@ public void TestSetup() [TestMethod] public void Check_BalanceOfTransferAndBurn() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, @@ -133,7 +129,7 @@ public void Check_BalanceOfTransferAndBurn() [TestMethod] public void Check_BadScript() { - var engine = new ApplicationEngine(TriggerType.Application, null, Store.GetSnapshot(), 0); + var engine = new ApplicationEngine(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), 0); var script = new ScriptBuilder(); script.Emit(OpCode.NOP); @@ -160,7 +156,7 @@ public void TestGetSysFeeAmount1() [TestMethod] public void TestGetSysFeeAmount2() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); NativeContract.GAS.GetSysFeeAmount(snapshot, 0).Should().Be(new BigInteger(0)); NativeContract.GAS.GetSysFeeAmount(snapshot, 1).Should().Be(new BigInteger(0)); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 19f7ad2f6a..8ae4516cab 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -21,13 +21,10 @@ namespace Neo.UnitTests.SmartContract.Native.Tokens [TestClass] public class UT_NeoToken { - private Store Store; - [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - Store = TestBlockchain.GetStore(); } [TestMethod] @@ -45,7 +42,7 @@ public void TestSetup() [TestMethod] public void Check_Vote() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, @@ -85,7 +82,7 @@ public void Check_Vote() [TestMethod] public void Check_UnclaimedGas() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, @@ -103,7 +100,7 @@ public void Check_UnclaimedGas() [TestMethod] public void Check_RegisterValidator() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var ret = Check_RegisterValidator(snapshot, new byte[0]); ret.State.Should().BeFalse(); @@ -148,7 +145,7 @@ public void Check_RegisterValidator() [TestMethod] public void Check_Transfer() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, @@ -201,7 +198,7 @@ public void Check_Transfer() [TestMethod] public void Check_BalanceOf() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); byte[] account = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, Blockchain.StandbyValidators).ToScriptHash().ToArray(); @@ -215,7 +212,7 @@ public void Check_BalanceOf() [TestMethod] public void Check_Initialize() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // StandbyValidators @@ -240,7 +237,7 @@ public void Check_Initialize() [TestMethod] public void Check_BadScript() { - var engine = new ApplicationEngine(TriggerType.Application, null, Store.GetSnapshot(), 0); + var engine = new ApplicationEngine(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), 0); var script = new ScriptBuilder(); script.Emit(OpCode.NOP); @@ -252,7 +249,7 @@ public void Check_BadScript() [TestMethod] public void TestCalculateBonus() { - Snapshot snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); StorageKey key = CreateStorageKey(20, UInt160.Zero.ToArray()); snapshot.Storages.Add(key, new StorageItem { @@ -295,7 +292,7 @@ public void TestGetNextBlockValidators1() [TestMethod] public void TestGetNextBlockValidators2() { - Snapshot snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var result = NativeContract.NEO.GetNextBlockValidators(snapshot); result.Length.Should().Be(7); result[0].ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); @@ -343,7 +340,7 @@ public void TestGetRegisteredValidators1() [TestMethod] public void TestGetRegisteredValidators2() { - Snapshot snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var result = NativeContract.NEO.GetRegisteredValidators(snapshot).ToArray(); result.Length.Should().Be(7); result[0].PublicKey.ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); @@ -390,7 +387,7 @@ public void TestGetValidators1() [TestMethod] public void TestGetValidators2() { - Snapshot snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var result = NativeContract.NEO.GetValidators(snapshot); result[0].ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); result[1].ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); @@ -416,7 +413,7 @@ public void TestGetValidators2() [TestMethod] public void TestInitialize() { - Snapshot snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var engine = new ApplicationEngine(TriggerType.System, null, snapshot, 0, true); Action action = () => NativeContract.NEO.Initialize(engine); action.Should().Throw(); @@ -425,6 +422,7 @@ public void TestInitialize() NativeContract.NEO.Initialize(engine).Should().BeFalse(); snapshot.Storages.Delete(CreateStorageKey(11)); + snapshot.PersistingBlock = Blockchain.GenesisBlock; engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); NativeContract.NEO.Initialize(engine).Should().BeTrue(); } @@ -448,14 +446,14 @@ public void TestOnBalanceChanging() [TestMethod] public void TestTotalSupply() { - Snapshot snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); NativeContract.NEO.TotalSupply(snapshot).Should().Be(new BigInteger(100000000)); } [TestMethod] public void TestUnclaimedGas() { - Snapshot snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); snapshot.Storages.Add(CreateStorageKey(20, UInt160.Zero.ToArray()), new StorageItem { @@ -467,7 +465,7 @@ public void TestUnclaimedGas() [TestMethod] public void TestVote() { - Snapshot snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); UInt160 account = UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4"); StorageKey keyAccount = CreateStorageKey(20, account.ToArray()); StorageKey keyValidator = CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()); @@ -529,7 +527,8 @@ public void TestValidatorState_ToByteArray() internal (bool State, bool Result) Transfer4TesingOnBalanceChanging(BigInteger amount, bool addVotes) { - Snapshot snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); + snapshot.PersistingBlock = Blockchain.GenesisBlock; var engine = new ApplicationEngine(TriggerType.Application, Blockchain.GenesisBlock, snapshot, 0, true); ScriptBuilder sb = new ScriptBuilder(); var tmp = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); @@ -578,7 +577,7 @@ public void TestValidatorState_ToByteArray() return (true, result.ToBoolean()); } - internal static (bool State, bool Result) Check_Vote(Snapshot snapshot, byte[] account, byte[][] pubkeys, bool signAccount) + internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] account, byte[][] pubkeys, bool signAccount) { var engine = new ApplicationEngine(TriggerType.Application, new Nep5NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot, 0, true); @@ -608,7 +607,7 @@ internal static (bool State, bool Result) Check_Vote(Snapshot snapshot, byte[] a return (true, result.ToBoolean()); } - internal static (bool State, bool Result) Check_RegisterValidator(Snapshot snapshot, byte[] pubkey) + internal static (bool State, bool Result) Check_RegisterValidator(StoreView snapshot, byte[] pubkey) { var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); @@ -632,7 +631,7 @@ internal static (bool State, bool Result) Check_RegisterValidator(Snapshot snaps return (true, result.ToBoolean()); } - internal static ECPoint[] Check_GetValidators(Snapshot snapshot) + internal static ECPoint[] Check_GetValidators(StoreView snapshot) { var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); @@ -652,7 +651,7 @@ internal static ECPoint[] Check_GetValidators(Snapshot snapshot) return (result as VM.Types.Array).Select(u => u.GetSpan().AsSerializable()).ToArray(); } - internal static (BigInteger Value, bool State) Check_UnclaimedGas(Snapshot snapshot, byte[] address) + internal static (BigInteger Value, bool State) Check_UnclaimedGas(StoreView snapshot, byte[] address) { var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs index a34c7a109f..a3bc7fa531 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs @@ -1,8 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using Neo.Ledger; -using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native.Tokens; using Neo.VM; @@ -20,8 +18,7 @@ public class UT_Nep5Token [TestMethod] public void TestTotalSupply() { - var mockSnapshot = new Mock(); - var myDataCache = new TestDataCache(); + var snapshot = Blockchain.Singleton.GetSnapshot(); StorageItem item = new StorageItem { Value = new byte[] { 0x01 } @@ -38,10 +35,9 @@ public void TestTotalSupply() var Hash = script.ToScriptHash(); key.ScriptHash = Hash; - myDataCache.Add(key, item); - mockSnapshot.SetupGet(p => p.Storages).Returns(myDataCache); + snapshot.Storages.Add(key, item); TestNep5Token test = new TestNep5Token(); - ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); StackItem stackItem = test.TotalSupply(ae, null); stackItem.GetBigInteger().Should().Be(1); } @@ -49,8 +45,7 @@ public void TestTotalSupply() [TestMethod] public void TestTotalSupplyDecimal() { - var mockSnapshot = new Mock(); - var myDataCache = new TestDataCache(); + var snapshot = Blockchain.Singleton.GetSnapshot(); TestNep5Token test = new TestNep5Token(); BigInteger totalSupply = 100_000_000; @@ -73,10 +68,9 @@ public void TestTotalSupplyDecimal() var Hash = script.ToScriptHash(); key.ScriptHash = Hash; - myDataCache.Add(key, item); - mockSnapshot.SetupGet(p => p.Storages).Returns(myDataCache); + snapshot.Storages.Add(key, item); - ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); StackItem stackItem = test.TotalSupply(ae, null); stackItem.GetBigInteger().Should().Be(10_000_000_000_000_000); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 61e93de6ad..71cfd3c093 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -1,6 +1,6 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Persistence; +using Neo.Ledger; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -13,13 +13,10 @@ namespace Neo.UnitTests.SmartContract.Native [TestClass] public class UT_NativeContract { - Store Store; - [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - Store = TestBlockchain.GetStore(); } [TestMethod] @@ -37,7 +34,7 @@ public void TestInitialize() [TestMethod] public void TestInvoke() { - ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, Store.GetSnapshot(), 0); + ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), 0); TestNativeContract testNativeContract = new TestNativeContract(); ScriptBuilder sb1 = new ScriptBuilder(); @@ -46,7 +43,7 @@ public void TestInvoke() engine1.LoadScript(sb1.ToArray()); testNativeContract.Invoke(engine1).Should().BeFalse(); - ApplicationEngine engine2 = new ApplicationEngine(TriggerType.Application, null, Store.GetSnapshot(), 0); + ApplicationEngine engine2 = new ApplicationEngine(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), 0); ScriptBuilder sb2 = new ScriptBuilder(); sb2.EmitSysCall("test".ToInteropMethodHash()); @@ -68,14 +65,14 @@ public void TestInvoke() [TestMethod] public void TestOnPersistWithArgs() { - ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, Store.GetSnapshot(), 0); + ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), 0); TestNativeContract testNativeContract = new TestNativeContract(); VMArray args = new VMArray(); VM.Types.Boolean result1 = new VM.Types.Boolean(false); testNativeContract.TestOnPersist(engine1, args).Should().Be(result1); - ApplicationEngine engine2 = new ApplicationEngine(TriggerType.System, null, Store.GetSnapshot(), 0); + ApplicationEngine engine2 = new ApplicationEngine(TriggerType.System, null, Blockchain.Singleton.GetSnapshot(), 0); VM.Types.Boolean result2 = new VM.Types.Boolean(true); testNativeContract.TestOnPersist(engine2, args).Should().Be(result2); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index b75141459f..0b15d0732d 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -15,13 +15,10 @@ namespace Neo.UnitTests.SmartContract.Native [TestClass] public class UT_PolicyContract { - Store Store; - [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - Store = TestBlockchain.GetStore(); } [TestMethod] @@ -30,7 +27,7 @@ public void TestSetup() [TestMethod] public void Check_Initialize() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var keyCount = snapshot.Storages.GetChangeSet().Count(); NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); @@ -57,7 +54,7 @@ public void Check_Initialize() [TestMethod] public void Check_SetMaxBlockSize() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // Fake blockchain @@ -103,7 +100,7 @@ public void Check_SetMaxBlockSize() [TestMethod] public void Check_SetMaxTransactionsPerBlock() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // Fake blockchain @@ -138,7 +135,7 @@ public void Check_SetMaxTransactionsPerBlock() [TestMethod] public void Check_SetFeePerByte() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // Fake blockchain @@ -173,7 +170,7 @@ public void Check_SetFeePerByte() [TestMethod] public void Check_Block_UnblockAccount() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); // Fake blockchain @@ -233,7 +230,7 @@ public void Check_Block_UnblockAccount() public void TestCheckPolicy() { Transaction tx = Blockchain.GenesisBlock.Transactions[0]; - Snapshot snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); StorageKey storageKey = new StorageKey { @@ -248,7 +245,7 @@ public void TestCheckPolicy() NativeContract.Policy.CheckPolicy(tx, snapshot).Should().BeFalse(); - snapshot = Store.GetSnapshot().Clone(); + snapshot = Blockchain.Singleton.GetSnapshot(); NativeContract.Policy.CheckPolicy(tx, snapshot).Should().BeTrue(); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index d8bdd8a857..3790aaf094 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -17,19 +17,17 @@ public class UT_ApplicationEngine { private string message = null; private StackItem item = null; - private Store Store; [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - Store = TestBlockchain.GetStore(); } [TestMethod] public void TestLog() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); ApplicationEngine.Log += Test_Log1; string logMessage = "TestMessage"; @@ -54,7 +52,7 @@ public void TestLog() [TestMethod] public void TestNotify() { - var snapshot = Store.GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); ApplicationEngine.Notify += Test_Notify1; StackItem notifyItem = "TestItem"; @@ -79,10 +77,10 @@ public void TestNotify() [TestMethod] public void TestDisposable() { - var snapshot = Store.GetSnapshot().Clone(); - var replica = snapshot.Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); + var m = new Mock(); var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); - engine.AddDisposable(replica).Should().Be(replica); + engine.AddDisposable(m.Object).Should().Be(m.Object); Action action = () => engine.Dispose(); action.Should().NotThrow(); } @@ -110,19 +108,12 @@ private void Test_Notify2(object sender, NotifyEventArgs e) [TestMethod] public void TestCreateDummyBlock() { - var mockSnapshot = new Mock(); - UInt256 currentBlockHash = UInt256.Parse("0x0000000000000000000000000000000000000000000000000000000000000000"); - TrimmedBlock block = new TrimmedBlock(); - var cache = new TestDataCache(); - cache.Add(currentBlockHash, block); - mockSnapshot.SetupGet(p => p.Blocks).Returns(cache); - TestMetaDataCache testCache = new TestMetaDataCache(); - mockSnapshot.SetupGet(p => p.BlockHashIndex).Returns(testCache); + var snapshot = Blockchain.Singleton.GetSnapshot(); byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; - ApplicationEngine.Run(SyscallSystemRuntimeCheckWitnessHash, mockSnapshot.Object); - mockSnapshot.Object.PersistingBlock.Version.Should().Be(0); - mockSnapshot.Object.PersistingBlock.PrevHash.Should().Be(currentBlockHash); - mockSnapshot.Object.PersistingBlock.MerkleRoot.Should().Be(new UInt256()); + 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()); } [TestMethod] @@ -134,10 +125,8 @@ public void TestOnSysCall() engine.LoadScript(SyscallSystemRuntimeCheckWitnessHash); engine.GetOnSysCall(descriptor.Hash).Should().BeFalse(); - var mockSnapshot = new Mock(); - TestMetaDataCache testCache = new TestMetaDataCache(); - mockSnapshot.SetupGet(p => p.BlockHashIndex).Returns(testCache); - engine = new TestApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0, true); + var snapshot = Blockchain.Singleton.GetSnapshot(); + engine = new TestApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(SyscallSystemRuntimeCheckWitnessHash); engine.GetOnSysCall(descriptor.Hash).Should().BeTrue(); } @@ -151,7 +140,7 @@ private static bool Blockchain_GetHeight(ApplicationEngine engine) public class TestApplicationEngine : ApplicationEngine { - public TestApplicationEngine(TriggerType trigger, IVerifiable container, Snapshot snapshot, long gas, bool testMode = false) : base(trigger, container, snapshot, gas, testMode) + public TestApplicationEngine(TriggerType trigger, IVerifiable container, StoreView snapshot, long gas, bool testMode = false) : base(trigger, container, snapshot, gas, testMode) { } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 3e60b91591..351f885f11 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -1,12 +1,10 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; @@ -142,10 +140,10 @@ public void TestAccount_IsStandard() InteropService.Invoke(engine, InteropService.Neo_Account_IsStandard).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(state.ScriptHash, state); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); InteropService.Invoke(engine, InteropService.Neo_Account_IsStandard).Should().BeTrue(); @@ -176,10 +174,10 @@ public void TestContract_Create() engine.CurrentContext.EvaluationStack.Push(script); InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeTrue(); - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(state.ScriptHash, state); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(state.Script); @@ -219,7 +217,7 @@ public void TestContract_Update() Signature = signature } }; - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); state.Manifest.Features = ContractFeatures.HasStorage; var storageItem = new StorageItem @@ -233,9 +231,9 @@ public void TestContract_Update() ScriptHash = state.ScriptHash, Key = new byte[] { 0x01 } }; - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(state.ScriptHash, state); + snapshot.Storages.Add(storageKey, storageItem); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(state.Script); engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); @@ -244,10 +242,10 @@ public void TestContract_Update() // Remove Storage flag with something stored state.Manifest.Features = ContractFeatures.NoProperty; - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); + snapshot.Contracts.Add(state.ScriptHash, state); + snapshot.Storages.Add(storageKey, storageItem); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(state.Script); engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); @@ -257,7 +255,7 @@ public void TestContract_Update() [TestMethod] public void TestStorage_Find() { - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); state.Manifest.Features = ContractFeatures.HasStorage; @@ -271,9 +269,9 @@ public void TestStorage_Find() ScriptHash = state.ScriptHash, Key = new byte[] { 0x01 } }; - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); - var engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(state.ScriptHash, state); + snapshot.Storages.Add(storageKey, storageItem); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 83f4a1485b..f04d6f5b6c 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -1,12 +1,10 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Manifest; using Neo.VM; @@ -31,7 +29,7 @@ public void TestSetup() public void Runtime_GetNotifications_Test() { UInt160 scriptHash2; - var snapshot = TestBlockchain.GetStore().GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); using (var script = new ScriptBuilder()) { @@ -445,10 +443,10 @@ public void TestBlockchain_GetContract() InteropService.Invoke(engine, InteropService.System_Blockchain_GetContract).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(state.ScriptHash, state); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); InteropService.Invoke(engine, InteropService.System_Blockchain_GetContract).Should().BeTrue(); @@ -483,7 +481,7 @@ public void TestStorage_GetReadOnlyContext() [TestMethod] public void TestStorage_Get() { - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); state.Manifest.Features = ContractFeatures.HasStorage; @@ -498,9 +496,9 @@ public void TestStorage_Get() Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, IsConstant = true }; - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); - var engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(state.ScriptHash, state); + snapshot.Storages.Add(storageKey, storageItem); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); @@ -512,8 +510,8 @@ public void TestStorage_Get() InteropService.Invoke(engine, InteropService.System_Storage_Get).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache()); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Delete(state.ScriptHash); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext @@ -574,7 +572,7 @@ public void TestStorage_Put() InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); //storage value is constant - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); state.Manifest.Features = ContractFeatures.HasStorage; var storageKey = new StorageKey @@ -587,9 +585,9 @@ public void TestStorage_Put() Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, IsConstant = true }; - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(state.ScriptHash, state); + snapshot.Storages.Add(storageKey, storageItem); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); key = new byte[] { 0x01 }; value = new byte[] { 0x02 }; @@ -622,7 +620,7 @@ public void TestStorage_PutEx() engine.CurrentContext.EvaluationStack.Push(1); InteropService.Invoke(engine, InteropService.System_Storage_PutEx).Should().BeFalse(); - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); state.Manifest.Features = ContractFeatures.HasStorage; var storageKey = new StorageKey @@ -635,9 +633,9 @@ public void TestStorage_PutEx() Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, IsConstant = false }; - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(state.ScriptHash, state); + snapshot.Storages.Add(storageKey, storageItem); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); var key = new byte[] { 0x01 }; var value = new byte[] { 0x02 }; @@ -661,7 +659,7 @@ public void TestStorage_Delete() InteropService.Invoke(engine, InteropService.System_Storage_Delete).Should().BeFalse(); - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); state.Manifest.Features = ContractFeatures.HasStorage; var storageKey = new StorageKey @@ -674,9 +672,9 @@ public void TestStorage_Delete() Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, IsConstant = false }; - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(state.ScriptHash, state); + snapshot.Storages.Add(storageKey, storageItem); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); state.Manifest.Features = ContractFeatures.HasStorage; var key = new byte[] { 0x01 }; @@ -733,13 +731,13 @@ public void TestInvoke() [TestMethod] public void TestContract_Call() { - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); state.Manifest.Features = ContractFeatures.HasStorage; byte[] method = Encoding.UTF8.GetBytes("method"); byte[] args = new byte[0]; - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(state.ScriptHash, state)); - var engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(state.ScriptHash, state); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(args); @@ -773,7 +771,7 @@ public void TestContract_Destroy() var engine = GetEngine(false, true); InteropService.Invoke(engine, InteropService.System_Contract_Destroy).Should().BeTrue(); - var mockSnapshot = new Mock(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); state.Manifest.Features = ContractFeatures.HasStorage; var scriptHash = UInt160.Parse("0xcb9f3b7c6fb1cf2c13a40637c189bdd066a272b4"); @@ -788,17 +786,17 @@ public void TestContract_Destroy() ScriptHash = scriptHash, Key = new byte[] { 0x01 } }; - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(scriptHash, state)); - mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache(storageKey, storageItem)); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(scriptHash, state); + snapshot.Storages.Add(storageKey, storageItem); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[0]); InteropService.Invoke(engine, InteropService.System_Contract_Destroy).Should().BeTrue(); //storages are removed - mockSnapshot = new Mock(); + snapshot = Blockchain.Singleton.GetSnapshot(); state = TestUtils.GetContract(); - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache(scriptHash, state)); - engine = new ApplicationEngine(TriggerType.Application, null, mockSnapshot.Object, 0); + snapshot.Contracts.Add(scriptHash, state); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[0]); InteropService.Invoke(engine, InteropService.System_Contract_Destroy).Should().BeTrue(); } @@ -812,7 +810,7 @@ public static void LogEvent(object sender, LogEventArgs args) private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSnapshot = false) { var tx = TestUtils.GetTransaction(); - var snapshot = TestBlockchain.GetStore().GetSnapshot().Clone(); + var snapshot = Blockchain.Singleton.GetSnapshot(); ApplicationEngine engine; if (hasContainer && hasSnapshot) { diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 8a85e7a4b4..6421bd1a31 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -1,9 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using Neo.Ledger; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract; using Neo.VM.Types; using Neo.Wallets; @@ -265,41 +263,31 @@ public void TestToScriptHash() [TestMethod] public void TestVerifyWitnesses() { - var mockSnapshot1 = new Mock(); + var snapshot1 = Blockchain.Singleton.GetSnapshot(); UInt256 index1 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); - TestDataCache testDataCache1 = new TestDataCache(); - testDataCache1.Add(index1, new TrimmedBlock()); - testDataCache1.Delete(index1); - mockSnapshot1.SetupGet(p => p.Blocks).Returns(testDataCache1); - Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(new Header() { PrevHash = index1 }, mockSnapshot1.Object, 100)); + snapshot1.Blocks.Add(index1, new TrimmedBlock()); + snapshot1.Blocks.Delete(index1); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(new Header() { PrevHash = index1 }, snapshot1, 100)); - var mockSnapshot2 = new Mock(); + var snapshot2 = Blockchain.Singleton.GetSnapshot(); UInt256 index2 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); TrimmedBlock block2 = new TrimmedBlock(); block2.NextConsensus = UInt160.Zero; - TestDataCache testDataCache21 = new TestDataCache(); - testDataCache21.Add(index2, block2); + snapshot2.Blocks.Add(index2, block2); Header header2 = new Header() { PrevHash = index2, Witness = new Witness { VerificationScript = new byte[0] } }; - mockSnapshot2.SetupGet(p => p.Blocks).Returns(testDataCache21); - TestDataCache testDataCache22 = new TestDataCache(); - testDataCache22.Add(UInt160.Zero, new ContractState()); - testDataCache22.Delete(UInt160.Zero); - mockSnapshot2.SetupGet(p => p.Contracts).Returns(testDataCache22); - Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header2, mockSnapshot2.Object, 100)); + snapshot2.Contracts.Add(UInt160.Zero, new ContractState()); + snapshot2.Contracts.Delete(UInt160.Zero); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header2, snapshot2, 100)); - var mockSnapshot3 = new Mock(); + var snapshot3 = Blockchain.Singleton.GetSnapshot(); UInt256 index3 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); TrimmedBlock block3 = new TrimmedBlock(); block3.NextConsensus = UInt160.Zero; - TestDataCache testDataCache31 = new TestDataCache(); - testDataCache31.Add(index3, block3); + snapshot3.Blocks.Add(index3, block3); Header header3 = new Header() { PrevHash = index3, Witness = new Witness { VerificationScript = new byte[0] } }; - mockSnapshot3.SetupGet(p => p.Blocks).Returns(testDataCache31); - TestDataCache testDataCache32 = new TestDataCache(); - testDataCache32.Add(UInt160.Zero, new ContractState()); - mockSnapshot3.SetupGet(p => p.Contracts).Returns(testDataCache32); - Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header3, mockSnapshot3.Object, 100)); + snapshot3.Contracts.Add(UInt160.Zero, new ContractState()); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header3, snapshot3, 100)); } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index f54b533106..7a1c4212c0 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -45,7 +45,7 @@ public void System_Blockchain_GetBlock() Transactions = new Transaction[] { tx } }; - var snapshot = TestBlockchain.GetStore().GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); using (var script = new ScriptBuilder()) { @@ -63,8 +63,8 @@ public void System_Blockchain_GetBlock() // With block - var blocks = (TestDataCache)snapshot.Blocks; - var txs = (TestDataCache)snapshot.Transactions; + 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 }); @@ -88,7 +88,7 @@ public void System_Blockchain_GetBlock() [TestMethod] public void System_ExecutionEngine_GetScriptContainer() { - var snapshot = TestBlockchain.GetStore().GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); using (var script = new ScriptBuilder()) { script.EmitSysCall(InteropService.System_ExecutionEngine_GetScriptContainer); @@ -136,8 +136,8 @@ public void System_ExecutionEngine_GetScriptContainer() public void System_Runtime_GetInvocationCounter() { ContractState contractA, contractB, contractC; - var snapshot = TestBlockchain.GetStore().GetSnapshot(); - var contracts = (TestDataCache)snapshot.Contracts; + var snapshot = Blockchain.Singleton.GetSnapshot(); + var contracts = snapshot.Contracts; // Create dummy contracts diff --git a/tests/neo.UnitTests/TestBlockchain.cs b/tests/neo.UnitTests/TestBlockchain.cs index 5740c13b08..6b0cdccd94 100644 --- a/tests/neo.UnitTests/TestBlockchain.cs +++ b/tests/neo.UnitTests/TestBlockchain.cs @@ -1,67 +1,29 @@ -using Moq; -using Neo.IO.Wrappers; using Neo.Ledger; using Neo.Persistence; using System; +using MemoryStore = Neo.Persistence.Memory.Store; namespace Neo.UnitTests { public static class TestBlockchain { - private static NeoSystem TheNeoSystem; - private static Mock _Store; - - public static Store GetStore() - { - if (_Store == null) InitializeMockNeoSystem(); - return _Store.Object; - } + public static readonly IStore Store; + public static readonly NeoSystem TheNeoSystem; static TestBlockchain() { - InitializeMockNeoSystem(); - GetStore(); - } - - public static NeoSystem InitializeMockNeoSystem() - { - if (TheNeoSystem == null) - { - var mockSnapshot = new Mock(); - mockSnapshot.SetupGet(p => p.Blocks).Returns(new TestDataCache()); - mockSnapshot.SetupGet(p => p.Transactions).Returns(new TestDataCache()); - mockSnapshot.SetupGet(p => p.Contracts).Returns(new TestDataCache()); - mockSnapshot.SetupGet(p => p.Storages).Returns(new TestDataCache()); - mockSnapshot.SetupGet(p => p.HeaderHashList).Returns(new TestDataCache()); - mockSnapshot.SetupGet(p => p.BlockHashIndex).Returns(new TestMetaDataCache()); - mockSnapshot.SetupGet(p => p.HeaderHashIndex).Returns(new TestMetaDataCache()); - - _Store = new Mock(); - - var defaultTx = TestUtils.CreateRandomHashTransaction(); - var txState = new TransactionState - { - BlockIndex = 1, - Transaction = defaultTx - }; - _Store.Setup(p => p.GetBlocks()).Returns(new TestDataCache()); - _Store.Setup(p => p.GetTransactions()).Returns(new TestDataCache(defaultTx.Hash, txState)); - _Store.Setup(p => p.GetContracts()).Returns(new TestDataCache()); - _Store.Setup(p => p.GetStorages()).Returns(new TestDataCache()); - _Store.Setup(p => p.GetHeaderHashList()).Returns(new TestDataCache()); - _Store.Setup(p => p.GetBlockHashIndex()).Returns(new TestMetaDataCache()); - _Store.Setup(p => p.GetHeaderHashIndex()).Returns(new TestMetaDataCache()); - _Store.Setup(p => p.GetSnapshot()).Returns(mockSnapshot.Object); + Store = new MemoryStore(); - Console.WriteLine("initialize NeoSystem"); - TheNeoSystem = new NeoSystem(_Store.Object); // new Mock(mockStore.Object); + Console.WriteLine("initialize NeoSystem"); + TheNeoSystem = new NeoSystem(Store); - // Ensure that blockchain is loaded + // Ensure that blockchain is loaded - var blockchain = Blockchain.Singleton; - } + var _ = Blockchain.Singleton; + } - return TheNeoSystem; + public static void InitializeMockNeoSystem() + { } } } diff --git a/tests/neo.UnitTests/TestDataCache.cs b/tests/neo.UnitTests/TestDataCache.cs deleted file mode 100644 index 37300f22e1..0000000000 --- a/tests/neo.UnitTests/TestDataCache.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Neo.UnitTests -{ - public class TestDataCache : DataCache - where TKey : IEquatable, ISerializable - where TValue : class, ICloneable, ISerializable, new() - { - private readonly Dictionary dic = new Dictionary(); - - public TestDataCache() { } - - public TestDataCache(TKey key, TValue value) - { - dic.Add(key, value); - } - - public override void DeleteInternal(TKey key) - { - dic.Remove(key); - } - - protected override void AddInternal(TKey key, TValue value) - { - dic.Add(key, value); - } - - protected override IEnumerable> FindInternal(byte[] key_prefix) - { - return dic.ToList(); - } - - protected override TValue GetInternal(TKey key) - { - if (dic[key] == null) throw new NotImplementedException(); - return dic[key]; - } - - protected override TValue TryGetInternal(TKey key) - { - return dic.TryGetValue(key, out TValue value) ? value : null; - } - - protected override void UpdateInternal(TKey key, TValue value) - { - dic[key] = value; - } - } -} diff --git a/tests/neo.UnitTests/TestMetaDataCache.cs b/tests/neo.UnitTests/TestMetaDataCache.cs deleted file mode 100644 index 0cb5646928..0000000000 --- a/tests/neo.UnitTests/TestMetaDataCache.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Neo.IO; -using Neo.IO.Caching; - -namespace Neo.UnitTests -{ - public class TestMetaDataCache : MetaDataCache where T : class, ICloneable, ISerializable, new() - { - public TestMetaDataCache() - : base(null) - { - } - - protected override void AddInternal(T item) - { - } - - protected override T TryGetInternal() - { - return null; - } - - protected override void UpdateInternal(T item) - { - } - } -} diff --git a/tests/neo.UnitTests/TestVerifiable.cs b/tests/neo.UnitTests/TestVerifiable.cs index c0920df480..7959eeec3b 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(Snapshot snapshot) + public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) { throw new NotImplementedException(); } diff --git a/tests/neo.UnitTests/UT_DataCache.cs b/tests/neo.UnitTests/UT_DataCache.cs index a428e72d8d..0cee679316 100644 --- a/tests/neo.UnitTests/UT_DataCache.cs +++ b/tests/neo.UnitTests/UT_DataCache.cs @@ -17,7 +17,7 @@ public void TestSetup() [TestMethod] public void TestCachedFind_Between() { - var snapshot = TestBlockchain.GetStore().GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var storages = snapshot.Storages; var cache = new CloneCache(storages); @@ -60,7 +60,7 @@ public void TestCachedFind_Between() [TestMethod] public void TestCachedFind_Last() { - var snapshot = TestBlockchain.GetStore().GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var storages = snapshot.Storages; var cache = new CloneCache(storages); @@ -98,7 +98,7 @@ public void TestCachedFind_Last() [TestMethod] public void TestCachedFind_Empty() { - var snapshot = TestBlockchain.GetStore().GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var storages = snapshot.Storages; var cache = new CloneCache(storages); diff --git a/tests/neo.UnitTests/UT_NeoSystem.cs b/tests/neo.UnitTests/UT_NeoSystem.cs index 2d71db97df..1ed00464fa 100644 --- a/tests/neo.UnitTests/UT_NeoSystem.cs +++ b/tests/neo.UnitTests/UT_NeoSystem.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Neo.UnitTests @@ -11,7 +11,7 @@ public class UT_NeoSystem [TestInitialize] public void Setup() { - neoSystem = TestBlockchain.InitializeMockNeoSystem(); + neoSystem = TestBlockchain.TheNeoSystem; } [TestMethod] diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index e6bf40721c..ed02f4d61e 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -3,7 +3,6 @@ using Neo.Cryptography.ECC; using Neo.Ledger; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.SmartContract.Native.Tokens; @@ -90,7 +89,6 @@ public override bool VerifyPassword(string password) [TestClass] public class UT_Wallet { - Store store; private static KeyPair glkey; private static string nep2Key; @@ -104,7 +102,7 @@ public static void ClassInit(TestContext context) [TestInitialize] public void TestSetup() { - store = TestBlockchain.GetStore(); + TestBlockchain.InitializeMockNeoSystem(); } [TestMethod] @@ -198,7 +196,7 @@ public void TestGetAvailable() account.Lock = false; // Fake balance - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem { @@ -209,6 +207,7 @@ public void TestGetAvailable() Balance = 10000 * NativeContract.GAS.Factor } .ToByteArray(); + snapshot.Commit(); wallet.GetAvailable(NativeContract.GAS.Hash).Should().Be(new BigDecimal(1000000000000, 8)); @@ -217,6 +216,7 @@ public void TestGetAvailable() Balance = 0 } .ToByteArray(); + snapshot.Commit(); } [TestMethod] @@ -228,7 +228,7 @@ public void TestGetBalance() account.Lock = false; // Fake balance - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem { @@ -239,6 +239,7 @@ public void TestGetBalance() Balance = 10000 * NativeContract.GAS.Factor } .ToByteArray(); + 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)); @@ -248,6 +249,7 @@ public void TestGetBalance() Balance = 0 } .ToByteArray(); + snapshot.Commit(); } [TestMethod] @@ -336,29 +338,31 @@ public void TestMakeTransaction1() action.Should().Throw(); // Fake balance - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + var entry1 = snapshot.Storages.GetAndChange(key, () => new StorageItem { Value = new Nep5AccountState().ToByteArray() }); - entry.Value = new Nep5AccountState() + entry1.Value = new Nep5AccountState() { Balance = 10000 * NativeContract.GAS.Factor } .ToByteArray(); key = NativeContract.NEO.CreateStorageKey(20, account.ScriptHash); - entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + var entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem { Value = new Nep5AccountState().ToByteArray() }); - entry.Value = new NeoToken.AccountState() + entry2.Value = new NeoToken.AccountState() { Balance = 10000 * NativeContract.NEO.Factor } .ToByteArray(); + snapshot.Commit(); + var tx = wallet.MakeTransaction(new TransferOutput[] { new TransferOutput() @@ -381,11 +385,17 @@ public void TestMakeTransaction1() }); tx.Should().NotBeNull(); - entry.Value = new NeoToken.AccountState() + entry1.Value = new Nep5AccountState() + { + Balance = 0 + } + .ToByteArray(); + entry2.Value = new NeoToken.AccountState() { Balance = 0 } .ToByteArray(); + snapshot.Commit(); } [TestMethod] @@ -400,7 +410,7 @@ public void TestMakeTransaction2() account.Lock = false; // Fake balance - var snapshot = store.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem { @@ -411,6 +421,7 @@ public void TestMakeTransaction2() Balance = 1000000 * NativeContract.GAS.Factor } .ToByteArray(); + snapshot.Commit(); var tx = wallet.MakeTransaction(new byte[] { }, account.ScriptHash, new TransactionAttribute[] { }); tx.Should().NotBeNull(); @@ -423,6 +434,7 @@ public void TestMakeTransaction2() Balance = 0 } .ToByteArray(); + snapshot.Commit(); } [TestMethod] From 10e141c3b504808b9d8686ab38cb072bce73c939 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 27 Nov 2019 16:58:36 +0100 Subject: [PATCH 147/305] Allow to iterate a byte array inside the smart contract (#1281) * Update changes * Fix UT * Rename * Update ByteArrayWrapper.cs * Support Neo.Iterator.Create * Add ut and Rename * Cover line * Revert rename --- src/neo/SmartContract/InteropService.NEO.cs | 20 ++++++-- .../Iterators/ByteArrayWrapper.cs | 41 ++++++++++++++++ .../Iterators/UT_PrimitiveWrapper.cs | 49 +++++++++++++++++++ .../SmartContract/UT_InteropService.NEO.cs | 8 ++- 4 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 src/neo/SmartContract/Iterators/ByteArrayWrapper.cs create mode 100644 tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs diff --git a/src/neo/SmartContract/InteropService.NEO.cs b/src/neo/SmartContract/InteropService.NEO.cs index 10798c053d..a133e882fa 100644 --- a/src/neo/SmartContract/InteropService.NEO.cs +++ b/src/neo/SmartContract/InteropService.NEO.cs @@ -259,13 +259,20 @@ private static bool Storage_Find(ApplicationEngine engine) private static bool Enumerator_Create(ApplicationEngine engine) { - if (engine.CurrentContext.EvaluationStack.Pop() is VMArray array) + IEnumerator enumerator; + switch (engine.CurrentContext.EvaluationStack.Pop()) { - IEnumerator enumerator = new ArrayWrapper(array); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(enumerator)); - return true; + case VMArray array: + enumerator = new ArrayWrapper(array); + break; + case PrimitiveType primitive: + enumerator = new ByteArrayWrapper(primitive); + break; + default: + return false; } - return false; + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(enumerator)); + return true; } private static bool Enumerator_Next(ApplicationEngine engine) @@ -312,6 +319,9 @@ private static bool Iterator_Create(ApplicationEngine engine) case Map map: iterator = new MapWrapper(map); break; + case PrimitiveType primitive: + iterator = new ByteArrayWrapper(primitive); + break; default: return false; } diff --git a/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs b/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs new file mode 100644 index 0000000000..d5baff7da0 --- /dev/null +++ b/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs @@ -0,0 +1,41 @@ +using Neo.VM.Types; +using System; + +namespace Neo.SmartContract.Iterators +{ + internal class ByteArrayWrapper : IIterator + { + private readonly byte[] array; + private int index = -1; + + public ByteArrayWrapper(PrimitiveType value) + { + this.array = value.ToByteArray().ToArray(); + } + + public void Dispose() { } + + public PrimitiveType Key() + { + if (index < 0) + throw new InvalidOperationException(); + return index; + } + + public bool Next() + { + int next = index + 1; + if (next >= array.Length) + return false; + index = next; + return true; + } + + public StackItem Value() + { + if (index < 0) + throw new InvalidOperationException(); + return new Integer(array[index]); + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs new file mode 100644 index 0000000000..d8fd144812 --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs @@ -0,0 +1,49 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Iterators; +using Neo.VM; +using Neo.VM.Types; +using System; + +namespace Neo.UnitTests.SmartContract.Iterators +{ + [TestClass] + public class UT_PrimitiveWrapper + { + [TestMethod] + public void TestGeneratorAndDispose() + { + ByteArrayWrapper arrayWrapper = new ByteArrayWrapper(new ByteArray(new byte[0])); + Assert.IsNotNull(arrayWrapper); + Action action = () => arrayWrapper.Dispose(); + action.Should().NotThrow(); + } + + [TestMethod] + 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().GetBigInteger()); + Assert.AreEqual(0x01, arrayWrapper.Value()); + arrayWrapper.Next(); + Assert.AreEqual(0x01, arrayWrapper.Key().GetBigInteger()); + Assert.AreEqual(0x02, arrayWrapper.Value()); + } + + [TestMethod] + public void TestNext() + { + ByteArrayWrapper arrayWrapper = new ByteArrayWrapper(new byte[] { 0x01, 0x02 }); + Assert.AreEqual(true, arrayWrapper.Next()); + Assert.AreEqual(0x01, arrayWrapper.Value()); + Assert.AreEqual(true, arrayWrapper.Next()); + Assert.AreEqual(0x02, arrayWrapper.Value()); + Assert.AreEqual(false, arrayWrapper.Next()); + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 351f885f11..e602cb0f74 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -306,7 +306,7 @@ public void TestEnumerator_Create() .Should().Be(new byte[] { 0x01 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Enumerator_Create).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Neo_Enumerator_Create).Should().BeTrue(); } [TestMethod] @@ -380,6 +380,10 @@ public void TestIterator_Create() ret.GetInterface().Value().GetSpan().ToHexString() .Should().Be(new byte[] { 0x01 }.ToHexString()); + var interop = new InteropInterface(1); + engine.CurrentContext.EvaluationStack.Push(interop); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeFalse(); + var map = new Map { { new Integer(1), new Integer(2) }, @@ -393,7 +397,7 @@ public void TestIterator_Create() ret.GetInterface().Value().GetBigInteger().Should().Be(2); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeTrue(); } [TestMethod] From 60dfeab64f5ef064d3176fed72be5e85ff217804 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 28 Nov 2019 20:43:04 +0800 Subject: [PATCH 148/305] New plugin interface: IStoragePlugin (#1087) --- src/neo/NeoSystem.cs | 6 ++++-- src/neo/Plugins/IStoragePlugin.cs | 9 +++++++++ src/neo/Plugins/Plugin.cs | 2 ++ tests/neo.UnitTests/Consensus/UT_Consensus.cs | 2 +- tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs | 3 ++- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 4 +--- tests/neo.UnitTests/TestBlockchain.cs | 7 +------ 7 files changed, 20 insertions(+), 13 deletions(-) create mode 100644 src/neo/Plugins/IStoragePlugin.cs diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index b1ef9b6ec8..9f1312322b 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -4,6 +4,7 @@ using Neo.Network.P2P; using Neo.Network.RPC; using Neo.Persistence; +using Neo.Persistence.Memory; using Neo.Plugins; using Neo.Wallets; using System; @@ -30,10 +31,10 @@ public class NeoSystem : IDisposable private ChannelsConfig start_message = null; private bool suspend = false; - public NeoSystem(IStore store) + public NeoSystem(string storageEngine = null) { - this.store = store; Plugin.LoadPlugins(this); + this.store = storageEngine is null ? new Store() : Plugin.Storages[storageEngine].GetStore(); 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)); @@ -49,6 +50,7 @@ public void Dispose() // Dispose will call ActorSystem.Terminate() ActorSystem.Dispose(); ActorSystem.WhenTerminated.Wait(); + store.Dispose(); } public void EnsureStoped(IActorRef actor) diff --git a/src/neo/Plugins/IStoragePlugin.cs b/src/neo/Plugins/IStoragePlugin.cs new file mode 100644 index 0000000000..68f75dd2bb --- /dev/null +++ b/src/neo/Plugins/IStoragePlugin.cs @@ -0,0 +1,9 @@ +using Neo.Persistence; + +namespace Neo.Plugins +{ + public interface IStoragePlugin + { + IStore GetStore(); + } +} diff --git a/src/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs index 14345d9de1..caacc17d85 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -12,6 +12,7 @@ public abstract class Plugin : IDisposable { public static readonly List Plugins = new List(); private static readonly List Loggers = new List(); + internal static readonly Dictionary Storages = new Dictionary(); internal static readonly List RpcPlugins = new List(); internal static readonly List PersistencePlugins = new List(); internal static readonly List P2PPlugins = new List(); @@ -48,6 +49,7 @@ protected Plugin() Plugins.Add(this); if (this is ILogPlugin logger) Loggers.Add(logger); + if (this is IStoragePlugin storage) Storages.Add(Name, storage); if (this is IP2PPlugin p2p) P2PPlugins.Add(p2p); if (this is IRpcPlugin rpc) RpcPlugins.Add(rpc); if (this is IPersistencePlugin persistence) PersistencePlugins.Add(persistence); diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index bc4c2277a0..68a538dcf5 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -41,7 +41,7 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() TestProbe subscriber = CreateTestProbe(); var mockWallet = new Mock(); mockWallet.Setup(p => p.GetAccount(It.IsAny())).Returns(p => new TestWalletAccount(p)); - ConsensusContext context = new ConsensusContext(mockWallet.Object, TestBlockchain.Store); + ConsensusContext context = new ConsensusContext(mockWallet.Object, Blockchain.Singleton.Store); int timeIndex = 0; var timeValues = new[] { diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs index ee5eb5f9d1..469c28d3ba 100644 --- a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs +++ b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs @@ -3,6 +3,7 @@ using Moq; using Neo.Consensus; using Neo.IO; +using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; using Neo.Wallets; @@ -38,7 +39,7 @@ public void TestSetup() _validatorKeys[x] = new KeyPair(pk); } - _context = new ConsensusContext(mockWallet.Object, TestBlockchain.Store) + _context = new ConsensusContext(mockWallet.Object, Blockchain.Singleton.Store) { Validators = _validatorKeys.Select(u => u.PublicKey).ToArray() }; diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index 27dd15b6aa..f46036edca 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -37,21 +37,19 @@ public static TestHeader Cast(Header input) public class UT_Blockchain { private NeoSystem system; - private IStore store; Transaction txSample = Blockchain.GenesisBlock.Transactions[0]; [TestInitialize] public void Initialize() { system = TestBlockchain.TheNeoSystem; - store = TestBlockchain.Store; Blockchain.Singleton.MemPool.TryAdd(txSample.Hash, txSample); } [TestMethod] public void TestConstructor() { - system.ActorSystem.ActorOf(Blockchain.Props(system, store)).Should().NotBeSameAs(system.Blockchain); + system.ActorSystem.ActorOf(Blockchain.Props(system, Blockchain.Singleton.Store)).Should().NotBeSameAs(system.Blockchain); } [TestMethod] diff --git a/tests/neo.UnitTests/TestBlockchain.cs b/tests/neo.UnitTests/TestBlockchain.cs index 6b0cdccd94..786b2e7062 100644 --- a/tests/neo.UnitTests/TestBlockchain.cs +++ b/tests/neo.UnitTests/TestBlockchain.cs @@ -1,21 +1,16 @@ using Neo.Ledger; -using Neo.Persistence; using System; -using MemoryStore = Neo.Persistence.Memory.Store; namespace Neo.UnitTests { public static class TestBlockchain { - public static readonly IStore Store; public static readonly NeoSystem TheNeoSystem; static TestBlockchain() { - Store = new MemoryStore(); - Console.WriteLine("initialize NeoSystem"); - TheNeoSystem = new NeoSystem(Store); + TheNeoSystem = new NeoSystem(); // Ensure that blockchain is loaded From c10b94203b04e116a6fcd26816eb87de1e94cd6f Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 28 Nov 2019 22:49:50 +0100 Subject: [PATCH 149/305] Optimize TaskManager (#1294) --- src/neo/Network/P2P/TaskManager.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 82e2e9b87a..8ceb0d404b 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -140,27 +140,27 @@ private void OnTaskCompleted(UInt256 hash) [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DecrementGlobalTask(UInt256 hash) { - if (globalTasks.ContainsKey(hash)) + if (globalTasks.TryGetValue(hash, out var value)) { - if (globalTasks[hash] == 1) + if (value == 1) globalTasks.Remove(hash); else - globalTasks[hash]--; + globalTasks[hash] = value - 1; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool IncrementGlobalTask(UInt256 hash) { - if (!globalTasks.ContainsKey(hash)) + if (!globalTasks.TryGetValue(hash, out var value)) { globalTasks[hash] = 1; return true; } - if (globalTasks[hash] >= MaxConncurrentTasks) + if (value >= MaxConncurrentTasks) return false; - globalTasks[hash]++; + globalTasks[hash] = value + 1; return true; } From be885163f7790419976086506906cc5800f4026a Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Fri, 29 Nov 2019 18:04:34 +0800 Subject: [PATCH 150/305] Cache `Transaction.Size` (#1282) --- src/neo/Network/P2P/Payloads/Transaction.cs | 133 +++++++++++++++----- tests/neo.UnitTests/Ledger/UT_PoolItem.cs | 12 +- 2 files changed, 110 insertions(+), 35 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 6bda3f8c6b..c2adb34017 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -4,7 +4,6 @@ using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; -using Neo.VM; using Neo.VM.Types; using Neo.Wallets; using System; @@ -28,22 +27,36 @@ public class Transaction : IEquatable, IInventory, IInteroperable /// private const int MaxCosigners = 16; - public byte Version; - public uint Nonce; - public UInt160 Sender; - /// - /// Distributed to NEO holders. - /// - public long SystemFee; - /// - /// Distributed to consensus nodes. - /// - public long NetworkFee; - public uint ValidUntilBlock; - public TransactionAttribute[] Attributes; - public Cosigner[] Cosigners { get; set; } - public byte[] Script; - public Witness[] Witnesses { get; set; } + private byte version; + private uint nonce; + private UInt160 sender; + private long sysfee; + private long netfee; + private uint validUntilBlock; + private TransactionAttribute[] attributes; + private Cosigner[] cosigners; + private byte[] script; + private Witness[] witnesses; + + public const int HeaderSize = + sizeof(byte) + //Version + sizeof(uint) + //Nonce + 20 + //Sender + sizeof(long) + //SystemFee + sizeof(long) + //NetworkFee + sizeof(uint); //ValidUntilBlock + + public TransactionAttribute[] Attributes + { + get => attributes; + set { attributes = value; _hash = null; _size = 0; } + } + + public Cosigner[] Cosigners + { + get => cosigners; + set { cosigners = value; _hash = null; _size = 0; } + } /// /// The NetworkFee for the transaction divided by its Size. @@ -66,24 +79,86 @@ public UInt256 Hash InventoryType IInventory.InventoryType => InventoryType.TX; - public const int HeaderSize = - sizeof(byte) + //Version - sizeof(uint) + //Nonce - 20 + //Sender - sizeof(long) + //SystemFee - sizeof(long) + //NetworkFee - sizeof(uint); //ValidUntilBlock + /// + /// Distributed to consensus nodes. + /// + public long NetworkFee + { + get => netfee; + set { netfee = value; _hash = null; } + } - public int Size => HeaderSize + - Attributes.GetVarSize() + //Attributes - Cosigners.GetVarSize() + //Cosigners - Script.GetVarSize() + //Script - Witnesses.GetVarSize(); //Witnesses + public uint Nonce + { + get => nonce; + set { nonce = value; _hash = null; } + } + + public byte[] Script + { + get => script; + set { script = value; _hash = null; _size = 0; } + } + + public UInt160 Sender + { + get => sender; + set { sender = value; _hash = null; } + } + + private int _size; + public int Size + { + get + { + if (_size == 0) + { + _size = HeaderSize + + Attributes.GetVarSize() + //Attributes + Cosigners.GetVarSize() + //Cosigners + Script.GetVarSize() + //Script + Witnesses.GetVarSize(); //Witnesses + } + return _size; + } + } + + /// + /// Distributed to NEO holders. + /// + public long SystemFee + { + get => sysfee; + set { sysfee = value; _hash = null; } + } + + public uint ValidUntilBlock + { + get => validUntilBlock; + set { validUntilBlock = value; _hash = null; } + } + + public byte Version + { + get => version; + set { version = value; _hash = null; } + } + + public Witness[] Witnesses + { + get => witnesses; + set { witnesses = value; _size = 0; } + } void ISerializable.Deserialize(BinaryReader reader) { + int startPosition = -1; + if (reader.BaseStream.CanSeek) + startPosition = (int)reader.BaseStream.Position; DeserializeUnsigned(reader); Witnesses = reader.ReadSerializableArray(); + if (startPosition >= 0) + _size = (int)reader.BaseStream.Position - startPosition; } public void DeserializeUnsigned(BinaryReader reader) diff --git a/tests/neo.UnitTests/Ledger/UT_PoolItem.cs b/tests/neo.UnitTests/Ledger/UT_PoolItem.cs index d94cec8e4c..0d37e6edfd 100644 --- a/tests/neo.UnitTests/Ledger/UT_PoolItem.cs +++ b/tests/neo.UnitTests/Ledger/UT_PoolItem.cs @@ -67,11 +67,11 @@ public void PoolItem_CompareTo_Hash() PoolItem pitem1 = new PoolItem(tx1); PoolItem pitem2 = new PoolItem(tx2); - // pitem2 < pitem1 (fee) => -1 - pitem2.CompareTo(pitem1).Should().Be(-1); + // pitem2.tx.Hash < pitem1.tx.Hash => 1 descending order + pitem2.CompareTo(pitem1).Should().Be(1); - // pitem1 > pitem2 (fee) => 1 - pitem1.CompareTo(pitem2).Should().Be(1); + // pitem2.tx.Hash > pitem1.tx.Hash => -1 descending order + pitem1.CompareTo(pitem2).Should().Be(-1); } } @@ -96,7 +96,7 @@ public Transaction GenerateTxWithFirstByteOfHashGreaterThanOrEqualTo(byte firstH do { tx = GenerateTx(networkFee, size); - } while (tx.Hash >= new UInt256(TestUtils.GetByteArray(32, firstHashByte))); + } while (tx.Hash < new UInt256(TestUtils.GetByteArray(32, firstHashByte))); return tx; } @@ -107,7 +107,7 @@ public Transaction GenerateTxWithFirstByteOfHashLessThanOrEqualTo(byte firstHash do { tx = GenerateTx(networkFee, size); - } while (tx.Hash <= new UInt256(TestUtils.GetByteArray(32, firstHashByte))); + } while (tx.Hash > new UInt256(TestUtils.GetByteArray(32, firstHashByte))); return tx; } From 497c2e9b4aee849b0a52596b32b89db561b4e4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Fri, 29 Nov 2019 08:52:10 -0300 Subject: [PATCH 151/305] Improving ConsensusService UT: Adding some coverage to OnPersistCompleted, OnPrepReq, OnPrepResponse, OnCommit and some auxiliary methods (#1169) * Adding random hashes for OnGetDataMessageReceived * Adding static readonly Random * Adding Recovery and OnTimer coverage on CN UT * Fixing files that should not be modified * Fixing files that should not be modified II * Greatly improving UT! * Dotnet format * Changing to 200ms * More messages and casting asserts * Forcing prepReq to 0 hashes because mempool is shared * UT reaching Commit! * Trying to check Commit for achieving a blockRelay. However, signatures need to match * Trying to mock ECPoints. However, errors when GetWitness of Block after adding signatures * Relaying block! Passing with sucess * A little bit more coverage and ensure Block Relay event from LocalNode * Cleaning thread * Fixing duplicated index of preparation replication and other minor fix * Returning prevHeader and block to original * Retuning merkle root of block to original value * dotnet format! * Forcing an old timestamp for avoiding OnTimer * Assert RecoveryMessage because of commitsent * Improving variable and function names * Cleaning and improving comments * Updating blocktime * Fixing nextconsensus expected hash due to some recent PR * Capeslock on methods * Trying to fix UT Consensus * Increasing coverage for OnPersistCompleted * Many workarounds but it is working again! * dotnet format * Cleaning UTs I * Cleaninng UTs II * Minor typo on variable * Improving comments * Adding some summary to methods * Adding clone to the comment * Adding variable to timestamp * Removing other timestamps --- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 304 +++++++++++++++--- tests/neo.UnitTests/UT_ProtocolSettings.cs | 2 +- tests/neo.UnitTests/protocol.json | 2 +- 3 files changed, 261 insertions(+), 47 deletions(-) diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 68a538dcf5..5a9cb1bf98 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -5,6 +5,7 @@ using Moq; using Neo.Consensus; using Neo.Cryptography; +using Neo.UnitTests.Cryptography; using Neo.IO; using Neo.Ledger; using Neo.Network.P2P; @@ -36,33 +37,40 @@ public void Cleanup() } [TestMethod] - public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() + public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Commits() { - TestProbe subscriber = CreateTestProbe(); var mockWallet = new Mock(); mockWallet.Setup(p => p.GetAccount(It.IsAny())).Returns(p => new TestWalletAccount(p)); - ConsensusContext context = new ConsensusContext(mockWallet.Object, Blockchain.Singleton.Store); + Console.WriteLine($"\n(UT-Consensus) Wallet is: {mockWallet.Object.GetAccount(UInt160.Zero).GetKey().PublicKey}"); + + var mockContext = new Mock(mockWallet.Object, Blockchain.Singleton.Store); + mockContext.Object.LastSeenMessage = new int[] { 0, 0, 0, 0, 0, 0, 0 }; - int timeIndex = 0; var timeValues = new[] { - //new DateTime(1968, 06, 01, 0, 0, 15, DateTimeKind.Utc), // For tests here - new DateTime(1980, 06, 01, 0, 0, 1, 001, DateTimeKind.Utc), // For receiving block - new DateTime(1980, 06, 01, 0, 0, (int) Blockchain.MillisecondsPerBlock / 1000, 100, DateTimeKind.Utc), // For Initialize + 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 - new DateTime(1980, 06, 01, 0, 0, 15, 001, DateTimeKind.Utc) // unused }; - //TimeProvider.Current.UtcNow.ToTimestamp().Should().Be(4244941711); //1968-06-01 00:00:15 - - Console.WriteLine($"time 0: {timeValues[0].ToString()} 1: {timeValues[1].ToString()} 2: {timeValues[2].ToString()} 3: {timeValues[3].ToString()}"); + for (int i = 0; i < timeValues.Length; i++) + Console.WriteLine($"time {i}: {timeValues[i].ToString()} "); + 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++); - //new DateTime(1968, 06, 01, 0, 0, 15, DateTimeKind.Utc)); + 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) - // TODO: create ILogPlugin for Tests + //create ILogPlugin for Tests /* mockConsensusContext.Setup(mr => mr.Log(It.IsAny(), It.IsAny())) .Callback((string message, LogLevel level) => { @@ -71,27 +79,18 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() ); */ - // Creating proposed block + // 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} timestamp {timestampVal}"); - - timestampVal.Should().Be(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 - // ============================================================================ - + 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, context }, null)) + Akka.Actor.Props.Create(() => (ConsensusService)Activator.CreateInstance(typeof(ConsensusService), BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { subscriber, subscriber, mockContext.Object }, null)) ); - Console.WriteLine("will trigger OnPersistCompleted!"); - actorConsensus.Tell(new Blockchain.PersistCompleted + var testPersistCompleted = new Blockchain.PersistCompleted { Block = new Block { @@ -100,35 +99,250 @@ public void ConsensusService_Primary_Sends_PrepareRequest_After_OnStart() MerkleRoot = header.MerkleRoot, Timestamp = header.Timestamp, Index = header.Index, - NextConsensus = header.NextConsensus + 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("OnTimer should expire!"); - Console.WriteLine("Waiting for subscriber message!"); - // Timer should expire in one second (block_received_time at :01, initialized at :02) - - var answer = subscriber.ExpectMsg(); - Console.WriteLine($"MESSAGE 1: {answer}"); - //var answer2 = subscriber.ExpectMsg(); // expects to fail! - + 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}"); + // Ensuring cast of type ConsensusPayload from the received message from subscriber + ConsensusPayload initialRecoveryPayload = (ConsensusPayload)askingForInitialRecovery.Inventory; + // Ensuring casting of type RecoveryRequest + RecoveryRequest rrm = (RecoveryRequest)initialRecoveryPayload.ConsensusMessage; + rrm.Timestamp.Should().Be(defaultTimestamp); + + Console.WriteLine("Waiting for backupChange View... "); + var backupOnAskingChangeView = subscriber.ExpectMsg(); + var changeViewPayload = (ConsensusPayload)backupOnAskingChangeView.Inventory; + ChangeView cvm = (ChangeView)changeViewPayload.ConsensusMessage; + cvm.Timestamp.Should().Be(defaultTimestamp); + cvm.ViewNumber.Should().Be(0); + cvm.Reason.Should().Be(ChangeViewReason.Timeout); + + 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 = new int[] { -1, -1, -1, -1, -1, -1, -1 }; + 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; + rrm.Timestamp.Should().Be(defaultTimestamp); + + Console.WriteLine("will tell PrepRequest!"); + mockContext.Object.PrevHeader.Timestamp = defaultTimestamp; + mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); + var prepReq = mockContext.Object.MakePrepareRequest(); + var ppToSend = (PrepareRequest)prepReq.ConsensusMessage; + + // Forcing hashes to 0 because mempool is currently shared + ppToSend.TransactionHashes = new UInt256[0]; + ppToSend.TransactionHashes.Length.Should().Be(0); + + actorConsensus.Tell(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; + + 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) + // cleaning old try with Self ValidatorIndex + mockContext.Object.PreparationPayloads[mockContext.Object.MyIndex] = null; + actorConsensus.Tell(prepReq); + var OnPrepResponse = subscriber.ExpectMsg(); + var prepResponsePayload = (ConsensusPayload)OnPrepResponse.Inventory; + PrepareResponse prm = (PrepareResponse)prepResponsePayload.ConsensusMessage; + prm.PreparationHash.Should().Be(prepReq.Hash); + + // Simulating CN 3 + actorConsensus.Tell(GetPayloadAndModifyValidator(prepResponsePayload, 2)); + + // Simulating CN 5 + actorConsensus.Tell(GetPayloadAndModifyValidator(prepResponsePayload, 4)); + + // Simulating CN 4 + actorConsensus.Tell(GetPayloadAndModifyValidator(prepResponsePayload, 3)); + + var onCommitPayload = subscriber.ExpectMsg(); + var commitPayload = (ConsensusPayload)onCommitPayload.Inventory; + Commit cm = (Commit)commitPayload.ConsensusMessage; + + // Original Contract + Contract originalContract = Contract.CreateMultiSigContract(mockContext.Object.M, mockContext.Object.Validators); + Console.WriteLine($"\nORIGINAL Contract is: {originalContract.ScriptHash}"); + originalContract.ScriptHash.Should().Be(UInt160.Parse("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); + mockContext.Object.Block.NextConsensus.Should().Be(UInt160.Parse("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); + + Console.WriteLine($"ORIGINAL BlockHash: {mockContext.Object.Block.Hash}"); + Console.WriteLine($"ORIGINAL Block NextConsensus: {mockContext.Object.Block.NextConsensus}"); + //Console.WriteLine($"VALIDATOR[0] {mockContext.Object.Validators[0]}"); + //Console.WriteLine($"VALIDATOR[0]ScriptHash: {mockWallet.Object.GetAccount(mockContext.Object.Validators[0]).ScriptHash}"); + + 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) + }; + for (int i = 0; i < mockContext.Object.Validators.Length; i++) + Console.WriteLine($"{mockContext.Object.Validators[i]}"); + mockContext.Object.Validators = new ECPoint[7] + { + mockContext.Object.Validators[0], + 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:"); + for (int i = 0; i < mockContext.Object.Validators.Length; i++) + Console.WriteLine($"{mockContext.Object.Validators[i]}"); + + // update Contract with some random validators + var updatedContract = Contract.CreateMultiSigContract(mockContext.Object.M, mockContext.Object.Validators); + Console.WriteLine($"\nContract updated: {updatedContract.ScriptHash}"); + + // Forcing next consensus + var originalBlockHashData = mockContext.Object.Block.GetHashData(); + mockContext.Object.Block.NextConsensus = updatedContract.ScriptHash; + mockContext.Object.Block.Header.NextConsensus = updatedContract.ScriptHash; + mockContext.Object.PrevHeader.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(commitPayload, 1, kp_array[1], updatedBlockHashData); + Crypto.Default.VerifySignature(originalBlockHashData, cm.Signature, mockContext.Object.Validators[0].EncodePoint(false)).Should().BeFalse(); + Crypto.Default.VerifySignature(updatedBlockHashData, cm.Signature, mockContext.Object.Validators[0].EncodePoint(false)).Should().BeFalse(); + Crypto.Default.VerifySignature(originalBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[1].EncodePoint(false)).Should().BeFalse(); + Crypto.Default.VerifySignature(updatedBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[1].EncodePoint(false)).Should().BeTrue(); + Console.WriteLine("\n=========================="); + + Console.WriteLine("\n=========================="); + Console.WriteLine("\nCN2 simulation time"); + actorConsensus.Tell(cmPayloadTemp); + + Console.WriteLine("\nCN3 simulation time"); + actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 2, kp_array[2], updatedBlockHashData)); + + Console.WriteLine("\nCN4 simulation time"); + actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 3, kp_array[3], updatedBlockHashData)); + + // ============================================= + // Testing commit with wrong signature not valid + // It will be invalid signature because we did not change ECPoint + Console.WriteLine("\nCN7 simulation time. Wrong signature, KeyPair is not known"); + actorConsensus.Tell(GetPayloadAndModifyValidator(commitPayload, 6)); + + Console.WriteLine("\nWaiting for recovery due to failed nodes... "); + var backupOnRecoveryMessageAfterCommit = subscriber.ExpectMsg(); + var rmPayload = (ConsensusPayload)backupOnRecoveryMessageAfterCommit.Inventory; + RecoveryMessage rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + // ============================================= + + mockContext.Object.CommitPayloads[0] = null; + Console.WriteLine("\nCN5 simulation time"); + actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 4, kp_array[4], updatedBlockHashData)); + + + Console.WriteLine($"\nFocing block PrevHash to UInt256.Zero {mockContext.Object.Block.GetHashData().ToScriptHash()}"); + 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()}"); + mockContext.Object.Block.Witness = new Witness { }; + mockContext.Object.Block.Witness.VerificationScript = updatedContract.Script; + Console.WriteLine($"\nUpdating BlockBase Witness scripthash is {mockContext.Object.Block.Witness.ScriptHash}"); + Console.WriteLine($"\nNew Hash is {mockContext.Object.Block.GetHashData().ToScriptHash()}"); + + Console.WriteLine("\nCN6 simulation time"); + // Here we used modified mockContext.Object.Block.GetHashData().ToScriptHash() for blockhash + actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 5, kp_array[5], mockContext.Object.Block.GetHashData())); + + Console.WriteLine("\nWait for subscriber Local.Node Relay"); + var onBlockRelay = subscriber.ExpectMsg(); + Console.WriteLine("\nAsserting time was Block..."); + var utBlock = (Block)onBlockRelay.Inventory; + + Console.WriteLine($"\nAsserting block NextConsensus..{utBlock.NextConsensus}"); + utBlock.NextConsensus.Should().Be(updatedContract.ScriptHash); + + Console.WriteLine("\n=========================="); // ============================================================================ // finalize ConsensusService actor // ============================================================================ - - //Thread.Sleep(4000); - Sys.Stop(actorConsensus); + Console.WriteLine("Finalizing consensus service actor and returning states."); TimeProvider.ResetToDefault(); - Assert.AreEqual(1, 1); + //Sys.Stop(actorConsensus); + } + + /// + /// 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 ConsensusPayload GetCommitPayloadModifiedAndSignedCopy(ConsensusPayload cpToCopy, ushort vI, KeyPair kp, byte[] blockHashToSign) + { + var cpCommitTemp = cpToCopy.ToArray().AsSerializable(); + cpCommitTemp.ValidatorIndex = vI; + cpCommitTemp.ConsensusMessage = cpToCopy.ConsensusMessage.ToArray().AsSerializable(); + ((Commit)cpCommitTemp.ConsensusMessage).Signature = Crypto.Default.Sign(blockHashToSign, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()); + // Payload is not being signed by vI, since we are bypassing this check as directly talking to subscriber + return cpCommitTemp; + } + + /// + /// Get a clone of a ConsensusPayload and change its currentValidatorIndex + /// + /// ConsensusPayload that will be modified + /// new ValidatorIndex for the cpToCopy + public ConsensusPayload GetPayloadAndModifyValidator(ConsensusPayload cpToCopy, ushort vI) + { + var cpTemp = cpToCopy.ToArray().AsSerializable(); + cpTemp.ValidatorIndex = vI; + return cpTemp; } [TestMethod] diff --git a/tests/neo.UnitTests/UT_ProtocolSettings.cs b/tests/neo.UnitTests/UT_ProtocolSettings.cs index 2e1b8c3143..a989be7f6f 100644 --- a/tests/neo.UnitTests/UT_ProtocolSettings.cs +++ b/tests/neo.UnitTests/UT_ProtocolSettings.cs @@ -100,7 +100,7 @@ public void TestGetMemoryPoolMaxTransactions() [TestMethod] public void TestGetMillisecondsPerBlock() { - ProtocolSettings.Default.MillisecondsPerBlock.Should().Be(2000); + ProtocolSettings.Default.MillisecondsPerBlock.Should().Be(200); } [TestMethod] diff --git a/tests/neo.UnitTests/protocol.json b/tests/neo.UnitTests/protocol.json index 06aec5ccce..50f802e7cf 100644 --- a/tests/neo.UnitTests/protocol.json +++ b/tests/neo.UnitTests/protocol.json @@ -1,5 +1,5 @@ { "ProtocolConfiguration": { - "MillisecondsPerBlock": 2000 + "MillisecondsPerBlock": 200 } } From 7c25da7f9adfb433d1d27a34590dda8ae0da3abb Mon Sep 17 00:00:00 2001 From: Shargon Date: Sat, 30 Nov 2019 07:54:59 +0100 Subject: [PATCH 152/305] Remove Linq from ECDSA and UInt classes (#1283) --- src/neo/Consensus/ConsensusService.cs | 2 +- src/neo/Cryptography/Base58.cs | 3 +- src/neo/Cryptography/Crypto.cs | 13 ++++---- src/neo/Cryptography/ECC/ECCurve.cs | 3 ++ src/neo/Cryptography/ECC/ECFieldElement.cs | 9 +++--- src/neo/Cryptography/ECC/ECPoint.cs | 31 ++++++++----------- src/neo/Cryptography/Helper.cs | 4 +-- src/neo/Helper.cs | 31 +++++++++++++++++-- src/neo/IO/Caching/DataCache.cs | 2 +- src/neo/IO/Data/LevelDB/Helper.cs | 3 +- src/neo/IO/Data/LevelDB/Slice.cs | 3 +- src/neo/Ledger/StorageKey.cs | 5 ++- src/neo/Network/P2P/Payloads/Block.cs | 3 +- src/neo/Network/P2P/Payloads/InvPayload.cs | 3 +- .../P2P/Payloads/MerkleBlockPayload.cs | 2 +- src/neo/Network/RPC/Nep5API.cs | 10 +++--- src/neo/Network/RPC/RpcServer.cs | 2 +- src/neo/Persistence/LevelDB/Snapshot.cs | 3 +- src/neo/Persistence/LevelDB/Store.cs | 3 +- src/neo/Persistence/Memory/Snapshot.cs | 3 +- src/neo/Persistence/Memory/Store.cs | 2 +- .../ContractParametersContext.cs | 6 ++-- src/neo/SmartContract/InteropService.NEO.cs | 2 +- .../SmartContract/Native/Tokens/NeoToken.cs | 2 +- src/neo/UInt160.cs | 8 +++-- src/neo/UInt256.cs | 8 +++-- src/neo/UIntBase.cs | 5 ++- src/neo/Wallets/Helper.cs | 5 ++- src/neo/Wallets/KeyPair.cs | 6 ++-- src/neo/Wallets/Wallet.cs | 6 ++-- 30 files changed, 104 insertions(+), 84 deletions(-) diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 3c5c334e11..ae901611b3 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -573,7 +573,7 @@ private void OnTimer(Timer timer) { var reason = ChangeViewReason.Timeout; - if (context.Block != null && context.TransactionHashes?.Count() > context.Transactions?.Count) + if (context.Block != null && context.TransactionHashes?.Length > context.Transactions?.Count) { reason = ChangeViewReason.TxNotFound; } diff --git a/src/neo/Cryptography/Base58.cs b/src/neo/Cryptography/Base58.cs index 1920bed10d..4a39acfbef 100644 --- a/src/neo/Cryptography/Base58.cs +++ b/src/neo/Cryptography/Base58.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Numerics; using System.Text; +using static Neo.Helper; namespace Neo.Cryptography { @@ -27,7 +28,7 @@ public static byte[] Decode(string input) var leadingZeros = new byte[leadingZeroCount]; if (bi.IsZero) return leadingZeros; var bytesWithoutLeadingZeros = bi.ToByteArray(isUnsigned: true, isBigEndian: true); - return leadingZeros.Concat(bytesWithoutLeadingZeros).ToArray(); + return Concat(leadingZeros, bytesWithoutLeadingZeros); } public static string Encode(byte[] input) diff --git a/src/neo/Cryptography/Crypto.cs b/src/neo/Cryptography/Crypto.cs index c835e3eebb..5bf90a6c97 100644 --- a/src/neo/Cryptography/Crypto.cs +++ b/src/neo/Cryptography/Crypto.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Security.Cryptography; namespace Neo.Cryptography @@ -26,8 +25,8 @@ public byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey) D = prikey, Q = new ECPoint { - X = pubkey.Take(32).ToArray(), - Y = pubkey.Skip(32).ToArray() + X = pubkey[..32], + Y = pubkey[32..] } })) { @@ -41,7 +40,7 @@ public bool VerifySignature(byte[] message, byte[] signature, byte[] pubkey) { try { - pubkey = Cryptography.ECC.ECPoint.DecodePoint(pubkey, Cryptography.ECC.ECCurve.Secp256r1).EncodePoint(false).Skip(1).ToArray(); + pubkey = ECC.ECPoint.DecodePoint(pubkey, ECC.ECCurve.Secp256r1).EncodePoint(false)[1..]; } catch { @@ -50,7 +49,7 @@ public bool VerifySignature(byte[] message, byte[] signature, byte[] pubkey) } else if (pubkey.Length == 65 && pubkey[0] == 0x04) { - pubkey = pubkey.Skip(1).ToArray(); + pubkey = pubkey[1..]; } else if (pubkey.Length != 64) { @@ -61,8 +60,8 @@ public bool VerifySignature(byte[] message, byte[] signature, byte[] pubkey) Curve = ECCurve.NamedCurves.nistP256, Q = new ECPoint { - X = pubkey.Take(32).ToArray(), - Y = pubkey.Skip(32).ToArray() + X = pubkey[..32], + Y = pubkey[32..] } })) { diff --git a/src/neo/Cryptography/ECC/ECCurve.cs b/src/neo/Cryptography/ECC/ECCurve.cs index 45d70d31f4..6fc476ff84 100644 --- a/src/neo/Cryptography/ECC/ECCurve.cs +++ b/src/neo/Cryptography/ECC/ECCurve.cs @@ -12,9 +12,12 @@ public class ECCurve public readonly ECPoint Infinity; public readonly ECPoint G; + public readonly int ExpectedECPointLength; + private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G) { this.Q = Q; + this.ExpectedECPointLength = (Q.GetBitLength() + 7) / 8; this.A = new ECFieldElement(A, this); this.B = new ECFieldElement(B, this); this.N = N; diff --git a/src/neo/Cryptography/ECC/ECFieldElement.cs b/src/neo/Cryptography/ECC/ECFieldElement.cs index 33d593b4cf..948afc263d 100644 --- a/src/neo/Cryptography/ECC/ECFieldElement.cs +++ b/src/neo/Cryptography/ECC/ECFieldElement.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using System.Numerics; namespace Neo.Cryptography.ECC @@ -28,9 +27,7 @@ public override bool Equals(object obj) if (obj == this) return true; - ECFieldElement other = obj as ECFieldElement; - - if (other == null) + if (!(obj is ECFieldElement other)) return false; return Equals(other); @@ -145,7 +142,9 @@ public byte[] ToByteArray() byte[] data = Value.ToByteArray(isUnsigned: true, isBigEndian: true); if (data.Length == 32) return data; - return Enumerable.Repeat(0, 32 - data.Length).Concat(data).ToArray(); + byte[] buffer = new byte[32]; + Buffer.BlockCopy(data, 0, buffer, buffer.Length - data.Length, data.Length); + return buffer; } public static ECFieldElement operator -(ECFieldElement x) diff --git a/src/neo/Cryptography/ECC/ECPoint.cs b/src/neo/Cryptography/ECC/ECPoint.cs index d597c4a8c5..7eef7362e2 100644 --- a/src/neo/Cryptography/ECC/ECPoint.cs +++ b/src/neo/Cryptography/ECC/ECPoint.cs @@ -1,8 +1,8 @@ using Neo.IO; using System; using System.IO; -using System.Linq; using System.Numerics; +using static Neo.Helper; namespace Neo.Cryptography.ECC { @@ -18,10 +18,7 @@ public bool IsInfinity public int Size => IsInfinity ? 1 : 33; - public ECPoint() - : this(null, null, ECCurve.Secp256r1) - { - } + public ECPoint() : this(null, null, ECCurve.Secp256r1) { } internal ECPoint(ECFieldElement x, ECFieldElement y, ECCurve curve) { @@ -43,13 +40,12 @@ public int CompareTo(ECPoint other) public static ECPoint DecodePoint(byte[] encoded, ECCurve curve) { ECPoint p = null; - int expectedLength = (curve.Q.GetBitLength() + 7) / 8; switch (encoded[0]) { case 0x02: // compressed case 0x03: // compressed { - if (encoded.Length != (expectedLength + 1)) + if (encoded.Length != (curve.ExpectedECPointLength + 1)) throw new FormatException("Incorrect length for compressed encoding"); int yTilde = encoded[0] & 1; BigInteger X1 = new BigInteger(encoded.AsSpan(1), isUnsigned: true, isBigEndian: true); @@ -58,10 +54,10 @@ public static ECPoint DecodePoint(byte[] encoded, ECCurve curve) } case 0x04: // uncompressed { - if (encoded.Length != (2 * expectedLength + 1)) + if (encoded.Length != (2 * curve.ExpectedECPointLength + 1)) throw new FormatException("Incorrect length for uncompressed/hybrid encoding"); - BigInteger X1 = new BigInteger(encoded.AsSpan(1, expectedLength), isUnsigned: true, isBigEndian: true); - BigInteger Y1 = new BigInteger(encoded.AsSpan(1 + expectedLength), isUnsigned: true, isBigEndian: true); + BigInteger X1 = new BigInteger(encoded.AsSpan(1, curve.ExpectedECPointLength), isUnsigned: true, isBigEndian: true); + BigInteger Y1 = new BigInteger(encoded.AsSpan(1 + curve.ExpectedECPointLength), isUnsigned: true, isBigEndian: true); p = new ECPoint(new ECFieldElement(X1, curve), new ECFieldElement(Y1, curve), curve); break; } @@ -105,23 +101,22 @@ void ISerializable.Deserialize(BinaryReader reader) public static ECPoint DeserializeFrom(BinaryReader reader, ECCurve curve) { - int expectedLength = (curve.Q.GetBitLength() + 7) / 8; - byte[] buffer = new byte[1 + expectedLength * 2]; + byte[] buffer = new byte[1 + curve.ExpectedECPointLength * 2]; buffer[0] = reader.ReadByte(); switch (buffer[0]) { case 0x02: case 0x03: { - if (reader.Read(buffer, 1, expectedLength) != expectedLength) + if (reader.Read(buffer, 1, curve.ExpectedECPointLength) != curve.ExpectedECPointLength) { throw new FormatException(); } - return DecodePoint(buffer.Take(1 + expectedLength).ToArray(), curve); + return DecodePoint(buffer[..(1 + curve.ExpectedECPointLength)], curve); } case 0x04: { - if (reader.Read(buffer, 1, expectedLength * 2) != expectedLength * 2) + if (reader.Read(buffer, 1, curve.ExpectedECPointLength * 2) != curve.ExpectedECPointLength * 2) { throw new FormatException(); } @@ -175,10 +170,10 @@ public static ECPoint FromBytes(byte[] pubkey, ECCurve curve) return DecodePoint(pubkey, curve); case 64: case 72: - return DecodePoint(new byte[] { 0x04 }.Concat(pubkey.Skip(pubkey.Length - 64)).ToArray(), curve); + return DecodePoint(Concat(new byte[] { 0x04 }, pubkey[^64..]), curve); case 96: case 104: - return DecodePoint(new byte[] { 0x04 }.Concat(pubkey.Skip(pubkey.Length - 96).Take(64)).ToArray(), curve); + return DecodePoint(Concat(new byte[] { 0x04 }, pubkey[^96..^32]), curve); default: throw new FormatException(); } @@ -241,7 +236,7 @@ internal static ECPoint Multiply(ECPoint p, BigInteger k) // The length of the precomputation array int preCompLen = 1; - ECPoint[] preComp = preComp = new ECPoint[] { p }; + ECPoint[] preComp = new ECPoint[] { p }; ECPoint twiceP = p.Twice(); if (preCompLen < reqPreCompLen) diff --git a/src/neo/Cryptography/Helper.cs b/src/neo/Cryptography/Helper.cs index 07c9ce74e9..83e0d7afa1 100644 --- a/src/neo/Cryptography/Helper.cs +++ b/src/neo/Cryptography/Helper.cs @@ -77,9 +77,9 @@ public static byte[] Base58CheckDecode(this string input) byte[] buffer = Base58.Decode(input); if (buffer.Length < 4) throw new FormatException(); byte[] checksum = buffer.Sha256(0, buffer.Length - 4).Sha256(); - if (!buffer.Skip(buffer.Length - 4).SequenceEqual(checksum.Take(4))) + if (!buffer.AsSpan(^4).SequenceEqual(checksum.AsSpan(..4))) throw new FormatException(); - var ret = buffer.Take(buffer.Length - 4).ToArray(); + var ret = buffer[..^4]; Array.Clear(buffer, 0, buffer.Length); return ret; } diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index 8fa7005f5e..6706355574 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -17,6 +17,7 @@ public static class Helper { private static readonly DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int BitLen(int w) { return (w < 1 << 15 ? (w < 1 << 7 @@ -34,6 +35,23 @@ private static int BitLen(int w) : (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31))))); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] Concat(params byte[][] buffers) + { + int length = 0; + for (int i = 0; i < buffers.Length; i++) + length += buffers[i].Length; + byte[] dst = new byte[length]; + int p = 0; + foreach (byte[] src in buffers) + { + Buffer.BlockCopy(src, 0, dst, p, src.Length); + p += src.Length; + } + return dst; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetBitLength(this BigInteger i) { byte[] b = i.ToByteArray(); @@ -110,6 +128,7 @@ public static byte[] HexToBytes(this string value) return result; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static BigInteger Mod(this BigInteger x, BigInteger y) { x %= y; @@ -172,13 +191,13 @@ public static BigInteger Sum(this IEnumerable source) return sum; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool TestBit(this BigInteger i, int index) { return (i & (BigInteger.One << index)) > BigInteger.Zero; } - - public static string ToHexString(this IEnumerable value) + public static string ToHexString(this byte[] value) { StringBuilder sb = new StringBuilder(); foreach (byte b in value) @@ -186,6 +205,14 @@ public static string ToHexString(this IEnumerable value) return sb.ToString(); } + public static string ToHexString(this byte[] value, bool reverse = false) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < value.Length; i++) + sb.AppendFormat("{0:x2}", value[reverse ? value.Length - i - 1 : i]); + return sb.ToString(); + } + public static string ToHexString(this ReadOnlySpan value) { StringBuilder sb = new StringBuilder(); diff --git a/src/neo/IO/Caching/DataCache.cs b/src/neo/IO/Caching/DataCache.cs index 373c4f2757..df6b6775a8 100644 --- a/src/neo/IO/Caching/DataCache.cs +++ b/src/neo/IO/Caching/DataCache.cs @@ -129,7 +129,7 @@ public IEnumerable<(TKey Key, TValue Value)> Find(byte[] key_prefix = null) lock (dictionary) { cached = dictionary - .Where(p => p.Value.State != TrackState.Deleted && (key_prefix == null || p.Key.ToArray().Take(key_prefix.Length).SequenceEqual(key_prefix))) + .Where(p => p.Value.State != TrackState.Deleted && (key_prefix == null || p.Key.ToArray().AsSpan().StartsWith(key_prefix))) .Select(p => ( KeyBytes: p.Key.ToArray(), diff --git a/src/neo/IO/Data/LevelDB/Helper.cs b/src/neo/IO/Data/LevelDB/Helper.cs index d4c451f574..585d282414 100644 --- a/src/neo/IO/Data/LevelDB/Helper.cs +++ b/src/neo/IO/Data/LevelDB/Helper.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; namespace Neo.IO.Data.LevelDB { @@ -26,7 +25,7 @@ public static IEnumerable Find(this DB db, ReadOptions options, Slice pref byte[] x = key.ToArray(); byte[] y = prefix.ToArray(); if (x.Length < y.Length) break; - if (!x.Take(y.Length).SequenceEqual(y)) break; + if (!x.AsSpan().StartsWith(y)) break; yield return resultSelector(key, it.Value()); } } diff --git a/src/neo/IO/Data/LevelDB/Slice.cs b/src/neo/IO/Data/LevelDB/Slice.cs index 1f9d927783..fc52e6eb08 100644 --- a/src/neo/IO/Data/LevelDB/Slice.cs +++ b/src/neo/IO/Data/LevelDB/Slice.cs @@ -1,6 +1,5 @@ using Neo.Cryptography; using System; -using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -29,7 +28,7 @@ public int CompareTo(Slice other) public bool Equals(Slice other) { if (buffer.Length != other.buffer.Length) return false; - return buffer.SequenceEqual(other.buffer); + return MemoryExtensions.SequenceEqual(buffer, other.buffer); } public override bool Equals(object obj) diff --git a/src/neo/Ledger/StorageKey.cs b/src/neo/Ledger/StorageKey.cs index c214260c21..70e045560e 100644 --- a/src/neo/Ledger/StorageKey.cs +++ b/src/neo/Ledger/StorageKey.cs @@ -2,7 +2,6 @@ using Neo.IO; using System; using System.IO; -using System.Linq; namespace Neo.Ledger { @@ -28,7 +27,7 @@ internal static byte[] CreateSearchPrefix(UInt160 hash, byte[] prefix) } if (remain > 0) ms.Write(prefix, index, remain); - return hash.ToArray().Concat(ms.ToArray()).ToArray(); + return Helper.Concat(hash.ToArray(), ms.ToArray()); } } @@ -44,7 +43,7 @@ public bool Equals(StorageKey other) return false; if (ReferenceEquals(this, other)) return true; - return ScriptHash.Equals(other.ScriptHash) && Key.SequenceEqual(other.Key); + return ScriptHash.Equals(other.ScriptHash) && MemoryExtensions.SequenceEqual(Key, other.Key); } public override bool Equals(object obj) diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index 305536c38d..0533e0f373 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -3,7 +3,6 @@ using Neo.IO.Json; using Neo.Ledger; using Neo.SmartContract; -using Neo.VM; using Neo.VM.Types; using System; using System.Collections.Generic; @@ -130,7 +129,7 @@ public TrimmedBlock Trim() Index = Index, NextConsensus = NextConsensus, Witness = Witness, - Hashes = new[] { ConsensusData.Hash }.Concat(Transactions.Select(p => p.Hash)).ToArray(), + Hashes = Transactions.Select(p => p.Hash).Prepend(ConsensusData.Hash).ToArray(), ConsensusData = ConsensusData }; } diff --git a/src/neo/Network/P2P/Payloads/InvPayload.cs b/src/neo/Network/P2P/Payloads/InvPayload.cs index 613d280234..662398e6a4 100644 --- a/src/neo/Network/P2P/Payloads/InvPayload.cs +++ b/src/neo/Network/P2P/Payloads/InvPayload.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; namespace Neo.Network.P2P.Payloads { @@ -30,7 +29,7 @@ public static IEnumerable CreateGroup(InventoryType type, UInt256[] yield return new InvPayload { Type = type, - Hashes = hashes.Skip(i).Take(MaxHashesCount).ToArray() + Hashes = hashes[i..(i + MaxHashesCount)] }; } diff --git a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs index 0d00434cd4..cf2dcba2d9 100644 --- a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs +++ b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs @@ -16,7 +16,7 @@ public class MerkleBlockPayload : BlockBase public static MerkleBlockPayload Create(Block block, BitArray flags) { - MerkleTree tree = new MerkleTree(new[] { block.ConsensusData.Hash }.Concat(block.Transactions.Select(p => p.Hash)).ToArray()); + MerkleTree tree = new MerkleTree(block.Transactions.Select(p => p.Hash).Prepend(block.ConsensusData.Hash).ToArray()); byte[] buffer = new byte[(flags.Length + 7) / 8]; flags.CopyTo(buffer, 0); return new MerkleBlockPayload diff --git a/src/neo/Network/RPC/Nep5API.cs b/src/neo/Network/RPC/Nep5API.cs index 26ae041ae0..619c755280 100644 --- a/src/neo/Network/RPC/Nep5API.cs +++ b/src/neo/Network/RPC/Nep5API.cs @@ -5,6 +5,7 @@ using Neo.Wallets; using System.Linq; using System.Numerics; +using static Neo.Helper; namespace Neo.Network.RPC { @@ -78,11 +79,10 @@ public BigInteger TotalSupply(UInt160 scriptHash) /// public RpcNep5TokenInfo GetTokenInfo(UInt160 scriptHash) { - byte[] script = scriptHash.MakeScript("name") - .Concat(scriptHash.MakeScript("symbol")) - .Concat(scriptHash.MakeScript("decimals")) - .Concat(scriptHash.MakeScript("totalSupply")) - .ToArray(); + byte[] script = Concat(scriptHash.MakeScript("name"), + scriptHash.MakeScript("symbol"), + scriptHash.MakeScript("decimals"), + scriptHash.MakeScript("totalSupply")); var result = rpcClient.InvokeScript(script).Stack; diff --git a/src/neo/Network/RPC/RpcServer.cs b/src/neo/Network/RPC/RpcServer.cs index 953783b5ed..4bdaf91caf 100644 --- a/src/neo/Network/RPC/RpcServer.cs +++ b/src/neo/Network/RPC/RpcServer.cs @@ -411,7 +411,7 @@ public void Start(IPAddress bindAddress, int port, string sslCert = null, string { // options.EnableForHttps = false; options.Providers.Add(); - options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/json-rpc" }); + options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Append("application/json-rpc"); }); services.Configure(options => diff --git a/src/neo/Persistence/LevelDB/Snapshot.cs b/src/neo/Persistence/LevelDB/Snapshot.cs index dbf5cbe278..712268924c 100644 --- a/src/neo/Persistence/LevelDB/Snapshot.cs +++ b/src/neo/Persistence/LevelDB/Snapshot.cs @@ -1,6 +1,5 @@ using Neo.IO.Data.LevelDB; using System.Collections.Generic; -using System.Linq; using LSnapshot = Neo.IO.Data.LevelDB.Snapshot; namespace Neo.Persistence.LevelDB @@ -37,7 +36,7 @@ public void Dispose() public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) { - return db.Find(options, SliceBuilder.Begin(table).Add(prefix), (k, v) => (k.ToArray().Skip(1).ToArray(), v.ToArray())); + return db.Find(options, SliceBuilder.Begin(table).Add(prefix), (k, v) => (k.ToArray()[1..], v.ToArray())); } public void Put(byte table, byte[] key, byte[] value) diff --git a/src/neo/Persistence/LevelDB/Store.cs b/src/neo/Persistence/LevelDB/Store.cs index fbded76391..6758744281 100644 --- a/src/neo/Persistence/LevelDB/Store.cs +++ b/src/neo/Persistence/LevelDB/Store.cs @@ -1,7 +1,6 @@ using Neo.IO.Data.LevelDB; using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; namespace Neo.Persistence.LevelDB @@ -41,7 +40,7 @@ public void Dispose() public IEnumerable<(byte[], byte[])> Find(byte table, byte[] prefix) { - return db.Find(ReadOptions.Default, SliceBuilder.Begin(table).Add(prefix), (k, v) => (k.ToArray().Skip(1).ToArray(), v.ToArray())); + return db.Find(ReadOptions.Default, SliceBuilder.Begin(table).Add(prefix), (k, v) => (k.ToArray()[1..], v.ToArray())); } public ISnapshot GetSnapshot() diff --git a/src/neo/Persistence/Memory/Snapshot.cs b/src/neo/Persistence/Memory/Snapshot.cs index 82455ddea8..a8edf8cf05 100644 --- a/src/neo/Persistence/Memory/Snapshot.cs +++ b/src/neo/Persistence/Memory/Snapshot.cs @@ -1,4 +1,5 @@ using Neo.IO; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; @@ -44,7 +45,7 @@ public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) { IEnumerable> records = immutableData[table]; if (prefix?.Length > 0) - records = records.Where(p => p.Key.Length >= prefix.Length && p.Key.Take(prefix.Length).SequenceEqual(prefix)); + records = records.Where(p => p.Key.AsSpan().StartsWith(prefix)); records = records.OrderBy(p => p.Key, ByteArrayComparer.Default); return records.Select(p => (p.Key, p.Value)); } diff --git a/src/neo/Persistence/Memory/Store.cs b/src/neo/Persistence/Memory/Store.cs index 5e2d11eeb9..067cf073d9 100644 --- a/src/neo/Persistence/Memory/Store.cs +++ b/src/neo/Persistence/Memory/Store.cs @@ -30,7 +30,7 @@ public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) { IEnumerable> records = innerData[table]; if (prefix?.Length > 0) - records = records.Where(p => p.Key.Length >= prefix.Length && p.Key.Take(prefix.Length).SequenceEqual(prefix)); + records = records.Where(p => p.Key.AsSpan().StartsWith(prefix)); records = records.OrderBy(p => p.Key, ByteArrayComparer.Default); foreach (var pair in records) yield return (pair.Key, pair.Value); diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index 4465e7831e..1ddc703830 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -153,7 +153,7 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) } while (contract.Script[i++] == 33) { - points.Add(ECPoint.DecodePoint(contract.Script.Skip(i).Take(33).ToArray(), ECCurve.Secp256r1)); + points.Add(ECPoint.DecodePoint(contract.Script[i..(i + 33)], ECCurve.Secp256r1)); i += 33; } } @@ -256,9 +256,9 @@ public Witness[] GetWitnesses() ContextItem item = ContextItems[ScriptHashes[i]]; using (ScriptBuilder sb = new ScriptBuilder()) { - foreach (ContractParameter parameter in item.Parameters.Reverse()) + for (int j = item.Parameters.Length - 1; j >= 0; j--) { - sb.EmitPush(parameter); + sb.EmitPush(item.Parameters[j]); } witnesses[i] = new Witness { diff --git a/src/neo/SmartContract/InteropService.NEO.cs b/src/neo/SmartContract/InteropService.NEO.cs index a133e882fa..07fac8e2b7 100644 --- a/src/neo/SmartContract/InteropService.NEO.cs +++ b/src/neo/SmartContract/InteropService.NEO.cs @@ -250,7 +250,7 @@ private static bool Storage_Find(ApplicationEngine engine) if (!CheckStorageContext(engine, context)) return false; byte[] prefix = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); byte[] prefix_key = StorageKey.CreateSearchPrefix(context.ScriptHash, prefix); - StorageIterator iterator = engine.AddDisposable(new StorageIterator(engine.Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.Take(prefix.Length).SequenceEqual(prefix)).GetEnumerator())); + StorageIterator iterator = engine.AddDisposable(new StorageIterator(engine.Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.AsSpan().StartsWith(prefix)).GetEnumerator())); engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator)); return true; } diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index e0023b3841..e3bfb34c11 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -204,7 +204,7 @@ public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetRegisteredValidator byte[] prefix_key = StorageKey.CreateSearchPrefix(Hash, new[] { Prefix_Validator }); return snapshot.Storages.Find(prefix_key).Select(p => ( - p.Key.Key.Skip(1).ToArray().AsSerializable(), + p.Key.Key.AsSerializable(1), ValidatorState.FromByteArray(p.Value.Value).Votes )); } diff --git a/src/neo/UInt160.cs b/src/neo/UInt160.cs index 0052d45569..8eaf0ffc23 100644 --- a/src/neo/UInt160.cs +++ b/src/neo/UInt160.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using System.Linq; namespace Neo { @@ -82,7 +81,9 @@ public static new UInt160 Parse(string value) value = value.Substring(2); if (value.Length != Length * 2) throw new FormatException(); - return new UInt160(value.HexToBytes().Reverse().ToArray()); + byte[] data = value.HexToBytes(); + Array.Reverse(data); + return new UInt160(data); } /// @@ -110,7 +111,8 @@ public static bool TryParse(string s, out UInt160 result) result = null; return false; } - result = new UInt160(data.Reverse().ToArray()); + Array.Reverse(data); + result = new UInt160(data); return true; } diff --git a/src/neo/UInt256.cs b/src/neo/UInt256.cs index 72719f0e90..57e91fadd7 100644 --- a/src/neo/UInt256.cs +++ b/src/neo/UInt256.cs @@ -1,6 +1,5 @@ using System; using System.Globalization; -using System.Linq; namespace Neo { @@ -83,7 +82,9 @@ public static new UInt256 Parse(string s) s = s.Substring(2); if (s.Length != Length * 2) throw new FormatException(); - return new UInt256(s.HexToBytes().Reverse().ToArray()); + byte[] data = s.HexToBytes(); + Array.Reverse(data); + return new UInt256(data); } /// @@ -111,7 +112,8 @@ public static bool TryParse(string s, out UInt256 result) result = null; return false; } - result = new UInt256(data.Reverse().ToArray()); + Array.Reverse(data); + result = new UInt256(data); return true; } diff --git a/src/neo/UIntBase.cs b/src/neo/UIntBase.cs index 6b5f6d6acd..ab07670660 100644 --- a/src/neo/UIntBase.cs +++ b/src/neo/UIntBase.cs @@ -1,7 +1,6 @@ using Neo.IO; using System; using System.IO; -using System.Linq; using System.Runtime.CompilerServices; namespace Neo @@ -62,7 +61,7 @@ public bool Equals(UIntBase other) return true; if (data_bytes.Length != other.data_bytes.Length) return false; - return data_bytes.SequenceEqual(other.data_bytes); + return MemoryExtensions.SequenceEqual(data_bytes, other.data_bytes); } /// @@ -123,7 +122,7 @@ public byte[] ToArray() /// public override string ToString() { - return "0x" + data_bytes.Reverse().ToHexString(); + return "0x" + data_bytes.ToHexString(reverse: true); } /// diff --git a/src/neo/Wallets/Helper.cs b/src/neo/Wallets/Helper.cs index 5960876fe2..9de3dde7f3 100644 --- a/src/neo/Wallets/Helper.cs +++ b/src/neo/Wallets/Helper.cs @@ -2,7 +2,6 @@ using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using System; -using System.Linq; namespace Neo.Wallets { @@ -10,7 +9,7 @@ public static class Helper { public static byte[] Sign(this IVerifiable verifiable, KeyPair key) { - return Crypto.Default.Sign(verifiable.GetHashData(), key.PrivateKey, key.PublicKey.EncodePoint(false).Skip(1).ToArray()); + return Crypto.Default.Sign(verifiable.GetHashData(), key.PrivateKey, key.PublicKey.EncodePoint(false)[1..]); } public static string ToAddress(this UInt160 scriptHash) @@ -28,7 +27,7 @@ public static UInt160 ToScriptHash(this string address) throw new FormatException(); if (data[0] != ProtocolSettings.Default.AddressVersion) throw new FormatException(); - return new UInt160(data.Skip(1).ToArray()); + return new UInt160(data[1..]); } } } diff --git a/src/neo/Wallets/KeyPair.cs b/src/neo/Wallets/KeyPair.cs index afea8e822a..55d720634b 100644 --- a/src/neo/Wallets/KeyPair.cs +++ b/src/neo/Wallets/KeyPair.cs @@ -56,10 +56,10 @@ public string Export(string passphrase, int N = 16384, int r = 8, int p = 8) { UInt160 script_hash = Contract.CreateSignatureRedeemScript(PublicKey).ToScriptHash(); string address = script_hash.ToAddress(); - byte[] addresshash = Encoding.ASCII.GetBytes(address).Sha256().Sha256().Take(4).ToArray(); + byte[] addresshash = Encoding.ASCII.GetBytes(address).Sha256().Sha256()[..4]; byte[] derivedkey = SCrypt.DeriveKey(Encoding.UTF8.GetBytes(passphrase), addresshash, N, r, p, 64); - byte[] derivedhalf1 = derivedkey.Take(32).ToArray(); - byte[] derivedhalf2 = derivedkey.Skip(32).ToArray(); + byte[] derivedhalf1 = derivedkey[..32]; + byte[] derivedhalf2 = derivedkey[32..]; byte[] encryptedkey = XOR(PrivateKey, derivedhalf1).AES256Encrypt(derivedhalf2); byte[] buffer = new byte[39]; buffer[0] = 0x01; diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index f2cf92e7b2..ebc1e6b58f 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -151,8 +151,8 @@ public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, int N byte[] datapassphrase = Encoding.UTF8.GetBytes(passphrase); byte[] derivedkey = SCrypt.DeriveKey(datapassphrase, addresshash, N, r, p, 64); Array.Clear(datapassphrase, 0, datapassphrase.Length); - byte[] derivedhalf1 = derivedkey.Take(32).ToArray(); - byte[] derivedhalf2 = derivedkey.Skip(32).ToArray(); + byte[] derivedhalf1 = derivedkey[..32]; + byte[] derivedhalf2 = derivedkey[32..]; Array.Clear(derivedkey, 0, derivedkey.Length); byte[] encryptedkey = new byte[32]; Buffer.BlockCopy(data, 7, encryptedkey, 0, 32); @@ -163,7 +163,7 @@ public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, int N ECPoint pubkey = Cryptography.ECC.ECCurve.Secp256r1.G * prikey; UInt160 script_hash = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); string address = script_hash.ToAddress(); - if (!Encoding.ASCII.GetBytes(address).Sha256().Sha256().Take(4).SequenceEqual(addresshash)) + if (!Encoding.ASCII.GetBytes(address).Sha256().Sha256().AsSpan(0, 4).SequenceEqual(addresshash)) throw new FormatException(); return prikey; } From 4a810e88024bfe3f71b4346daf06b6005703b2ad Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 30 Nov 2019 20:51:29 +0800 Subject: [PATCH 153/305] Unify the BigInteger serialization standard with NeoVM (#1307) --- src/neo/Helper.cs | 7 +++++++ src/neo/SmartContract/Native/Tokens/GasToken.cs | 2 +- src/neo/SmartContract/Native/Tokens/NeoToken.cs | 4 ++-- src/neo/SmartContract/Native/Tokens/Nep5Token.cs | 6 +++--- .../SmartContract/Native/Tokens/UT_GasToken.cs | 2 +- .../SmartContract/Native/Tokens/UT_Nep5Token.cs | 3 +-- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index 6706355574..b20c179cbe 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -197,6 +197,13 @@ internal static bool TestBit(this BigInteger i, int index) return (i & (BigInteger.One << index)) > BigInteger.Zero; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] ToByteArrayStandard(this BigInteger i) + { + if (i.IsZero) return Array.Empty(); + return i.ToByteArray(); + } + public static string ToHexString(this byte[] value) { StringBuilder sb = new StringBuilder(); diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index be46335940..daa57b6747 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -47,7 +47,7 @@ protected override bool OnPersist(ApplicationEngine engine) StorageKey key = CreateStorageKey(Prefix_SystemFeeAmount, BitConverter.GetBytes(engine.Snapshot.PersistingBlock.Index)); engine.Snapshot.Storages.Add(key, new StorageItem { - Value = sys_fee.ToByteArray(), + Value = sys_fee.ToByteArrayStandard(), IsConstant = true }); return true; diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index e3bfb34c11..f9ab8d5d8f 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -292,7 +292,7 @@ public static ValidatorState FromByteArray(byte[] data) public byte[] ToByteArray() { - return Votes.ToByteArray(); + return Votes.ToByteArrayStandard(); } } @@ -322,7 +322,7 @@ public byte[] ToByteArray() { w.WriteVarInt(Votes.Length); foreach (BigInteger vote in Votes) - w.WriteVarBytes(vote.ToByteArray()); + w.WriteVarBytes(vote.ToByteArrayStandard()); w.Flush(); return ms.ToArray(); } diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index 5728c2d8be..71dfdac9bc 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -80,11 +80,11 @@ internal protected virtual void Mint(ApplicationEngine engine, UInt160 account, storage.Value = state.ToByteArray(); storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem { - Value = BigInteger.Zero.ToByteArray() + Value = BigInteger.Zero.ToByteArrayStandard() }); BigInteger totalSupply = new BigInteger(storage.Value); totalSupply += amount; - storage.Value = totalSupply.ToByteArray(); + storage.Value = totalSupply.ToByteArrayStandard(); engine.SendNotification(Hash, new StackItem[] { "Transfer", StackItem.Null, account.ToArray(), amount }); } @@ -110,7 +110,7 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply)); BigInteger totalSupply = new BigInteger(storage.Value); totalSupply -= amount; - storage.Value = totalSupply.ToByteArray(); + storage.Value = totalSupply.ToByteArrayStandard(); engine.SendNotification(Hash, new StackItem[] { "Transfer", account.ToArray(), StackItem.Null, amount }); } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index 96beff3cbd..a045785178 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -172,7 +172,7 @@ public void TestGetSysFeeAmount2() BigInteger sys_fee = new BigInteger(10); snapshot.Storages.Add(storageKey, new StorageItem { - Value = sys_fee.ToByteArray(), + Value = sys_fee.ToByteArrayStandard(), IsConstant = true }); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs index a3bc7fa531..5e9dbe9617 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs @@ -51,10 +51,9 @@ public void TestTotalSupplyDecimal() BigInteger totalSupply = 100_000_000; totalSupply *= test.Factor; - byte[] value = totalSupply.ToByteArray(); StorageItem item = new StorageItem { - Value = value + Value = totalSupply.ToByteArrayStandard() }; var key = CreateStorageKey(Prefix_TotalSupply); From 6abf897de113b8d232bc4906ead1732f27b9342d Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 1 Dec 2019 17:25:33 +0800 Subject: [PATCH 154/305] Optimizations for .NET Standard 2.1 --- src/neo/Consensus/ConsensusContext.cs | 6 +- src/neo/Consensus/ConsensusService.cs | 4 +- src/neo/Cryptography/Base58.cs | 25 ++- src/neo/Cryptography/Crypto.cs | 12 +- src/neo/Cryptography/ECC/ECDsa.cs | 6 +- src/neo/Cryptography/ECC/ECPoint.cs | 14 +- src/neo/Cryptography/Helper.cs | 56 +++--- src/neo/Cryptography/MerkleTree.cs | 21 +- src/neo/Cryptography/Murmur3.cs | 3 +- src/neo/Helper.cs | 66 +------ src/neo/IO/Caching/DataCache.cs | 2 +- src/neo/IO/Caching/ReflectionCache.cs | 3 +- src/neo/IO/Data/LevelDB/Slice.cs | 2 +- src/neo/IO/Helper.cs | 35 +++- src/neo/IO/SerializableWrapper.cs | 67 +++++++ src/neo/IO/Wrappers/SerializableWrapper.cs | 27 --- src/neo/IO/Wrappers/UInt32Wrapper.cs | 44 ----- src/neo/Ledger/Blockchain.cs | 4 +- src/neo/NeoSystem.cs | 2 +- src/neo/Network/P2P/Connection.cs | 3 +- src/neo/Network/P2P/Helper.cs | 47 ----- src/neo/Network/P2P/Message.cs | 11 +- src/neo/Network/P2P/Payloads/Block.cs | 10 +- src/neo/Network/P2P/Payloads/BlockBase.cs | 2 +- src/neo/Network/P2P/Payloads/ConsensusData.cs | 2 +- .../Network/P2P/Payloads/ConsensusPayload.cs | 2 +- .../Network/P2P/Payloads/HeadersPayload.cs | 6 +- src/neo/Network/P2P/Payloads/InventoryType.cs | 6 +- src/neo/Network/P2P/Payloads/Transaction.cs | 2 +- src/neo/Network/P2P/Peer.cs | 4 +- src/neo/Network/P2P/ProtocolHandler.cs | 2 +- src/neo/Network/P2P/RemoteNode.cs | 2 +- src/neo/Network/RPC/RpcServer.cs | 5 - src/neo/Network/RPC/TransactionManager.cs | 4 +- src/neo/Network/UPnP.cs | 4 +- src/neo/Persistence/ClonedView.cs | 4 +- src/neo/Persistence/IStore.cs | 5 +- src/neo/Persistence/Memory/Helper.cs | 6 +- src/neo/Persistence/Memory/Store.cs | 5 - src/neo/Persistence/ReadOnlyView.cs | 4 +- src/neo/Persistence/SnapshotView.cs | 6 +- src/neo/Persistence/StoreView.cs | 4 +- src/neo/ProtocolSettings.cs | 2 +- src/neo/SmartContract/ApplicationEngine.cs | 5 +- src/neo/SmartContract/Contract.cs | 2 +- src/neo/SmartContract/ContractParameter.cs | 2 +- .../ContractParametersContext.cs | 4 +- src/neo/SmartContract/Helper.cs | 175 +---------------- src/neo/SmartContract/InteropService.NEO.cs | 21 +- src/neo/SmartContract/InteropService.cs | 9 +- .../SmartContract/Manifest/ContractGroup.cs | 3 +- .../Native/ContractMethodAttribute.cs | 4 +- .../SmartContract/Native/PolicyContract.cs | 8 +- .../Native/Tokens/Nep5AccountState.cs | 4 +- .../SmartContract/Native/Tokens/Nep5Token.cs | 1 + src/neo/SmartContract/NefFile.cs | 6 +- src/neo/SmartContract/StackItemSerializer.cs | 183 ++++++++++++++++++ src/neo/SmartContract/StorageContext.cs | 5 - src/neo/UInt160.cs | 106 ++++++---- src/neo/UInt256.cs | 111 +++++++---- src/neo/UIntBase.cs | 112 +---------- src/neo/Utility.cs | 15 ++ src/neo/VM/Helper.cs | 28 +-- src/neo/Wallets/Helper.cs | 18 +- src/neo/Wallets/KeyPair.cs | 27 +-- src/neo/Wallets/SQLite/UserWallet.cs | 9 +- src/neo/Wallets/Wallet.cs | 7 +- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 16 +- tests/neo.UnitTests/Cryptography/UT_Crypto.cs | 14 +- .../Cryptography/UT_Cryptography_Helper.cs | 1 + .../Cryptography/UT_MerkleTree.cs | 22 +-- .../Cryptography/UT_MerkleTreeNode.cs | 2 +- .../{Wrappers => }/UT_SerializableWrapper.cs | 12 +- .../IO/Wrappers/UT_UInt32Wrapper.cs | 97 ---------- .../Network/RPC/UT_TransactionManager.cs | 2 +- .../Manifest/UT_ContractGroup.cs | 3 +- .../Native/Tokens/UT_GasToken.cs | 1 + .../Native/Tokens/UT_NeoToken.cs | 2 +- .../SmartContract/UT_InteropService.NEO.cs | 9 +- .../SmartContract/UT_InteropService.cs | 3 +- .../SmartContract/UT_SmartContractHelper.cs | 140 -------------- .../SmartContract/UT_StackItemSerializer.cs | 128 ++++++++++++ .../SmartContract/UT_StorageContext.cs | 5 +- .../SmartContract/UT_Syscalls.cs | 1 + tests/neo.UnitTests/UT_Helper.cs | 27 --- tests/neo.UnitTests/UT_UInt256.cs | 44 +++++ tests/neo.UnitTests/UT_UIntBase.cs | 53 ----- tests/neo.UnitTests/VM/UT_Helper.cs | 1 + .../Wallets/NEP6/UT_NEP6Account.cs | 4 +- tests/neo.UnitTests/Wallets/UT_KeyPair.cs | 2 +- .../Wallets/UT_Wallets_Helper.cs | 9 +- 91 files changed, 891 insertions(+), 1119 deletions(-) create mode 100644 src/neo/IO/SerializableWrapper.cs delete mode 100644 src/neo/IO/Wrappers/SerializableWrapper.cs delete mode 100644 src/neo/IO/Wrappers/UInt32Wrapper.cs create mode 100644 src/neo/SmartContract/StackItemSerializer.cs rename tests/neo.UnitTests/IO/{Wrappers => }/UT_SerializableWrapper.cs (78%) delete mode 100644 tests/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs create mode 100644 tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index cab0ce8370..695b92a683 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -280,9 +280,9 @@ internal void EnsureMaxBlockSize(IEnumerable txs) public ConsensusPayload MakePrepareRequest() { var random = new Random(); - byte[] buffer = new byte[sizeof(ulong)]; + Span buffer = stackalloc byte[sizeof(ulong)]; random.NextBytes(buffer); - Block.ConsensusData.Nonce = BitConverter.ToUInt64(buffer, 0); + Block.ConsensusData.Nonce = BitConverter.ToUInt64(buffer); EnsureMaxBlockSize(Blockchain.Singleton.MemPool.GetSortedVerifiedTransactions()); Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1); @@ -346,7 +346,7 @@ public void Reset(byte viewNumber) { PrevHash = Snapshot.CurrentBlockHash, Index = Snapshot.Height + 1, - NextConsensus = Blockchain.GetConsensusAddress(NativeContract.NEO.GetValidators(Snapshot).ToArray()) + NextConsensus = Blockchain.GetConsensusAddress(NativeContract.NEO.GetValidators(Snapshot)) }; var pv = Validators; Validators = NativeContract.NEO.GetNextBlockValidators(Snapshot); diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index ae901611b3..1afbd20322 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -231,7 +231,7 @@ private void OnCommitReceived(ConsensusPayload payload, Commit commit) { existingCommitPayload = payload; } - else if (Crypto.Default.VerifySignature(hashData, commit.Signature, + else if (Crypto.VerifySignature(hashData, commit.Signature, context.Validators[payload.ValidatorIndex].EncodePoint(false))) { existingCommitPayload = payload; @@ -433,7 +433,7 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m byte[] hashData = context.EnsureHeader().GetHashData(); for (int i = 0; i < context.CommitPayloads.Length; i++) if (context.CommitPayloads[i]?.ConsensusMessage.ViewNumber == context.ViewNumber) - if (!Crypto.Default.VerifySignature(hashData, context.CommitPayloads[i].GetDeserializedMessage().Signature, context.Validators[i].EncodePoint(false))) + if (!Crypto.VerifySignature(hashData, context.CommitPayloads[i].GetDeserializedMessage().Signature, context.Validators[i].EncodePoint(false))) context.CommitPayloads[i] = null; if (context.TransactionHashes.Length == 0) diff --git a/src/neo/Cryptography/Base58.cs b/src/neo/Cryptography/Base58.cs index 4a39acfbef..316f1de72b 100644 --- a/src/neo/Cryptography/Base58.cs +++ b/src/neo/Cryptography/Base58.cs @@ -10,6 +10,29 @@ public static class Base58 { public const string Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + public static byte[] Base58CheckDecode(this string input) + { + byte[] buffer = Decode(input); + if (buffer.Length < 4) throw new FormatException(); + byte[] checksum = buffer.Sha256(0, buffer.Length - 4).Sha256(); + if (!buffer.AsSpan(^4).SequenceEqual(checksum.AsSpan(..4))) + throw new FormatException(); + var ret = buffer[..^4]; + Array.Clear(buffer, 0, buffer.Length); + return ret; + } + + public static string Base58CheckEncode(this ReadOnlySpan data) + { + byte[] checksum = data.Sha256().Sha256(); + Span buffer = stackalloc byte[data.Length + 4]; + data.CopyTo(buffer); + checksum.AsSpan(..4).CopyTo(buffer[data.Length..]); + var ret = Encode(buffer); + buffer.Clear(); + return ret; + } + public static byte[] Decode(string input) { // Decode Base58 string to BigInteger @@ -31,7 +54,7 @@ public static byte[] Decode(string input) return Concat(leadingZeros, bytesWithoutLeadingZeros); } - public static string Encode(byte[] input) + public static string Encode(ReadOnlySpan input) { // Decode byte[] to BigInteger BigInteger value = new BigInteger(input, isUnsigned: true, isBigEndian: true); diff --git a/src/neo/Cryptography/Crypto.cs b/src/neo/Cryptography/Crypto.cs index 5bf90a6c97..bef425a044 100644 --- a/src/neo/Cryptography/Crypto.cs +++ b/src/neo/Cryptography/Crypto.cs @@ -3,21 +3,19 @@ namespace Neo.Cryptography { - public class Crypto + public static class Crypto { - public static readonly Crypto Default = new Crypto(); - - public byte[] Hash160(byte[] message) + public static byte[] Hash160(ReadOnlySpan message) { return message.Sha256().RIPEMD160(); } - public byte[] Hash256(byte[] message) + public static byte[] Hash256(ReadOnlySpan message) { return message.Sha256().Sha256(); } - public byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey) + public static byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey) { using (var ecdsa = ECDsa.Create(new ECParameters { @@ -34,7 +32,7 @@ public byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey) } } - public bool VerifySignature(byte[] message, byte[] signature, byte[] pubkey) + public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, byte[] pubkey) { if (pubkey.Length == 33 && (pubkey[0] == 0x02 || pubkey[0] == 0x03)) { diff --git a/src/neo/Cryptography/ECC/ECDsa.cs b/src/neo/Cryptography/ECC/ECDsa.cs index cae8dc4e7c..08f48b1dc3 100644 --- a/src/neo/Cryptography/ECC/ECDsa.cs +++ b/src/neo/Cryptography/ECC/ECDsa.cs @@ -22,7 +22,7 @@ public ECDsa(ECPoint publicKey) this.curve = publicKey.Curve; } - private BigInteger CalculateE(BigInteger n, byte[] message) + private BigInteger CalculateE(BigInteger n, ReadOnlySpan message) { int messageBitLength = message.Length * 8; BigInteger trunc = new BigInteger(message, isUnsigned: true, isBigEndian: true); @@ -33,7 +33,7 @@ private BigInteger CalculateE(BigInteger n, byte[] message) return trunc; } - public BigInteger[] GenerateSignature(byte[] message) + public BigInteger[] GenerateSignature(ReadOnlySpan message) { if (privateKey == null) throw new InvalidOperationException(); BigInteger e = CalculateE(curve.N, message); @@ -91,7 +91,7 @@ private static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger k, ECPoint Q, Bi return R; } - public bool VerifySignature(byte[] message, BigInteger r, BigInteger s) + public bool VerifySignature(ReadOnlySpan message, BigInteger r, BigInteger s) { if (r.Sign < 1 || s.Sign < 1 || r.CompareTo(curve.N) >= 0 || s.CompareTo(curve.N) >= 0) return false; diff --git a/src/neo/Cryptography/ECC/ECPoint.cs b/src/neo/Cryptography/ECC/ECPoint.cs index 7eef7362e2..4189dcbb9b 100644 --- a/src/neo/Cryptography/ECC/ECPoint.cs +++ b/src/neo/Cryptography/ECC/ECPoint.cs @@ -37,7 +37,7 @@ public int CompareTo(ECPoint other) return Y.CompareTo(other.Y); } - public static ECPoint DecodePoint(byte[] encoded, ECCurve curve) + public static ECPoint DecodePoint(ReadOnlySpan encoded, ECCurve curve) { ECPoint p = null; switch (encoded[0]) @@ -48,7 +48,7 @@ public static ECPoint DecodePoint(byte[] encoded, ECCurve curve) if (encoded.Length != (curve.ExpectedECPointLength + 1)) throw new FormatException("Incorrect length for compressed encoding"); int yTilde = encoded[0] & 1; - BigInteger X1 = new BigInteger(encoded.AsSpan(1), isUnsigned: true, isBigEndian: true); + BigInteger X1 = new BigInteger(encoded[1..], isUnsigned: true, isBigEndian: true); p = DecompressPoint(yTilde, X1, curve); break; } @@ -56,8 +56,8 @@ public static ECPoint DecodePoint(byte[] encoded, ECCurve curve) { if (encoded.Length != (2 * curve.ExpectedECPointLength + 1)) throw new FormatException("Incorrect length for uncompressed/hybrid encoding"); - BigInteger X1 = new BigInteger(encoded.AsSpan(1, curve.ExpectedECPointLength), isUnsigned: true, isBigEndian: true); - BigInteger Y1 = new BigInteger(encoded.AsSpan(1 + curve.ExpectedECPointLength), isUnsigned: true, isBigEndian: true); + BigInteger X1 = new BigInteger(encoded[1..(1 + curve.ExpectedECPointLength)], isUnsigned: true, isBigEndian: true); + BigInteger Y1 = new BigInteger(encoded[(1 + curve.ExpectedECPointLength)..], isUnsigned: true, isBigEndian: true); p = new ECPoint(new ECFieldElement(X1, curve), new ECFieldElement(Y1, curve), curve); break; } @@ -101,14 +101,14 @@ void ISerializable.Deserialize(BinaryReader reader) public static ECPoint DeserializeFrom(BinaryReader reader, ECCurve curve) { - byte[] buffer = new byte[1 + curve.ExpectedECPointLength * 2]; + Span buffer = stackalloc byte[1 + curve.ExpectedECPointLength * 2]; buffer[0] = reader.ReadByte(); switch (buffer[0]) { case 0x02: case 0x03: { - if (reader.Read(buffer, 1, curve.ExpectedECPointLength) != curve.ExpectedECPointLength) + if (reader.Read(buffer[1..(1 + curve.ExpectedECPointLength)]) != curve.ExpectedECPointLength) { throw new FormatException(); } @@ -116,7 +116,7 @@ public static ECPoint DeserializeFrom(BinaryReader reader, ECCurve curve) } case 0x04: { - if (reader.Read(buffer, 1, curve.ExpectedECPointLength * 2) != curve.ExpectedECPointLength * 2) + if (reader.Read(buffer[1..(1 + curve.ExpectedECPointLength * 2)]) != curve.ExpectedECPointLength * 2) { throw new FormatException(); } diff --git a/src/neo/Cryptography/Helper.cs b/src/neo/Cryptography/Helper.cs index 83e0d7afa1..3cedb53236 100644 --- a/src/neo/Cryptography/Helper.cs +++ b/src/neo/Cryptography/Helper.cs @@ -1,21 +1,18 @@ using Neo.IO; using Neo.Network.P2P.Payloads; using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Security; using System.Security.Cryptography; using System.Text; -using System.Threading; namespace Neo.Cryptography { public static class Helper { - private static ThreadLocal _sha256 = new ThreadLocal(() => SHA256.Create()); - private static ThreadLocal _ripemd160 = new ThreadLocal(() => new RIPEMD160Managed()); - internal static byte[] AES256Decrypt(this byte[] block, byte[] key) { using (Aes aes = Aes.Create()) @@ -72,50 +69,43 @@ internal static byte[] AesEncrypt(this byte[] data, byte[] key, byte[] iv) } } - public static byte[] Base58CheckDecode(this string input) + public static byte[] RIPEMD160(this byte[] value) { - byte[] buffer = Base58.Decode(input); - if (buffer.Length < 4) throw new FormatException(); - byte[] checksum = buffer.Sha256(0, buffer.Length - 4).Sha256(); - if (!buffer.AsSpan(^4).SequenceEqual(checksum.AsSpan(..4))) - throw new FormatException(); - var ret = buffer[..^4]; - Array.Clear(buffer, 0, buffer.Length); - return ret; + using var ripemd160 = new RIPEMD160Managed(); + return ripemd160.ComputeHash(value); } - public static string Base58CheckEncode(this byte[] data) + public static uint Murmur32(this byte[] value, uint seed) { - byte[] checksum = data.Sha256().Sha256(); - byte[] buffer = new byte[data.Length + 4]; - Buffer.BlockCopy(data, 0, buffer, 0, data.Length); - Buffer.BlockCopy(checksum, 0, buffer, data.Length, 4); - var ret = Base58.Encode(buffer); - Array.Clear(buffer, 0, buffer.Length); - return ret; + using (Murmur3 murmur = new Murmur3(seed)) + { + return BinaryPrimitives.ReadUInt32LittleEndian(murmur.ComputeHash(value)); + } } - public static byte[] RIPEMD160(this IEnumerable value) + public static byte[] Sha256(this byte[] value) { - return _ripemd160.Value.ComputeHash(value.ToArray()); + using var sha256 = SHA256.Create(); + return sha256.ComputeHash(value); } - public static uint Murmur32(this IEnumerable value, uint seed) + public static byte[] Sha256(this byte[] value, int offset, int count) { - using (Murmur3 murmur = new Murmur3(seed)) - { - return murmur.ComputeHash(value.ToArray()).ToUInt32(0); - } + using var sha256 = SHA256.Create(); + return sha256.ComputeHash(value, offset, count); } - public static byte[] Sha256(this IEnumerable value) + public static byte[] Sha256(this ReadOnlySpan value) { - return _sha256.Value.ComputeHash(value.ToArray()); + byte[] buffer = new byte[32]; + using var sha256 = SHA256.Create(); + sha256.TryComputeHash(value, buffer, out _); + return buffer; } - public static byte[] Sha256(this byte[] value, int offset, int count) + public static byte[] Sha256(this Span value) { - return _sha256.Value.ComputeHash(value, offset, count); + return Sha256((ReadOnlySpan)value); } internal static bool Test(this BloomFilter filter, Transaction tx) @@ -157,7 +147,7 @@ internal static byte[] ToArray(this SecureString s) if (s == null) throw new NullReferenceException(); if (s.Length == 0) - return new byte[0]; + return Array.Empty(); List result = new List(); IntPtr ptr = SecureStringMarshal.SecureStringToGlobalAllocAnsi(s); try diff --git a/src/neo/Cryptography/MerkleTree.cs b/src/neo/Cryptography/MerkleTree.cs index 348c439350..89d0a275b5 100644 --- a/src/neo/Cryptography/MerkleTree.cs +++ b/src/neo/Cryptography/MerkleTree.cs @@ -1,3 +1,4 @@ +using Neo.IO; using System; using System.Collections; using System.Collections.Generic; @@ -12,9 +13,9 @@ public class MerkleTree public int Depth { get; private set; } - internal MerkleTree(IReadOnlyList hashes) + internal MerkleTree(UInt256[] hashes) { - if (hashes.Count == 0) throw new ArgumentException(); + if (hashes.Length == 0) throw new ArgumentException(); this.root = Build(hashes.Select(p => new MerkleTreeNode { Hash = p }).ToArray()); int depth = 1; for (MerkleTreeNode i = root; i.LeftChild != null; i = i.LeftChild) @@ -27,7 +28,7 @@ private static MerkleTreeNode Build(MerkleTreeNode[] leaves) if (leaves.Length == 0) throw new ArgumentException(); if (leaves.Length == 1) return leaves[0]; - var buffer = new byte[64]; + Span buffer = stackalloc byte[64]; MerkleTreeNode[] parents = new MerkleTreeNode[(leaves.Length + 1) / 2]; for (int i = 0; i < parents.Length; i++) { @@ -49,18 +50,18 @@ private static MerkleTreeNode Build(MerkleTreeNode[] leaves) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static UInt256 Concat(byte[] buffer, UInt256 hash1, UInt256 hash2) + private static UInt256 Concat(Span buffer, UInt256 hash1, UInt256 hash2) { - Buffer.BlockCopy(hash1.ToArray(), 0, buffer, 0, 32); - Buffer.BlockCopy(hash2.ToArray(), 0, buffer, 32, 32); + hash1.ToArray().CopyTo(buffer); + hash2.ToArray().CopyTo(buffer[32..]); - return new UInt256(Crypto.Default.Hash256(buffer)); + return new UInt256(Crypto.Hash256(buffer)); } - public static UInt256 ComputeRoot(IReadOnlyList hashes) + public static UInt256 ComputeRoot(UInt256[] hashes) { - if (hashes.Count == 0) throw new ArgumentException(); - if (hashes.Count == 1) return hashes[0]; + if (hashes.Length == 0) throw new ArgumentException(); + if (hashes.Length == 1) return hashes[0]; MerkleTree tree = new MerkleTree(hashes); return tree.root.Hash; } diff --git a/src/neo/Cryptography/Murmur3.cs b/src/neo/Cryptography/Murmur3.cs index 6296bb20be..82642baf7f 100644 --- a/src/neo/Cryptography/Murmur3.cs +++ b/src/neo/Cryptography/Murmur3.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers.Binary; using System.Runtime.CompilerServices; using System.Security.Cryptography; @@ -32,7 +33,7 @@ protected override void HashCore(byte[] array, int ibStart, int cbSize) int alignedLength = ibStart + (cbSize - remainder); for (int i = ibStart; i < alignedLength; i += 4) { - uint k = array.ToUInt32(i); + uint k = BinaryPrimitives.ReadUInt32LittleEndian(array.AsSpan(i)); k *= c1; k = RotateLeft(k, r1); k *= c2; diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index b20c179cbe..ebafdc16cc 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -1,4 +1,3 @@ -using Microsoft.Extensions.Configuration; using Neo.IO.Caching; using System; using System.Collections.Generic; @@ -119,7 +118,7 @@ internal static string GetVersion(this Assembly assembly) public static byte[] HexToBytes(this string value) { if (value == null || value.Length == 0) - return new byte[0]; + return Array.Empty(); if (value.Length % 2 == 1) throw new FormatException(); byte[] result = new byte[value.Length / 2]; @@ -160,7 +159,7 @@ internal static BigInteger NextBigInteger(this Random rand, int sizeInBits) throw new ArgumentException("sizeInBits must be non-negative"); if (sizeInBits == 0) return 0; - byte[] b = new byte[sizeInBits / 8 + 1]; + Span b = stackalloc byte[sizeInBits / 8 + 1]; rand.NextBytes(b); if (sizeInBits % 8 == 0) b[b.Length - 1] = 0; @@ -175,7 +174,7 @@ internal static BigInteger NextBigInteger(this RandomNumberGenerator rng, int si throw new ArgumentException("sizeInBits must be non-negative"); if (sizeInBits == 0) return 0; - byte[] b = new byte[sizeInBits / 8 + 1]; + Span b = stackalloc byte[sizeInBits / 8 + 1]; rng.GetBytes(b); if (sizeInBits % 8 == 0) b[b.Length - 1] = 0; @@ -228,24 +227,6 @@ public static string ToHexString(this ReadOnlySpan value) return sb.ToString(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - unsafe internal static int ToInt32(this byte[] value, int startIndex) - { - fixed (byte* pbyte = &value[startIndex]) - { - return *((int*)pbyte); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - unsafe internal static long ToInt64(this byte[] value, int startIndex) - { - fixed (byte* pbyte = &value[startIndex]) - { - return *((long*)pbyte); - } - } - public static uint ToTimestamp(this DateTime time) { return (uint)(time.ToUniversalTime() - unixEpoch).TotalSeconds; @@ -256,33 +237,6 @@ public static ulong ToTimestampMS(this DateTime time) return (ulong)(time.ToUniversalTime() - unixEpoch).TotalMilliseconds; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - unsafe internal static ushort ToUInt16(this byte[] value, int startIndex) - { - fixed (byte* pbyte = &value[startIndex]) - { - return *((ushort*)pbyte); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - unsafe internal static uint ToUInt32(this byte[] value, int startIndex) - { - fixed (byte* pbyte = &value[startIndex]) - { - return *((uint*)pbyte); - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - unsafe internal static ulong ToUInt64(this byte[] value, int startIndex) - { - fixed (byte* pbyte = &value[startIndex]) - { - return *((ulong*)pbyte); - } - } - internal static IPAddress Unmap(this IPAddress address) { if (address.IsIPv4MappedToIPv6) @@ -348,19 +302,5 @@ internal static BigInteger WeightedAverage(this IEnumerable source, Func - /// Load configuration with different Environment Variable - /// - /// Configuration - /// IConfigurationRoot - public static IConfigurationRoot LoadConfig(string config) - { - var env = Environment.GetEnvironmentVariable("NEO_NETWORK"); - var configFile = string.IsNullOrWhiteSpace(env) ? $"{config}.json" : $"{config}.{env}.json"; - return new ConfigurationBuilder() - .AddJsonFile(configFile, true) - .Build(); - } } } diff --git a/src/neo/IO/Caching/DataCache.cs b/src/neo/IO/Caching/DataCache.cs index df6b6775a8..1665c8f238 100644 --- a/src/neo/IO/Caching/DataCache.cs +++ b/src/neo/IO/Caching/DataCache.cs @@ -139,7 +139,7 @@ public IEnumerable<(TKey Key, TValue Value)> Find(byte[] key_prefix = null) .OrderBy(p => p.KeyBytes, ByteArrayComparer.Default) .ToArray(); } - var uncached = FindInternal(key_prefix ?? new byte[0]) + var uncached = FindInternal(key_prefix ?? Array.Empty()) .Where(p => !dictionary.ContainsKey(p.Key)) .Select(p => ( diff --git a/src/neo/IO/Caching/ReflectionCache.cs b/src/neo/IO/Caching/ReflectionCache.cs index e0b92b2a92..5709ccd8df 100644 --- a/src/neo/IO/Caching/ReflectionCache.cs +++ b/src/neo/IO/Caching/ReflectionCache.cs @@ -12,8 +12,7 @@ internal static class ReflectionCache where T : Enum static ReflectionCache() { - Type enumType = typeof(T); - foreach (FieldInfo field in enumType.GetFields(BindingFlags.Public | BindingFlags.Static)) + foreach (FieldInfo field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static)) { // Get attribute ReflectionCacheAttribute attribute = field.GetCustomAttribute(); diff --git a/src/neo/IO/Data/LevelDB/Slice.cs b/src/neo/IO/Data/LevelDB/Slice.cs index fc52e6eb08..83b3fe78d3 100644 --- a/src/neo/IO/Data/LevelDB/Slice.cs +++ b/src/neo/IO/Data/LevelDB/Slice.cs @@ -45,7 +45,7 @@ public override int GetHashCode() public byte[] ToArray() { - return buffer ?? new byte[0]; + return buffer ?? Array.Empty(); } unsafe public bool ToBoolean() diff --git a/src/neo/IO/Helper.cs b/src/neo/IO/Helper.cs index adba79fad6..df74602cdd 100644 --- a/src/neo/IO/Helper.cs +++ b/src/neo/IO/Helper.cs @@ -1,4 +1,6 @@ +using K4os.Compression.LZ4; using System; +using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; @@ -65,6 +67,27 @@ public static unsafe T[] AsSerializableArray(this ReadOnlySpan value, i } } + public static byte[] CompressLz4(this byte[] data) + { + int maxLength = LZ4Codec.MaximumOutputSize(data.Length); + using var buffer = MemoryPool.Shared.Rent(maxLength); + int length = LZ4Codec.Encode(data, buffer.Memory.Span); + byte[] result = new byte[length]; + buffer.Memory[..length].CopyTo(result); + return result; + } + + public static byte[] DecompressLz4(this byte[] data, int maxOutput) + { + maxOutput = Math.Min(maxOutput, data.Length * 255); + using var buffer = MemoryPool.Shared.Rent(maxOutput); + int length = LZ4Codec.Decode(data, buffer.Memory.Span); + if (length < 0 || length > maxOutput) throw new FormatException(); + byte[] result = new byte[length]; + buffer.Memory[..length].CopyTo(result); + return result; + } + public static int GetVarSize(int value) { if (value < 0xFD) @@ -197,7 +220,7 @@ public static byte[] ToArray(this ISerializable value) } } - public static byte[] ToByteArray(this T[] value) where T : ISerializable + public static byte[] ToByteArray(this IReadOnlyCollection value) where T : ISerializable { using (MemoryStream ms = new MemoryStream()) using (BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8)) @@ -213,12 +236,12 @@ public static void Write(this BinaryWriter writer, ISerializable value) value.Serialize(writer); } - public static void Write(this BinaryWriter writer, T[] value) where T : ISerializable + public static void Write(this BinaryWriter writer, IReadOnlyCollection value) where T : ISerializable { - writer.WriteVarInt(value.Length); - for (int i = 0; i < value.Length; i++) + writer.WriteVarInt(value.Count); + foreach (T item in value) { - value[i].Serialize(writer); + item.Serialize(writer); } } @@ -252,7 +275,7 @@ public static void WriteFixedString(this BinaryWriter writer, string value, int throw new ArgumentException(); writer.Write(bytes); if (bytes.Length < length) - writer.Write(new byte[length - bytes.Length]); + writer.Write(stackalloc byte[length - bytes.Length]); } public static void WriteNullableArray(this BinaryWriter writer, T[] value) where T : class, ISerializable diff --git a/src/neo/IO/SerializableWrapper.cs b/src/neo/IO/SerializableWrapper.cs new file mode 100644 index 0000000000..5533a44171 --- /dev/null +++ b/src/neo/IO/SerializableWrapper.cs @@ -0,0 +1,67 @@ +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/IO/Wrappers/SerializableWrapper.cs b/src/neo/IO/Wrappers/SerializableWrapper.cs deleted file mode 100644 index 098e4b9c7a..0000000000 --- a/src/neo/IO/Wrappers/SerializableWrapper.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.IO; - -namespace Neo.IO.Wrappers -{ - public abstract class SerializableWrapper : IEquatable, IEquatable>, ISerializable - where T : struct, IEquatable - { - protected T value; - - public abstract int Size { get; } - - public abstract void Deserialize(BinaryReader reader); - - public bool Equals(T other) - { - return value.Equals(other); - } - - public bool Equals(SerializableWrapper other) - { - return value.Equals(other.value); - } - - public abstract void Serialize(BinaryWriter writer); - } -} diff --git a/src/neo/IO/Wrappers/UInt32Wrapper.cs b/src/neo/IO/Wrappers/UInt32Wrapper.cs deleted file mode 100644 index 6e1fb71da1..0000000000 --- a/src/neo/IO/Wrappers/UInt32Wrapper.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.IO; - -namespace Neo.IO.Wrappers -{ - public sealed class UInt32Wrapper : SerializableWrapper, IEquatable - { - public override int Size => sizeof(uint); - - public UInt32Wrapper() - { - } - - private UInt32Wrapper(uint value) - { - this.value = value; - } - - public override void Deserialize(BinaryReader reader) - { - value = reader.ReadUInt32(); - } - - public bool Equals(UInt32Wrapper other) - { - return value == other.value; - } - - public override void Serialize(BinaryWriter writer) - { - writer.Write(value); - } - - public static implicit operator UInt32Wrapper(uint value) - { - return new UInt32Wrapper(value); - } - - public static implicit operator uint(UInt32Wrapper wrapper) - { - return wrapper.value; - } - } -} diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 14b38860cf..2d894ab9ea 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -42,7 +42,7 @@ public class FillCompleted { } NextConsensus = GetConsensusAddress(StandbyValidators), Witness = new Witness { - InvocationScript = new byte[0], + InvocationScript = Array.Empty(), VerificationScript = new[] { (byte)OpCode.PUSHT } }, ConsensusData = new ConsensusData @@ -170,7 +170,7 @@ private static Transaction DeployNativeContracts() { new Witness { - InvocationScript = new byte[0], + InvocationScript = Array.Empty(), VerificationScript = new[] { (byte)OpCode.PUSHT } } } diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 9f1312322b..2ec0ae29cb 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -55,7 +55,7 @@ public void Dispose() public void EnsureStoped(IActorRef actor) { - Inbox inbox = Inbox.Create(ActorSystem); + using Inbox inbox = Inbox.Create(ActorSystem); inbox.Watch(actor); ActorSystem.Stop(actor); inbox.Receive(TimeSpan.FromMinutes(5)); diff --git a/src/neo/Network/P2P/Connection.cs b/src/neo/Network/P2P/Connection.cs index 5a00a588ef..7dff7e3a02 100644 --- a/src/neo/Network/P2P/Connection.cs +++ b/src/neo/Network/P2P/Connection.cs @@ -48,8 +48,7 @@ protected Connection(object connection, IPEndPoint remote, IPEndPoint local) private void WsReceive() { byte[] buffer = new byte[512]; - ArraySegment segment = new ArraySegment(buffer); - ws.ReceiveAsync(segment, CancellationToken.None).PipeTo(Self, + ws.ReceiveAsync(buffer, CancellationToken.None).PipeTo(Self, success: p => { switch (p.MessageType) diff --git a/src/neo/Network/P2P/Helper.cs b/src/neo/Network/P2P/Helper.cs index e3e7cece1b..5accd35631 100644 --- a/src/neo/Network/P2P/Helper.cs +++ b/src/neo/Network/P2P/Helper.cs @@ -1,42 +1,10 @@ -using K4os.Compression.LZ4; using Neo.Network.P2P.Payloads; -using System; -using System.Buffers; using System.IO; namespace Neo.Network.P2P { public static class Helper { - public static byte[] CompressLz4(this byte[] data) - { - int maxLength = LZ4Codec.MaximumOutputSize(data.Length); - byte[] buffer = ArrayPool.Shared.Rent(maxLength); - int length = LZ4Codec.Encode(data, 0, data.Length, buffer, 0, buffer.Length); - data = new byte[length]; - Buffer.BlockCopy(buffer, 0, data, 0, length); - ArrayPool.Shared.Return(buffer); - return data; - } - - public static byte[] DecompressLz4(this byte[] data, int maxOutput) - { - maxOutput = Math.Min(maxOutput, data.Length * 255); - byte[] buffer = ArrayPool.Shared.Rent(maxOutput); - try - { - int length = LZ4Codec.Decode(data, 0, data.Length, buffer, 0, buffer.Length); - if (length < 0 || length > maxOutput) throw new FormatException(); - data = new byte[length]; - Buffer.BlockCopy(buffer, 0, data, 0, length); - return data; - } - finally - { - ArrayPool.Shared.Return(buffer); - } - } - public static byte[] GetHashData(this IVerifiable verifiable) { using (MemoryStream ms = new MemoryStream()) @@ -47,20 +15,5 @@ public static byte[] GetHashData(this IVerifiable verifiable) return ms.ToArray(); } } - - internal static MessageCommand ToMessageCommand(this InventoryType inventoryType) - { - switch (inventoryType) - { - case InventoryType.TX: - return MessageCommand.Transaction; - case InventoryType.Block: - return MessageCommand.Block; - case InventoryType.Consensus: - return MessageCommand.Consensus; - default: - throw new ArgumentOutOfRangeException(nameof(inventoryType)); - } - } } } diff --git a/src/neo/Network/P2P/Message.cs b/src/neo/Network/P2P/Message.cs index 578259f789..2e5c1f8258 100644 --- a/src/neo/Network/P2P/Message.cs +++ b/src/neo/Network/P2P/Message.cs @@ -3,6 +3,7 @@ using Neo.IO; using Neo.IO.Caching; using System; +using System.Buffers.Binary; using System.IO; namespace Neo.Network.P2P @@ -28,7 +29,7 @@ public static Message Create(MessageCommand command, ISerializable payload = nul Flags = MessageFlags.None, Command = command, Payload = payload, - _payload_compressed = payload?.ToArray() ?? new byte[0] + _payload_compressed = payload?.ToArray() ?? Array.Empty() }; // Try compression @@ -82,19 +83,19 @@ internal static int TryDeserialize(ByteString data, out Message msg) if (length == 0xFD) { if (data.Count < 5) return 0; - length = data.Slice(payloadIndex, 2).ToArray().ToUInt16(0); + length = BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(payloadIndex, 2).ToArray()); payloadIndex += 2; } else if (length == 0xFE) { if (data.Count < 7) return 0; - length = data.Slice(payloadIndex, 4).ToArray().ToUInt32(0); + length = BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(payloadIndex, 4).ToArray()); payloadIndex += 4; } else if (length == 0xFF) { if (data.Count < 11) return 0; - length = data.Slice(payloadIndex, 8).ToArray().ToUInt64(0); + length = BinaryPrimitives.ReadUInt64LittleEndian(data.Slice(payloadIndex, 8).ToArray()); payloadIndex += 8; } @@ -106,7 +107,7 @@ internal static int TryDeserialize(ByteString data, out Message msg) { Flags = flags, Command = (MessageCommand)header[1], - _payload_compressed = length <= 0 ? new byte[0] : data.Slice(payloadIndex, (int)length).ToArray() + _payload_compressed = length <= 0 ? Array.Empty() : data.Slice(payloadIndex, (int)length).ToArray() }; msg.DecompressPayload(); diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index 0533e0f373..c6ada9adac 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -47,11 +47,9 @@ public Header Header + ConsensusData.Size //ConsensusData + Transactions.Sum(p => p.Size); //Transactions - public static UInt256 CalculateMerkleRoot(UInt256 consensusDataHash, params UInt256[] transactionHashes) + public static UInt256 CalculateMerkleRoot(UInt256 consensusDataHash, IEnumerable transactionHashes) { - List hashes = new List(transactionHashes.Length + 1) { consensusDataHash }; - hashes.AddRange(transactionHashes); - return MerkleTree.ComputeRoot(hashes); + return MerkleTree.ComputeRoot(transactionHashes.Prepend(consensusDataHash).ToArray()); } public override void Deserialize(BinaryReader reader) @@ -65,7 +63,7 @@ public override void Deserialize(BinaryReader reader) Transactions[i] = reader.ReadSerializable(); if (Transactions.Distinct().Count() != Transactions.Length) throw new FormatException(); - if (CalculateMerkleRoot(ConsensusData.Hash, Transactions.Select(p => p.Hash).ToArray()) != MerkleRoot) + if (CalculateMerkleRoot(ConsensusData.Hash, Transactions.Select(p => p.Hash)) != MerkleRoot) throw new FormatException(); } @@ -88,7 +86,7 @@ public override int GetHashCode() public void RebuildMerkleRoot() { - MerkleRoot = CalculateMerkleRoot(ConsensusData.Hash, Transactions.Select(p => p.Hash).ToArray()); + MerkleRoot = CalculateMerkleRoot(ConsensusData.Hash, Transactions.Select(p => p.Hash)); } public override void Serialize(BinaryWriter writer) diff --git a/src/neo/Network/P2P/Payloads/BlockBase.cs b/src/neo/Network/P2P/Payloads/BlockBase.cs index 37ab7abe66..c41869bab4 100644 --- a/src/neo/Network/P2P/Payloads/BlockBase.cs +++ b/src/neo/Network/P2P/Payloads/BlockBase.cs @@ -27,7 +27,7 @@ public UInt256 Hash { if (_hash == null) { - _hash = new UInt256(Crypto.Default.Hash256(this.GetHashData())); + _hash = new UInt256(Crypto.Hash256(this.GetHashData())); } return _hash; } diff --git a/src/neo/Network/P2P/Payloads/ConsensusData.cs b/src/neo/Network/P2P/Payloads/ConsensusData.cs index 48d3d14ac8..1a8019633d 100644 --- a/src/neo/Network/P2P/Payloads/ConsensusData.cs +++ b/src/neo/Network/P2P/Payloads/ConsensusData.cs @@ -19,7 +19,7 @@ public UInt256 Hash { if (_hash == null) { - _hash = new UInt256(Crypto.Default.Hash256(this.ToArray())); + _hash = new UInt256(Crypto.Hash256(this.ToArray())); } return _hash; } diff --git a/src/neo/Network/P2P/Payloads/ConsensusPayload.cs b/src/neo/Network/P2P/Payloads/ConsensusPayload.cs index 21730a3470..b8b389829d 100644 --- a/src/neo/Network/P2P/Payloads/ConsensusPayload.cs +++ b/src/neo/Network/P2P/Payloads/ConsensusPayload.cs @@ -45,7 +45,7 @@ public UInt256 Hash { if (_hash == null) { - _hash = new UInt256(Crypto.Default.Hash256(this.GetHashData())); + _hash = new UInt256(Crypto.Hash256(this.GetHashData())); } return _hash; } diff --git a/src/neo/Network/P2P/Payloads/HeadersPayload.cs b/src/neo/Network/P2P/Payloads/HeadersPayload.cs index 3b13405d24..2e95d4ebf4 100644 --- a/src/neo/Network/P2P/Payloads/HeadersPayload.cs +++ b/src/neo/Network/P2P/Payloads/HeadersPayload.cs @@ -1,7 +1,5 @@ using Neo.IO; -using System.Collections.Generic; using System.IO; -using System.Linq; namespace Neo.Network.P2P.Payloads { @@ -13,11 +11,11 @@ public class HeadersPayload : ISerializable public int Size => Headers.GetVarSize(); - public static HeadersPayload Create(IEnumerable
headers) + public static HeadersPayload Create(params Header[] headers) { return new HeadersPayload { - Headers = headers.ToArray() + Headers = headers }; } diff --git a/src/neo/Network/P2P/Payloads/InventoryType.cs b/src/neo/Network/P2P/Payloads/InventoryType.cs index 775eed958f..0a1b831d12 100644 --- a/src/neo/Network/P2P/Payloads/InventoryType.cs +++ b/src/neo/Network/P2P/Payloads/InventoryType.cs @@ -2,8 +2,8 @@ namespace Neo.Network.P2P.Payloads { public enum InventoryType : byte { - TX = 0x01, - Block = 0x02, - Consensus = 0xe0 + TX = MessageCommand.Transaction, + Block = MessageCommand.Block, + Consensus = MessageCommand.Consensus } } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index c2adb34017..a7dd9e2ebf 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -71,7 +71,7 @@ public UInt256 Hash { if (_hash == null) { - _hash = new UInt256(Crypto.Default.Hash256(this.GetHashData())); + _hash = new UInt256(Crypto.Hash256(this.GetHashData())); } return _hash; } diff --git a/src/neo/Network/P2P/Peer.cs b/src/neo/Network/P2P/Peer.cs index 0665567fc8..bc852fb8fa 100644 --- a/src/neo/Network/P2P/Peer.cs +++ b/src/neo/Network/P2P/Peer.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Http; using Neo.IO; using System; +using System.Buffers.Binary; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; @@ -91,8 +92,7 @@ protected void ConnectToPeer(IPEndPoint endPoint, bool isTrusted = false) private static bool IsIntranetAddress(IPAddress address) { byte[] data = address.MapToIPv4().GetAddressBytes(); - Array.Reverse(data); - uint value = data.ToUInt32(0); + uint value = BinaryPrimitives.ReadUInt32BigEndian(data); return (value & 0xff000000) == 0x0a000000 || (value & 0xff000000) == 0x7f000000 || (value & 0xfff00000) == 0xac100000 || (value & 0xffff0000) == 0xc0a80000 || (value & 0xffff0000) == 0xa9fe0000; } diff --git a/src/neo/Network/P2P/ProtocolHandler.cs b/src/neo/Network/P2P/ProtocolHandler.cs index e16d7f238c..bd48a6f57f 100644 --- a/src/neo/Network/P2P/ProtocolHandler.cs +++ b/src/neo/Network/P2P/ProtocolHandler.cs @@ -251,7 +251,7 @@ private void OnGetHeadersMessageReceived(GetBlocksPayload payload) headers.Add(header); } if (headers.Count == 0) return; - Context.Parent.Tell(Message.Create(MessageCommand.Headers, HeadersPayload.Create(headers))); + Context.Parent.Tell(Message.Create(MessageCommand.Headers, HeadersPayload.Create(headers.ToArray()))); } private void OnHeadersMessageReceived(HeadersPayload payload) diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index ec2a16e047..2cd37a8056 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -171,7 +171,7 @@ private void OnSend(IInventory inventory) if (bloom_filter != null && !bloom_filter.Test((Transaction)inventory)) return; } - EnqueueMessage(inventory.InventoryType.ToMessageCommand(), inventory); + EnqueueMessage((MessageCommand)inventory.InventoryType, inventory); } private void OnSetFilter(BloomFilter filter) diff --git a/src/neo/Network/RPC/RpcServer.cs b/src/neo/Network/RPC/RpcServer.cs index 4bdaf91caf..c7475e5f0f 100644 --- a/src/neo/Network/RPC/RpcServer.cs +++ b/src/neo/Network/RPC/RpcServer.cs @@ -631,11 +631,6 @@ private JObject InvokeFunction(UInt160 script_hash, string operation, ContractPa return GetInvokeResult(script); } - private JObject InvokeScript(byte[] script) - { - return GetInvokeResult(script); - } - private JObject ListPlugins() { return new JArray(Plugin.Plugins diff --git a/src/neo/Network/RPC/TransactionManager.cs b/src/neo/Network/RPC/TransactionManager.cs index 27741a58e3..7841ba8527 100644 --- a/src/neo/Network/RPC/TransactionManager.cs +++ b/src/neo/Network/RPC/TransactionManager.cs @@ -1,6 +1,5 @@ using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; using Neo.Network.P2P.Payloads; using Neo.Network.RPC.Models; using Neo.SmartContract; @@ -8,7 +7,6 @@ using Neo.VM; using Neo.Wallets; using System; -using System.Linq; namespace Neo.Network.RPC { @@ -70,7 +68,7 @@ public TransactionManager MakeTransaction(byte[] script, TransactionAttribute[] }; // Add witness hashes parameter to pass CheckWitness - UInt160[] hashes = Tx.GetScriptHashesForVerifying(null).ToArray(); + UInt160[] hashes = Tx.GetScriptHashesForVerifying(null); RpcInvokeResult result = rpcClient.InvokeScript(script, hashes); Tx.SystemFee = Math.Max(long.Parse(result.GasConsumed) - ApplicationEngine.GasFree, 0); if (Tx.SystemFee > 0) diff --git a/src/neo/Network/UPnP.cs b/src/neo/Network/UPnP.cs index 2d0d1d1cc5..6ec770271c 100644 --- a/src/neo/Network/UPnP.cs +++ b/src/neo/Network/UPnP.cs @@ -41,7 +41,7 @@ public static bool Discover() return false; } - byte[] buffer = new byte[0x1000]; + Span buffer = stackalloc byte[0x1000]; do { @@ -50,7 +50,7 @@ public static bool Discover() { length = s.Receive(buffer); - string resp = Encoding.ASCII.GetString(buffer, 0, length).ToLowerInvariant(); + string resp = Encoding.ASCII.GetString(buffer[..length]).ToLowerInvariant(); if (resp.Contains("upnp:rootdevice")) { resp = resp.Substring(resp.IndexOf("location:") + 9); diff --git a/src/neo/Persistence/ClonedView.cs b/src/neo/Persistence/ClonedView.cs index 927207d6a1..f820b8f31b 100644 --- a/src/neo/Persistence/ClonedView.cs +++ b/src/neo/Persistence/ClonedView.cs @@ -1,5 +1,5 @@ +using Neo.IO; using Neo.IO.Caching; -using Neo.IO.Wrappers; using Neo.Ledger; namespace Neo.Persistence @@ -10,7 +10,7 @@ internal class ClonedView : StoreView public override DataCache Transactions { get; } public override DataCache Contracts { get; } public override DataCache Storages { get; } - public override DataCache HeaderHashList { get; } + public override DataCache, HeaderHashList> HeaderHashList { get; } public override MetaDataCache BlockHashIndex { get; } public override MetaDataCache HeaderHashIndex { get; } diff --git a/src/neo/Persistence/IStore.cs b/src/neo/Persistence/IStore.cs index e91e0b9386..197cecdca3 100644 --- a/src/neo/Persistence/IStore.cs +++ b/src/neo/Persistence/IStore.cs @@ -10,6 +10,9 @@ public interface IStore : IDisposable, IReadOnlyStore void Delete(byte table, byte[] key); ISnapshot GetSnapshot(); void Put(byte table, byte[] key, byte[] value); - void PutSync(byte table, byte[] key, byte[] value); + void PutSync(byte table, byte[] key, byte[] value) + { + Put(table, key, value); + } } } diff --git a/src/neo/Persistence/Memory/Helper.cs b/src/neo/Persistence/Memory/Helper.cs index c3535d897d..e25a44d699 100644 --- a/src/neo/Persistence/Memory/Helper.cs +++ b/src/neo/Persistence/Memory/Helper.cs @@ -1,12 +1,12 @@ +using System; + namespace Neo.Persistence.Memory { internal static class Helper { - private static readonly byte[] EmptyBytes = new byte[0]; - public static byte[] EnsureNotNull(this byte[] source) { - return source ?? EmptyBytes; + return source ?? Array.Empty(); } } } diff --git a/src/neo/Persistence/Memory/Store.cs b/src/neo/Persistence/Memory/Store.cs index 067cf073d9..73e7dc5719 100644 --- a/src/neo/Persistence/Memory/Store.cs +++ b/src/neo/Persistence/Memory/Store.cs @@ -42,11 +42,6 @@ public ISnapshot GetSnapshot() } public void Put(byte table, byte[] key, byte[] value) - { - PutSync(table, key, value); - } - - public void PutSync(byte table, byte[] key, byte[] value) { innerData[table][key.EnsureNotNull()] = value; } diff --git a/src/neo/Persistence/ReadOnlyView.cs b/src/neo/Persistence/ReadOnlyView.cs index f138b616c1..18bf05fae5 100644 --- a/src/neo/Persistence/ReadOnlyView.cs +++ b/src/neo/Persistence/ReadOnlyView.cs @@ -1,5 +1,5 @@ +using Neo.IO; using Neo.IO.Caching; -using Neo.IO.Wrappers; using Neo.Ledger; using System; @@ -16,7 +16,7 @@ public class ReadOnlyView : StoreView public override DataCache Transactions => new StoreDataCache(store, Prefixes.DATA_Transaction); public override DataCache Contracts => new StoreDataCache(store, Prefixes.ST_Contract); public override DataCache Storages => new StoreDataCache(store, Prefixes.ST_Storage); - public override DataCache HeaderHashList => new StoreDataCache(store, Prefixes.IX_HeaderHashList); + 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); diff --git a/src/neo/Persistence/SnapshotView.cs b/src/neo/Persistence/SnapshotView.cs index 5eb9071fe3..3b20025cd0 100644 --- a/src/neo/Persistence/SnapshotView.cs +++ b/src/neo/Persistence/SnapshotView.cs @@ -1,5 +1,5 @@ +using Neo.IO; using Neo.IO.Caching; -using Neo.IO.Wrappers; using Neo.Ledger; using System; @@ -16,7 +16,7 @@ public class SnapshotView : StoreView, IDisposable public override DataCache Transactions { get; } public override DataCache Contracts { get; } public override DataCache Storages { get; } - public override DataCache HeaderHashList { get; } + public override DataCache, HeaderHashList> HeaderHashList { get; } public override MetaDataCache BlockHashIndex { get; } public override MetaDataCache HeaderHashIndex { get; } @@ -27,7 +27,7 @@ public SnapshotView(IStore store) Transactions = new StoreDataCache(snapshot, Prefixes.DATA_Transaction); Contracts = new StoreDataCache(snapshot, Prefixes.ST_Contract); Storages = new StoreDataCache(snapshot, Prefixes.ST_Storage); - HeaderHashList = new StoreDataCache(snapshot, Prefixes.IX_HeaderHashList); + HeaderHashList = new StoreDataCache, HeaderHashList>(snapshot, Prefixes.IX_HeaderHashList); BlockHashIndex = new StoreMetaDataCache(snapshot, Prefixes.IX_CurrentBlock); HeaderHashIndex = new StoreMetaDataCache(snapshot, Prefixes.IX_CurrentHeader); } diff --git a/src/neo/Persistence/StoreView.cs b/src/neo/Persistence/StoreView.cs index c23e000ff2..4c2a1eed78 100644 --- a/src/neo/Persistence/StoreView.cs +++ b/src/neo/Persistence/StoreView.cs @@ -1,5 +1,5 @@ +using Neo.IO; using Neo.IO.Caching; -using Neo.IO.Wrappers; using Neo.Ledger; using Neo.Network.P2P.Payloads; @@ -15,7 +15,7 @@ public abstract class StoreView public abstract DataCache Transactions { get; } public abstract DataCache Contracts { get; } public abstract DataCache Storages { get; } - public abstract DataCache HeaderHashList { get; } + public abstract DataCache, HeaderHashList> HeaderHashList { get; } public abstract MetaDataCache BlockHashIndex { get; } public abstract MetaDataCache HeaderHashIndex { get; } diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index 18c915d614..f1483c5341 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -33,7 +33,7 @@ public static ProtocolSettings Default { if (_default == null) { - var configuration = Helper.LoadConfig("protocol"); + var configuration = Utility.LoadConfig("protocol"); UpdateDefault(configuration); } diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index eb4eb5e7c2..bc3694f712 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -5,6 +5,7 @@ using Neo.VM.Types; using System; using System.Collections.Generic; +using Array = System.Array; namespace Neo.SmartContract { @@ -97,8 +98,8 @@ private static Block CreateDummyBlock(StoreView snapshot) NextConsensus = currentBlock.NextConsensus, Witness = new Witness { - InvocationScript = new byte[0], - VerificationScript = new byte[0] + InvocationScript = Array.Empty(), + VerificationScript = Array.Empty() }, ConsensusData = new ConsensusData(), Transactions = new Transaction[0] diff --git a/src/neo/SmartContract/Contract.cs b/src/neo/SmartContract/Contract.cs index 35fd0cb95b..63384af675 100644 --- a/src/neo/SmartContract/Contract.cs +++ b/src/neo/SmartContract/Contract.cs @@ -54,7 +54,7 @@ public static Contract Create(UInt160 scriptHash, params ContractParameterType[] { return new Contract { - Script = new byte[0], + Script = Array.Empty(), _scriptHash = scriptHash, ParameterList = parameterList }; diff --git a/src/neo/SmartContract/ContractParameter.cs b/src/neo/SmartContract/ContractParameter.cs index 5cdc520808..c739d9af7e 100644 --- a/src/neo/SmartContract/ContractParameter.cs +++ b/src/neo/SmartContract/ContractParameter.cs @@ -36,7 +36,7 @@ public ContractParameter(ContractParameterType type) this.Value = new UInt256(); break; case ContractParameterType.ByteArray: - this.Value = new byte[0]; + this.Value = Array.Empty(); break; case ContractParameterType.PublicKey: this.Value = ECCurve.Secp256r1.G; diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index 1ddc703830..2b0b99623f 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -153,7 +153,7 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) } while (contract.Script[i++] == 33) { - points.Add(ECPoint.DecodePoint(contract.Script[i..(i + 33)], ECCurve.Secp256r1)); + points.Add(ECPoint.DecodePoint(contract.Script.AsSpan(i, 33), ECCurve.Secp256r1)); i += 33; } } @@ -263,7 +263,7 @@ public Witness[] GetWitnesses() witnesses[i] = new Witness { InvocationScript = sb.ToArray(), - VerificationScript = item.Script ?? new byte[0] + VerificationScript = item.Script ?? Array.Empty() }; } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 4ddaf5fca5..d544edb3d9 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -1,116 +1,15 @@ using Neo.Cryptography; -using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.VM; -using Neo.VM.Types; using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Numerics; +using System.Buffers.Binary; using System.Text; -using VMArray = Neo.VM.Types.Array; -using VMBoolean = Neo.VM.Types.Boolean; namespace Neo.SmartContract { public static class Helper { - public static StackItem DeserializeStackItem(this byte[] data, uint maxArraySize, uint maxItemSize) - { - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader reader = new BinaryReader(ms)) - { - return DeserializeStackItem(reader, maxArraySize, maxItemSize); - } - } - - private static StackItem DeserializeStackItem(BinaryReader reader, uint maxArraySize, uint maxItemSize) - { - Stack deserialized = new Stack(); - int undeserialized = 1; - while (undeserialized-- > 0) - { - StackItemType type = (StackItemType)reader.ReadByte(); - switch (type) - { - case StackItemType.ByteArray: - deserialized.Push(new ByteArray(reader.ReadVarBytes((int)maxItemSize))); - break; - case StackItemType.Boolean: - deserialized.Push(new VMBoolean(reader.ReadBoolean())); - break; - case StackItemType.Integer: - deserialized.Push(new Integer(new BigInteger(reader.ReadVarBytes(ExecutionEngine.MaxSizeForBigInteger)))); - break; - case StackItemType.Array: - case StackItemType.Struct: - { - int count = (int)reader.ReadVarInt(maxArraySize); - deserialized.Push(new ContainerPlaceholder - { - Type = type, - ElementCount = count - }); - undeserialized += count; - } - break; - case StackItemType.Map: - { - int count = (int)reader.ReadVarInt(maxArraySize); - deserialized.Push(new ContainerPlaceholder - { - Type = type, - ElementCount = count - }); - undeserialized += count * 2; - } - break; - case StackItemType.Null: - deserialized.Push(StackItem.Null); - break; - default: - throw new FormatException(); - } - } - Stack stack_temp = new Stack(); - while (deserialized.Count > 0) - { - StackItem item = deserialized.Pop(); - if (item is ContainerPlaceholder placeholder) - { - switch (placeholder.Type) - { - case StackItemType.Array: - VMArray array = new VMArray(); - for (int i = 0; i < placeholder.ElementCount; i++) - array.Add(stack_temp.Pop()); - item = array; - break; - case StackItemType.Struct: - Struct @struct = new Struct(); - for (int i = 0; i < placeholder.ElementCount; i++) - @struct.Add(stack_temp.Pop()); - item = @struct; - break; - case StackItemType.Map: - Map map = new Map(); - for (int i = 0; i < placeholder.ElementCount; i++) - { - StackItem key = stack_temp.Pop(); - StackItem value = stack_temp.Pop(); - map.Add((PrimitiveType)key, value); - } - item = map; - break; - } - } - stack_temp.Push(item); - } - return stack_temp.Peek(); - } - public static bool IsMultiSigContract(this byte[] script, out int m, out int n) { m = 0; n = 0; @@ -125,7 +24,7 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n) ++i; break; case 2: - m = script.ToUInt16(++i); + m = BinaryPrimitives.ReadUInt16LittleEndian(script.AsSpan(++i)); i += 2; break; default: @@ -147,7 +46,7 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n) ++i; break; case 2: - if (script.Length < i + 3 || n != script.ToUInt16(++i)) return false; + if (script.Length < i + 3 || n != BinaryPrimitives.ReadUInt16LittleEndian(script.AsSpan(++i))) return false; i += 2; break; default: @@ -178,72 +77,6 @@ public static bool IsStandardContract(this byte[] script) return script.IsSignatureContract() || script.IsMultiSigContract(out _, out _); } - public static byte[] Serialize(this StackItem item) - { - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms)) - { - SerializeStackItem(item, writer); - writer.Flush(); - return ms.ToArray(); - } - } - - private static void SerializeStackItem(StackItem item, BinaryWriter writer) - { - List serialized = new List(); - Stack unserialized = new Stack(); - unserialized.Push(item); - while (unserialized.Count > 0) - { - item = unserialized.Pop(); - switch (item) - { - case ByteArray bytes: - writer.Write((byte)StackItemType.ByteArray); - writer.WriteVarBytes(bytes.ToByteArray()); - break; - case VMBoolean _: - writer.Write((byte)StackItemType.Boolean); - writer.Write(item.ToBoolean()); - break; - case Integer integer: - writer.Write((byte)StackItemType.Integer); - writer.WriteVarBytes(integer.ToByteArray()); - break; - case InteropInterface _: - throw new NotSupportedException(); - case VMArray array: - if (serialized.Any(p => ReferenceEquals(p, array))) - throw new NotSupportedException(); - serialized.Add(array); - if (array is Struct) - writer.Write((byte)StackItemType.Struct); - else - writer.Write((byte)StackItemType.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))) - throw new NotSupportedException(); - serialized.Add(map); - writer.Write((byte)StackItemType.Map); - writer.WriteVarInt(map.Count); - foreach (var pair in map.Reverse()) - { - unserialized.Push(pair.Value); - unserialized.Push(pair.Key); - } - break; - case Null _: - writer.Write((byte)StackItemType.Null); - break; - } - } - } - public static uint ToInteropMethodHash(this string method) { return BitConverter.ToUInt32(Encoding.ASCII.GetBytes(method).Sha256(), 0); @@ -251,7 +84,7 @@ public static uint ToInteropMethodHash(this string method) public static UInt160 ToScriptHash(this byte[] script) { - return new UInt160(Crypto.Default.Hash160(script)); + return new UInt160(Crypto.Hash160(script)); } internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snapshot, long gas) diff --git a/src/neo/SmartContract/InteropService.NEO.cs b/src/neo/SmartContract/InteropService.NEO.cs index 07fac8e2b7..1e0034050d 100644 --- a/src/neo/SmartContract/InteropService.NEO.cs +++ b/src/neo/SmartContract/InteropService.NEO.cs @@ -1,4 +1,5 @@ using Neo.Cryptography; +using Neo.IO; using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P; @@ -77,17 +78,17 @@ private static bool Native_Deploy(ApplicationEngine engine) private static bool Crypto_ECDsaVerify(ApplicationEngine engine) { StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); - byte[] message = item0 switch + ReadOnlySpan message = item0 switch { InteropInterface _interface => _interface.GetInterface().GetHashData(), Null _ => engine.ScriptContainer.GetHashData(), - _ => item0.GetSpan().ToArray() + _ => item0.GetSpan() }; byte[] pubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - byte[] signature = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + ReadOnlySpan signature = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); try { - engine.CurrentContext.EvaluationStack.Push(Crypto.Default.VerifySignature(message, signature, pubkey)); + engine.CurrentContext.EvaluationStack.Push(Crypto.VerifySignature(message, signature, pubkey)); } catch (ArgumentException) { @@ -99,11 +100,11 @@ private static bool Crypto_ECDsaVerify(ApplicationEngine engine) private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) { StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); - byte[] message = item0 switch + ReadOnlySpan message = item0 switch { InteropInterface _interface => _interface.GetInterface().GetHashData(), Null _ => engine.ScriptContainer.GetHashData(), - _ => item0.GetSpan().ToArray() + _ => item0.GetSpan() }; int n; byte[][] pubkeys; @@ -144,7 +145,7 @@ private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) { for (int i = 0, j = 0; fSuccess && i < m && j < n;) { - if (Crypto.Default.VerifySignature(message, signatures[i], pubkeys[j])) + if (Crypto.VerifySignature(message, signatures[i], pubkeys[j])) i++; j++; if (m - i > n - j) @@ -216,15 +217,15 @@ private static bool Contract_Update(ApplicationEngine engine) engine.Snapshot.Contracts.Add(hash_new, contract); if (contract.HasStorage) { - foreach (var pair in engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).ToArray()) + foreach (var (key, value) in engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).ToArray()) { engine.Snapshot.Storages.Add(new StorageKey { ScriptHash = hash_new, - Key = pair.Key.Key + Key = key.Key }, new StorageItem { - Value = pair.Value.Value, + Value = value.Value, IsConstant = false }); } diff --git a/src/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs index 012876c96e..c1085ff1c4 100644 --- a/src/neo/SmartContract/InteropService.cs +++ b/src/neo/SmartContract/InteropService.cs @@ -3,7 +3,6 @@ using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; @@ -229,10 +228,10 @@ private static bool CheckWitness(ApplicationEngine engine, ECPoint pubkey) private static bool Runtime_CheckWitness(ApplicationEngine engine) { - byte[] hashOrPubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + ReadOnlySpan hashOrPubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); bool result; if (hashOrPubkey.Length == 20) - result = CheckWitness(engine, new UInt160(hashOrPubkey)); + result = CheckWitness(engine, new UInt160(hashOrPubkey.ToArray())); else if (hashOrPubkey.Length == 33) result = CheckWitness(engine, ECPoint.DecodePoint(hashOrPubkey, ECCurve.Secp256r1)); else @@ -269,7 +268,7 @@ private static bool Runtime_Serialize(ApplicationEngine engine) byte[] serialized; try { - serialized = engine.CurrentContext.EvaluationStack.Pop().Serialize(); + serialized = StackItemSerializer.Serialize(engine.CurrentContext.EvaluationStack.Pop()); } catch (NotSupportedException) { @@ -313,7 +312,7 @@ private static bool Runtime_Deserialize(ApplicationEngine engine) StackItem item; try { - item = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray().DeserializeStackItem(engine.MaxArraySize, engine.MaxItemSize); + item = StackItemSerializer.Deserialize(engine.CurrentContext.EvaluationStack.Pop().GetSpan(), engine.MaxArraySize, engine.MaxItemSize); } catch (FormatException) { diff --git a/src/neo/SmartContract/Manifest/ContractGroup.cs b/src/neo/SmartContract/Manifest/ContractGroup.cs index 249c6b8a53..b138427cfa 100644 --- a/src/neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/neo/SmartContract/Manifest/ContractGroup.cs @@ -1,5 +1,6 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; +using Neo.IO; using Neo.IO.Json; using System; @@ -42,7 +43,7 @@ public static ContractGroup FromJson(JObject json) /// Return true or false public bool IsValid(UInt160 hash) { - return Crypto.Default.VerifySignature(hash.ToArray(), Signature, PubKey.EncodePoint(false)); + return Crypto.VerifySignature(hash.ToArray(), Signature, PubKey.EncodePoint(false)); } public virtual JObject ToJson() diff --git a/src/neo/SmartContract/Native/ContractMethodAttribute.cs b/src/neo/SmartContract/Native/ContractMethodAttribute.cs index 42b414eed6..22051d72f7 100644 --- a/src/neo/SmartContract/Native/ContractMethodAttribute.cs +++ b/src/neo/SmartContract/Native/ContractMethodAttribute.cs @@ -8,8 +8,8 @@ internal class ContractMethodAttribute : Attribute public string Name { get; set; } public long Price { get; } public ContractParameterType ReturnType { get; } - public ContractParameterType[] ParameterTypes { get; set; } = new ContractParameterType[0]; - public string[] ParameterNames { get; set; } = new string[0]; + public ContractParameterType[] ParameterTypes { get; set; } = Array.Empty(); + public string[] ParameterNames { get; set; } = Array.Empty(); public bool SafeMethod { get; set; } = false; public ContractMethodAttribute(long price, ContractParameterType returnType) diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 3726cf49fb..84e230adf6 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -148,10 +148,10 @@ private StackItem BlockAccount(ApplicationEngine engine, VMArray args) UInt160 account = new UInt160(args[0].GetSpan().ToArray()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; - HashSet accounts = new HashSet(storage.Value.AsSerializableArray()); + SortedSet accounts = new SortedSet(storage.Value.AsSerializableArray()); if (!accounts.Add(account)) return false; storage = engine.Snapshot.Storages.GetAndChange(key); - storage.Value = accounts.ToArray().ToByteArray(); + storage.Value = accounts.ToByteArray(); return true; } @@ -162,10 +162,10 @@ private StackItem UnblockAccount(ApplicationEngine engine, VMArray args) UInt160 account = new UInt160(args[0].GetSpan().ToArray()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; - HashSet accounts = new HashSet(storage.Value.AsSerializableArray()); + SortedSet accounts = new SortedSet(storage.Value.AsSerializableArray()); if (!accounts.Remove(account)) return false; storage = engine.Snapshot.Storages.GetAndChange(key); - storage.Value = accounts.ToArray().ToByteArray(); + storage.Value = accounts.ToByteArray(); return true; } } diff --git a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs index aa62cf4bec..e547f5615e 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs @@ -19,7 +19,7 @@ public Nep5AccountState(byte[] data) public void FromByteArray(byte[] data) { - FromStruct((Struct)data.DeserializeStackItem(16, 34)); + FromStruct((Struct)StackItemSerializer.Deserialize(data, 16, 34)); } protected virtual void FromStruct(Struct @struct) @@ -29,7 +29,7 @@ protected virtual void FromStruct(Struct @struct) public byte[] ToByteArray() { - return ToStruct().Serialize(); + return StackItemSerializer.Serialize(ToStruct()); } protected virtual Struct ToStruct() diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index 71dfdac9bc..c2ec377c2a 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -1,5 +1,6 @@ #pragma warning disable IDE0060 +using Neo.IO; using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract.Manifest; diff --git a/src/neo/SmartContract/NefFile.cs b/src/neo/SmartContract/NefFile.cs index e0068b7213..815f2252fb 100644 --- a/src/neo/SmartContract/NefFile.cs +++ b/src/neo/SmartContract/NefFile.cs @@ -75,7 +75,7 @@ public void Serialize(BinaryWriter writer) writer.Write(ScriptHash); writer.Write(CheckSum); - writer.WriteVarBytes(Script ?? new byte[0]); + writer.WriteVarBytes(Script ?? Array.Empty()); } public void Deserialize(BinaryReader reader) @@ -118,9 +118,9 @@ public static uint ComputeChecksum(NefFile file) // Read header without CRC - var buffer = new byte[HeaderSize - sizeof(uint)]; + Span buffer = stackalloc byte[HeaderSize - sizeof(uint)]; ms.Seek(0, SeekOrigin.Begin); - ms.Read(buffer, 0, buffer.Length); + ms.Read(buffer); return BitConverter.ToUInt32(buffer.Sha256(), 0); } diff --git a/src/neo/SmartContract/StackItemSerializer.cs b/src/neo/SmartContract/StackItemSerializer.cs new file mode 100644 index 0000000000..5ed61a1b72 --- /dev/null +++ b/src/neo/SmartContract/StackItemSerializer.cs @@ -0,0 +1,183 @@ +using Neo.IO; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Numerics; +using Array = Neo.VM.Types.Array; +using Boolean = Neo.VM.Types.Boolean; + +namespace Neo.SmartContract +{ + internal static class StackItemSerializer + { + public static StackItem Deserialize(byte[] data, uint maxArraySize, uint maxItemSize) + { + using MemoryStream ms = new MemoryStream(data, false); + using BinaryReader reader = new BinaryReader(ms); + return Deserialize(reader, maxArraySize, maxItemSize); + } + + public static unsafe StackItem Deserialize(ReadOnlySpan data, uint maxArraySize, uint maxItemSize) + { + if (data.IsEmpty) throw new FormatException(); + fixed (byte* pointer = data) + { + using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, data.Length); + using BinaryReader reader = new BinaryReader(ms); + return Deserialize(reader, maxArraySize, maxItemSize); + } + } + + private static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uint maxItemSize) + { + Stack deserialized = new Stack(); + int undeserialized = 1; + while (undeserialized-- > 0) + { + StackItemType type = (StackItemType)reader.ReadByte(); + switch (type) + { + case StackItemType.ByteArray: + deserialized.Push(new ByteArray(reader.ReadVarBytes((int)maxItemSize))); + break; + case StackItemType.Boolean: + deserialized.Push(new Boolean(reader.ReadBoolean())); + break; + case StackItemType.Integer: + deserialized.Push(new Integer(new BigInteger(reader.ReadVarBytes(ExecutionEngine.MaxSizeForBigInteger)))); + break; + case StackItemType.Array: + case StackItemType.Struct: + { + int count = (int)reader.ReadVarInt(maxArraySize); + deserialized.Push(new ContainerPlaceholder + { + Type = type, + ElementCount = count + }); + undeserialized += count; + } + break; + case StackItemType.Map: + { + int count = (int)reader.ReadVarInt(maxArraySize); + deserialized.Push(new ContainerPlaceholder + { + Type = type, + ElementCount = count + }); + undeserialized += count * 2; + } + break; + case StackItemType.Null: + deserialized.Push(StackItem.Null); + break; + default: + throw new FormatException(); + } + } + Stack stack_temp = new Stack(); + while (deserialized.Count > 0) + { + StackItem item = deserialized.Pop(); + if (item is ContainerPlaceholder placeholder) + { + switch (placeholder.Type) + { + case StackItemType.Array: + Array array = new Array(); + for (int i = 0; i < placeholder.ElementCount; i++) + array.Add(stack_temp.Pop()); + item = array; + break; + case StackItemType.Struct: + Struct @struct = new Struct(); + for (int i = 0; i < placeholder.ElementCount; i++) + @struct.Add(stack_temp.Pop()); + item = @struct; + break; + case StackItemType.Map: + Map map = new Map(); + for (int i = 0; i < placeholder.ElementCount; i++) + { + StackItem key = stack_temp.Pop(); + StackItem value = stack_temp.Pop(); + map.Add((PrimitiveType)key, value); + } + item = map; + break; + } + } + stack_temp.Push(item); + } + return stack_temp.Peek(); + } + + public static byte[] Serialize(StackItem item) + { + using MemoryStream ms = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(ms); + Serialize(item, writer); + writer.Flush(); + return ms.ToArray(); + } + + private static void Serialize(StackItem item, BinaryWriter writer) + { + List serialized = new List(); + Stack unserialized = new Stack(); + unserialized.Push(item); + while (unserialized.Count > 0) + { + item = unserialized.Pop(); + switch (item) + { + case ByteArray bytes: + writer.Write((byte)StackItemType.ByteArray); + writer.WriteVarBytes(bytes.ToByteArray()); + break; + case Boolean _: + writer.Write((byte)StackItemType.Boolean); + writer.Write(item.ToBoolean()); + break; + case Integer integer: + writer.Write((byte)StackItemType.Integer); + writer.WriteVarBytes(integer.ToByteArray()); + break; + case InteropInterface _: + throw new NotSupportedException(); + case Array array: + if (serialized.Any(p => ReferenceEquals(p, array))) + throw new NotSupportedException(); + serialized.Add(array); + if (array is Struct) + writer.Write((byte)StackItemType.Struct); + else + writer.Write((byte)StackItemType.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))) + throw new NotSupportedException(); + serialized.Add(map); + writer.Write((byte)StackItemType.Map); + writer.WriteVarInt(map.Count); + foreach (var pair in map.Reverse()) + { + unserialized.Push(pair.Value); + unserialized.Push(pair.Key); + } + break; + case Null _: + writer.Write((byte)StackItemType.Null); + break; + } + } + } + } +} diff --git a/src/neo/SmartContract/StorageContext.cs b/src/neo/SmartContract/StorageContext.cs index abea2e343d..9937caa4f8 100644 --- a/src/neo/SmartContract/StorageContext.cs +++ b/src/neo/SmartContract/StorageContext.cs @@ -4,10 +4,5 @@ internal class StorageContext { public UInt160 ScriptHash; public bool IsReadOnly; - - public byte[] ToArray() - { - return ScriptHash.ToArray(); - } } } diff --git a/src/neo/UInt160.cs b/src/neo/UInt160.cs index 8eaf0ffc23..e2ce4e5bcf 100644 --- a/src/neo/UInt160.cs +++ b/src/neo/UInt160.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.IO; namespace Neo { @@ -11,62 +12,68 @@ public class UInt160 : UIntBase, IComparable, IEquatable public const int Length = 20; public static readonly UInt160 Zero = new UInt160(); - /// - /// The empty constructor stores a null byte array - /// + private ulong value1; + private ulong value2; + private uint value3; + + public override int Size => Length; + public UInt160() - : this(null) { } - /// - /// The byte[] constructor invokes base class UIntBase constructor for 20 bytes - /// - public UInt160(byte[] value) - : base(Length, value) + public unsafe UInt160(byte[] value) { + fixed (ulong* p = &value1) + { + Span dst = new Span(p, Length); + value[..Length].CopyTo(dst); + } } /// /// Method CompareTo returns 1 if this UInt160 is bigger than other UInt160; -1 if it's smaller; 0 if it's equals /// Example: assume this is 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4, this.CompareTo(02ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) returns 1 /// - public unsafe int CompareTo(UInt160 other) + public int CompareTo(UInt160 other) { - fixed (byte* px = ToArray(), py = other.ToArray()) - { - uint* lpx = (uint*)px; - uint* lpy = (uint*)py; - //160 bit / 32 bit step -1 - for (int i = (160 / 32 - 1); i >= 0; i--) - { - if (lpx[i] > lpy[i]) - return 1; - if (lpx[i] < lpy[i]) - return -1; - } - } - return 0; + int result = value3.CompareTo(other.value3); + if (result != 0) return result; + result = value2.CompareTo(other.value2); + if (result != 0) return result; + return value1.CompareTo(other.value1); + } + + public override void Deserialize(BinaryReader reader) + { + value1 = reader.ReadUInt64(); + value2 = reader.ReadUInt64(); + value3 = reader.ReadUInt32(); + } + + /// + /// Method Equals returns true if objects are equal, false otherwise + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(obj, this)) return true; + return Equals(obj as UInt160); } /// /// Method Equals returns true if objects are equal, false otherwise /// - public unsafe bool Equals(UInt160 other) + public bool Equals(UInt160 other) { if (other is null) return false; - fixed (byte* px = ToArray(), py = other.ToArray()) - { - uint* lpx = (uint*)px; - uint* lpy = (uint*)py; - //160 bit / 32 bit(uint step) -1 - for (int i = (160 / 32 - 1); i >= 0; i--) - { - if (lpx[i] != lpy[i]) - return false; - } - } - return true; + return value1 == other.value1 + && value2 == other.value2 + && value3 == other.value3; + } + + public override int GetHashCode() + { + return (int)value1; } /// @@ -86,6 +93,13 @@ public static new UInt160 Parse(string value) return new UInt160(data); } + public override void Serialize(BinaryWriter writer) + { + writer.Write(value1); + writer.Write(value2); + writer.Write(value3); + } + /// /// Method TryParse tries to parse a big-endian hex string and store it as a UInt160 little-endian 20-bytes array /// Example: TryParse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01", result) should create result UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 @@ -116,6 +130,24 @@ public static bool TryParse(string s, out UInt160 result) return true; } + /// + /// Returns true if left UInt160 is equals to right UInt160 + /// + public static bool operator ==(UInt160 left, UInt160 right) + { + if (ReferenceEquals(left, right)) return true; + if (left is null || right is null) return false; + return left.Equals(right); + } + + /// + /// Returns true if left UIntBase is not equals to right UIntBase + /// + public static bool operator !=(UInt160 left, UInt160 right) + { + return !(left == right); + } + /// /// Operator > returns true if left UInt160 is bigger than right UInt160 /// Example: UInt160(01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) > UInt160 (02ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) is true diff --git a/src/neo/UInt256.cs b/src/neo/UInt256.cs index 57e91fadd7..bee5743c3c 100644 --- a/src/neo/UInt256.cs +++ b/src/neo/UInt256.cs @@ -1,5 +1,6 @@ using System; using System.Globalization; +using System.IO; namespace Neo { @@ -11,63 +12,73 @@ public class UInt256 : UIntBase, IComparable, IEquatable public const int Length = 32; public static readonly UInt256 Zero = new UInt256(); + private ulong value1; + private ulong value2; + private ulong value3; + private ulong value4; + + public override int Size => Length; - /// - /// The empty constructor stores a null byte array - /// public UInt256() - : this(null) { } - /// - /// The byte[] constructor invokes base class UIntBase constructor for 32 bytes - /// - public UInt256(byte[] value) - : base(Length, value) + public unsafe UInt256(ReadOnlySpan value) { + fixed (ulong* p = &value1) + { + Span dst = new Span(p, Length); + value[..Length].CopyTo(dst); + } } /// /// Method CompareTo returns 1 if this UInt256 is bigger than other UInt256; -1 if it's smaller; 0 if it's equals /// Example: assume this is 01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4, this.CompareTo(02ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) returns 1 /// - public unsafe int CompareTo(UInt256 other) + public int CompareTo(UInt256 other) { - fixed (byte* px = ToArray(), py = other.ToArray()) - { - ulong* lpx = (ulong*)px; - ulong* lpy = (ulong*)py; - //256bit / 64bit(ulong step) -1 - for (int i = (256 / 64 - 1); i >= 0; i--) - { - if (lpx[i] > lpy[i]) - return 1; - if (lpx[i] < lpy[i]) - return -1; - } - } - return 0; + int result = value4.CompareTo(other.value4); + if (result != 0) return result; + result = value3.CompareTo(other.value3); + if (result != 0) return result; + result = value2.CompareTo(other.value2); + if (result != 0) return result; + return value1.CompareTo(other.value1); + } + + public override void Deserialize(BinaryReader reader) + { + value1 = reader.ReadUInt64(); + value2 = reader.ReadUInt64(); + value3 = reader.ReadUInt64(); + value4 = reader.ReadUInt64(); + } + + /// + /// Method Equals returns true if objects are equal, false otherwise + /// + public override bool Equals(object obj) + { + if (ReferenceEquals(obj, this)) return true; + return Equals(obj as UInt256); } /// /// Method Equals returns true if objects are equal, false otherwise /// - public unsafe bool Equals(UInt256 other) + public bool Equals(UInt256 other) { if (other is null) return false; - fixed (byte* px = ToArray(), py = other.ToArray()) - { - ulong* lpx = (ulong*)px; - ulong* lpy = (ulong*)py; - //256bit / 64bit(ulong step) -1 - for (int i = (256 / 64 - 1); i >= 0; i--) - { - if (lpx[i] != lpy[i]) - return false; - } - } - return true; + return value1 == other.value1 + && value2 == other.value2 + && value3 == other.value3 + && value4 == other.value4; + } + + public override int GetHashCode() + { + return (int)value1; } /// @@ -87,6 +98,14 @@ public static new UInt256 Parse(string s) return new UInt256(data); } + public override void Serialize(BinaryWriter writer) + { + writer.Write(value1); + writer.Write(value2); + writer.Write(value3); + writer.Write(value4); + } + /// /// Method TryParse tries to parse a big-endian hex string and store it as a UInt256 little-endian 32-bytes array /// Example: TryParse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01", result) should create result UInt256 01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 @@ -117,6 +136,24 @@ public static bool TryParse(string s, out UInt256 result) return true; } + /// + /// Returns true if left UInt256 is equals to right UInt256 + /// + public static bool operator ==(UInt256 left, UInt256 right) + { + if (ReferenceEquals(left, right)) return true; + if (left is null || right is null) return false; + return left.Equals(right); + } + + /// + /// Returns true if left UIntBase is not equals to right UIntBase + /// + public static bool operator !=(UInt256 left, UInt256 right) + { + return !(left == right); + } + /// /// Operator > returns true if left UInt256 is bigger than right UInt256 /// Example: UInt256(01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) > UInt256(02ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) is true diff --git a/src/neo/UIntBase.cs b/src/neo/UIntBase.cs index ab07670660..b78ccd3023 100644 --- a/src/neo/UIntBase.cs +++ b/src/neo/UIntBase.cs @@ -1,7 +1,6 @@ using Neo.IO; using System; using System.IO; -using System.Runtime.CompilerServices; namespace Neo { @@ -9,81 +8,22 @@ namespace Neo /// Base class for little-endian unsigned integers. Two classes inherit from this: UInt160 and UInt256. /// Only basic comparison/serialization are proposed for these classes. For arithmetic purposes, use BigInteger class. /// - public abstract class UIntBase : IEquatable, ISerializable + public abstract class UIntBase : ISerializable { - /// - /// Storing unsigned int in a little-endian byte array. - /// - private readonly byte[] data_bytes; - /// /// Number of bytes of the unsigned int. /// Currently, inherited classes use 20-bytes (UInt160) or 32-bytes (UInt256) /// - public int Size => data_bytes.Length; - - /// - /// Base constructor receives the intended number of bytes and a byte array. - /// If byte array is null, it's automatically initialized with given size. - /// - protected UIntBase(int bytes, byte[] value) - { - if (value == null) - { - this.data_bytes = new byte[bytes]; - return; - } - if (value.Length != bytes) - throw new ArgumentException(); - this.data_bytes = value; - } + public abstract int Size { get; } - /// - /// Deserialize function reads the expected size in bytes from the given BinaryReader and stores in data_bytes array. - /// - void ISerializable.Deserialize(BinaryReader reader) - { - if (reader.Read(data_bytes, 0, data_bytes.Length) != data_bytes.Length) - { - throw new FormatException(); - } - } + public abstract void Deserialize(BinaryReader reader); - /// - /// Method Equals returns true if objects are equal, false otherwise - /// If null is passed as parameter, this method returns false. If it's a self-reference, it returns true. - /// - public bool Equals(UIntBase other) - { - if (other is null) - return false; - if (ReferenceEquals(this, other)) - return true; - if (data_bytes.Length != other.data_bytes.Length) - return false; - return MemoryExtensions.SequenceEqual(data_bytes, other.data_bytes); - } - - /// - /// Method Equals returns true if objects are equal, false otherwise - /// If null is passed as parameter or if it's not a UIntBase object, this method returns false. - /// - public override bool Equals(object obj) - { - if (obj is null) - return false; - if (!(obj is UIntBase)) - return false; - return this.Equals((UIntBase)obj); - } + public abstract override bool Equals(object obj); /// /// Method GetHashCode returns a 32-bit int representing a hash code, composed of the first 4 bytes. /// - public override int GetHashCode() - { - return data_bytes.ToInt32(0); - } + public abstract override int GetHashCode(); /// /// Method Parse receives a big-endian hex string and stores as a UInt160 or UInt256 little-endian byte array @@ -99,22 +39,7 @@ public static UIntBase Parse(string s) throw new FormatException(); } - /// - /// Method Serialize writes the data_bytes array into a BinaryWriter object - /// - void ISerializable.Serialize(BinaryWriter writer) - { - writer.Write(data_bytes); - } - - /// - /// Method ToArray() returns the byte array data_bytes, which stores the little-endian unsigned int - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public byte[] ToArray() - { - return data_bytes; - } + public abstract void Serialize(BinaryWriter writer); /// /// Method ToString returns a big-endian string starting by "0x" representing the little-endian unsigned int @@ -122,7 +47,7 @@ public byte[] ToArray() /// public override string ToString() { - return "0x" + data_bytes.ToHexString(reverse: true); + return "0x" + this.ToArray().ToHexString(reverse: true); } /// @@ -161,28 +86,5 @@ public override string ToString() result = null; return false; } - - /// - /// Operator == returns true if left UIntBase is equals to right UIntBase - /// If any parameter is null, it returns false. If both are the same object, it returns true. - /// Example: UIntBase(02ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) == UIntBase(02ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) is true - /// - public static bool operator ==(UIntBase left, UIntBase right) - { - if (ReferenceEquals(left, right)) - return true; - if (ReferenceEquals(left, null) || ReferenceEquals(right, null)) - return false; - return left.Equals(right); - } - - /// - /// Operator != returns true if left UIntBase is not equals to right UIntBase - /// Example: UIntBase(02ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) != UIntBase(01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) is true - /// - public static bool operator !=(UIntBase left, UIntBase right) - { - return !(left == right); - } } } diff --git a/src/neo/Utility.cs b/src/neo/Utility.cs index 56e048a512..76c5b80794 100644 --- a/src/neo/Utility.cs +++ b/src/neo/Utility.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Configuration; using Neo.Cryptography.ECC; using Neo.SmartContract; using Neo.Wallets; @@ -57,5 +58,19 @@ public static UInt160 GetScriptHash(string account) throw new FormatException(); } + + /// + /// Load configuration with different Environment Variable + /// + /// Configuration + /// IConfigurationRoot + public static IConfigurationRoot LoadConfig(string config) + { + var env = Environment.GetEnvironmentVariable("NEO_NETWORK"); + var configFile = string.IsNullOrWhiteSpace(env) ? $"{config}.json" : $"{config}.{env}.json"; + return new ConfigurationBuilder() + .AddJsonFile(configFile, true) + .Build(); + } } } diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 37a4d964c2..5d42b1ae73 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -216,32 +216,32 @@ public static ContractParameter ToParameter(this StackItem item) return ToParameter(item, null); } - private static ContractParameter ToParameter(StackItem item, List> context) + private static ContractParameter ToParameter(StackItem item, List<(StackItem, ContractParameter)> context) { ContractParameter parameter = null; switch (item) { case VMArray array: if (context is null) - context = new List>(); + context = new List<(StackItem, ContractParameter)>(); else - parameter = context.FirstOrDefault(p => ReferenceEquals(p.Item1, item))?.Item2; + (_, parameter) = context.FirstOrDefault(p => ReferenceEquals(p.Item1, item)); if (parameter is null) { parameter = new ContractParameter { Type = ContractParameterType.Array }; - context.Add(new Tuple(item, parameter)); + context.Add((item, parameter)); parameter.Value = array.Select(p => ToParameter(p, context)).ToList(); } break; case Map map: if (context is null) - context = new List>(); + context = new List<(StackItem, ContractParameter)>(); else - parameter = context.FirstOrDefault(p => ReferenceEquals(p.Item1, item))?.Item2; + (_, parameter) = context.FirstOrDefault(p => ReferenceEquals(p.Item1, item)); if (parameter is null) { parameter = new ContractParameter { Type = ContractParameterType.Map }; - context.Add(new Tuple(item, parameter)); + context.Add((item, parameter)); parameter.Value = map.Select(p => new KeyValuePair(ToParameter(p.Key, context), ToParameter(p.Value, context))).ToList(); } break; @@ -283,31 +283,31 @@ public static StackItem ToStackItem(this ContractParameter parameter) return ToStackItem(parameter, null); } - private static StackItem ToStackItem(ContractParameter parameter, List> context) + private static StackItem ToStackItem(ContractParameter parameter, List<(StackItem, ContractParameter)> context) { StackItem stackItem = null; switch (parameter.Type) { case ContractParameterType.Array: if (context is null) - context = new List>(); + context = new List<(StackItem, ContractParameter)>(); else - stackItem = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter))?.Item1; + (stackItem, _) = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter)); if (stackItem is null) { stackItem = ((IList)parameter.Value).Select(p => ToStackItem(p, context)).ToList(); - context.Add(new Tuple(stackItem, parameter)); + context.Add((stackItem, parameter)); } break; case ContractParameterType.Map: if (context is null) - context = new List>(); + context = new List<(StackItem, ContractParameter)>(); else - stackItem = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter))?.Item1; + (stackItem, _) = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter)); if (stackItem is null) { stackItem = new Map(((IList>)parameter.Value).ToDictionary(p => (PrimitiveType)ToStackItem(p.Key, context), p => ToStackItem(p.Value, context))); - context.Add(new Tuple(stackItem, parameter)); + context.Add((stackItem, parameter)); } break; case ContractParameterType.Boolean: diff --git a/src/neo/Wallets/Helper.cs b/src/neo/Wallets/Helper.cs index 9de3dde7f3..c5b203df90 100644 --- a/src/neo/Wallets/Helper.cs +++ b/src/neo/Wallets/Helper.cs @@ -1,4 +1,5 @@ using Neo.Cryptography; +using Neo.IO; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using System; @@ -9,15 +10,15 @@ public static class Helper { public static byte[] Sign(this IVerifiable verifiable, KeyPair key) { - return Crypto.Default.Sign(verifiable.GetHashData(), key.PrivateKey, key.PublicKey.EncodePoint(false)[1..]); + return Crypto.Sign(verifiable.GetHashData(), key.PrivateKey, key.PublicKey.EncodePoint(false)[1..]); } public static string ToAddress(this UInt160 scriptHash) { - byte[] data = new byte[21]; + Span data = stackalloc byte[21]; data[0] = ProtocolSettings.Default.AddressVersion; - Buffer.BlockCopy(scriptHash.ToArray(), 0, data, 1, 20); - return data.Base58CheckEncode(); + scriptHash.ToArray().CopyTo(data[1..]); + return Base58.Base58CheckEncode(data); } public static UInt160 ToScriptHash(this string address) @@ -29,5 +30,14 @@ public static UInt160 ToScriptHash(this string address) throw new FormatException(); return new UInt160(data[1..]); } + + internal static byte[] XOR(byte[] x, byte[] y) + { + if (x.Length != y.Length) throw new ArgumentException(); + byte[] r = new byte[x.Length]; + for (int i = 0; i < r.Length; i++) + r[i] = (byte)(x[i] ^ y[i]); + return r; + } } } diff --git a/src/neo/Wallets/KeyPair.cs b/src/neo/Wallets/KeyPair.cs index 55d720634b..01b5c71756 100644 --- a/src/neo/Wallets/KeyPair.cs +++ b/src/neo/Wallets/KeyPair.cs @@ -1,8 +1,8 @@ using Neo.Cryptography; using Neo.SmartContract; using System; -using System.Linq; using System.Text; +using static Neo.Wallets.Helper; namespace Neo.Wallets { @@ -17,8 +17,7 @@ public KeyPair(byte[] privateKey) { if (privateKey.Length != 32 && privateKey.Length != 96 && privateKey.Length != 104) throw new ArgumentException(); - this.PrivateKey = new byte[32]; - Buffer.BlockCopy(privateKey, privateKey.Length - 32, PrivateKey, 0, 32); + this.PrivateKey = privateKey[^32..]; if (privateKey.Length == 32) { this.PublicKey = Cryptography.ECC.ECCurve.Secp256r1.G * privateKey; @@ -43,12 +42,12 @@ public override bool Equals(object obj) public string Export() { - byte[] data = new byte[34]; + Span data = stackalloc byte[34]; data[0] = 0x80; - Buffer.BlockCopy(PrivateKey, 0, data, 1, 32); + PrivateKey.CopyTo(data[1..]); data[33] = 0x01; - string wif = data.Base58CheckEncode(); - Array.Clear(data, 0, data.Length); + string wif = Base58.Base58CheckEncode(data); + data.Clear(); return wif; } @@ -61,13 +60,13 @@ public string Export(string passphrase, int N = 16384, int r = 8, int p = 8) byte[] derivedhalf1 = derivedkey[..32]; byte[] derivedhalf2 = derivedkey[32..]; byte[] encryptedkey = XOR(PrivateKey, derivedhalf1).AES256Encrypt(derivedhalf2); - byte[] buffer = new byte[39]; + Span buffer = stackalloc byte[39]; buffer[0] = 0x01; buffer[1] = 0x42; buffer[2] = 0xe0; - Buffer.BlockCopy(addresshash, 0, buffer, 3, addresshash.Length); - Buffer.BlockCopy(encryptedkey, 0, buffer, 7, encryptedkey.Length); - return buffer.Base58CheckEncode(); + addresshash.CopyTo(buffer[3..]); + encryptedkey.CopyTo(buffer[7..]); + return Base58.Base58CheckEncode(buffer); } public override int GetHashCode() @@ -79,11 +78,5 @@ public override string ToString() { return PublicKey.ToString(); } - - private static byte[] XOR(byte[] x, byte[] y) - { - if (x.Length != y.Length) throw new ArgumentException(); - return x.Zip(y, (a, b) => (byte)(a ^ b)).ToArray(); - } } } diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs index efb477a61a..53d0795708 100644 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ b/src/neo/Wallets/SQLite/UserWallet.cs @@ -3,6 +3,7 @@ using Neo.IO; using Neo.SmartContract; using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Linq; @@ -28,10 +29,10 @@ public override Version Version { byte[] buffer = LoadStoredData("Version"); if (buffer == null || buffer.Length < 16) return new Version(0, 0); - int major = buffer.ToInt32(0); - int minor = buffer.ToInt32(4); - int build = buffer.ToInt32(8); - int revision = buffer.ToInt32(12); + int major = BinaryPrimitives.ReadInt32LittleEndian(buffer); + int minor = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(4)); + int build = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(8)); + int revision = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(12)); return new Version(major, minor, build, revision); } } diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index ebc1e6b58f..52fa839b16 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -13,6 +13,7 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; +using static Neo.Wallets.Helper; using ECPoint = Neo.Cryptography.ECC.ECPoint; namespace Neo.Wallets @@ -390,11 +391,5 @@ public bool Sign(ContractParametersContext context) } public abstract bool VerifyPassword(string password); - - private static byte[] XOR(byte[] x, byte[] y) - { - if (x.Length != y.Length) throw new ArgumentException(); - return x.Zip(y, (a, b) => (byte)(a ^ b)).ToArray(); - } } } diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 5a9cb1bf98..343a0cece5 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -5,12 +5,12 @@ using Moq; using Neo.Consensus; using Neo.Cryptography; -using Neo.UnitTests.Cryptography; using Neo.IO; using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.SmartContract; +using Neo.UnitTests.Cryptography; using Neo.Wallets; using System; using System.Collections.Generic; @@ -249,10 +249,10 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("\nBasic commits Signatures verification"); // Basic tests for understanding signatures and ensuring signatures of commits are correct on tests var cmPayloadTemp = GetCommitPayloadModifiedAndSignedCopy(commitPayload, 1, kp_array[1], updatedBlockHashData); - Crypto.Default.VerifySignature(originalBlockHashData, cm.Signature, mockContext.Object.Validators[0].EncodePoint(false)).Should().BeFalse(); - Crypto.Default.VerifySignature(updatedBlockHashData, cm.Signature, mockContext.Object.Validators[0].EncodePoint(false)).Should().BeFalse(); - Crypto.Default.VerifySignature(originalBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[1].EncodePoint(false)).Should().BeFalse(); - Crypto.Default.VerifySignature(updatedBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[1].EncodePoint(false)).Should().BeTrue(); + Crypto.VerifySignature(originalBlockHashData, cm.Signature, mockContext.Object.Validators[0].EncodePoint(false)).Should().BeFalse(); + Crypto.VerifySignature(updatedBlockHashData, cm.Signature, mockContext.Object.Validators[0].EncodePoint(false)).Should().BeFalse(); + Crypto.VerifySignature(originalBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[1].EncodePoint(false)).Should().BeFalse(); + Crypto.VerifySignature(updatedBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[1].EncodePoint(false)).Should().BeTrue(); Console.WriteLine("\n=========================="); Console.WriteLine("\n=========================="); @@ -328,7 +328,7 @@ public ConsensusPayload GetCommitPayloadModifiedAndSignedCopy(ConsensusPayload c var cpCommitTemp = cpToCopy.ToArray().AsSerializable(); cpCommitTemp.ValidatorIndex = vI; cpCommitTemp.ConsensusMessage = cpToCopy.ConsensusMessage.ToArray().AsSerializable(); - ((Commit)cpCommitTemp.ConsensusMessage).Signature = Crypto.Default.Sign(blockHashToSign, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()); + ((Commit)cpCommitTemp.ConsensusMessage).Signature = Crypto.Sign(blockHashToSign, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()); // Payload is not being signed by vI, since we are bypassing this check as directly talking to subscriber return cpCommitTemp; } @@ -489,7 +489,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndNoPrepar } } }, - PreparationHash = new UInt256(Crypto.Default.Hash256(new[] { (byte)'a' })), + PreparationHash = new UInt256(Crypto.Hash256(new[] { (byte)'a' })), PreparationMessages = new Dictionary() { { @@ -589,7 +589,7 @@ public void TestSerializeAndDeserializeRecoveryMessageWithChangeViewsAndPrepareR { TransactionHashes = txs.Select(p => p.Hash).ToArray() }, - PreparationHash = new UInt256(Crypto.Default.Hash256(new[] { (byte)'a' })), + PreparationHash = new UInt256(Crypto.Hash256(new[] { (byte)'a' })), PreparationMessages = new Dictionary() { { diff --git a/tests/neo.UnitTests/Cryptography/UT_Crypto.cs b/tests/neo.UnitTests/Cryptography/UT_Crypto.cs index 0fff2a26c3..38f2006a7f 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Crypto.cs +++ b/tests/neo.UnitTests/Cryptography/UT_Crypto.cs @@ -43,21 +43,21 @@ public void TestSetup() public void TestVerifySignature() { byte[] message = System.Text.Encoding.Default.GetBytes("HelloWorld"); - byte[] signature = Crypto.Default.Sign(message, key.PrivateKey, key.PublicKey.EncodePoint(false).Skip(1).ToArray()); - Crypto.Default.VerifySignature(message, signature, key.PublicKey.EncodePoint(false)).Should().BeTrue(); - Crypto.Default.VerifySignature(message, signature, key.PublicKey.EncodePoint(false).Skip(1).ToArray()).Should().BeTrue(); - Crypto.Default.VerifySignature(message, signature, key.PublicKey.EncodePoint(false).Skip(1).ToArray()).Should().BeTrue(); + byte[] signature = Crypto.Sign(message, key.PrivateKey, key.PublicKey.EncodePoint(false).Skip(1).ToArray()); + Crypto.VerifySignature(message, signature, key.PublicKey.EncodePoint(false)).Should().BeTrue(); + Crypto.VerifySignature(message, signature, key.PublicKey.EncodePoint(false).Skip(1).ToArray()).Should().BeTrue(); + Crypto.VerifySignature(message, signature, key.PublicKey.EncodePoint(false).Skip(1).ToArray()).Should().BeTrue(); byte[] wrongKey = new byte[33]; wrongKey[0] = 0x02; - Crypto.Default.VerifySignature(message, signature, wrongKey).Should().BeFalse(); + Crypto.VerifySignature(message, signature, wrongKey).Should().BeFalse(); wrongKey[0] = 0x03; for (int i = 1; i < 33; i++) wrongKey[i] = byte.MaxValue; - Crypto.Default.VerifySignature(message, signature, wrongKey).Should().BeFalse(); + Crypto.VerifySignature(message, signature, wrongKey).Should().BeFalse(); wrongKey = new byte[36]; - Action action = () => Crypto.Default.VerifySignature(message, signature, wrongKey).Should().BeFalse(); + Action action = () => Crypto.VerifySignature(message, signature, wrongKey).Should().BeFalse(); action.Should().Throw(); } } diff --git a/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index 58071f4994..b44d0a74d1 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; +using Neo.IO; using Neo.Network.P2P.Payloads; using System; using System.Security; diff --git a/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs b/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs index 870405caf8..600e43faaf 100644 --- a/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs +++ b/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs @@ -1,9 +1,9 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; +using Neo.IO; using System; using System.Collections; -using System.Collections.Generic; using System.Linq; namespace Neo.UnitTests.Cryptography @@ -14,14 +14,14 @@ public class UT_MerkleTree public UInt256 GetByteArrayHash(byte[] byteArray) { if (byteArray == null || byteArray.Length == 0) throw new ArgumentNullException(); - var hash = new UInt256(Crypto.Default.Hash256(byteArray)); + var hash = new UInt256(Crypto.Hash256(byteArray)); return hash; } [TestMethod] public void TestBuildAndDepthFirstSearch() { - IReadOnlyList hashNull = new UInt256[] { }; + UInt256[] hashNull = new UInt256[0]; Action action = () => new MerkleTree(hashNull); action.Should().Throw(); @@ -34,7 +34,7 @@ public void TestBuildAndDepthFirstSearch() byte[] array3 = { 0x03 }; var hash3 = GetByteArrayHash(array3); - IReadOnlyList hashes = new UInt256[] { hash1, hash2, hash3 }; + UInt256[] hashes = { hash1, hash2, hash3 }; MerkleTree tree = new MerkleTree(hashes); var hashArray = tree.ToHashArray(); hashArray[0].Should().Be(hash1); @@ -43,9 +43,9 @@ public void TestBuildAndDepthFirstSearch() hashArray[3].Should().Be(hash3); var rootHash = MerkleTree.ComputeRoot(hashes); - var hash4 = Crypto.Default.Hash256(hash1.ToArray().Concat(hash2.ToArray()).ToArray()); - var hash5 = Crypto.Default.Hash256(hash3.ToArray().Concat(hash3.ToArray()).ToArray()); - var result = new UInt256(Crypto.Default.Hash256(hash4.ToArray().Concat(hash5.ToArray()).ToArray())); + var hash4 = Crypto.Hash256(hash1.ToArray().Concat(hash2.ToArray()).ToArray()); + var hash5 = Crypto.Hash256(hash3.ToArray().Concat(hash3.ToArray()).ToArray()); + var result = new UInt256(Crypto.Hash256(hash4.ToArray().Concat(hash5.ToArray()).ToArray())); rootHash.Should().Be(result); } @@ -61,7 +61,7 @@ public void TestTrim() byte[] array3 = { 0x03 }; var hash3 = GetByteArrayHash(array3); - IReadOnlyList hashes = new UInt256[] { hash1, hash2, hash3 }; + UInt256[] hashes = { hash1, hash2, hash3 }; MerkleTree tree = new MerkleTree(hashes); bool[] boolArray = { false, false, false }; @@ -71,9 +71,9 @@ public void TestTrim() hashArray.Length.Should().Be(1); var rootHash = MerkleTree.ComputeRoot(hashes); - var hash4 = Crypto.Default.Hash256(hash1.ToArray().Concat(hash2.ToArray()).ToArray()); - var hash5 = Crypto.Default.Hash256(hash3.ToArray().Concat(hash3.ToArray()).ToArray()); - var result = new UInt256(Crypto.Default.Hash256(hash4.ToArray().Concat(hash5.ToArray()).ToArray())); + var hash4 = Crypto.Hash256(hash1.ToArray().Concat(hash2.ToArray()).ToArray()); + var hash5 = Crypto.Hash256(hash3.ToArray().Concat(hash3.ToArray()).ToArray()); + var result = new UInt256(Crypto.Hash256(hash4.ToArray().Concat(hash5.ToArray()).ToArray())); hashArray[0].Should().Be(result); } } diff --git a/tests/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs b/tests/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs index 78274c2d18..7ae6f7658b 100644 --- a/tests/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs +++ b/tests/neo.UnitTests/Cryptography/UT_MerkleTreeNode.cs @@ -23,7 +23,7 @@ public void TestSetup() public void TestConstructor() { byte[] byteArray = Encoding.ASCII.GetBytes("hello world"); - var hash = new UInt256(Crypto.Default.Hash256(byteArray)); + var hash = new UInt256(Crypto.Hash256(byteArray)); node.Hash = hash; node.Hash.Should().Be(hash); diff --git a/tests/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs b/tests/neo.UnitTests/IO/UT_SerializableWrapper.cs similarity index 78% rename from tests/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs rename to tests/neo.UnitTests/IO/UT_SerializableWrapper.cs index aa6c0cfbc8..5a1ec884ed 100644 --- a/tests/neo.UnitTests/IO/Wrappers/UT_SerializableWrapper.cs +++ b/tests/neo.UnitTests/IO/UT_SerializableWrapper.cs @@ -1,5 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Wrappers; +using Neo.IO; using System.IO; namespace Neo.UnitTests @@ -10,7 +10,7 @@ public class UT_SerializableWrapper [TestMethod] public void TestGetSize() { - SerializableWrapper temp = new UInt32Wrapper(); + SerializableWrapper temp = new SerializableWrapper(); Assert.AreEqual(4, temp.Size); } @@ -22,9 +22,9 @@ public void TestEqualsOtherObject() BinaryReader reader = new BinaryReader(stream); writer.Write((uint)1); stream.Seek(0, SeekOrigin.Begin); - SerializableWrapper temp = new UInt32Wrapper(); + SerializableWrapper temp = new SerializableWrapper(); temp.Deserialize(reader); - Assert.AreEqual(true, temp.Equals(1)); + Assert.AreEqual(true, temp.Equals(1u)); } [TestMethod] @@ -35,14 +35,14 @@ public void TestEqualsOtherSerializableWrapper() BinaryReader reader = new BinaryReader(stream); writer.Write((uint)1); stream.Seek(0, SeekOrigin.Begin); - SerializableWrapper temp = new UInt32Wrapper(); + 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 UInt32Wrapper(); + SerializableWrapper temp2 = new SerializableWrapper(); temp2.Deserialize(reader2); Assert.AreEqual(true, temp.Equals(temp2)); } diff --git a/tests/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs b/tests/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs deleted file mode 100644 index 766ed8e694..0000000000 --- a/tests/neo.UnitTests/IO/Wrappers/UT_UInt32Wrapper.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Wrappers; -using System.IO; -using System.Text; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_UInt32Wrapper - { - [TestMethod] - public void TestGetSize() - { - UInt32Wrapper temp = new UInt32Wrapper(); - Assert.AreEqual(4, temp.Size); - } - - [TestMethod] - public void TestDeserialize() - { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - BinaryReader reader = new BinaryReader(stream); - writer.Write(new byte[] { 0x00, 0x00, 0x00, 0x01 }); - stream.Seek(0, SeekOrigin.Begin); - UInt32Wrapper temp = new UInt32Wrapper(); - temp.Deserialize(reader); - MemoryStream stream2 = new MemoryStream(); - BinaryWriter writer2 = new BinaryWriter(stream2); - temp.Serialize(writer2); - stream2.Seek(0, SeekOrigin.Begin); - byte[] byteArray = new byte[stream2.Length]; - stream2.Read(byteArray, 0, (int)stream2.Length); - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00, 0x00, 0x00, 0x01 }), Encoding.Default.GetString(byteArray)); - } - - [TestMethod] - public void TestSerialize() - { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - BinaryReader reader = new BinaryReader(stream); - writer.Write(new byte[] { 0x00, 0x00, 0x00, 0x01 }); - stream.Seek(0, SeekOrigin.Begin); - UInt32Wrapper temp = new UInt32Wrapper(); - temp.Deserialize(reader); - MemoryStream stream2 = new MemoryStream(); - BinaryWriter writer2 = new BinaryWriter(stream2); - temp.Serialize(writer2); - stream2.Seek(0, SeekOrigin.Begin); - byte[] byteArray = new byte[stream2.Length]; - stream2.Read(byteArray, 0, (int)stream2.Length); - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00, 0x00, 0x00, 0x01 }), Encoding.Default.GetString(byteArray)); - } - - [TestMethod] - public void TestEqualsUInt32Wrapper() - { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - BinaryReader reader = new BinaryReader(stream); - writer.Write((uint)1); - stream.Seek(0, SeekOrigin.Begin); - UInt32Wrapper temp = new UInt32Wrapper(); - 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); - UInt32Wrapper temp2 = new UInt32Wrapper(); - temp2.Deserialize(reader2); - Assert.AreEqual(true, temp.Equals(temp2)); - } - - [TestMethod] - public void TestOperatorUint() - { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - BinaryReader reader = new BinaryReader(stream); - writer.Write((uint)1); - stream.Seek(0, SeekOrigin.Begin); - UInt32Wrapper temp = new UInt32Wrapper(); - temp.Deserialize(reader); - uint result = temp; - Assert.AreEqual((uint)1, result); - } - - [TestMethod] - public void TestOperatorUInt32Wrapper() - { - UInt32Wrapper temp = 1; - Assert.AreEqual(true, temp.Equals(1)); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs b/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs index e11c551a9a..6fb69800cb 100644 --- a/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs +++ b/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs @@ -123,7 +123,7 @@ public void TestSign() var tx = txManager.Tx; byte[] signature = tx.Witnesses[0].InvocationScript.Skip(1).ToArray(); - Assert.IsTrue(Crypto.Default.VerifySignature(tx.GetHashData(), signature, keyPair1.PublicKey.EncodePoint(false).Skip(1).ToArray())); + Assert.IsTrue(Crypto.VerifySignature(tx.GetHashData(), signature, keyPair1.PublicKey.EncodePoint(false).Skip(1).ToArray())); // duplicate sign should not add new witness txManager.AddSignature(keyPair1).Sign(); diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs index 92519e26e5..8b6330cde1 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractGroup.cs @@ -1,6 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; -using Neo.Cryptography.ECC; using Neo.SmartContract.Manifest; using Neo.Wallets; using System; @@ -30,7 +29,7 @@ public void TestIsValid() 0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01,0x01 }; - byte[] signature = Crypto.Default.Sign(message, keyPair.PrivateKey, keyPair.PublicKey.EncodePoint(false).Skip(1).ToArray()); + byte[] signature = Crypto.Sign(message, keyPair.PrivateKey, keyPair.PublicKey.EncodePoint(false).Skip(1).ToArray()); contractGroup = new ContractGroup { PubKey = keyPair.PublicKey, diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index a045785178..2693a40690 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -1,5 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 8ae4516cab..49d086e42c 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -687,7 +687,7 @@ internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint[] votes) { - var st = (VM.Types.Struct)trackable.Item.Value.DeserializeStackItem(3, 32); + var st = (VM.Types.Struct)StackItemSerializer.Deserialize(trackable.Item.Value, 3, 32); st.Count.Should().Be(3); st.Select(u => u.GetType()).ToArray().Should().BeEquivalentTo(new Type[] { typeof(VM.Types.Integer), typeof(VM.Types.Integer), typeof(VM.Types.ByteArray) }); // Balance diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index e602cb0f74..86f62c6ed3 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.Cryptography.ECC; +using Neo.IO; using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; @@ -29,7 +30,7 @@ public void TestCheckSig() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; KeyPair keyPair = new KeyPair(privateKey); ECPoint pubkey = keyPair.PublicKey; - byte[] signature = Crypto.Default.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); + byte[] signature = Crypto.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); @@ -54,13 +55,13 @@ public void TestCrypto_CheckMultiSig() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; KeyPair key1 = new KeyPair(privkey1); ECPoint pubkey1 = key1.PublicKey; - byte[] signature1 = Crypto.Default.Sign(message, privkey1, pubkey1.EncodePoint(false).Skip(1).ToArray()); + byte[] signature1 = Crypto.Sign(message, privkey1, pubkey1.EncodePoint(false).Skip(1).ToArray()); byte[] privkey2 = { 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, 0x02}; KeyPair key2 = new KeyPair(privkey2); ECPoint pubkey2 = key2.PublicKey; - byte[] signature2 = Crypto.Default.Sign(message, privkey2, pubkey2.EncodePoint(false).Skip(1).ToArray()); + byte[] signature2 = Crypto.Sign(message, privkey2, pubkey2.EncodePoint(false).Skip(1).ToArray()); var pubkeys = new VMArray { @@ -208,7 +209,7 @@ public void TestContract_Update() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; KeyPair key = new KeyPair(privkey); ECPoint pubkey = key.PublicKey; - byte[] signature = Crypto.Default.Sign(script.ToScriptHash().ToArray(), privkey, pubkey.EncodePoint(false).Skip(1).ToArray()); + byte[] signature = Crypto.Sign(script.ToScriptHash().ToArray(), privkey, pubkey.EncodePoint(false).Skip(1).ToArray()); manifest.Groups = new ContractGroup[] { new ContractGroup() diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index f04d6f5b6c..e1f29ad4ad 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.Cryptography.ECC; +using Neo.IO; using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; @@ -358,7 +359,7 @@ public void TestCrypto_Verify() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; KeyPair keyPair = new KeyPair(privateKey); ECPoint pubkey = keyPair.PublicKey; - byte[] signature = Crypto.Default.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); + byte[] signature = Crypto.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 6421bd1a31..dc7729f626 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -1,12 +1,9 @@ -using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; -using Neo.VM.Types; using Neo.Wallets; using System; -using System.Collections.Generic; using System.Security.Cryptography; using System.Text; @@ -105,143 +102,6 @@ public void TestIsStandardContract() Assert.AreEqual(true, Neo.SmartContract.Helper.IsStandardContract(script2)); } - [TestMethod] - public void TestSerialize() - { - StackItem stackItem1 = new ByteArray(new byte[5]); - byte[] result1 = Neo.SmartContract.Helper.Serialize(stackItem1); - byte[] expectedArray1 = new byte[] { - 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - Assert.AreEqual(Encoding.Default.GetString(expectedArray1), Encoding.Default.GetString(result1)); - - StackItem stackItem2 = new VM.Types.Boolean(true); - byte[] result2 = Neo.SmartContract.Helper.Serialize(stackItem2); - byte[] expectedArray2 = new byte[] { - 0x01, 0x01 - }; - Assert.AreEqual(Encoding.Default.GetString(expectedArray2), Encoding.Default.GetString(result2)); - - StackItem stackItem3 = new VM.Types.Integer(1); - byte[] result3 = Neo.SmartContract.Helper.Serialize(stackItem3); - byte[] expectedArray3 = new byte[] { - 0x02, 0x01, 0x01 - }; - Assert.AreEqual(Encoding.Default.GetString(expectedArray3), Encoding.Default.GetString(result3)); - - StackItem stackItem4 = new InteropInterface(new object()); - Action action4 = () => Neo.SmartContract.Helper.Serialize(stackItem4); - action4.Should().Throw(); - - StackItem stackItem5 = new VM.Types.Integer(1); - byte[] result5 = Neo.SmartContract.Helper.Serialize(stackItem5); - byte[] expectedArray5 = new byte[] { - 0x02, 0x01, 0x01 - }; - Assert.AreEqual(Encoding.Default.GetString(expectedArray5), Encoding.Default.GetString(result5)); - - - StackItem stackItem61 = new VM.Types.Integer(1); - List list6 = new List - { - stackItem61 - }; - StackItem stackItem62 = new VM.Types.Array(list6); - byte[] result6 = Neo.SmartContract.Helper.Serialize(stackItem62); - byte[] expectedArray6 = new byte[] { - 0x80,0x01,0x02,0x01,0x01 - }; - Assert.AreEqual(Encoding.Default.GetString(expectedArray6), Encoding.Default.GetString(result6)); - - StackItem stackItem71 = new VM.Types.Integer(1); - List list7 = new List - { - stackItem71 - }; - StackItem stackItem72 = new VM.Types.Struct(list7); - byte[] result7 = Neo.SmartContract.Helper.Serialize(stackItem72); - byte[] expectedArray7 = new byte[] { - 0x81,0x01,0x02,0x01,0x01 - }; - Assert.AreEqual(Encoding.Default.GetString(expectedArray7), Encoding.Default.GetString(result7)); - - StackItem stackItem81 = new VM.Types.Integer(1); - Dictionary list8 = new Dictionary - { - { new VM.Types.Integer(2), stackItem81 } - }; - StackItem stackItem82 = new VM.Types.Map(list8); - byte[] result8 = Neo.SmartContract.Helper.Serialize(stackItem82); - byte[] expectedArray8 = new byte[] { - 0x82,0x01,0x02,0x01,0x02,0x02,0x01,0x01 - }; - Assert.AreEqual(Encoding.Default.GetString(expectedArray8), Encoding.Default.GetString(result8)); - - Integer stackItem9 = new VM.Types.Integer(1); - Map stackItem91 = new VM.Types.Map(); - stackItem91.Add(stackItem9, stackItem91); - Action action9 = () => Neo.SmartContract.Helper.Serialize(stackItem91); - action9.Should().Throw(); - - VM.Types.Array stackItem10 = new VM.Types.Array(); - stackItem10.Add(stackItem10); - Action action10 = () => Neo.SmartContract.Helper.Serialize(stackItem10); - action10.Should().Throw(); - } - - [TestMethod] - public void TestDeserializeStackItem() - { - StackItem stackItem1 = new ByteArray(new byte[5]); - byte[] byteArray1 = Neo.SmartContract.Helper.Serialize(stackItem1); - StackItem result1 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray1, 1, (uint)byteArray1.Length); - Assert.AreEqual(stackItem1, result1); - - StackItem stackItem2 = new VM.Types.Boolean(true); - byte[] byteArray2 = Neo.SmartContract.Helper.Serialize(stackItem2); - StackItem result2 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray2, 1, (uint)byteArray2.Length); - Assert.AreEqual(stackItem2, result2); - - StackItem stackItem3 = new VM.Types.Integer(1); - byte[] byteArray3 = Neo.SmartContract.Helper.Serialize(stackItem3); - StackItem result3 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray3, 1, (uint)byteArray3.Length); - Assert.AreEqual(stackItem3, result3); - - StackItem stackItem4 = new VM.Types.Integer(1); - byte[] byteArray4 = Neo.SmartContract.Helper.Serialize(stackItem4); - byteArray4[0] = 0x40; - Action action4 = () => Neo.SmartContract.Helper.DeserializeStackItem(byteArray4, 1, (uint)byteArray4.Length); - action4.Should().Throw(); - - StackItem stackItem51 = new VM.Types.Integer(1); - List list5 = new List(); - list5.Add(stackItem51); - StackItem stackItem52 = new VM.Types.Array(list5); - byte[] byteArray5 = Neo.SmartContract.Helper.Serialize(stackItem52); - StackItem result5 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray5, 1, (uint)byteArray5.Length); - Assert.AreEqual(((VM.Types.Array)stackItem52).Count, ((VM.Types.Array)result5).Count); - Assert.AreEqual(((VM.Types.Array)stackItem52).GetEnumerator().Current, ((VM.Types.Array)result5).GetEnumerator().Current); - - StackItem stackItem61 = new VM.Types.Integer(1); - List list6 = new List(); - list6.Add(stackItem61); - StackItem stackItem62 = new VM.Types.Struct(list6); - byte[] byteArray6 = Neo.SmartContract.Helper.Serialize(stackItem62); - StackItem result6 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray6, 1, (uint)byteArray6.Length); - Assert.AreEqual(((VM.Types.Struct)stackItem62).Count, ((VM.Types.Struct)result6).Count); - Assert.AreEqual(((VM.Types.Struct)stackItem62).GetEnumerator().Current, ((VM.Types.Struct)result6).GetEnumerator().Current); - - StackItem stackItem71 = new VM.Types.Integer(1); - Dictionary list7 = new Dictionary(); - list7.Add(new VM.Types.Integer(2), stackItem71); - StackItem stackItem72 = new VM.Types.Map(list7); - byte[] byteArray7 = Neo.SmartContract.Helper.Serialize(stackItem72); - StackItem result7 = Neo.SmartContract.Helper.DeserializeStackItem(byteArray7, 1, (uint)byteArray7.Length); - Assert.AreEqual(((VM.Types.Map)stackItem72).Count, ((VM.Types.Map)result7).Count); - Assert.AreEqual(((VM.Types.Map)stackItem72).Keys.GetEnumerator().Current, ((VM.Types.Map)result7).Keys.GetEnumerator().Current); - Assert.AreEqual(((VM.Types.Map)stackItem72).Values.GetEnumerator().Current, ((VM.Types.Map)result7).Values.GetEnumerator().Current); - } - [TestMethod] public void TestToInteropMethodHash() { diff --git a/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs b/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs new file mode 100644 index 0000000000..cb27ffcea9 --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs @@ -0,0 +1,128 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.VM.Types; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Neo.UnitTests.SmartContract +{ + [TestClass] + public class UT_StackItemSerializer + { + + [TestMethod] + public void TestSerialize() + { + byte[] result1 = StackItemSerializer.Serialize(new byte[5]); + byte[] expectedArray1 = new byte[] { + 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray1), Encoding.Default.GetString(result1)); + + byte[] result2 = StackItemSerializer.Serialize(true); + byte[] expectedArray2 = new byte[] { + 0x01, 0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray2), Encoding.Default.GetString(result2)); + + byte[] result3 = StackItemSerializer.Serialize(1); + byte[] expectedArray3 = new byte[] { + 0x02, 0x01, 0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray3), Encoding.Default.GetString(result3)); + + StackItem stackItem4 = new InteropInterface(new object()); + Action action4 = () => StackItemSerializer.Serialize(stackItem4); + action4.Should().Throw(); + + byte[] result5 = StackItemSerializer.Serialize(1); + byte[] expectedArray5 = new byte[] { + 0x02, 0x01, 0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray5), Encoding.Default.GetString(result5)); + + + List list6 = new List { 1 }; + StackItem stackItem62 = new VM.Types.Array(list6); + byte[] result6 = StackItemSerializer.Serialize(stackItem62); + byte[] expectedArray6 = new byte[] { + 0x80,0x01,0x02,0x01,0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray6), Encoding.Default.GetString(result6)); + + List list7 = new List { 1 }; + StackItem stackItem72 = new Struct(list7); + byte[] result7 = StackItemSerializer.Serialize(stackItem72); + byte[] expectedArray7 = new byte[] { + 0x81,0x01,0x02,0x01,0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray7), Encoding.Default.GetString(result7)); + + Dictionary list8 = new Dictionary { [2] = 1 }; + StackItem stackItem82 = new Map(list8); + byte[] result8 = StackItemSerializer.Serialize(stackItem82); + byte[] expectedArray8 = new byte[] { + 0x82,0x01,0x02,0x01,0x02,0x02,0x01,0x01 + }; + Assert.AreEqual(Encoding.Default.GetString(expectedArray8), Encoding.Default.GetString(result8)); + + Map stackItem91 = new Map(); + stackItem91.Add(1, stackItem91); + Action action9 = () => StackItemSerializer.Serialize(stackItem91); + action9.Should().Throw(); + + VM.Types.Array stackItem10 = new VM.Types.Array(); + stackItem10.Add(stackItem10); + Action action10 = () => StackItemSerializer.Serialize(stackItem10); + action10.Should().Throw(); + } + + [TestMethod] + public void TestDeserializeStackItem() + { + StackItem stackItem1 = new ByteArray(new byte[5]); + byte[] byteArray1 = StackItemSerializer.Serialize(stackItem1); + StackItem result1 = StackItemSerializer.Deserialize(byteArray1, 1, (uint)byteArray1.Length); + Assert.AreEqual(stackItem1, result1); + + StackItem stackItem2 = new VM.Types.Boolean(true); + byte[] byteArray2 = StackItemSerializer.Serialize(stackItem2); + StackItem result2 = StackItemSerializer.Deserialize(byteArray2, 1, (uint)byteArray2.Length); + Assert.AreEqual(stackItem2, result2); + + StackItem stackItem3 = new Integer(1); + byte[] byteArray3 = StackItemSerializer.Serialize(stackItem3); + StackItem result3 = StackItemSerializer.Deserialize(byteArray3, 1, (uint)byteArray3.Length); + Assert.AreEqual(stackItem3, result3); + + byte[] byteArray4 = StackItemSerializer.Serialize(1); + byteArray4[0] = 0x40; + Action action4 = () => StackItemSerializer.Deserialize(byteArray4, 1, (uint)byteArray4.Length); + action4.Should().Throw(); + + List list5 = new List { 1 }; + StackItem stackItem52 = new VM.Types.Array(list5); + byte[] byteArray5 = StackItemSerializer.Serialize(stackItem52); + StackItem result5 = StackItemSerializer.Deserialize(byteArray5, 1, (uint)byteArray5.Length); + Assert.AreEqual(((VM.Types.Array)stackItem52).Count, ((VM.Types.Array)result5).Count); + Assert.AreEqual(((VM.Types.Array)stackItem52).GetEnumerator().Current, ((VM.Types.Array)result5).GetEnumerator().Current); + + List list6 = new List { 1 }; + StackItem stackItem62 = new Struct(list6); + byte[] byteArray6 = StackItemSerializer.Serialize(stackItem62); + StackItem result6 = StackItemSerializer.Deserialize(byteArray6, 1, (uint)byteArray6.Length); + Assert.AreEqual(((Struct)stackItem62).Count, ((Struct)result6).Count); + Assert.AreEqual(((Struct)stackItem62).GetEnumerator().Current, ((Struct)result6).GetEnumerator().Current); + + Dictionary list7 = new Dictionary { [2] = 1 }; + StackItem stackItem72 = new Map(list7); + byte[] byteArray7 = StackItemSerializer.Serialize(stackItem72); + StackItem result7 = StackItemSerializer.Deserialize(byteArray7, 1, (uint)byteArray7.Length); + Assert.AreEqual(((Map)stackItem72).Count, ((Map)result7).Count); + Assert.AreEqual(((Map)stackItem72).Keys.GetEnumerator().Current, ((Map)result7).Keys.GetEnumerator().Current); + Assert.AreEqual(((Map)stackItem72).Values.GetEnumerator().Current, ((Map)result7).Values.GetEnumerator().Current); + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/UT_StorageContext.cs b/tests/neo.UnitTests/SmartContract/UT_StorageContext.cs index eff7166a66..d1210169dd 100644 --- a/tests/neo.UnitTests/SmartContract/UT_StorageContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_StorageContext.cs @@ -1,5 +1,6 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.SmartContract; namespace Neo.UnitTests.SmartContract @@ -16,7 +17,7 @@ public void TestToArray() ScriptHash = script_hash, IsReadOnly = true }; - context.ToArray().Should().BeEquivalentTo(new byte[] { 0x00 }.ToScriptHash().ToArray()); + context.ScriptHash.ToArray().Should().BeEquivalentTo(new byte[] { 0x00 }.ToScriptHash().ToArray()); } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 7a1c4212c0..6485ba0961 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -1,4 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index 45da27146c..daeaabc43b 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -102,33 +102,6 @@ public void TestNextBigIntegerForRandomNumberGenerator() ran.NextBigInteger(9).Should().NotBeNull(); } - [TestMethod] - public void TestToInt64() - { - byte[] bytes = new byte[] { 0x01, 0x02, 0x03, 0x04 }; - var ret = bytes.ToInt64(0); - ret.GetType().Should().Be(typeof(long)); - ret.Should().Be(67305985); - } - - [TestMethod] - public void TestToUInt16() - { - byte[] bytes = new byte[] { 0x01, 0x02, 0x03, 0x04 }; - var ret = bytes.ToUInt16(0); - ret.GetType().Should().Be(typeof(ushort)); - ret.Should().Be(513); - } - - [TestMethod] - public void TestToUInt64() - { - byte[] bytes = new byte[] { 0x01, 0x02, 0x03, 0x04 }; - var ret = bytes.ToUInt64(0); - ret.GetType().Should().Be(typeof(ulong)); - ret.Should().Be(67305985); - } - [TestMethod] public void TestUnmapForIPAddress() { diff --git a/tests/neo.UnitTests/UT_UInt256.cs b/tests/neo.UnitTests/UT_UInt256.cs index d263effdee..2ffdaf271b 100644 --- a/tests/neo.UnitTests/UT_UInt256.cs +++ b/tests/neo.UnitTests/UT_UInt256.cs @@ -2,7 +2,9 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using System; +using System.IO; namespace Neo.UnitTests.IO { @@ -34,6 +36,19 @@ public void TestCompareTo() Assert.AreEqual(1, result.CompareTo(UInt256.Zero)); } + [TestMethod] + public void TestDeserialize() + { + using MemoryStream stream = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(stream); + using BinaryReader reader = new BinaryReader(stream); + writer.Write(new byte[20]); + stream.Seek(0, SeekOrigin.Begin); + UInt256 uInt256 = new UInt256(); + Action action = () => ((ISerializable)uInt256).Deserialize(reader); + action.Should().Throw(); + } + [TestMethod] public void TestEquals() { @@ -45,6 +60,28 @@ public void TestEquals() Assert.AreEqual(false, result.Equals(null)); } + [TestMethod] + public void TestEquals1() + { + UInt256 temp1 = new UInt256(); + UInt256 temp2 = new UInt256(); + UInt160 temp3 = new UInt160(); + Assert.AreEqual(false, temp1.Equals(null)); + Assert.AreEqual(true, temp1.Equals(temp1)); + Assert.AreEqual(true, temp1.Equals(temp2)); + Assert.AreEqual(false, temp1.Equals(temp3)); + } + + [TestMethod] + public void TestEquals2() + { + UInt256 temp1 = new UInt256(); + object temp2 = null; + object temp3 = new object(); + Assert.AreEqual(false, temp1.Equals(temp2)); + Assert.AreEqual(false, temp1.Equals(temp3)); + } + [TestMethod] public void TestParse() { @@ -69,6 +106,13 @@ public void TestTryParse() Assert.AreEqual(false, UInt256.TryParse("0xKK00000000000000000000000000000000000000000000000000000000000000", out temp)); } + [TestMethod] + public void TestOperatorEqual() + { + Assert.IsFalse(new UInt256() == null); + Assert.IsFalse(null == new UInt256()); + } + [TestMethod] public void TestOperatorLarger() { diff --git a/tests/neo.UnitTests/UT_UIntBase.cs b/tests/neo.UnitTests/UT_UIntBase.cs index 516b3d0504..5f02b92c25 100644 --- a/tests/neo.UnitTests/UT_UIntBase.cs +++ b/tests/neo.UnitTests/UT_UIntBase.cs @@ -1,51 +1,12 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; using System; -using System.IO; namespace Neo.UnitTests.IO { [TestClass] public class UT_UIntBase { - [TestMethod] - public void TestDeserialize() - { - using (MemoryStream stream = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(stream)) - using (BinaryReader reader = new BinaryReader(stream)) - { - writer.Write(new byte[20]); - stream.Seek(0, SeekOrigin.Begin); - MyUIntBase uIntBase = new MyUIntBase(); - Action action = () => ((ISerializable)uIntBase).Deserialize(reader); - action.Should().Throw(); - } - } - - [TestMethod] - public void TestEquals1() - { - MyUIntBase temp1 = new MyUIntBase(); - MyUIntBase temp2 = new MyUIntBase(); - UInt160 temp3 = new UInt160(); - Assert.AreEqual(false, temp1.Equals(null)); - Assert.AreEqual(true, temp1.Equals(temp1)); - Assert.AreEqual(true, temp1.Equals(temp2)); - Assert.AreEqual(false, temp1.Equals(temp3)); - } - - [TestMethod] - public void TestEquals2() - { - MyUIntBase temp1 = new MyUIntBase(); - object temp2 = null; - object temp3 = new object(); - Assert.AreEqual(false, temp1.Equals(temp2)); - Assert.AreEqual(false, temp1.Equals(temp3)); - } - [TestMethod] public void TestParse() { @@ -84,19 +45,5 @@ public void TestTryParse() Assert.AreEqual(UInt256.Zero, uIntBase); Assert.AreEqual(false, UIntBase.TryParse("00000000000000000000000000000000000000000000000000000000000000", out uIntBase)); } - - [TestMethod] - public void TestOperatorEqual() - { - Assert.AreEqual(false, new MyUIntBase() == null); - Assert.AreEqual(false, null == new MyUIntBase()); - } - } - - internal class MyUIntBase : UIntBase - { - public const int Length = 32; - public MyUIntBase() : this(null) { } - public MyUIntBase(byte[] value) : base(Length, value) { } } } diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index f394b63f0b..5f1632824d 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; +using Neo.IO; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index 10dd9ba015..b43ab41a16 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -31,7 +31,7 @@ public void TestSetup() { _wallet = TestUtils.GenerateTestWallet(); byte[] array1 = { 0x01 }; - _hash = new UInt160(Crypto.Default.Hash160(array1)); + _hash = new UInt160(Crypto.Hash160(array1)); _account = new NEP6Account(_wallet, _hash); } @@ -48,7 +48,7 @@ public void TestConstructorWithKeyPair() { var wallet = TestUtils.GenerateTestWallet(); byte[] array1 = { 0x01 }; - var hash = new UInt160(Crypto.Default.Hash160(array1)); + var hash = new UInt160(Crypto.Hash160(array1)); string password = "hello world"; NEP6Account account = new NEP6Account(wallet, hash, _keyPair, password); account.ScriptHash.Should().Be(hash); diff --git a/tests/neo.UnitTests/Wallets/UT_KeyPair.cs b/tests/neo.UnitTests/Wallets/UT_KeyPair.cs index cb2bb20a6e..e14bb83fd7 100644 --- a/tests/neo.UnitTests/Wallets/UT_KeyPair.cs +++ b/tests/neo.UnitTests/Wallets/UT_KeyPair.cs @@ -81,7 +81,7 @@ public void TestExport() byte[] data = { 0x80, 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, 0x01}; KeyPair keyPair = new KeyPair(privateKey); - keyPair.Export().Should().Be(data.Base58CheckEncode()); + keyPair.Export().Should().Be(Base58.Base58CheckEncode(data)); } [TestMethod] diff --git a/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs b/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs index c14b4566b3..df3c353a3f 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; +using Neo.IO; using Neo.Wallets; using System; @@ -13,18 +14,18 @@ public class UT_Wallets_Helper public void TestToScriptHash() { byte[] array = { 0x01 }; - UInt160 scriptHash = new UInt160(Crypto.Default.Hash160(array)); + UInt160 scriptHash = new UInt160(Crypto.Hash160(array)); "AZk5bAanTtD6AvpeesmYgL8CLRYUt5JQsX".ToScriptHash().Should().Be(scriptHash); Action action = () => "3vQB7B6MrGQZaxCuFg4oh".ToScriptHash(); action.Should().Throw(); var address = scriptHash.ToAddress(); - byte[] data = new byte[21]; + Span data = stackalloc byte[21]; // NEO version is 0x17 data[0] = 0x01; - Buffer.BlockCopy(scriptHash.ToArray(), 0, data, 1, 20); - address = data.Base58CheckEncode(); + scriptHash.ToArray().CopyTo(data[1..]); + address = Base58.Base58CheckEncode(data); action = () => address.ToScriptHash(); action.Should().Throw(); } From 5009915e54fbb83469c30dd8b2b3d641a206fa47 Mon Sep 17 00:00:00 2001 From: belane Date: Sun, 1 Dec 2019 10:52:29 +0100 Subject: [PATCH 155/305] Change address version (#1299) --- src/neo/ProtocolSettings.cs | 2 +- src/neo/Wallets/NEP6/NEP6Wallet.cs | 6 +++-- .../Network/P2P/Payloads/UT_Block.cs | 2 +- tests/neo.UnitTests/TestUtils.cs | 4 ++-- tests/neo.UnitTests/UT_ProtocolSettings.cs | 10 +++++++++ .../Wallets/NEP6/UT_NEP6Account.cs | 4 ++-- .../Wallets/NEP6/UT_NEP6Wallet.cs | 22 +++++++++---------- .../neo.UnitTests/Wallets/UT_WalletAccount.cs | 2 +- .../Wallets/UT_Wallets_Helper.cs | 2 +- 9 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index f1483c5341..8f0a48101e 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -44,7 +44,7 @@ public static ProtocolSettings Default private ProtocolSettings(IConfigurationSection section) { this.Magic = section.GetValue("Magic", 0x4F454Eu); - this.AddressVersion = section.GetValue("AddressVersion", (byte)0x17); + this.AddressVersion = section.GetValue("AddressVersion", (byte)0x35); IConfigurationSection section_sv = section.GetSection("StandbyValidators"); if (section_sv.Exists()) this.StandbyValidators = section_sv.GetChildren().Select(p => p.Get()).ToArray(); diff --git a/src/neo/Wallets/NEP6/NEP6Wallet.cs b/src/neo/Wallets/NEP6/NEP6Wallet.cs index f76640e886..3668287b3e 100644 --- a/src/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/neo/Wallets/NEP6/NEP6Wallet.cs @@ -38,7 +38,7 @@ public NEP6Wallet(string path, string name = null) else { this.name = name; - this.version = Version.Parse("1.0"); + this.version = Version.Parse("3.0"); this.Scrypt = ScryptParameters.Default; this.accounts = new Dictionary(); this.extra = JObject.Null; @@ -53,8 +53,10 @@ public NEP6Wallet(JObject wallet) private void LoadFromJson(JObject wallet, out ScryptParameters scrypt, out Dictionary accounts, out JObject extra) { - this.name = wallet["name"]?.AsString(); this.version = Version.Parse(wallet["version"].AsString()); + if (this.version.Major < 3) throw new FormatException(); + + this.name = wallet["name"]?.AsString(); scrypt = ScryptParameters.FromJson(wallet["scrypt"]); accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson(p, this)).ToDictionary(p => p.ScriptHash); extra = wallet["extra"]; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index a26741c732..fd8f821c6a 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -206,7 +206,7 @@ public void ToJson() jObj["merkleroot"].AsString().Should().Be("0xd841af3d6bd7adb4bca24306725f9aec363edb10de3cafc5f8cca948d7b0290f"); jObj["time"].AsNumber().Should().Be(328665601001); jObj["index"].AsNumber().Should().Be(0); - jObj["nextconsensus"].AsString().Should().Be("AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM"); + jObj["nextconsensus"].AsString().Should().Be("NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf"); JObject scObj = ((JArray)jObj["witnesses"])[0]; scObj["invocation"].AsString().Should().Be(""); diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index 554b492d4b..83f3d6515e 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -30,11 +30,11 @@ public static NEP6Wallet GenerateTestWallet() { JObject wallet = new JObject(); wallet["name"] = "noname"; - wallet["version"] = new System.Version().ToString(); + wallet["version"] = new System.Version("3.0").ToString(); wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = null; - wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"0.0\",\"scrypt\":{\"n\":0,\"r\":0,\"p\":0},\"accounts\":[],\"extra\":null}"); + wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"3.0\",\"scrypt\":{\"n\":0,\"r\":0,\"p\":0},\"accounts\":[],\"extra\":null}"); return new NEP6Wallet(wallet); } diff --git a/tests/neo.UnitTests/UT_ProtocolSettings.cs b/tests/neo.UnitTests/UT_ProtocolSettings.cs index a989be7f6f..e1a9e91cbb 100644 --- a/tests/neo.UnitTests/UT_ProtocolSettings.cs +++ b/tests/neo.UnitTests/UT_ProtocolSettings.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Microsoft.Extensions.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Wallets; using System.Collections.Generic; using System.Reflection; @@ -30,6 +31,15 @@ public void Cleanup() ResetProtocolSettings(); } + [TestMethod] + public void CheckFirstLetterOfAddresses() + { + UInt160 min = UInt160.Parse("0x0000000000000000000000000000000000000000"); + min.ToAddress()[0].Should().Be('N'); + UInt160 max = UInt160.Parse("0xffffffffffffffffffffffffffffffffffffffff"); + max.ToAddress()[0].Should().Be('N'); + } + [TestMethod] public void Default_Magic_should_be_mainnet_Magic_value() { diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index b43ab41a16..f1c719c853 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -60,7 +60,7 @@ public void TestConstructorWithKeyPair() public void TestFromJson() { JObject json = new JObject(); - json["address"] = "ARxgjcH2K1yeW5f5ryuRQNaBzSa9TZzmVS"; + json["address"] = "NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf"; json["key"] = null; json["label"] = null; json["isDefault"] = true; @@ -68,7 +68,7 @@ public void TestFromJson() json["contract"] = null; json["extra"] = null; NEP6Account account = NEP6Account.FromJson(json, _wallet); - account.ScriptHash.Should().Be("ARxgjcH2K1yeW5f5ryuRQNaBzSa9TZzmVS".ToScriptHash()); + account.ScriptHash.Should().Be("NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash()); account.Label.Should().BeNull(); account.IsDefault.Should().BeTrue(); account.Lock.Should().BeFalse(); diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index de902a115e..dcf436e993 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -52,7 +52,7 @@ private string CreateWalletFile() rootPath = GetRandomPath(); if (!Directory.Exists(rootPath)) Directory.CreateDirectory(rootPath); string path = Path.Combine(rootPath, "wallet.json"); - File.WriteAllText(path, "{\"name\":\"name\",\"version\":\"0.0\",\"scrypt\":{\"n\":0,\"r\":0,\"p\":0},\"accounts\":[],\"extra\":{}}"); + File.WriteAllText(path, "{\"name\":\"name\",\"version\":\"3.0\",\"scrypt\":{\"n\":0,\"r\":0,\"p\":0},\"accounts\":[],\"extra\":{}}"); return path; } @@ -76,11 +76,11 @@ public void TestConstructorWithPathAndName() NEP6Wallet wallet = new NEP6Wallet(wPath); Assert.AreEqual("name", wallet.Name); Assert.AreEqual(new ScryptParameters(0, 0, 0).ToJson().ToString(), wallet.Scrypt.ToJson().ToString()); - Assert.AreEqual(new Version().ToString(), wallet.Version.ToString()); + Assert.AreEqual(new Version("3.0").ToString(), wallet.Version.ToString()); wallet = new NEP6Wallet("", "test"); Assert.AreEqual("test", wallet.Name); Assert.AreEqual(ScryptParameters.Default.ToJson().ToString(), wallet.Scrypt.ToJson().ToString()); - Assert.AreEqual(Version.Parse("1.0"), wallet.Version); + Assert.AreEqual(Version.Parse("3.0"), wallet.Version); } [TestMethod] @@ -88,14 +88,14 @@ public void TestConstructorWithJObject() { JObject wallet = new JObject(); wallet["name"] = "test"; - wallet["version"] = Version.Parse("1.0").ToString(); + wallet["version"] = Version.Parse("3.0").ToString(); wallet["scrypt"] = ScryptParameters.Default.ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); - wallet.ToString().Should().Be("{\"name\":\"test\",\"version\":\"1.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":{}}"); + wallet.ToString().Should().Be("{\"name\":\"test\",\"version\":\"3.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":{}}"); NEP6Wallet w = new NEP6Wallet(wallet); Assert.AreEqual("test", w.Name); - Assert.AreEqual(Version.Parse("1.0").ToString(), w.Version.ToString()); + Assert.AreEqual(Version.Parse("3.0").ToString(), w.Version.ToString()); } [TestMethod] @@ -107,7 +107,7 @@ public void TestGetName() [TestMethod] public void TestGetVersion() { - Assert.AreEqual(new System.Version().ToString(), uut.Version.ToString()); + Assert.AreEqual(new System.Version("3.0").ToString(), uut.Version.ToString()); } [TestMethod] @@ -295,7 +295,7 @@ public void TestImportNep2() Assert.AreEqual(false, result); JObject wallet = new JObject(); wallet["name"] = "name"; - wallet["version"] = new Version().ToString(); + wallet["version"] = new Version("3.0").ToString(); wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); @@ -339,7 +339,7 @@ public void TestSave() { JObject wallet = new JObject(); wallet["name"] = "name"; - wallet["version"] = new System.Version().ToString(); + wallet["version"] = new System.Version("3.0").ToString(); wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); @@ -381,7 +381,7 @@ public void TestVerifyPassword() Assert.AreEqual(false, uut.Contains(testScriptHash)); JObject wallet = new JObject(); wallet["name"] = "name"; - wallet["version"] = new Version().ToString(); + wallet["version"] = new Version("3.0").ToString(); wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); @@ -396,7 +396,7 @@ public void TestVerifyPassword() public void Test_NEP6Wallet_Json() { uut.Name.Should().Be("noname"); - uut.Version.Should().Be(new Version()); + uut.Version.Should().Be(new Version("3.0")); uut.Scrypt.Should().NotBeNull(); uut.Scrypt.N.Should().Be(new ScryptParameters(0, 0, 0).N); } diff --git a/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs b/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs index 413caf6880..42ebf6bb63 100644 --- a/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs +++ b/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs @@ -33,7 +33,7 @@ public class UT_WalletAccount public void TestGetAddress() { MyWalletAccount walletAccount = new MyWalletAccount(UInt160.Zero); - walletAccount.Address.Should().Be("AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM"); + walletAccount.Address.Should().Be("NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf"); } [TestMethod] diff --git a/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs b/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs index df3c353a3f..9f0167c55a 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs @@ -15,7 +15,7 @@ public void TestToScriptHash() { byte[] array = { 0x01 }; UInt160 scriptHash = new UInt160(Crypto.Hash160(array)); - "AZk5bAanTtD6AvpeesmYgL8CLRYUt5JQsX".ToScriptHash().Should().Be(scriptHash); + "NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash().Should().Be(scriptHash); Action action = () => "3vQB7B6MrGQZaxCuFg4oh".ToScriptHash(); action.Should().Throw(); From 87d15db958c908ca4d9fa0b01848342b1f23c978 Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Sun, 1 Dec 2019 19:35:15 +0800 Subject: [PATCH 156/305] Simple Way to Parallel Verification Transaction (#1298) --- src/neo/Consensus/ConsensusService.cs | 2 +- src/neo/Ledger/Blockchain.cs | 61 ++++++++++++++----- src/neo/Ledger/MemoryPool.cs | 2 +- src/neo/Ledger/RelayResultReason.cs | 2 + src/neo/Network/P2P/Payloads/Transaction.cs | 58 ++++++++++-------- src/neo/Network/RPC/RpcServer.cs | 27 +++----- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 8 +-- .../Ledger/UT_SendersFeeMonitor.cs | 4 +- .../Network/P2P/Payloads/UT_Transaction.cs | 2 +- 9 files changed, 97 insertions(+), 69 deletions(-) diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 1afbd20322..57151481d7 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -61,7 +61,7 @@ internal ConsensusService(IActorRef localNode, IActorRef taskManager, ConsensusC private bool AddTransaction(Transaction tx, bool verify) { - if (verify && !tx.Verify(context.Snapshot, context.SendersFeeMonitor.GetSenderFee(tx.Sender))) + if (verify && tx.Verify(context.Snapshot, context.SendersFeeMonitor.GetSenderFee(tx.Sender)) != RelayResultReason.Succeed) { Log($"Invalid transaction: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning); RequestChangeView(ChangeViewReason.TxInvalid); diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 2d894ab9ea..d6c2840526 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -15,6 +15,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Threading.Tasks; namespace Neo.Ledger { @@ -26,6 +27,7 @@ public class Import { public IEnumerable Blocks; } public class ImportCompleted { } public class FillMemoryPool { public IEnumerable Transactions; } public class FillCompleted { } + private class ParallelVerified { public Transaction Transaction; public bool ShouldRelay; public RelayResultReason VerifyResult; } public static readonly uint MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock; public const uint DecrementInterval = 2000000; @@ -276,7 +278,7 @@ private void OnFillMemoryPool(IEnumerable transactions) // First remove the tx if it is unverified in the pool. MemPool.TryRemoveUnVerified(tx.Hash, out _); // Verify the the transaction - if (!tx.Verify(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(tx.Sender))) + if (tx.Verify(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(tx.Sender)) != RelayResultReason.Succeed) continue; // Add to the memory pool MemPool.TryAdd(tx.Hash, tx); @@ -395,22 +397,46 @@ private void OnNewHeaders(Header[] headers) system.TaskManager.Tell(new TaskManager.HeaderTaskCompleted(), Sender); } - private RelayResultReason OnNewTransaction(Transaction transaction, bool relay) + private void OnNewTransaction(Transaction transaction, bool relay) { + RelayResultReason reason; if (ContainsTransaction(transaction.Hash)) - return RelayResultReason.AlreadyExists; - if (!MemPool.CanTransactionFitInPool(transaction)) - return RelayResultReason.OutOfMemory; - if (!transaction.Verify(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(transaction.Sender))) - return RelayResultReason.Invalid; - if (!NativeContract.Policy.CheckPolicy(transaction, currentSnapshot)) - return RelayResultReason.PolicyFail; - - if (!MemPool.TryAdd(transaction.Hash, transaction)) - return RelayResultReason.OutOfMemory; - if (relay) - system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = transaction }); - return RelayResultReason.Succeed; + reason = RelayResultReason.AlreadyExists; + else if (!MemPool.CanTransactionFitInPool(transaction)) + reason = RelayResultReason.OutOfMemory; + else + reason = transaction.VerifyForEachBlock(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(transaction.Sender)); + if (reason == RelayResultReason.Succeed) + { + Task.Run(() => + { + return new ParallelVerified + { + Transaction = transaction, + ShouldRelay = relay, + VerifyResult = transaction.VerifyParallelParts(currentSnapshot) + }; + }).PipeTo(Self, Sender); + } + else + { + Sender.Tell(reason); + } + } + + private void OnParallelVerified(ParallelVerified parallelVerified) + { + RelayResultReason reason = parallelVerified.VerifyResult; + if (reason == RelayResultReason.Succeed) + { + if (!MemPool.CanTransactionFitInPool(parallelVerified.Transaction)) + reason = RelayResultReason.OutOfMemory; + else if (!MemPool.TryAdd(parallelVerified.Transaction.Hash, parallelVerified.Transaction)) + reason = RelayResultReason.OutOfMemory; + else if (parallelVerified.ShouldRelay) + system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = parallelVerified.Transaction }); + } + Sender.Tell(reason); } private void OnPersistCompleted(Block block) @@ -443,7 +469,10 @@ protected override void OnReceive(object message) break; } case Transaction transaction: - Sender.Tell(OnNewTransaction(transaction, true)); + OnNewTransaction(transaction, true); + break; + case ParallelVerified parallelVerified: + OnParallelVerified(parallelVerified); break; case ConsensusPayload payload: Sender.Tell(OnNewConsensus(payload)); diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index 4b9f1b8139..f0409b9e54 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -416,7 +416,7 @@ internal void InvalidateAllTransactions() // Since unverifiedSortedTxPool is ordered in an ascending manner, we take from the end. foreach (PoolItem item in unverifiedSortedTxPool.Reverse().Take(count)) { - if (item.Tx.Reverify(snapshot, SendersFeeMonitor.GetSenderFee(item.Tx.Sender))) + if (item.Tx.VerifyForEachBlock(snapshot, SendersFeeMonitor.GetSenderFee(item.Tx.Sender)) == RelayResultReason.Succeed) { reverifiedItems.Add(item); SendersFeeMonitor.AddSenderFee(item.Tx); diff --git a/src/neo/Ledger/RelayResultReason.cs b/src/neo/Ledger/RelayResultReason.cs index 7bf92afac6..38ddfff73e 100644 --- a/src/neo/Ledger/RelayResultReason.cs +++ b/src/neo/Ledger/RelayResultReason.cs @@ -7,6 +7,8 @@ public enum RelayResultReason : byte OutOfMemory, UnableToVerify, Invalid, + Expired, + InsufficientFunds, PolicyFail, Unknown } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index a7dd9e2ebf..f213722660 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -1,6 +1,7 @@ using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; +using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -205,25 +206,6 @@ public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) return hashes.OrderBy(p => p).ToArray(); } - public virtual bool Reverify(StoreView snapshot, BigInteger totalSenderFeeFromPool) - { - if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) - return false; - if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(GetScriptHashesForVerifying(snapshot)).Count() > 0) - return false; - BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); - BigInteger fee = SystemFee + NetworkFee + totalSenderFeeFromPool; - if (balance < fee) return false; - UInt160[] hashes = GetScriptHashesForVerifying(snapshot); - if (hashes.Length != Witnesses.Length) return false; - for (int i = 0; i < hashes.Length; i++) - { - if (Witnesses[i].VerificationScript.Length > 0) continue; - if (snapshot.Contracts.TryGet(hashes[i]) is null) return false; - } - return true; - } - void ISerializable.Serialize(BinaryWriter writer) { ((IVerifiable)this).SerializeUnsigned(writer); @@ -279,17 +261,43 @@ public static Transaction FromJson(JObject json) bool IInventory.Verify(StoreView snapshot) { - return Verify(snapshot, BigInteger.Zero); + return Verify(snapshot, BigInteger.Zero) == RelayResultReason.Succeed; + } + + public virtual RelayResultReason Verify(StoreView snapshot, BigInteger totalSenderFeeFromPool) + { + RelayResultReason result = VerifyForEachBlock(snapshot, totalSenderFeeFromPool); + if (result != RelayResultReason.Succeed) return result; + return VerifyParallelParts(snapshot); + } + + public virtual RelayResultReason VerifyForEachBlock(StoreView snapshot, BigInteger totalSenderFeeFromPool) + { + if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) + return RelayResultReason.Expired; + UInt160[] hashes = GetScriptHashesForVerifying(snapshot); + if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(hashes).Any()) + return RelayResultReason.PolicyFail; + BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); + BigInteger fee = SystemFee + NetworkFee + totalSenderFeeFromPool; + if (balance < fee) return RelayResultReason.InsufficientFunds; + if (hashes.Length != Witnesses.Length) return RelayResultReason.Invalid; + for (int i = 0; i < hashes.Length; i++) + { + if (Witnesses[i].VerificationScript.Length > 0) continue; + if (snapshot.Contracts.TryGet(hashes[i]) is null) return RelayResultReason.Invalid; + } + return RelayResultReason.Succeed; } - public virtual bool Verify(StoreView snapshot, BigInteger totalSenderFeeFromPool) + public RelayResultReason VerifyParallelParts(StoreView snapshot) { - if (!Reverify(snapshot, totalSenderFeeFromPool)) return false; int size = Size; - if (size > MaxTransactionSize) return false; + if (size > MaxTransactionSize) return RelayResultReason.Invalid; long net_fee = NetworkFee - size * NativeContract.Policy.GetFeePerByte(snapshot); - if (net_fee < 0) return false; - return this.VerifyWitnesses(snapshot, net_fee); + if (net_fee < 0) return RelayResultReason.InsufficientFunds; + if (!this.VerifyWitnesses(snapshot, net_fee)) return RelayResultReason.Invalid; + return RelayResultReason.Succeed; } public StackItem ToStackItem() diff --git a/src/neo/Network/RPC/RpcServer.cs b/src/neo/Network/RPC/RpcServer.cs index c7475e5f0f..5649cce92f 100644 --- a/src/neo/Network/RPC/RpcServer.cs +++ b/src/neo/Network/RPC/RpcServer.cs @@ -129,26 +129,15 @@ private JObject GetInvokeResult(byte[] script, IVerifiable checkWitnessHashes = private static JObject GetRelayResult(RelayResultReason reason, UInt256 hash) { - switch (reason) + if (reason == RelayResultReason.Succeed) { - case RelayResultReason.Succeed: - { - var ret = new JObject(); - ret["hash"] = hash.ToString(); - return ret; - } - case RelayResultReason.AlreadyExists: - throw new RpcException(-501, "Block or transaction already exists and cannot be sent repeatedly."); - case RelayResultReason.OutOfMemory: - throw new RpcException(-502, "The memory pool is full and no more transactions can be sent."); - case RelayResultReason.UnableToVerify: - throw new RpcException(-503, "The block cannot be validated."); - case RelayResultReason.Invalid: - throw new RpcException(-504, "Block or transaction validation failed."); - case RelayResultReason.PolicyFail: - throw new RpcException(-505, "One of the Policy filters failed."); - default: - throw new RpcException(-500, "Unknown error."); + var ret = new JObject(); + ret["hash"] = hash.ToString(); + return ret; + } + else + { + throw new RpcException(-500, reason.ToString()); } } diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index c68504f4c6..510f7340ed 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -74,8 +74,8 @@ private Transaction CreateTransactionWithFee(long fee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); - mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(true); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); + mock.Setup(p => p.VerifyForEachBlock(It.IsAny(), It.IsAny())).Returns(RelayResultReason.Succeed); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(RelayResultReason.Succeed); mock.Object.Script = randomBytes; mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = fee; @@ -99,8 +99,8 @@ private Transaction CreateTransactionWithFeeAndBalanceVerify(long fee) random.NextBytes(randomBytes); Mock mock = new Mock(); UInt160 sender = UInt160.Zero; - mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(((StoreView snapshot, BigInteger amount) => NativeContract.GAS.BalanceOf(snapshot, sender) >= amount + fee)); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); + mock.Setup(p => p.VerifyForEachBlock(It.IsAny(), It.IsAny())).Returns((StoreView snapshot, BigInteger amount) => NativeContract.GAS.BalanceOf(snapshot, sender) >= amount + fee ? RelayResultReason.Succeed : RelayResultReason.InsufficientFunds); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(RelayResultReason.Succeed); mock.Object.Script = randomBytes; mock.Object.Sender = sender; mock.Object.NetworkFee = fee; diff --git a/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs b/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs index ac5cf57b7c..6268fa0fde 100644 --- a/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs +++ b/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs @@ -18,8 +18,8 @@ private Transaction CreateTransactionWithFee(long networkFee, long systemFee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); - mock.Setup(p => p.Reverify(It.IsAny(), It.IsAny())).Returns(true); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(true); + mock.Setup(p => p.VerifyForEachBlock(It.IsAny(), It.IsAny())).Returns(RelayResultReason.Succeed); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(RelayResultReason.Succeed); mock.Object.Script = randomBytes; mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = networkFee; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index af12fe148e..3430d84390 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -806,7 +806,7 @@ public void Transaction_Reverify_Hashes_Length_Unequal_To_Witnesses_Length() }; UInt160[] hashes = txSimple.GetScriptHashesForVerifying(snapshot); Assert.AreEqual(2, hashes.Length); - Assert.IsFalse(txSimple.Reverify(snapshot, BigInteger.Zero)); + Assert.AreNotEqual(RelayResultReason.Succeed, txSimple.VerifyForEachBlock(snapshot, BigInteger.Zero)); } [TestMethod] From c97b230860c25e1ddf8010c7e677f8f1a896c084 Mon Sep 17 00:00:00 2001 From: Yongjie Ma <20391402+yongjiema@users.noreply.github.com> Date: Sun, 1 Dec 2019 19:42:44 +0800 Subject: [PATCH 157/305] Fix/Improve syncing (3x): Adding ping mechanism to TaskManager for replacing StartHeight and PendingKnownHashes strategy (#899) --- src/neo/Network/P2P/ProtocolHandler.cs | 60 +++++++++++++++++++++++++- src/neo/Network/P2P/RemoteNode.cs | 3 ++ src/neo/Network/P2P/TaskManager.cs | 31 ++++++++++++- src/neo/Network/P2P/TaskSession.cs | 2 + 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/src/neo/Network/P2P/ProtocolHandler.cs b/src/neo/Network/P2P/ProtocolHandler.cs index bd48a6f57f..12aaae48d9 100644 --- a/src/neo/Network/P2P/ProtocolHandler.cs +++ b/src/neo/Network/P2P/ProtocolHandler.cs @@ -11,6 +11,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Net; @@ -19,24 +20,52 @@ namespace Neo.Network.P2P internal class ProtocolHandler : UntypedActor { public class SetFilter { public BloomFilter Filter; } + internal class Timer { } + + private class PendingKnownHashesCollection : KeyedCollection + { + protected override UInt256 GetKeyForItem((UInt256, DateTime) item) + { + return item.Item1; + } + } private readonly NeoSystem system; + private readonly PendingKnownHashesCollection pendingKnownHashes; private readonly FIFOSet knownHashes; private readonly FIFOSet sentHashes; private VersionPayload version; private bool verack = false; 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); + public ProtocolHandler(NeoSystem system) { this.system = system; + this.pendingKnownHashes = new PendingKnownHashesCollection(); this.knownHashes = new FIFOSet(Blockchain.Singleton.MemPool.Capacity * 2); this.sentHashes = new FIFOSet(Blockchain.Singleton.MemPool.Capacity * 2); } protected override void OnReceive(object message) { - if (!(message is Message msg)) return; + switch (message) + { + case Message msg: + OnMessage(msg); + break; + case Timer _: + OnTimer(); + break; + } + } + + private void OnMessage(Message msg) + { foreach (IP2PPlugin plugin in Plugin.P2PPlugins) if (!plugin.OnP2PMessage(msg)) return; @@ -264,11 +293,13 @@ private void OnInventoryReceived(IInventory inventory) { system.TaskManager.Tell(new TaskManager.TaskCompleted { Hash = inventory.Hash }, Context.Parent); system.LocalNode.Tell(new LocalNode.Relay { Inventory = inventory }); + pendingKnownHashes.Remove(inventory.Hash); + knownHashes.Add(inventory.Hash); } private void OnInvMessageReceived(InvPayload payload) { - UInt256[] hashes = payload.Hashes.Where(p => knownHashes.Add(p) && !sentHashes.Contains(p)).ToArray(); + UInt256[] hashes = payload.Hashes.Where(p => !pendingKnownHashes.Contains(p) && !knownHashes.Contains(p) && !sentHashes.Contains(p)).ToArray(); if (hashes.Length == 0) return; switch (payload.Type) { @@ -282,6 +313,8 @@ private void OnInvMessageReceived(InvPayload payload) break; } if (hashes.Length == 0) return; + foreach (UInt256 hash in hashes) + pendingKnownHashes.Add((hash, DateTime.UtcNow)); system.TaskManager.Tell(new TaskManager.NewTasks { Payload = InvPayload.Create(payload.Type, hashes) }, Context.Parent); } @@ -314,6 +347,28 @@ private void OnVersionMessageReceived(VersionPayload payload) Context.Parent.Tell(payload); } + private void OnTimer() + { + RefreshPendingKnownHashes(); + } + + protected override void PostStop() + { + timer.CancelIfNotNull(); + base.PostStop(); + } + + private void RefreshPendingKnownHashes() + { + while (pendingKnownHashes.Count > 0) + { + var (_, time) = pendingKnownHashes[0]; + if (DateTime.UtcNow - time <= PendingTimeout) + break; + pendingKnownHashes.RemoveAt(0); + } + } + public static Props Props(NeoSystem system) { return Akka.Actor.Props.Create(() => new ProtocolHandler(system)).WithMailbox("protocol-handler-mailbox"); @@ -347,6 +402,7 @@ internal protected override bool IsHighPriority(object message) internal protected override bool ShallDrop(object message, IEnumerable queue) { + if (message is ProtocolHandler.Timer) return false; if (!(message is Message msg)) return true; switch (msg.Command) { diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index 2cd37a8056..5d4e712729 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -149,7 +149,10 @@ protected override void OnReceive(object message) private void OnPingPayload(PingPayload payload) { if (payload.LastBlockIndex > LastBlockIndex) + { LastBlockIndex = payload.LastBlockIndex; + system.TaskManager.Tell(new TaskManager.Update { LastBlockIndex = LastBlockIndex }); + } } private void OnRelay(IInventory inventory) diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 8ceb0d404b..4fb1a2fd09 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 System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -14,6 +15,7 @@ namespace Neo.Network.P2P internal class TaskManager : UntypedActor { public class Register { public VersionPayload Version; } + public class Update { public uint LastBlockIndex; } public class NewTasks { public InvPayload Payload; } public class TaskCompleted { public UInt256 Hash; } public class HeaderTaskCompleted { } @@ -25,6 +27,7 @@ private class Timer { } private readonly NeoSystem system; private const int MaxConncurrentTasks = 3; + private const int PingCoolingOffPeriod = 60; // in secconds. private readonly FIFOSet knownHashes; private readonly Dictionary globalTasks = new Dictionary(); private readonly Dictionary sessions = new Dictionary(); @@ -86,6 +89,9 @@ protected override void OnReceive(object message) case Register register: OnRegister(register.Version); break; + case Update update: + OnUpdate(update.LastBlockIndex); + break; case NewTasks tasks: OnNewTasks(tasks.Payload); break; @@ -115,6 +121,13 @@ private void OnRegister(VersionPayload version) RequestTasks(session); } + private void OnUpdate(uint lastBlockIndex) + { + if (!sessions.TryGetValue(Sender, out TaskSession session)) + return; + session.LastBlockIndex = lastBlockIndex; + } + private void OnRestartTasks(InvPayload payload) { knownHashes.ExceptWith(payload.Hashes); @@ -221,13 +234,13 @@ private void RequestTasks(TaskSession session) return; } } - if ((!HasHeaderTask || globalTasks[HeaderTaskHash] < MaxConncurrentTasks) && Blockchain.Singleton.HeaderHeight < session.StartHeight) + if ((!HasHeaderTask || globalTasks[HeaderTaskHash] < MaxConncurrentTasks) && Blockchain.Singleton.HeaderHeight < session.LastBlockIndex) { session.Tasks[HeaderTaskHash] = DateTime.UtcNow; IncrementGlobalTask(HeaderTaskHash); session.RemoteNode.Tell(Message.Create(MessageCommand.GetHeaders, GetBlocksPayload.Create(Blockchain.Singleton.CurrentHeaderHash))); } - else if (Blockchain.Singleton.Height < session.StartHeight) + else if (Blockchain.Singleton.Height < session.LastBlockIndex) { UInt256 hash = Blockchain.Singleton.CurrentBlockHash; for (uint i = Blockchain.Singleton.Height + 1; i <= Blockchain.Singleton.HeaderHeight; i++) @@ -241,6 +254,11 @@ private void RequestTasks(TaskSession session) } session.RemoteNode.Tell(Message.Create(MessageCommand.GetBlocks, GetBlocksPayload.Create(hash))); } + else if (Blockchain.Singleton.HeaderHeight >= session.LastBlockIndex + && TimeProvider.Current.UtcNow.ToTimestamp() - PingCoolingOffPeriod >= Blockchain.Singleton.GetBlock(Blockchain.Singleton.CurrentHeaderHash)?.Timestamp) + { + session.RemoteNode.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(Blockchain.Singleton.Height))); + } } } @@ -256,6 +274,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: @@ -266,5 +285,13 @@ internal protected override bool IsHighPriority(object message) return false; } } + + internal protected override bool ShallDrop(object message, IEnumerable queue) + { + if (!(message is TaskManager.NewTasks tasks)) return false; + // Remove duplicate tasks + if (queue.OfType().Any(x => x.Payload.Type == tasks.Payload.Type && x.Payload.Hashes.SequenceEqual(tasks.Payload.Hashes))) return true; + return false; + } } } diff --git a/src/neo/Network/P2P/TaskSession.cs b/src/neo/Network/P2P/TaskSession.cs index a1cf8a0ffc..eb0ab631bd 100644 --- a/src/neo/Network/P2P/TaskSession.cs +++ b/src/neo/Network/P2P/TaskSession.cs @@ -16,6 +16,7 @@ internal class TaskSession public bool HasTask => Tasks.Count > 0; public uint StartHeight { get; } + public uint LastBlockIndex { get; set; } public TaskSession(IActorRef node, VersionPayload version) { @@ -24,6 +25,7 @@ public TaskSession(IActorRef node, VersionPayload version) this.StartHeight = version.Capabilities .OfType() .FirstOrDefault()?.StartHeight ?? 0; + this.LastBlockIndex = this.StartHeight; } } } From a945e57fff4a1c0f14b6a57691361d0f8c1fd72c Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 1 Dec 2019 21:03:06 +0800 Subject: [PATCH 158/305] Remove leveldb --- src/neo/IO/Data/LevelDB/DB.cs | 128 ------ src/neo/IO/Data/LevelDB/Helper.cs | 65 --- src/neo/IO/Data/LevelDB/Iterator.cs | 78 ---- src/neo/IO/Data/LevelDB/LevelDBException.cs | 12 - src/neo/IO/Data/LevelDB/Native.cs | 249 ----------- src/neo/IO/Data/LevelDB/Options.cs | 87 ---- src/neo/IO/Data/LevelDB/ReadOptions.cs | 39 -- src/neo/IO/Data/LevelDB/Slice.cs | 243 ----------- src/neo/IO/Data/LevelDB/SliceBuilder.cs | 75 ---- src/neo/IO/Data/LevelDB/Snapshot.cs | 24 -- src/neo/IO/Data/LevelDB/WriteBatch.cs | 29 -- src/neo/IO/Data/LevelDB/WriteOptions.cs | 23 - src/neo/NeoSystem.cs | 3 +- .../{Memory => }/ByteArrayEqualityComparer.cs | 2 +- src/neo/Persistence/{Memory => }/Helper.cs | 2 +- src/neo/Persistence/LevelDB/Snapshot.cs | 54 --- src/neo/Persistence/LevelDB/Store.cs | 68 --- .../{Memory/Snapshot.cs => MemorySnapshot.cs} | 6 +- .../{Memory/Store.cs => MemoryStore.cs} | 8 +- .../neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs | 408 ------------------ 20 files changed, 10 insertions(+), 1593 deletions(-) delete mode 100644 src/neo/IO/Data/LevelDB/DB.cs delete mode 100644 src/neo/IO/Data/LevelDB/Helper.cs delete mode 100644 src/neo/IO/Data/LevelDB/Iterator.cs delete mode 100644 src/neo/IO/Data/LevelDB/LevelDBException.cs delete mode 100644 src/neo/IO/Data/LevelDB/Native.cs delete mode 100644 src/neo/IO/Data/LevelDB/Options.cs delete mode 100644 src/neo/IO/Data/LevelDB/ReadOptions.cs delete mode 100644 src/neo/IO/Data/LevelDB/Slice.cs delete mode 100644 src/neo/IO/Data/LevelDB/SliceBuilder.cs delete mode 100644 src/neo/IO/Data/LevelDB/Snapshot.cs delete mode 100644 src/neo/IO/Data/LevelDB/WriteBatch.cs delete mode 100644 src/neo/IO/Data/LevelDB/WriteOptions.cs rename src/neo/Persistence/{Memory => }/ByteArrayEqualityComparer.cs (97%) rename src/neo/Persistence/{Memory => }/Helper.cs (85%) delete mode 100644 src/neo/Persistence/LevelDB/Snapshot.cs delete mode 100644 src/neo/Persistence/LevelDB/Store.cs rename src/neo/Persistence/{Memory/Snapshot.cs => MemorySnapshot.cs} (93%) rename src/neo/Persistence/{Memory/Store.cs => MemoryStore.cs} (91%) delete mode 100644 tests/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs diff --git a/src/neo/IO/Data/LevelDB/DB.cs b/src/neo/IO/Data/LevelDB/DB.cs deleted file mode 100644 index 04e8f50e8f..0000000000 --- a/src/neo/IO/Data/LevelDB/DB.cs +++ /dev/null @@ -1,128 +0,0 @@ -using System; - -namespace Neo.IO.Data.LevelDB -{ - public class DB : IDisposable - { - private IntPtr handle; - - /// - /// Return true if haven't got valid handle - /// - public bool IsDisposed => handle == IntPtr.Zero; - - private DB(IntPtr handle) - { - this.handle = handle; - } - - public void Dispose() - { - if (handle != IntPtr.Zero) - { - Native.leveldb_close(handle); - handle = IntPtr.Zero; - } - } - - public void Delete(WriteOptions options, Slice key) - { - IntPtr error; - Native.leveldb_delete(handle, options.handle, key.buffer, (UIntPtr)key.buffer.Length, out error); - NativeHelper.CheckError(error); - } - - public Slice Get(ReadOptions options, Slice key) - { - UIntPtr length; - IntPtr error; - IntPtr value = Native.leveldb_get(handle, options.handle, key.buffer, (UIntPtr)key.buffer.Length, out length, out error); - try - { - NativeHelper.CheckError(error); - if (value == IntPtr.Zero) - throw new LevelDBException("not found"); - return new Slice(value, length); - } - finally - { - if (value != IntPtr.Zero) Native.leveldb_free(value); - } - } - - public Snapshot GetSnapshot() - { - return new Snapshot(handle); - } - - public Iterator NewIterator(ReadOptions options) - { - return new Iterator(Native.leveldb_create_iterator(handle, options.handle)); - } - - public static DB Open(string name) - { - return Open(name, Options.Default); - } - - public static DB Open(string name, Options options) - { - IntPtr error; - IntPtr handle = Native.leveldb_open(options.handle, name, out error); - NativeHelper.CheckError(error); - return new DB(handle); - } - - public void Put(WriteOptions options, Slice key, Slice value) - { - IntPtr error; - Native.leveldb_put(handle, options.handle, key.buffer, (UIntPtr)key.buffer.Length, value.buffer, (UIntPtr)value.buffer.Length, out error); - NativeHelper.CheckError(error); - } - - public bool TryGet(ReadOptions options, Slice key, out Slice value) - { - UIntPtr length; - IntPtr error; - IntPtr v = Native.leveldb_get(handle, options.handle, key.buffer, (UIntPtr)key.buffer.Length, out length, out error); - if (error != IntPtr.Zero) - { - Native.leveldb_free(error); - value = default(Slice); - return false; - } - if (v == IntPtr.Zero) - { - value = default(Slice); - return false; - } - value = new Slice(v, length); - Native.leveldb_free(v); - return true; - } - - public void Write(WriteOptions options, WriteBatch write_batch) - { - // There's a bug in .Net Core. - // When calling DB.Write(), it will throw LevelDBException sometimes. - // But when you try to catch the exception, the bug disappears. - // We shall remove the "try...catch" clause when Microsoft fix the bug. - byte retry = 0; - while (true) - { - try - { - IntPtr error; - Native.leveldb_write(handle, options.handle, write_batch.handle, out error); - NativeHelper.CheckError(error); - break; - } - catch (LevelDBException ex) - { - if (++retry >= 4) throw; - System.IO.File.AppendAllText("leveldb.log", ex.Message + "\r\n"); - } - } - } - } -} diff --git a/src/neo/IO/Data/LevelDB/Helper.cs b/src/neo/IO/Data/LevelDB/Helper.cs deleted file mode 100644 index 585d282414..0000000000 --- a/src/neo/IO/Data/LevelDB/Helper.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Neo.IO.Data.LevelDB -{ - public static class Helper - { - public static void Delete(this WriteBatch batch, byte prefix, ISerializable key) - { - batch.Delete(SliceBuilder.Begin(prefix).Add(key)); - } - - public static IEnumerable Find(this DB db, ReadOptions options, byte prefix) where T : class, ISerializable, new() - { - return Find(db, options, SliceBuilder.Begin(prefix), (k, v) => v.ToArray().AsSerializable()); - } - - public static IEnumerable Find(this DB db, ReadOptions options, Slice prefix, Func resultSelector) - { - using (Iterator it = db.NewIterator(options)) - { - for (it.Seek(prefix); it.Valid(); it.Next()) - { - Slice key = it.Key(); - byte[] x = key.ToArray(); - byte[] y = prefix.ToArray(); - if (x.Length < y.Length) break; - if (!x.AsSpan().StartsWith(y)) break; - yield return resultSelector(key, it.Value()); - } - } - } - - public static T Get(this DB db, ReadOptions options, byte prefix, ISerializable key) where T : class, ISerializable, new() - { - return db.Get(options, SliceBuilder.Begin(prefix).Add(key)).ToArray().AsSerializable(); - } - - public static T Get(this DB db, ReadOptions options, byte prefix, ISerializable key, Func resultSelector) - { - return resultSelector(db.Get(options, SliceBuilder.Begin(prefix).Add(key))); - } - - public static void Put(this WriteBatch batch, byte prefix, ISerializable key, ISerializable value) - { - batch.Put(SliceBuilder.Begin(prefix).Add(key), value.ToArray()); - } - - public static T TryGet(this DB db, ReadOptions options, byte prefix, ISerializable key) where T : class, ISerializable, new() - { - Slice slice; - if (!db.TryGet(options, SliceBuilder.Begin(prefix).Add(key), out slice)) - return null; - return slice.ToArray().AsSerializable(); - } - - public static T TryGet(this DB db, ReadOptions options, byte prefix, ISerializable key, Func resultSelector) where T : class - { - Slice slice; - if (!db.TryGet(options, SliceBuilder.Begin(prefix).Add(key), out slice)) - return null; - return resultSelector(slice); - } - } -} diff --git a/src/neo/IO/Data/LevelDB/Iterator.cs b/src/neo/IO/Data/LevelDB/Iterator.cs deleted file mode 100644 index b3a6a0bfe0..0000000000 --- a/src/neo/IO/Data/LevelDB/Iterator.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; - -namespace Neo.IO.Data.LevelDB -{ - public class Iterator : IDisposable - { - private IntPtr handle; - - internal Iterator(IntPtr handle) - { - this.handle = handle; - } - - private void CheckError() - { - IntPtr error; - Native.leveldb_iter_get_error(handle, out error); - NativeHelper.CheckError(error); - } - - public void Dispose() - { - if (handle != IntPtr.Zero) - { - Native.leveldb_iter_destroy(handle); - handle = IntPtr.Zero; - } - } - - public Slice Key() - { - UIntPtr length; - IntPtr key = Native.leveldb_iter_key(handle, out length); - CheckError(); - return new Slice(key, length); - } - - public void Next() - { - Native.leveldb_iter_next(handle); - CheckError(); - } - - public void Prev() - { - Native.leveldb_iter_prev(handle); - CheckError(); - } - - public void Seek(Slice target) - { - Native.leveldb_iter_seek(handle, target.buffer, (UIntPtr)target.buffer.Length); - } - - public void SeekToFirst() - { - Native.leveldb_iter_seek_to_first(handle); - } - - public void SeekToLast() - { - Native.leveldb_iter_seek_to_last(handle); - } - - public bool Valid() - { - return Native.leveldb_iter_valid(handle); - } - - public Slice Value() - { - UIntPtr length; - IntPtr value = Native.leveldb_iter_value(handle, out length); - CheckError(); - return new Slice(value, length); - } - } -} diff --git a/src/neo/IO/Data/LevelDB/LevelDBException.cs b/src/neo/IO/Data/LevelDB/LevelDBException.cs deleted file mode 100644 index 8804f1f7f2..0000000000 --- a/src/neo/IO/Data/LevelDB/LevelDBException.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Data.Common; - -namespace Neo.IO.Data.LevelDB -{ - public class LevelDBException : DbException - { - internal LevelDBException(string message) - : base(message) - { - } - } -} diff --git a/src/neo/IO/Data/LevelDB/Native.cs b/src/neo/IO/Data/LevelDB/Native.cs deleted file mode 100644 index c83d3913f8..0000000000 --- a/src/neo/IO/Data/LevelDB/Native.cs +++ /dev/null @@ -1,249 +0,0 @@ -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace Neo.IO.Data.LevelDB -{ - public enum CompressionType : byte - { - kNoCompression = 0x0, - kSnappyCompression = 0x1 - } - - public static class Native - { - #region Logger - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_logger_create(IntPtr /* Action */ logger); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_logger_destroy(IntPtr /* logger*/ option); - #endregion - - #region DB - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_open(IntPtr /* Options*/ options, string name, out IntPtr error); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_close(IntPtr /*DB */ db); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_put(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, byte[] key, UIntPtr keylen, byte[] val, UIntPtr vallen, out IntPtr errptr); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_delete(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, byte[] key, UIntPtr keylen, out IntPtr errptr); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_write(IntPtr /* DB */ db, IntPtr /* WriteOptions*/ options, IntPtr /* WriteBatch */ batch, out IntPtr errptr); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_get(IntPtr /* DB */ db, IntPtr /* ReadOptions*/ options, byte[] key, UIntPtr keylen, out UIntPtr vallen, out IntPtr errptr); - - //[DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - //static extern void leveldb_approximate_sizes(IntPtr /* DB */ db, int num_ranges, byte[] range_start_key, long range_start_key_len, byte[] range_limit_key, long range_limit_key_len, out long sizes); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_create_iterator(IntPtr /* DB */ db, IntPtr /* ReadOption */ options); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_create_snapshot(IntPtr /* DB */ db); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_release_snapshot(IntPtr /* DB */ db, IntPtr /* SnapShot*/ snapshot); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_property_value(IntPtr /* DB */ db, string propname); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_repair_db(IntPtr /* Options*/ options, string name, out IntPtr error); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_destroy_db(IntPtr /* Options*/ options, string name, out IntPtr error); - - #region extensions - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_free(IntPtr /* void */ ptr); - - #endregion - - - #endregion - - #region Env - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_create_default_env(); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_env_destroy(IntPtr /*Env*/ cache); - #endregion - - #region Iterator - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_destroy(IntPtr /*Iterator*/ iterator); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.U1)] - public static extern bool leveldb_iter_valid(IntPtr /*Iterator*/ iterator); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_seek_to_first(IntPtr /*Iterator*/ iterator); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_seek_to_last(IntPtr /*Iterator*/ iterator); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_seek(IntPtr /*Iterator*/ iterator, byte[] key, UIntPtr length); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_next(IntPtr /*Iterator*/ iterator); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_prev(IntPtr /*Iterator*/ iterator); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_iter_key(IntPtr /*Iterator*/ iterator, out UIntPtr length); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_iter_value(IntPtr /*Iterator*/ iterator, out UIntPtr length); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_iter_get_error(IntPtr /*Iterator*/ iterator, out IntPtr error); - #endregion - - #region Options - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_options_create(); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_destroy(IntPtr /*Options*/ options); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_create_if_missing(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_error_if_exists(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_info_log(IntPtr /*Options*/ options, IntPtr /* Logger */ logger); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_paranoid_checks(IntPtr /*Options*/ options, [MarshalAs(UnmanagedType.U1)] bool o); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_env(IntPtr /*Options*/ options, IntPtr /*Env*/ env); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_write_buffer_size(IntPtr /*Options*/ options, UIntPtr size); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_max_open_files(IntPtr /*Options*/ options, int max); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_cache(IntPtr /*Options*/ options, IntPtr /*Cache*/ cache); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_block_size(IntPtr /*Options*/ options, UIntPtr size); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_block_restart_interval(IntPtr /*Options*/ options, int interval); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_compression(IntPtr /*Options*/ options, CompressionType level); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_comparator(IntPtr /*Options*/ options, IntPtr /*Comparator*/ comparer); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_options_set_filter_policy(IntPtr /*Options*/ options, IntPtr /*FilterPolicy*/ policy); - #endregion - - #region ReadOptions - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_readoptions_create(); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_readoptions_destroy(IntPtr /*ReadOptions*/ options); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_readoptions_set_verify_checksums(IntPtr /*ReadOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_readoptions_set_fill_cache(IntPtr /*ReadOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_readoptions_set_snapshot(IntPtr /*ReadOptions*/ options, IntPtr /*SnapShot*/ snapshot); - #endregion - - #region WriteBatch - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_writebatch_create(); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_destroy(IntPtr /* WriteBatch */ batch); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_clear(IntPtr /* WriteBatch */ batch); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_put(IntPtr /* WriteBatch */ batch, byte[] key, UIntPtr keylen, byte[] val, UIntPtr vallen); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_delete(IntPtr /* WriteBatch */ batch, byte[] key, UIntPtr keylen); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writebatch_iterate(IntPtr /* WriteBatch */ batch, object state, Action put, Action deleted); - #endregion - - #region WriteOptions - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_writeoptions_create(); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writeoptions_destroy(IntPtr /*WriteOptions*/ options); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_writeoptions_set_sync(IntPtr /*WriteOptions*/ options, [MarshalAs(UnmanagedType.U1)] bool o); - #endregion - - #region Cache - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr leveldb_cache_create_lru(int capacity); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_cache_destroy(IntPtr /*Cache*/ cache); - #endregion - - #region Comparator - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr /* leveldb_comparator_t* */ - leveldb_comparator_create( - IntPtr /* void* */ state, - IntPtr /* void (*)(void*) */ destructor, - IntPtr - /* int (*compare)(void*, - const char* a, size_t alen, - const char* b, size_t blen) */ - compare, - IntPtr /* const char* (*)(void*) */ name); - - [DllImport("libleveldb", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - public static extern void leveldb_comparator_destroy(IntPtr /* leveldb_comparator_t* */ cmp); - - #endregion - } - - internal static class NativeHelper - { - public static void CheckError(IntPtr error) - { - if (error != IntPtr.Zero) - { - string message = Marshal.PtrToStringAnsi(error); - Native.leveldb_free(error); - throw new LevelDBException(message); - } - } - } -} diff --git a/src/neo/IO/Data/LevelDB/Options.cs b/src/neo/IO/Data/LevelDB/Options.cs deleted file mode 100644 index 53dd6e488b..0000000000 --- a/src/neo/IO/Data/LevelDB/Options.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; - -namespace Neo.IO.Data.LevelDB -{ - public class Options - { - public static readonly Options Default = new Options(); - internal readonly IntPtr handle = Native.leveldb_options_create(); - - public bool CreateIfMissing - { - set - { - Native.leveldb_options_set_create_if_missing(handle, value); - } - } - - public bool ErrorIfExists - { - set - { - Native.leveldb_options_set_error_if_exists(handle, value); - } - } - - public bool ParanoidChecks - { - set - { - Native.leveldb_options_set_paranoid_checks(handle, value); - } - } - - public int WriteBufferSize - { - set - { - Native.leveldb_options_set_write_buffer_size(handle, (UIntPtr)value); - } - } - - public int MaxOpenFiles - { - set - { - Native.leveldb_options_set_max_open_files(handle, value); - } - } - - public int BlockSize - { - set - { - Native.leveldb_options_set_block_size(handle, (UIntPtr)value); - } - } - - public int BlockRestartInterval - { - set - { - Native.leveldb_options_set_block_restart_interval(handle, value); - } - } - - public CompressionType Compression - { - set - { - Native.leveldb_options_set_compression(handle, value); - } - } - - public IntPtr FilterPolicy - { - set - { - Native.leveldb_options_set_filter_policy(handle, value); - } - } - - ~Options() - { - Native.leveldb_options_destroy(handle); - } - } -} diff --git a/src/neo/IO/Data/LevelDB/ReadOptions.cs b/src/neo/IO/Data/LevelDB/ReadOptions.cs deleted file mode 100644 index 9c198cfba1..0000000000 --- a/src/neo/IO/Data/LevelDB/ReadOptions.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; - -namespace Neo.IO.Data.LevelDB -{ - public class ReadOptions - { - public static readonly ReadOptions Default = new ReadOptions(); - internal readonly IntPtr handle = Native.leveldb_readoptions_create(); - - public bool VerifyChecksums - { - set - { - Native.leveldb_readoptions_set_verify_checksums(handle, value); - } - } - - public bool FillCache - { - set - { - Native.leveldb_readoptions_set_fill_cache(handle, value); - } - } - - public Snapshot Snapshot - { - set - { - Native.leveldb_readoptions_set_snapshot(handle, value.handle); - } - } - - ~ReadOptions() - { - Native.leveldb_readoptions_destroy(handle); - } - } -} diff --git a/src/neo/IO/Data/LevelDB/Slice.cs b/src/neo/IO/Data/LevelDB/Slice.cs deleted file mode 100644 index 83b3fe78d3..0000000000 --- a/src/neo/IO/Data/LevelDB/Slice.cs +++ /dev/null @@ -1,243 +0,0 @@ -using Neo.Cryptography; -using System; -using System.Runtime.InteropServices; -using System.Text; - -namespace Neo.IO.Data.LevelDB -{ - public struct Slice : IComparable, IEquatable - { - internal byte[] buffer; - - internal Slice(IntPtr data, UIntPtr length) - { - buffer = new byte[(int)length]; - Marshal.Copy(data, buffer, 0, (int)length); - } - - public int CompareTo(Slice other) - { - for (int i = 0; i < buffer.Length && i < other.buffer.Length; i++) - { - int r = buffer[i].CompareTo(other.buffer[i]); - if (r != 0) return r; - } - return buffer.Length.CompareTo(other.buffer.Length); - } - - public bool Equals(Slice other) - { - if (buffer.Length != other.buffer.Length) return false; - return MemoryExtensions.SequenceEqual(buffer, other.buffer); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (!(obj is Slice)) return false; - return Equals((Slice)obj); - } - - public override int GetHashCode() - { - return (int)buffer.Murmur32(0); - } - - public byte[] ToArray() - { - return buffer ?? Array.Empty(); - } - - unsafe public bool ToBoolean() - { - if (buffer.Length != sizeof(bool)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((bool*)pbyte); - } - } - - public byte ToByte() - { - if (buffer.Length != sizeof(byte)) - throw new InvalidCastException(); - return buffer[0]; - } - - unsafe public double ToDouble() - { - if (buffer.Length != sizeof(double)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((double*)pbyte); - } - } - - unsafe public short ToInt16() - { - if (buffer.Length != sizeof(short)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((short*)pbyte); - } - } - - unsafe public int ToInt32() - { - if (buffer.Length != sizeof(int)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((int*)pbyte); - } - } - - unsafe public long ToInt64() - { - if (buffer.Length != sizeof(long)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((long*)pbyte); - } - } - - unsafe public float ToSingle() - { - if (buffer.Length != sizeof(float)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((float*)pbyte); - } - } - - public override string ToString() - { - return Encoding.UTF8.GetString(buffer); - } - - unsafe public ushort ToUInt16() - { - if (buffer.Length != sizeof(ushort)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((ushort*)pbyte); - } - } - - unsafe public uint ToUInt32(int index = 0) - { - if (buffer.Length != sizeof(uint) + index) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[index]) - { - return *((uint*)pbyte); - } - } - - unsafe public ulong ToUInt64() - { - if (buffer.Length != sizeof(ulong)) - throw new InvalidCastException(); - fixed (byte* pbyte = &buffer[0]) - { - return *((ulong*)pbyte); - } - } - - public static implicit operator Slice(byte[] data) - { - return new Slice { buffer = data }; - } - - public static implicit operator Slice(bool data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(byte data) - { - return new Slice { buffer = new[] { data } }; - } - - public static implicit operator Slice(double data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(short data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(int data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(long data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(float data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(string data) - { - return new Slice { buffer = Encoding.UTF8.GetBytes(data) }; - } - - public static implicit operator Slice(ushort data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(uint data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static implicit operator Slice(ulong data) - { - return new Slice { buffer = BitConverter.GetBytes(data) }; - } - - public static bool operator <(Slice x, Slice y) - { - return x.CompareTo(y) < 0; - } - - public static bool operator <=(Slice x, Slice y) - { - return x.CompareTo(y) <= 0; - } - - public static bool operator >(Slice x, Slice y) - { - return x.CompareTo(y) > 0; - } - - public static bool operator >=(Slice x, Slice y) - { - return x.CompareTo(y) >= 0; - } - - public static bool operator ==(Slice x, Slice y) - { - return x.Equals(y); - } - - public static bool operator !=(Slice x, Slice y) - { - return !x.Equals(y); - } - } -} diff --git a/src/neo/IO/Data/LevelDB/SliceBuilder.cs b/src/neo/IO/Data/LevelDB/SliceBuilder.cs deleted file mode 100644 index cf1cff0264..0000000000 --- a/src/neo/IO/Data/LevelDB/SliceBuilder.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Neo.IO.Data.LevelDB -{ - public class SliceBuilder - { - private List data = new List(); - - private SliceBuilder() - { - } - - public SliceBuilder Add(byte value) - { - data.Add(value); - return this; - } - - public SliceBuilder Add(ushort value) - { - data.AddRange(BitConverter.GetBytes(value)); - return this; - } - - public SliceBuilder Add(uint value) - { - data.AddRange(BitConverter.GetBytes(value)); - return this; - } - - public SliceBuilder Add(long value) - { - data.AddRange(BitConverter.GetBytes(value)); - return this; - } - - public SliceBuilder Add(IEnumerable value) - { - if (value != null) - data.AddRange(value); - return this; - } - - public SliceBuilder Add(string value) - { - if (value != null) - data.AddRange(Encoding.UTF8.GetBytes(value)); - return this; - } - - public SliceBuilder Add(ISerializable value) - { - if (value != null) - data.AddRange(value.ToArray()); - return this; - } - - public static SliceBuilder Begin() - { - return new SliceBuilder(); - } - - public static SliceBuilder Begin(byte prefix) - { - return new SliceBuilder().Add(prefix); - } - - public static implicit operator Slice(SliceBuilder value) - { - return value.data.ToArray(); - } - } -} diff --git a/src/neo/IO/Data/LevelDB/Snapshot.cs b/src/neo/IO/Data/LevelDB/Snapshot.cs deleted file mode 100644 index d651098388..0000000000 --- a/src/neo/IO/Data/LevelDB/Snapshot.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace Neo.IO.Data.LevelDB -{ - public class Snapshot : IDisposable - { - internal IntPtr db, handle; - - internal Snapshot(IntPtr db) - { - this.db = db; - this.handle = Native.leveldb_create_snapshot(db); - } - - public void Dispose() - { - if (handle != IntPtr.Zero) - { - Native.leveldb_release_snapshot(db, handle); - handle = IntPtr.Zero; - } - } - } -} diff --git a/src/neo/IO/Data/LevelDB/WriteBatch.cs b/src/neo/IO/Data/LevelDB/WriteBatch.cs deleted file mode 100644 index b3a9782108..0000000000 --- a/src/neo/IO/Data/LevelDB/WriteBatch.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; - -namespace Neo.IO.Data.LevelDB -{ - public class WriteBatch - { - internal readonly IntPtr handle = Native.leveldb_writebatch_create(); - - ~WriteBatch() - { - Native.leveldb_writebatch_destroy(handle); - } - - public void Clear() - { - Native.leveldb_writebatch_clear(handle); - } - - public void Delete(Slice key) - { - Native.leveldb_writebatch_delete(handle, key.buffer, (UIntPtr)key.buffer.Length); - } - - public void Put(Slice key, Slice value) - { - Native.leveldb_writebatch_put(handle, key.buffer, (UIntPtr)key.buffer.Length, value.buffer, (UIntPtr)value.buffer.Length); - } - } -} diff --git a/src/neo/IO/Data/LevelDB/WriteOptions.cs b/src/neo/IO/Data/LevelDB/WriteOptions.cs deleted file mode 100644 index 8d120c3997..0000000000 --- a/src/neo/IO/Data/LevelDB/WriteOptions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace Neo.IO.Data.LevelDB -{ - public class WriteOptions - { - public static readonly WriteOptions Default = new WriteOptions(); - internal readonly IntPtr handle = Native.leveldb_writeoptions_create(); - - public bool Sync - { - set - { - Native.leveldb_writeoptions_set_sync(handle, value); - } - } - - ~WriteOptions() - { - Native.leveldb_writeoptions_destroy(handle); - } - } -} diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 2ec0ae29cb..047ee71604 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -4,7 +4,6 @@ using Neo.Network.P2P; using Neo.Network.RPC; using Neo.Persistence; -using Neo.Persistence.Memory; using Neo.Plugins; using Neo.Wallets; using System; @@ -34,7 +33,7 @@ public class NeoSystem : IDisposable public NeoSystem(string storageEngine = null) { Plugin.LoadPlugins(this); - this.store = storageEngine is null ? new Store() : Plugin.Storages[storageEngine].GetStore(); + this.store = storageEngine is null ? new MemoryStore() : Plugin.Storages[storageEngine].GetStore(); 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)); diff --git a/src/neo/Persistence/Memory/ByteArrayEqualityComparer.cs b/src/neo/Persistence/ByteArrayEqualityComparer.cs similarity index 97% rename from src/neo/Persistence/Memory/ByteArrayEqualityComparer.cs rename to src/neo/Persistence/ByteArrayEqualityComparer.cs index 97096498aa..29d6acbe1e 100644 --- a/src/neo/Persistence/Memory/ByteArrayEqualityComparer.cs +++ b/src/neo/Persistence/ByteArrayEqualityComparer.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Neo.Persistence.Memory +namespace Neo.Persistence { internal class ByteArrayEqualityComparer : IEqualityComparer { diff --git a/src/neo/Persistence/Memory/Helper.cs b/src/neo/Persistence/Helper.cs similarity index 85% rename from src/neo/Persistence/Memory/Helper.cs rename to src/neo/Persistence/Helper.cs index e25a44d699..51d9314bfc 100644 --- a/src/neo/Persistence/Memory/Helper.cs +++ b/src/neo/Persistence/Helper.cs @@ -1,6 +1,6 @@ using System; -namespace Neo.Persistence.Memory +namespace Neo.Persistence { internal static class Helper { diff --git a/src/neo/Persistence/LevelDB/Snapshot.cs b/src/neo/Persistence/LevelDB/Snapshot.cs deleted file mode 100644 index 712268924c..0000000000 --- a/src/neo/Persistence/LevelDB/Snapshot.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Neo.IO.Data.LevelDB; -using System.Collections.Generic; -using LSnapshot = Neo.IO.Data.LevelDB.Snapshot; - -namespace Neo.Persistence.LevelDB -{ - internal class Snapshot : ISnapshot - { - private readonly DB db; - private readonly LSnapshot snapshot; - private readonly ReadOptions options; - private readonly WriteBatch batch; - - public Snapshot(DB db) - { - this.db = db; - this.snapshot = db.GetSnapshot(); - this.options = new ReadOptions { FillCache = false, Snapshot = snapshot }; - this.batch = new WriteBatch(); - } - - public void Commit() - { - db.Write(WriteOptions.Default, batch); - } - - public void Delete(byte table, byte[] key) - { - batch.Delete(SliceBuilder.Begin(table).Add(key)); - } - - public void Dispose() - { - snapshot.Dispose(); - } - - public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) - { - return db.Find(options, SliceBuilder.Begin(table).Add(prefix), (k, v) => (k.ToArray()[1..], v.ToArray())); - } - - public void Put(byte table, byte[] key, byte[] value) - { - batch.Put(SliceBuilder.Begin(table).Add(key), value); - } - - public byte[] TryGet(byte table, byte[] key) - { - if (!db.TryGet(options, SliceBuilder.Begin(table).Add(key), out Slice slice)) - return null; - return slice.ToArray(); - } - } -} diff --git a/src/neo/Persistence/LevelDB/Store.cs b/src/neo/Persistence/LevelDB/Store.cs deleted file mode 100644 index 6758744281..0000000000 --- a/src/neo/Persistence/LevelDB/Store.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Neo.IO.Data.LevelDB; -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace Neo.Persistence.LevelDB -{ - public class Store : IStore - { - private const byte SYS_Version = 0xf0; - private readonly DB db; - - public Store(string path) - { - this.db = DB.Open(path, new Options { CreateIfMissing = true }); - if (db.TryGet(ReadOptions.Default, SliceBuilder.Begin(SYS_Version), out Slice value) && Version.TryParse(value.ToString(), out Version version) && version >= Version.Parse("2.9.1")) - return; - WriteBatch batch = new WriteBatch(); - ReadOptions options = new ReadOptions { FillCache = false }; - using (Iterator it = db.NewIterator(options)) - { - for (it.SeekToFirst(); it.Valid(); it.Next()) - { - batch.Delete(it.Key()); - } - } - db.Put(WriteOptions.Default, SliceBuilder.Begin(SYS_Version), Assembly.GetExecutingAssembly().GetName().Version.ToString()); - db.Write(WriteOptions.Default, batch); - } - - public void Delete(byte table, byte[] key) - { - db.Delete(WriteOptions.Default, SliceBuilder.Begin(table).Add(key)); - } - - public void Dispose() - { - db.Dispose(); - } - - public IEnumerable<(byte[], byte[])> Find(byte table, byte[] prefix) - { - return db.Find(ReadOptions.Default, SliceBuilder.Begin(table).Add(prefix), (k, v) => (k.ToArray()[1..], v.ToArray())); - } - - public ISnapshot GetSnapshot() - { - return new Snapshot(db); - } - - public void Put(byte table, byte[] key, byte[] value) - { - db.Put(WriteOptions.Default, SliceBuilder.Begin(table).Add(key), value); - } - - public void PutSync(byte table, byte[] key, byte[] value) - { - db.Put(new WriteOptions { Sync = true }, SliceBuilder.Begin(table).Add(key), value); - } - - public byte[] TryGet(byte table, byte[] key) - { - if (!db.TryGet(ReadOptions.Default, SliceBuilder.Begin(table).Add(key), out Slice slice)) - return null; - return slice.ToArray(); - } - } -} diff --git a/src/neo/Persistence/Memory/Snapshot.cs b/src/neo/Persistence/MemorySnapshot.cs similarity index 93% rename from src/neo/Persistence/Memory/Snapshot.cs rename to src/neo/Persistence/MemorySnapshot.cs index a8edf8cf05..5b1dc35742 100644 --- a/src/neo/Persistence/Memory/Snapshot.cs +++ b/src/neo/Persistence/MemorySnapshot.cs @@ -5,15 +5,15 @@ using System.Collections.Immutable; using System.Linq; -namespace Neo.Persistence.Memory +namespace Neo.Persistence { - internal class Snapshot : ISnapshot + internal class MemorySnapshot : ISnapshot { private readonly ConcurrentDictionary[] innerData; private readonly ImmutableDictionary[] immutableData; private readonly ConcurrentDictionary[] writeBatch; - public Snapshot(ConcurrentDictionary[] innerData) + public MemorySnapshot(ConcurrentDictionary[] innerData) { this.innerData = innerData; this.immutableData = innerData.Select(p => p.ToImmutableDictionary(ByteArrayEqualityComparer.Default)).ToArray(); diff --git a/src/neo/Persistence/Memory/Store.cs b/src/neo/Persistence/MemoryStore.cs similarity index 91% rename from src/neo/Persistence/Memory/Store.cs rename to src/neo/Persistence/MemoryStore.cs index 73e7dc5719..5b6c09c58f 100644 --- a/src/neo/Persistence/Memory/Store.cs +++ b/src/neo/Persistence/MemoryStore.cs @@ -4,13 +4,13 @@ using System.Collections.Generic; using System.Linq; -namespace Neo.Persistence.Memory +namespace Neo.Persistence { - public class Store : IStore + public class MemoryStore : IStore { private readonly ConcurrentDictionary[] innerData; - public Store() + public MemoryStore() { innerData = new ConcurrentDictionary[256]; for (int i = 0; i < innerData.Length; i++) @@ -38,7 +38,7 @@ public IEnumerable<(byte[] Key, byte[] Value)> Find(byte table, byte[] prefix) public ISnapshot GetSnapshot() { - return new Snapshot(innerData); + return new MemorySnapshot(innerData); } public void Put(byte table, byte[] key, byte[] value) diff --git a/tests/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs b/tests/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs deleted file mode 100644 index d9ad5af951..0000000000 --- a/tests/neo.UnitTests/IO/Data/LevelDb/UT_Slice.cs +++ /dev/null @@ -1,408 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Cryptography; -using Neo.IO.Data.LevelDB; -using System; -using System.Runtime.InteropServices; -using System.Text; - -namespace Neo.UnitTests.IO.Data.LevelDb -{ - public class Test { } - - [TestClass] - public class UT_Slice - { - private Slice sliceTest; - - [TestMethod] - public void TestConstructor() - { - IntPtr parr = Marshal.AllocHGlobal(1); - Marshal.WriteByte(parr, 0x01); - UIntPtr plength = new UIntPtr(1); - sliceTest = new Slice(parr, plength); - Assert.IsNotNull(sliceTest); - Assert.IsInstanceOfType(sliceTest, typeof(Slice)); - Slice slice = (byte)0x01; - Assert.AreEqual(slice, sliceTest); - Marshal.FreeHGlobal(parr); - } - - [TestMethod] - public void TestCompareTo() - { - Slice slice = new byte[] { 0x01, 0x02 }; - sliceTest = new byte[] { 0x01, 0x02 }; - int result = sliceTest.CompareTo(slice); - Assert.AreEqual(0, result); - sliceTest = new byte[] { 0x01 }; - result = sliceTest.CompareTo(slice); - Assert.AreEqual(-1, result); - sliceTest = new byte[] { 0x01, 0x02, 0x03 }; - result = sliceTest.CompareTo(slice); - Assert.AreEqual(1, result); - sliceTest = new byte[] { 0x01, 0x03 }; - result = sliceTest.CompareTo(slice); - Assert.AreEqual(1, result); - sliceTest = new byte[] { 0x01, 0x01 }; - result = sliceTest.CompareTo(slice); - Assert.AreEqual(-1, result); - } - - [TestMethod] - public void TestEqualsSlice() - { - byte[] arr1 = { 0x01, 0x02 }; - byte[] arr2 = { 0x01, 0x02 }; - Slice slice = arr1; - sliceTest = arr1; - Assert.IsTrue(sliceTest.Equals(slice)); - sliceTest = arr2; - Assert.IsTrue(sliceTest.Equals(slice)); - sliceTest = new byte[] { 0x01, 0x03 }; - Assert.IsFalse(sliceTest.Equals(slice)); - } - - [TestMethod] - public void TestEqualsObj() - { - sliceTest = new byte[] { 0x01 }; - object slice = null; - bool result = sliceTest.Equals(slice); - Assert.AreEqual(false, result); - slice = new Test(); - result = sliceTest.Equals(slice); - Assert.AreEqual(false, result); - slice = sliceTest; - result = sliceTest.Equals(slice); - Assert.AreEqual(true, result); - Slice s = new byte[] { 0x01 }; - result = sliceTest.Equals(s); - Assert.AreEqual(true, result); - s = new byte[] { 0x01, 0x02 }; - result = sliceTest.Equals(s); - Assert.AreEqual(false, result); - } - - [TestMethod] - public void TestGetHashCode() - { - byte[] arr = new byte[] { 0x01, 0x02 }; - sliceTest = arr; - int hash1 = (int)arr.Murmur32(0); - int hash2 = sliceTest.GetHashCode(); - Assert.AreEqual(hash2, hash1); - } - - [TestMethod] - public void TestFromArray() - { - byte[] arr = new byte[]{ - 0x01,0x01,0x01,0x01, - }; - IntPtr parr = Marshal.AllocHGlobal(arr.Length); - for (int i = 0; i < arr.Length; i++) - { - Marshal.WriteByte(parr + i, 0x01); - } - UIntPtr plength = new UIntPtr((uint)arr.Length); - Slice slice = new Slice(parr, plength); - sliceTest = arr; - Assert.AreEqual(slice, sliceTest); - Marshal.FreeHGlobal(parr); - } - - [TestMethod] - public void TestToArray() - { - sliceTest = new Slice(); - byte[] arr = sliceTest.ToArray(); - Assert.AreEqual(0, arr.Length); - arr = new byte[] { 0x01, 0x02 }; - sliceTest = arr; - byte[] parr = sliceTest.ToArray(); - Assert.AreSame(parr, arr); - } - - [TestMethod] - public void TestToBoolean() - { - sliceTest = new byte[] { 0x01, 0x02 }; - Assert.ThrowsException(() => sliceTest.ToBoolean()); - sliceTest = (byte)0x01; - bool result = sliceTest.ToBoolean(); - Assert.AreEqual(true, result); - } - - [TestMethod] - public void TestToByte() - { - sliceTest = new byte[] { 0x01, 0x02 }; - Assert.ThrowsException(() => sliceTest.ToByte()); - sliceTest = (byte)0x01; - byte result = sliceTest.ToByte(); - Assert.AreEqual((byte)0x01, result); - } - - [TestMethod] - public void TestToDouble() - { - sliceTest = new byte[] { 0x01 }; - Assert.ThrowsException(() => sliceTest.ToDouble()); - byte[] arr = new byte[sizeof(double)]; - sliceTest = arr; - double result = sliceTest.ToDouble(); - Assert.AreEqual(0D, result); - sliceTest = 0.5D; - Assert.AreEqual(0.5D, sliceTest.ToDouble()); - } - - [TestMethod] - public void TestToInt16() - { - sliceTest = new byte[] { 0x01 }; - Assert.ThrowsException(() => sliceTest.ToInt16()); - sliceTest = (Int16)(-15); - Assert.AreEqual((Int16)(-15), sliceTest.ToInt16()); - } - - [TestMethod] - public void TestToInt32() - { - sliceTest = new byte[] { 0x01 }; - Assert.ThrowsException(() => sliceTest.ToInt32()); - sliceTest = (Int32)(-15); - Assert.AreEqual((Int32)(-15), sliceTest.ToInt32()); - } - - [TestMethod] - public void TestToInt64() - { - sliceTest = new byte[] { 0x01 }; - Assert.ThrowsException(() => sliceTest.ToInt64()); - sliceTest = Int64.MaxValue; - Assert.AreEqual(Int64.MaxValue, sliceTest.ToInt64()); - } - - [TestMethod] - public void TestToSingle() - { - sliceTest = new byte[] { 0x01 }; - Assert.ThrowsException(() => sliceTest.ToSingle()); - sliceTest = (float)(-15.5); - Assert.AreEqual((float)(-15.5), sliceTest.ToSingle()); - } - - [TestMethod] - public void TestToString() - { - sliceTest = "abc你好"; - Assert.AreEqual("abc你好", sliceTest.ToString()); - } - - [TestMethod] - public void TestToUint16() - { - sliceTest = new byte[] { 0x01 }; - Assert.ThrowsException(() => sliceTest.ToUInt16()); - sliceTest = (UInt16)(25); - Assert.AreEqual((UInt16)25, sliceTest.ToUInt16()); - } - - [TestMethod] - public void TestToUint32() - { - sliceTest = new byte[] { 0x01 }; - Assert.ThrowsException(() => sliceTest.ToUInt32()); - sliceTest = (UInt32)(2525252525); - Assert.AreEqual((UInt32)2525252525, sliceTest.ToUInt32()); - } - - [TestMethod] - public void TestToUint64() - { - sliceTest = new byte[] { 0x01 }; - Assert.ThrowsException(() => sliceTest.ToUInt64()); - sliceTest = (UInt64)(0x2525252525252525); - Assert.AreEqual((UInt64)(0x2525252525252525), sliceTest.ToUInt64()); - } - - [TestMethod] - public void TestFromBool() - { - byte[] arr = { 0x01 }; - Slice slice = arr; - sliceTest = true; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestFromByte() - { - sliceTest = (byte)0x01; - byte[] arr = { 0x01 }; - Slice slice = arr; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestFromDouble() - { - Slice slice = BitConverter.GetBytes(1.23D); - sliceTest = 1.23D; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestFromShort() - { - Slice slice = BitConverter.GetBytes((short)1234); - sliceTest = (short)1234; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestFromInt() - { - Slice slice = BitConverter.GetBytes(-1234); - sliceTest = -1234; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestFromLong() - { - Slice slice = BitConverter.GetBytes(-1234L); - sliceTest = -1234L; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestFromFloat() - { - Slice slice = BitConverter.GetBytes(1.234F); - sliceTest = 1.234F; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestFromString() - { - string str = "abcdefghijklmnopqrstuvwxwz!@#$%^&*&()_+?><你好"; - Slice slice = Encoding.UTF8.GetBytes(str); - sliceTest = str; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestFromUnshort() - { - Slice slice = BitConverter.GetBytes((ushort)12345); - sliceTest = (ushort)12345; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestFromUint() - { - Slice slice = BitConverter.GetBytes((uint)12345); - sliceTest = (uint)12345; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestFromUlong() - { - Slice slice = BitConverter.GetBytes(12345678UL); - sliceTest = 12345678UL; - Assert.AreEqual(slice, sliceTest); - } - - [TestMethod] - public void TestLessThan() - { - sliceTest = new byte[] { 0x01 }; - Slice slice = new byte[] { 0x02 }; - bool result = sliceTest < slice; - Assert.AreEqual(true, result); - slice = new byte[] { 0x01 }; - result = sliceTest < slice; - Assert.AreEqual(false, result); - slice = new byte[] { 0x00 }; - result = sliceTest < slice; - Assert.AreEqual(false, result); - } - - [TestMethod] - public void TestLessThanAndEqual() - { - sliceTest = new byte[] { 0x01 }; - Slice slice = new byte[] { 0x02 }; - bool result = sliceTest <= slice; - Assert.AreEqual(true, result); - slice = new byte[] { 0x01 }; - result = sliceTest <= slice; - Assert.AreEqual(true, result); - slice = new byte[] { 0x00 }; - result = sliceTest <= slice; - Assert.AreEqual(false, result); - } - - [TestMethod] - public void TestGreatThan() - { - sliceTest = new byte[] { 0x01 }; - Slice slice = new byte[] { 0x00 }; - bool result = sliceTest > slice; - Assert.AreEqual(true, result); - slice = new byte[] { 0x01 }; - result = sliceTest > slice; - Assert.AreEqual(false, result); - slice = new byte[] { 0x02 }; - result = sliceTest > slice; - Assert.AreEqual(false, result); - } - - [TestMethod] - public void TestGreatThanAndEqual() - { - sliceTest = new byte[] { 0x01 }; - Slice slice = new byte[] { 0x00 }; - bool result = sliceTest >= slice; - Assert.AreEqual(true, result); - slice = new byte[] { 0x01 }; - result = sliceTest >= slice; - Assert.AreEqual(true, result); - slice = new byte[] { 0x02 }; - result = sliceTest >= slice; - Assert.AreEqual(false, result); - } - - [TestMethod] - public void TestEqual() - { - sliceTest = new byte[] { 0x01 }; - Slice slice = new byte[] { 0x00 }; - bool result = sliceTest == slice; - Assert.AreEqual(false, result); - slice = new byte[] { 0x01 }; - result = sliceTest == slice; - Assert.AreEqual(true, result); - slice = new byte[] { 0x02 }; - result = sliceTest == slice; - Assert.AreEqual(false, result); - } - - [TestMethod] - public void TestUnequal() - { - sliceTest = new byte[] { 0x01 }; - Slice slice = new byte[] { 0x00 }; - bool result = sliceTest != slice; - Assert.AreEqual(true, result); - slice = new byte[] { 0x01 }; - result = sliceTest != slice; - Assert.AreEqual(false, result); - } - } -} From 87981a85714985476be2cca852ed3a1f44d6c7e5 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 2 Dec 2019 18:20:10 +0800 Subject: [PATCH 159/305] Fix `InvPayload.CreateGroup()` (#1314) --- src/neo/Network/P2P/Payloads/InvPayload.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/neo/Network/P2P/Payloads/InvPayload.cs b/src/neo/Network/P2P/Payloads/InvPayload.cs index 662398e6a4..b54a834ed5 100644 --- a/src/neo/Network/P2P/Payloads/InvPayload.cs +++ b/src/neo/Network/P2P/Payloads/InvPayload.cs @@ -26,11 +26,15 @@ public static InvPayload Create(InventoryType type, params UInt256[] hashes) public static IEnumerable CreateGroup(InventoryType type, UInt256[] hashes) { for (int i = 0; i < hashes.Length; i += MaxHashesCount) + { + int endIndex = i + MaxHashesCount; + if (endIndex > hashes.Length) endIndex = hashes.Length; yield return new InvPayload { Type = type, - Hashes = hashes[i..(i + MaxHashesCount)] + Hashes = hashes[i..endIndex] }; + } } void ISerializable.Deserialize(BinaryReader reader) From f5fdea7ce1799f5e07e0de0bada53b367d282310 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 2 Dec 2019 14:33:55 +0100 Subject: [PATCH 160/305] Add Blockchain UT for parallel TX (#1310) --- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 77 ++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index f46036edca..f419e948d4 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -1,9 +1,17 @@ +using Akka.TestKit.Xunit2; using FluentAssertions; 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.SmartContract.Native.Tokens; +using Neo.Wallets; +using Neo.Wallets.NEP6; +using System.Linq; +using System.Reflection; namespace Neo.UnitTests.Ledger { @@ -34,7 +42,7 @@ public static TestHeader Cast(Header input) } [TestClass] - public class UT_Blockchain + public class UT_Blockchain : TestKit { private NeoSystem system; Transaction txSample = Blockchain.GenesisBlock.Transactions[0]; @@ -96,5 +104,72 @@ public void TestGetTransaction() Blockchain.Singleton.GetTransaction(UInt256.Zero).Should().BeNull(); Blockchain.Singleton.GetTransaction(txSample.Hash).Should().NotBeNull(); } + + [TestMethod] + public void TestValidTransaction() + { + var senderProbe = CreateTestProbe(); + var snapshot = Blockchain.Singleton.GetSnapshot(); + var walletA = TestUtils.GenerateTestWallet(); + + using (var unlockA = walletA.Unlock("123")) + { + var acc = walletA.CreateAccount(); + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem + { + Value = new Nep5AccountState().ToByteArray() + }); + + entry.Value = new Nep5AccountState() + { + Balance = 100_000_000 * NativeContract.GAS.Factor + } + .ToByteArray(); + + snapshot.Commit(); + + typeof(Blockchain) + .GetMethod("UpdateCurrentSnapshot", BindingFlags.Instance | BindingFlags.NonPublic) + .Invoke(Blockchain.Singleton, null); + + // Make transaction + + var tx = CreateValidTx(walletA, acc.ScriptHash, 0); + + senderProbe.Send(system.Blockchain, tx); + senderProbe.ExpectMsg(RelayResultReason.Succeed); + + senderProbe.Send(system.Blockchain, tx); + senderProbe.ExpectMsg(RelayResultReason.AlreadyExists); + } + } + + private Transaction CreateValidTx(NEP6Wallet wallet, UInt160 account, uint nonce) + { + var tx = wallet.MakeTransaction(new TransferOutput[] + { + new TransferOutput() + { + AssetId = NativeContract.GAS.Hash, + ScriptHash = account, + Value = new BigDecimal(1,8) + } + }, + account); + + tx.Nonce = nonce; + + var data = new ContractParametersContext(tx); + Assert.IsTrue(wallet.Sign(data)); + Assert.IsTrue(data.Completed); + + tx.Witnesses = data.GetWitnesses(); + + return tx; + } } } From 9cf1a74b9063f6e1579990f03c85ca6d9dc35b6f Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 2 Dec 2019 21:47:10 +0800 Subject: [PATCH 161/305] Allow users to more easily select MemoryStore. (#1311) --- src/neo/NeoSystem.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 047ee71604..c5e0d0d8e5 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -33,7 +33,9 @@ public class NeoSystem : IDisposable public NeoSystem(string storageEngine = null) { Plugin.LoadPlugins(this); - this.store = storageEngine is null ? new MemoryStore() : Plugin.Storages[storageEngine].GetStore(); + this.store = string.IsNullOrEmpty(storageEngine) || storageEngine == nameof(MemoryStore) + ? new MemoryStore() + : Plugin.Storages[storageEngine].GetStore(); 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)); From e73b5a08b9519a8f80b66ab3600674a4148411c4 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 2 Dec 2019 22:08:18 +0800 Subject: [PATCH 162/305] Remove meaningless UT (#1317) --- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index f419e948d4..d93555262a 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -54,12 +54,6 @@ public void Initialize() Blockchain.Singleton.MemPool.TryAdd(txSample.Hash, txSample); } - [TestMethod] - public void TestConstructor() - { - system.ActorSystem.ActorOf(Blockchain.Props(system, Blockchain.Singleton.Store)).Should().NotBeSameAs(system.Blockchain); - } - [TestMethod] public void TestContainsBlock() { From 6313dd1e3a3a6f7db226a5a5d81940790c02646b Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 3 Dec 2019 09:03:53 +0800 Subject: [PATCH 163/305] Improve plugins (#1312) --- src/neo/Consensus/ConsensusService.cs | 2 +- src/neo/{Plugins => }/LogLevel.cs | 2 +- src/neo/NeoSystem.cs | 3 +- src/neo/Plugins/IP2PPlugin.cs | 4 +- src/neo/Plugins/IPersistencePlugin.cs | 6 +- src/neo/Plugins/Plugin.cs | 152 ++++++++++--------- src/neo/Utility.cs | 7 + tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 2 +- tests/neo.UnitTests/Plugins/TestLogPlugin.cs | 4 +- tests/neo.UnitTests/Plugins/UT_Plugin.cs | 8 - 10 files changed, 101 insertions(+), 89 deletions(-) rename src/neo/{Plugins => }/LogLevel.cs (84%) diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 57151481d7..5ead54d233 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -189,7 +189,7 @@ private void InitializeConsensus(byte viewNumber) private void Log(string message, LogLevel level = LogLevel.Info) { - Plugin.Log(nameof(ConsensusService), level, message); + Utility.Log(nameof(ConsensusService), level, message); } private void OnChangeViewReceived(ConsensusPayload payload, ChangeView message) diff --git a/src/neo/Plugins/LogLevel.cs b/src/neo/LogLevel.cs similarity index 84% rename from src/neo/Plugins/LogLevel.cs rename to src/neo/LogLevel.cs index 8f6e0b9df7..1aeef70a10 100644 --- a/src/neo/Plugins/LogLevel.cs +++ b/src/neo/LogLevel.cs @@ -1,4 +1,4 @@ -namespace Neo.Plugins +namespace Neo { public enum LogLevel : byte { diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index c5e0d0d8e5..e9e8335bb4 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -39,7 +39,8 @@ public NeoSystem(string storageEngine = null) 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)); - Plugin.NotifyPluginsLoadedAfterSystemConstructed(); + foreach (var plugin in Plugin.Plugins) + plugin.OnPluginsLoaded(); } public void Dispose() diff --git a/src/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs index e2043104fc..40c083545b 100644 --- a/src/neo/Plugins/IP2PPlugin.cs +++ b/src/neo/Plugins/IP2PPlugin.cs @@ -5,7 +5,7 @@ namespace Neo.Plugins { public interface IP2PPlugin { - bool OnP2PMessage(Message message); - bool OnConsensusMessage(ConsensusPayload payload); + bool OnP2PMessage(Message message) => true; + bool OnConsensusMessage(ConsensusPayload payload) => true; } } diff --git a/src/neo/Plugins/IPersistencePlugin.cs b/src/neo/Plugins/IPersistencePlugin.cs index 14a3316115..2884607b39 100644 --- a/src/neo/Plugins/IPersistencePlugin.cs +++ b/src/neo/Plugins/IPersistencePlugin.cs @@ -7,8 +7,8 @@ namespace Neo.Plugins { public interface IPersistencePlugin { - void OnPersist(StoreView snapshot, IReadOnlyList applicationExecutedList); - void OnCommit(StoreView snapshot); - bool ShouldThrowExceptionFromCommit(Exception ex); + void OnPersist(StoreView snapshot, IReadOnlyList applicationExecutedList) { } + void OnCommit(StoreView snapshot) { } + bool ShouldThrowExceptionFromCommit(Exception ex) => false; } } diff --git a/src/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs index caacc17d85..918a1e4ae3 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -5,34 +5,35 @@ using System.Linq; using System.Reflection; using System.Threading; +using static System.IO.Path; namespace Neo.Plugins { public abstract class Plugin : IDisposable { public static readonly List Plugins = new List(); - private static readonly List Loggers = new List(); + internal static readonly List Loggers = new List(); internal static readonly Dictionary Storages = new Dictionary(); internal static readonly List RpcPlugins = new List(); internal static readonly List PersistencePlugins = new List(); internal static readonly List P2PPlugins = new List(); internal static readonly List TxObserverPlugins = new List(); - private static readonly string pluginsPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Plugins"); + public static readonly string PluginsDirectory = Combine(GetDirectoryName(Assembly.GetEntryAssembly().Location), "Plugins"); private static readonly FileSystemWatcher configWatcher; - private static int suspend = 0; - protected static NeoSystem System { get; private set; } + public virtual string ConfigFile => Combine(PluginsDirectory, GetType().Assembly.GetName().Name, "config.json"); public virtual string Name => GetType().Name; + public string Path => Combine(PluginsDirectory, GetType().Assembly.ManifestModule.ScopeName); + protected static NeoSystem System { get; private set; } public virtual Version Version => GetType().Assembly.GetName().Version; - public virtual string ConfigFile => Path.Combine(pluginsPath, GetType().Assembly.GetName().Name, "config.json"); static Plugin() { - if (Directory.Exists(pluginsPath)) + if (Directory.Exists(PluginsDirectory)) { - configWatcher = new FileSystemWatcher(pluginsPath, "*.json") + configWatcher = new FileSystemWatcher(PluginsDirectory) { EnableRaisingEvents = true, IncludeSubdirectories = true, @@ -58,74 +59,112 @@ protected Plugin() Configure(); } - public abstract void Configure(); - - protected virtual void OnPluginsLoaded() + protected virtual void Configure() { } private static void ConfigWatcher_Changed(object sender, FileSystemEventArgs e) { - foreach (var plugin in Plugins) + switch (GetExtension(e.Name)) { - if (plugin.ConfigFile == e.FullPath) - { - plugin.Configure(); - plugin.Log($"Reloaded config for {plugin.Name}"); + case ".json": + Plugins.FirstOrDefault(p => p.ConfigFile == e.FullPath)?.Configure(); + break; + case ".dll": + if (e.ChangeType != WatcherChangeTypes.Created) return; + if (GetDirectoryName(e.FullPath) != PluginsDirectory) return; + try + { + LoadPlugin(Assembly.Load(File.ReadAllBytes(e.FullPath))); + } + catch { } break; - } } } + private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + if (args.Name.Contains(".resources")) + return null; + + Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name); + if (assembly != null) + return assembly; + + AssemblyName an = new AssemblyName(args.Name); + string filename = an.Name + ".dll"; + + try + { + return Assembly.Load(File.ReadAllBytes(filename)); + } + catch (Exception ex) + { + Utility.Log(nameof(Plugin), LogLevel.Error, $"Failed to resolve assembly or its dependency: {ex.Message}"); + return null; + } + } + + public virtual void Dispose() + { + } + protected IConfigurationSection GetConfiguration() { return new ConfigurationBuilder().AddJsonFile(ConfigFile, optional: true).Build().GetSection("PluginConfiguration"); } - internal static void LoadPlugins(NeoSystem system) + private static void LoadPlugin(Assembly assembly) { - System = system; - if (!Directory.Exists(pluginsPath)) return; - foreach (string filename in Directory.EnumerateFiles(pluginsPath, "*.dll", SearchOption.TopDirectoryOnly)) + foreach (Type type in assembly.ExportedTypes) { - var file = File.ReadAllBytes(filename); - Assembly assembly = Assembly.Load(file); - foreach (Type type in assembly.ExportedTypes) - { - if (!type.IsSubclassOf(typeof(Plugin))) continue; - if (type.IsAbstract) continue; + if (!type.IsSubclassOf(typeof(Plugin))) continue; + if (type.IsAbstract) continue; - ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes); - try - { - constructor?.Invoke(null); - } - catch (Exception ex) - { - Log(nameof(Plugin), LogLevel.Error, $"Failed to initialize plugin: {ex.Message}"); - } + ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes); + try + { + constructor?.Invoke(null); + } + catch (Exception ex) + { + Utility.Log(nameof(Plugin), LogLevel.Error, $"Failed to initialize plugin: {ex.Message}"); } } } - internal static void NotifyPluginsLoadedAfterSystemConstructed() + internal static void LoadPlugins(NeoSystem system) { - foreach (var plugin in Plugins) - plugin.OnPluginsLoaded(); + System = system; + if (!Directory.Exists(PluginsDirectory)) return; + List assemblies = new List(); + foreach (string filename in Directory.EnumerateFiles(PluginsDirectory, "*.dll", SearchOption.TopDirectoryOnly)) + { + try + { + assemblies.Add(Assembly.Load(File.ReadAllBytes(filename))); + } + catch { } + } + foreach (Assembly assembly in assemblies) + { + LoadPlugin(assembly); + } } protected void Log(string message, LogLevel level = LogLevel.Info) { - Log($"{nameof(Plugin)}:{Name}", level, message); + Utility.Log($"{nameof(Plugin)}:{Name}", level, message); } - public static void Log(string source, LogLevel level, string message) + protected virtual bool OnMessage(object message) { - foreach (ILogPlugin plugin in Loggers) - plugin.Log(source, level, message); + return false; } - protected virtual bool OnMessage(object message) => false; + internal protected virtual void OnPluginsLoaded() + { + } protected static bool ResumeNodeStartup() { @@ -148,32 +187,5 @@ protected static void SuspendNodeStartup() Interlocked.Increment(ref suspend); System.SuspendNodeStartup(); } - - private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) - { - if (args.Name.Contains(".resources")) - return null; - - Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name); - if (assembly != null) - return assembly; - - AssemblyName an = new AssemblyName(args.Name); - string filename = an.Name + ".dll"; - - try - { - return Assembly.LoadFrom(filename); - } - catch (Exception ex) - { - Log(nameof(Plugin), LogLevel.Error, $"Failed to resolve assembly or its dependency: {ex.Message}"); - return null; - } - } - - public virtual void Dispose() - { - } } } diff --git a/src/neo/Utility.cs b/src/neo/Utility.cs index 76c5b80794..b6402a91c6 100644 --- a/src/neo/Utility.cs +++ b/src/neo/Utility.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.Configuration; using Neo.Cryptography.ECC; +using Neo.Plugins; using Neo.SmartContract; using Neo.Wallets; using System; @@ -72,5 +73,11 @@ public static IConfigurationRoot LoadConfig(string config) .AddJsonFile(configFile, true) .Build(); } + + public static void Log(string source, LogLevel level, string message) + { + foreach (ILogPlugin plugin in Plugin.Loggers) + plugin.Log(source, level, message); + } } } diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 510f7340ed..38a456bcf3 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -19,7 +19,7 @@ namespace Neo.UnitTests.Ledger { internal class TestIMemoryPoolTxObserverPlugin : Plugin, IMemoryPoolTxObserverPlugin { - public override void Configure() { } + protected override void Configure() { } public void TransactionAdded(Transaction tx) { } public void TransactionsRemoved(MemoryPoolTxRemovalReason reason, IEnumerable transactions) { } } diff --git a/tests/neo.UnitTests/Plugins/TestLogPlugin.cs b/tests/neo.UnitTests/Plugins/TestLogPlugin.cs index d9f0c824d2..215715d236 100644 --- a/tests/neo.UnitTests/Plugins/TestLogPlugin.cs +++ b/tests/neo.UnitTests/Plugins/TestLogPlugin.cs @@ -9,9 +9,9 @@ public class TestLogPlugin : Plugin, ILogPlugin public string Output { set; get; } - public override void Configure() { } + protected override void Configure() { } - public new void Log(string source, LogLevel level, string message) + void ILogPlugin.Log(string source, LogLevel level, string message) { Output = source + "_" + level.ToString() + "_" + message; } diff --git a/tests/neo.UnitTests/Plugins/UT_Plugin.cs b/tests/neo.UnitTests/Plugins/UT_Plugin.cs index 7c3224c82a..cee7194c47 100644 --- a/tests/neo.UnitTests/Plugins/UT_Plugin.cs +++ b/tests/neo.UnitTests/Plugins/UT_Plugin.cs @@ -54,14 +54,6 @@ public void TestSendMessage() } } - [TestMethod] - public void TestNotifyPluginsLoadedAfterSystemConstructed() - { - var pp = new TestLogPlugin(); - Action action = () => Plugin.NotifyPluginsLoadedAfterSystemConstructed(); - action.Should().NotThrow(); - } - [TestMethod] public void TestResumeNodeStartupAndSuspendNodeStartup() { From 15716dd112338145bb635c837536d2530c2c859a Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 3 Dec 2019 09:31:17 +0100 Subject: [PATCH 164/305] Fix typo (#1319) --- .github/workflows/dotnetcore.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 2aae8920e8..2f31794695 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -13,7 +13,7 @@ jobs: Test: runs-on: ubuntu-latest steps: - - name: Chectout + - name: Checkout uses: actions/checkout@v1 - name: Setup .NET Core uses: actions/setup-dotnet@v1 @@ -37,7 +37,7 @@ jobs: needs: Test runs-on: ubuntu-latest steps: - - name: Chectout + - name: Checkout uses: actions/checkout@v1 - name: Setup .NET Core uses: actions/setup-dotnet@v1 @@ -59,7 +59,7 @@ jobs: needs: Test runs-on: ubuntu-latest steps: - - name: Chectout + - name: Checkout uses: actions/checkout@v1 - name: Setup .NET Core uses: actions/setup-dotnet@v1 From 56ccec75a169b1b3888eb84d1216561b242e6154 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 4 Dec 2019 08:28:23 +0800 Subject: [PATCH 165/305] Neo.VM v3.0.0-CI00174 (#1320) --- src/neo/Ledger/ContractState.cs | 13 ++---- src/neo/Network/P2P/Payloads/Block.cs | 42 +++++++++---------- src/neo/Network/P2P/Payloads/Transaction.cs | 38 ++++++++--------- src/neo/SmartContract/Helper.cs | 3 +- src/neo/SmartContract/IInteroperable.cs | 3 +- src/neo/SmartContract/InteropDescriptor.cs | 7 ++-- src/neo/SmartContract/InteropService.NEO.cs | 6 +-- src/neo/SmartContract/InteropService.cs | 22 +++++----- .../SmartContract/Iterators/ArrayWrapper.cs | 4 +- src/neo/SmartContract/JsonSerializer.cs | 20 ++++----- .../SmartContract/Native/NativeContract.cs | 14 +++---- .../SmartContract/Native/PolicyContract.cs | 22 +++++----- .../SmartContract/Native/Tokens/NeoToken.cs | 22 +++++----- .../Native/Tokens/Nep5AccountState.cs | 2 +- .../SmartContract/Native/Tokens/Nep5Token.cs | 20 ++++----- src/neo/SmartContract/StackItemSerializer.cs | 22 +++++----- src/neo/VM/Helper.cs | 10 ++--- src/neo/neo.csproj | 2 +- .../Native/Tokens/UT_NeoToken.cs | 2 +- .../SmartContract/UT_InteropService.NEO.cs | 4 +- .../SmartContract/UT_StackItemSerializer.cs | 16 +++---- 21 files changed, 140 insertions(+), 154 deletions(-) diff --git a/src/neo/Ledger/ContractState.cs b/src/neo/Ledger/ContractState.cs index ea1860668e..f6a50b67d0 100644 --- a/src/neo/Ledger/ContractState.cs +++ b/src/neo/Ledger/ContractState.cs @@ -6,6 +6,7 @@ using Neo.VM.Types; using System; using System.IO; +using Array = Neo.VM.Types.Array; namespace Neo.Ledger { @@ -76,17 +77,9 @@ public static ContractState FromJson(JObject json) return contractState; } - public StackItem ToStackItem() + public StackItem ToStackItem(ReferenceCounter referenceCounter) { - return new VM.Types.Array - ( - new StackItem[] - { - new ByteArray(Script), - new VM.Types.Boolean(HasStorage), - new VM.Types.Boolean(Payable), - } - ); + return new Array(referenceCounter, new StackItem[] { Script, HasStorage, Payable }); } } } diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index c6ada9adac..a3623a4c94 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -3,11 +3,13 @@ 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 { @@ -132,30 +134,24 @@ public TrimmedBlock Trim() }; } - public StackItem ToStackItem() + public StackItem ToStackItem(ReferenceCounter referenceCounter) { - return new VM.Types.Array - ( - new StackItem[] - { - // Computed properties - new ByteArray(Hash.ToArray()), - - // BlockBase properties - new Integer(Version), - new ByteArray(PrevHash.ToArray()), - new ByteArray(MerkleRoot.ToArray()), - new Integer(Timestamp), - new Integer(Index), - new ByteArray(NextConsensus.ToArray()), - // Witness - - // Block properties - // Count - // ConsensusData - new Integer(Transactions.Length) - } - ); + 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/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index f213722660..2b6523a6aa 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -5,6 +5,7 @@ using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; +using Neo.VM; using Neo.VM.Types; using Neo.Wallets; using System; @@ -12,6 +13,7 @@ using System.IO; using System.Linq; using System.Numerics; +using Array = Neo.VM.Types.Array; namespace Neo.Network.P2P.Payloads { @@ -300,28 +302,22 @@ public RelayResultReason VerifyParallelParts(StoreView snapshot) return RelayResultReason.Succeed; } - public StackItem ToStackItem() + public StackItem ToStackItem(ReferenceCounter referenceCounter) { - return new VM.Types.Array - ( - new StackItem[] - { - // Computed properties - new ByteArray(Hash.ToArray()), - - // Transaction properties - new Integer(Version), - new Integer(Nonce), - new ByteArray(Sender.ToArray()), - new Integer(SystemFee), - new Integer(NetworkFee), - new Integer(ValidUntilBlock), - // Attributes - // Cosigners - new ByteArray(Script), - // Witnesses - } - ); + return new Array(referenceCounter, new StackItem[] + { + // Computed properties + Hash.ToArray(), + + // Transaction properties + (int)Version, + Nonce, + Sender.ToArray(), + SystemFee, + NetworkFee, + ValidUntilBlock, + Script, + }); } } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index d544edb3d9..28c6045dcd 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -2,6 +2,7 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.VM; +using Neo.VM.Types; using System; using System.Buffers.Binary; using System.Text; @@ -118,7 +119,7 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snap engine.LoadScript(verification); engine.LoadScript(verifiable.Witnesses[i].InvocationScript); if (engine.Execute() == VMState.FAULT) return false; - if (!engine.ResultStack.TryPop(out var result) || !result.ToBoolean()) return false; + if (!engine.ResultStack.TryPop(out StackItem result) || !result.ToBoolean()) return false; } } return true; diff --git a/src/neo/SmartContract/IInteroperable.cs b/src/neo/SmartContract/IInteroperable.cs index 047ce05168..41807cd10a 100644 --- a/src/neo/SmartContract/IInteroperable.cs +++ b/src/neo/SmartContract/IInteroperable.cs @@ -1,9 +1,10 @@ +using Neo.VM; using Neo.VM.Types; namespace Neo.SmartContract { public interface IInteroperable { - StackItem ToStackItem(); + StackItem ToStackItem(ReferenceCounter referenceCounter); } } diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index f446d4fbfc..9ae9e5c992 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -1,5 +1,4 @@ using Neo.VM; -using Neo.VM.Types; using System; namespace Neo.SmartContract @@ -10,7 +9,7 @@ internal class InteropDescriptor public uint Hash { get; } public Func Handler { get; } public long Price { get; } - public Func, long> PriceCalculator { get; } + public Func PriceCalculator { get; } public TriggerType AllowedTriggers { get; } public InteropDescriptor(string method, Func handler, long price, TriggerType allowedTriggers) @@ -19,7 +18,7 @@ public InteropDescriptor(string method, Func handler, l this.Price = price; } - public InteropDescriptor(string method, Func handler, Func, long> priceCalculator, TriggerType allowedTriggers) + public InteropDescriptor(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers) : this(method, handler, allowedTriggers) { this.PriceCalculator = priceCalculator; @@ -33,7 +32,7 @@ private InteropDescriptor(string method, Func handler, this.AllowedTriggers = allowedTriggers; } - public long GetPrice(RandomAccessStack stack) + public long GetPrice(EvaluationStack stack) { return PriceCalculator is null ? Price : PriceCalculator(stack); } diff --git a/src/neo/SmartContract/InteropService.NEO.cs b/src/neo/SmartContract/InteropService.NEO.cs index 1e0034050d..b57cb3da22 100644 --- a/src/neo/SmartContract/InteropService.NEO.cs +++ b/src/neo/SmartContract/InteropService.NEO.cs @@ -43,7 +43,7 @@ static InteropService() Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application); } - private static long GetECDsaCheckMultiSigPrice(RandomAccessStack stack) + private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack) { if (stack.Count < 2) return 0; var item = stack.Peek(1); @@ -54,7 +54,7 @@ private static long GetECDsaCheckMultiSigPrice(RandomAccessStack stac return GetPrice(Neo_Crypto_ECDsaVerify, stack) * n; } - private static long GetDeploymentPrice(RandomAccessStack stack) + private static long GetDeploymentPrice(EvaluationStack stack) { int size = stack.Peek(0).GetByteLength() + stack.Peek(1).GetByteLength(); return GasPerByte * size; @@ -378,7 +378,7 @@ private static bool Json_Deserialize(ApplicationEngine engine) { var json = engine.CurrentContext.EvaluationStack.Pop().GetString(); var obj = JObject.Parse(json, 10); - var item = JsonSerializer.Deserialize(obj); + var item = JsonSerializer.Deserialize(obj, engine.ReferenceCounter); engine.CurrentContext.EvaluationStack.Push(item); return true; diff --git a/src/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs index c1085ff1c4..cd30deca8f 100644 --- a/src/neo/SmartContract/InteropService.cs +++ b/src/neo/SmartContract/InteropService.cs @@ -112,7 +112,7 @@ private static bool CheckStorageContext(ApplicationEngine engine, StorageContext return true; } - public static long GetPrice(uint hash, RandomAccessStack stack) + public static long GetPrice(uint hash, EvaluationStack stack) { return methods[hash].GetPrice(stack); } @@ -122,7 +122,7 @@ public static long GetPrice(uint hash, RandomAccessStack stack) return methods.ToDictionary(p => p.Key, p => p.Value.Method); } - private static long GetStoragePrice(RandomAccessStack stack) + private static long GetStoragePrice(EvaluationStack stack) { return (stack.Peek(1).GetByteLength() + stack.Peek(2).GetByteLength()) * GasPerByte; } @@ -143,7 +143,7 @@ private static uint Register(string method, Func handle return descriptor.Hash; } - private static uint Register(string method, Func handler, Func, long> priceCalculator, TriggerType allowedTriggers) + private static uint Register(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers) { InteropDescriptor descriptor = new InteropDescriptor(method, handler, priceCalculator, allowedTriggers); methods.Add(descriptor.Hash, descriptor); @@ -153,7 +153,7 @@ private static uint Register(string method, Func handle private static bool ExecutionEngine_GetScriptContainer(ApplicationEngine engine) { engine.CurrentContext.EvaluationStack.Push( - engine.ScriptContainer is IInteroperable value ? value.ToStackItem() : + engine.ScriptContainer is IInteroperable value ? value.ToStackItem(engine.ReferenceCounter) : StackItem.FromInterface(engine.ScriptContainer)); return true; } @@ -291,8 +291,8 @@ private static bool Runtime_GetNotifications(ApplicationEngine engine) notifications = notifications.Where(p => p.ScriptHash == hash); } - if (!engine.CheckArraySize(notifications.Count())) return false; - engine.CurrentContext.EvaluationStack.Push(notifications.Select(u => new VM.Types.Array(new StackItem[] { u.ScriptHash.ToArray(), u.State })).ToArray()); + if (notifications.Count() > engine.MaxStackSize) return false; + engine.Push(new Array(engine.ReferenceCounter, notifications.Select(u => new Array(engine.ReferenceCounter, new[] { u.ScriptHash.ToArray(), u.State })))); return true; } @@ -312,7 +312,7 @@ private static bool Runtime_Deserialize(ApplicationEngine engine) StackItem item; try { - item = StackItemSerializer.Deserialize(engine.CurrentContext.EvaluationStack.Pop().GetSpan(), engine.MaxArraySize, engine.MaxItemSize); + item = StackItemSerializer.Deserialize(engine.CurrentContext.EvaluationStack.Pop().GetSpan(), engine.MaxItemSize, engine.ReferenceCounter); } catch (FormatException) { @@ -347,7 +347,7 @@ private static bool Blockchain_GetBlock(ApplicationEngine engine) if (block == null) engine.CurrentContext.EvaluationStack.Push(StackItem.Null); else - engine.CurrentContext.EvaluationStack.Push(block.ToStackItem()); + engine.CurrentContext.EvaluationStack.Push(block.ToStackItem(engine.ReferenceCounter)); return true; } @@ -358,7 +358,7 @@ private static bool Blockchain_GetTransaction(ApplicationEngine engine) if (tx == null) engine.CurrentContext.EvaluationStack.Push(StackItem.Null); else - engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem()); + engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem(engine.ReferenceCounter)); return true; } @@ -395,7 +395,7 @@ private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) if (tx == null) engine.CurrentContext.EvaluationStack.Push(StackItem.Null); else - engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem()); + engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem(engine.ReferenceCounter)); } return true; } @@ -407,7 +407,7 @@ private static bool Blockchain_GetContract(ApplicationEngine engine) if (contract == null) engine.CurrentContext.EvaluationStack.Push(StackItem.Null); else - engine.CurrentContext.EvaluationStack.Push(contract.ToStackItem()); + engine.CurrentContext.EvaluationStack.Push(contract.ToStackItem(engine.ReferenceCounter)); return true; } diff --git a/src/neo/SmartContract/Iterators/ArrayWrapper.cs b/src/neo/SmartContract/Iterators/ArrayWrapper.cs index 84c248c679..c010466c99 100644 --- a/src/neo/SmartContract/Iterators/ArrayWrapper.cs +++ b/src/neo/SmartContract/Iterators/ArrayWrapper.cs @@ -6,10 +6,10 @@ namespace Neo.SmartContract.Iterators { internal class ArrayWrapper : IIterator { - private readonly IList array; + private readonly IReadOnlyList array; private int index = -1; - public ArrayWrapper(IList array) + public ArrayWrapper(IReadOnlyList array) { this.array = array; } diff --git a/src/neo/SmartContract/JsonSerializer.cs b/src/neo/SmartContract/JsonSerializer.cs index 14edaa5caa..b0ed09383c 100644 --- a/src/neo/SmartContract/JsonSerializer.cs +++ b/src/neo/SmartContract/JsonSerializer.cs @@ -4,8 +4,8 @@ using System; using System.Linq; using System.Numerics; -using VMArray = Neo.VM.Types.Array; -using VMBoolean = Neo.VM.Types.Boolean; +using Array = Neo.VM.Types.Array; +using Boolean = Neo.VM.Types.Boolean; namespace Neo.SmartContract { @@ -20,7 +20,7 @@ public static JObject Serialize(StackItem item) { switch (item) { - case VMArray array: + case Array array: { return array.Select(p => Serialize(p)).ToArray(); } @@ -35,7 +35,7 @@ public static JObject Serialize(StackItem item) return integer.ToString(); return (double)num.GetBigInteger(); } - case VMBoolean boolean: + case Boolean boolean: { return boolean.ToBoolean(); } @@ -66,7 +66,7 @@ public static JObject Serialize(StackItem item) /// /// Json /// Return stack item - public static StackItem Deserialize(JObject json) + public static StackItem Deserialize(JObject json, ReferenceCounter referenceCounter = null) { switch (json) { @@ -76,7 +76,7 @@ public static StackItem Deserialize(JObject json) } case JArray array: { - return array.Select(p => Deserialize(p)).ToList(); + return new Array(referenceCounter, array.Select(p => Deserialize(p, referenceCounter))); } case JString str: { @@ -90,18 +90,18 @@ public static StackItem Deserialize(JObject json) } case JBoolean boolean: { - return new VMBoolean(boolean.Value); + return new Boolean(boolean.Value); } case JObject obj: { - var item = new Map(); + var item = new Map(referenceCounter); foreach (var entry in obj.Properties) { var key = entry.Key; - var value = Deserialize(entry.Value); + var value = Deserialize(entry.Value, referenceCounter); - item.Add(key, value); + item[key] = value; } return item; diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index b9d3c9ecba..80edebd11b 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -10,7 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using VMArray = Neo.VM.Types.Array; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native { @@ -57,7 +57,7 @@ protected NativeContract() if (attribute.SafeMethod) safeMethods.Add(name); methods.Add(name, new ContractMethodMetadata { - Delegate = (Func)method.CreateDelegate(typeof(Func), this), + Delegate = (Func)method.CreateDelegate(typeof(Func), this), Price = attribute.Price }); } @@ -89,7 +89,7 @@ internal bool Invoke(ApplicationEngine engine) if (!engine.CurrentScriptHash.Equals(Hash)) return false; string operation = engine.CurrentContext.EvaluationStack.Pop().GetString(); - VMArray args = (VMArray)engine.CurrentContext.EvaluationStack.Pop(); + Array args = (Array)engine.CurrentContext.EvaluationStack.Pop(); if (!methods.TryGetValue(operation, out ContractMethodMetadata method)) return false; StackItem result = method.Delegate(engine, args); @@ -97,7 +97,7 @@ internal bool Invoke(ApplicationEngine engine) return true; } - internal long GetPrice(RandomAccessStack stack) + internal long GetPrice(EvaluationStack stack) { return methods.TryGetValue(stack.Peek().GetString(), out ContractMethodMetadata method) ? method.Price : 0; } @@ -110,7 +110,7 @@ internal virtual bool Initialize(ApplicationEngine engine) } [ContractMethod(0, ContractParameterType.Boolean)] - protected StackItem OnPersist(ApplicationEngine engine, VMArray args) + protected StackItem OnPersist(ApplicationEngine engine, Array args) { if (engine.Trigger != TriggerType.System) return false; return OnPersist(engine); @@ -122,9 +122,9 @@ protected virtual bool OnPersist(ApplicationEngine engine) } [ContractMethod(0, ContractParameterType.Array, Name = "supportedStandards", SafeMethod = true)] - protected StackItem SupportedStandardsMethod(ApplicationEngine engine, VMArray args) + protected StackItem SupportedStandardsMethod(ApplicationEngine engine, Array args) { - return SupportedStandards.Select(p => (StackItem)p).ToList(); + return new Array(engine.ReferenceCounter, SupportedStandards.Select(p => (StackItem)p)); } public ApplicationEngine TestCall(string operation, params object[] args) diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 84e230adf6..d51e1a75b3 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -11,7 +11,7 @@ using System; using System.Collections.Generic; using System.Linq; -using VMArray = Neo.VM.Types.Array; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native { @@ -67,7 +67,7 @@ internal override bool Initialize(ApplicationEngine engine) } [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] - private StackItem GetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray args) + private StackItem GetMaxTransactionsPerBlock(ApplicationEngine engine, Array args) { return GetMaxTransactionsPerBlock(engine.Snapshot); } @@ -78,7 +78,7 @@ public uint GetMaxTransactionsPerBlock(StoreView snapshot) } [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] - private StackItem GetMaxBlockSize(ApplicationEngine engine, VMArray args) + private StackItem GetMaxBlockSize(ApplicationEngine engine, Array args) { return GetMaxBlockSize(engine.Snapshot); } @@ -89,7 +89,7 @@ public uint GetMaxBlockSize(StoreView snapshot) } [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] - private StackItem GetFeePerByte(ApplicationEngine engine, VMArray args) + private StackItem GetFeePerByte(ApplicationEngine engine, Array args) { return GetFeePerByte(engine.Snapshot); } @@ -100,9 +100,9 @@ public long GetFeePerByte(StoreView snapshot) } [ContractMethod(0_01000000, ContractParameterType.Array, SafeMethod = true)] - private StackItem GetBlockedAccounts(ApplicationEngine engine, VMArray args) + private StackItem GetBlockedAccounts(ApplicationEngine engine, Array args) { - return GetBlockedAccounts(engine.Snapshot).Select(p => (StackItem)p.ToArray()).ToList(); + return new Array(engine.ReferenceCounter, GetBlockedAccounts(engine.Snapshot).Select(p => (StackItem)p.ToArray())); } public UInt160[] GetBlockedAccounts(StoreView snapshot) @@ -111,7 +111,7 @@ public UInt160[] GetBlockedAccounts(StoreView snapshot) } [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] - private StackItem SetMaxBlockSize(ApplicationEngine engine, VMArray args) + private StackItem SetMaxBlockSize(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; uint value = (uint)args[0].GetBigInteger(); @@ -122,7 +122,7 @@ private StackItem SetMaxBlockSize(ApplicationEngine engine, VMArray args) } [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] - private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray args) + private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; uint value = (uint)args[0].GetBigInteger(); @@ -132,7 +132,7 @@ private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, VMArray a } [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] - private StackItem SetFeePerByte(ApplicationEngine engine, VMArray args) + private StackItem SetFeePerByte(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; long value = (long)args[0].GetBigInteger(); @@ -142,7 +142,7 @@ private StackItem SetFeePerByte(ApplicationEngine engine, VMArray args) } [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] - private StackItem BlockAccount(ApplicationEngine engine, VMArray args) + private StackItem BlockAccount(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; UInt160 account = new UInt160(args[0].GetSpan().ToArray()); @@ -156,7 +156,7 @@ private StackItem BlockAccount(ApplicationEngine engine, VMArray args) } [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] - private StackItem UnblockAccount(ApplicationEngine engine, VMArray args) + private StackItem UnblockAccount(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; UInt160 account = new UInt160(args[0].GetSpan().ToArray()); diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index f9ab8d5d8f..3e8f79ad96 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -12,7 +12,7 @@ using System.IO; using System.Linq; using System.Numerics; -using VMArray = Neo.VM.Types.Array; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native.Tokens { @@ -117,7 +117,7 @@ protected override bool OnPersist(ApplicationEngine engine) } [ContractMethod(0_03000000, ContractParameterType.Integer, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "account", "end" }, SafeMethod = true)] - private StackItem UnclaimedGas(ApplicationEngine engine, VMArray args) + private StackItem UnclaimedGas(ApplicationEngine engine, Array args) { UInt160 account = new UInt160(args[0].GetSpan().ToArray()); uint end = (uint)args[1].GetBigInteger(); @@ -133,7 +133,7 @@ public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) } [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] - private StackItem RegisterValidator(ApplicationEngine engine, VMArray args) + private StackItem RegisterValidator(ApplicationEngine engine, Array args) { ECPoint pubkey = args[0].GetSpan().AsSerializable(); return RegisterValidator(engine.Snapshot, pubkey); @@ -151,10 +151,10 @@ private bool RegisterValidator(StoreView snapshot, ECPoint pubkey) } [ContractMethod(5_00000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" })] - private StackItem Vote(ApplicationEngine engine, VMArray args) + private StackItem Vote(ApplicationEngine engine, Array args) { UInt160 account = new UInt160(args[0].GetSpan().ToArray()); - ECPoint[] pubkeys = ((VMArray)args[1]).Select(p => p.GetSpan().AsSerializable()).ToArray(); + ECPoint[] pubkeys = ((Array)args[1]).Select(p => p.GetSpan().AsSerializable()).ToArray(); if (!InteropService.CheckWitness(engine, account)) return false; StorageKey key_account = CreateAccountKey(account); if (engine.Snapshot.Storages.TryGet(key_account) is null) return false; @@ -194,9 +194,9 @@ private StackItem Vote(ApplicationEngine engine, VMArray args) } [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] - private StackItem GetRegisteredValidators(ApplicationEngine engine, VMArray args) + private StackItem GetRegisteredValidators(ApplicationEngine engine, Array args) { - return GetRegisteredValidators(engine.Snapshot).Select(p => new Struct(new StackItem[] { p.PublicKey.ToArray(), p.Votes })).ToArray(); + return new Array(engine.ReferenceCounter, GetRegisteredValidators(engine.Snapshot).Select(p => new Struct(engine.ReferenceCounter, new StackItem[] { p.PublicKey.ToArray(), p.Votes }))); } public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetRegisteredValidators(StoreView snapshot) @@ -210,9 +210,9 @@ public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetRegisteredValidator } [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] - private StackItem GetValidators(ApplicationEngine engine, VMArray args) + private StackItem GetValidators(ApplicationEngine engine, Array args) { - return GetValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray()).ToList(); + return new Array(engine.ReferenceCounter, GetValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray())); } public ECPoint[] GetValidators(StoreView snapshot) @@ -235,9 +235,9 @@ public ECPoint[] GetValidators(StoreView snapshot) } [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] - private StackItem GetNextBlockValidators(ApplicationEngine engine, VMArray args) + private StackItem GetNextBlockValidators(ApplicationEngine engine, Array args) { - return GetNextBlockValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray()).ToList(); + return new Array(engine.ReferenceCounter, GetNextBlockValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray())); } public ECPoint[] GetNextBlockValidators(StoreView snapshot) diff --git a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs index e547f5615e..a7bd3fffc0 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs @@ -19,7 +19,7 @@ public Nep5AccountState(byte[] data) public void FromByteArray(byte[] data) { - FromStruct((Struct)StackItemSerializer.Deserialize(data, 16, 34)); + FromStruct((Struct)StackItemSerializer.Deserialize(data, 34)); } protected virtual void FromStruct(Struct @struct) diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index c2ec377c2a..0ee3952350 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -9,7 +9,7 @@ using System; using System.Collections.Generic; using System.Numerics; -using VMArray = Neo.VM.Types.Array; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native.Tokens { @@ -86,7 +86,7 @@ internal protected virtual void Mint(ApplicationEngine engine, UInt160 account, BigInteger totalSupply = new BigInteger(storage.Value); totalSupply += amount; storage.Value = totalSupply.ToByteArrayStandard(); - engine.SendNotification(Hash, new StackItem[] { "Transfer", StackItem.Null, account.ToArray(), amount }); + engine.SendNotification(Hash, new Array(engine.ReferenceCounter, new StackItem[] { "Transfer", StackItem.Null, account.ToArray(), amount })); } internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, BigInteger amount) @@ -112,29 +112,29 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, BigInteger totalSupply = new BigInteger(storage.Value); totalSupply -= amount; storage.Value = totalSupply.ToByteArrayStandard(); - engine.SendNotification(Hash, new StackItem[] { "Transfer", account.ToArray(), StackItem.Null, amount }); + engine.SendNotification(Hash, new Array(engine.ReferenceCounter, new StackItem[] { "Transfer", account.ToArray(), StackItem.Null, amount })); } [ContractMethod(0, ContractParameterType.String, Name = "name", SafeMethod = true)] - protected StackItem NameMethod(ApplicationEngine engine, VMArray args) + protected StackItem NameMethod(ApplicationEngine engine, Array args) { return Name; } [ContractMethod(0, ContractParameterType.String, Name = "symbol", SafeMethod = true)] - protected StackItem SymbolMethod(ApplicationEngine engine, VMArray args) + protected StackItem SymbolMethod(ApplicationEngine engine, Array args) { return Symbol; } [ContractMethod(0, ContractParameterType.Integer, Name = "decimals", SafeMethod = true)] - protected StackItem DecimalsMethod(ApplicationEngine engine, VMArray args) + protected StackItem DecimalsMethod(ApplicationEngine engine, Array args) { return (uint)Decimals; } [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] - protected StackItem TotalSupply(ApplicationEngine engine, VMArray args) + protected StackItem TotalSupply(ApplicationEngine engine, Array args) { return TotalSupply(engine.Snapshot); } @@ -147,7 +147,7 @@ public virtual BigInteger TotalSupply(StoreView snapshot) } [ContractMethod(0_01000000, ContractParameterType.Integer, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" }, SafeMethod = true)] - protected StackItem BalanceOf(ApplicationEngine engine, VMArray args) + protected StackItem BalanceOf(ApplicationEngine engine, Array args) { return BalanceOf(engine.Snapshot, new UInt160(args[0].GetSpan().ToArray())); } @@ -161,7 +161,7 @@ public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) } [ContractMethod(0_08000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" })] - protected StackItem Transfer(ApplicationEngine engine, VMArray args) + protected StackItem Transfer(ApplicationEngine engine, Array args) { UInt160 from = new UInt160(args[0].GetSpan().ToArray()); UInt160 to = new UInt160(args[1].GetSpan().ToArray()); @@ -222,7 +222,7 @@ protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 storage_to.Value = state_to.ToByteArray(); } } - engine.SendNotification(Hash, new StackItem[] { "Transfer", from.ToArray(), to.ToArray(), amount }); + engine.SendNotification(Hash, new Array(engine.ReferenceCounter, new StackItem[] { "Transfer", from.ToArray(), to.ToArray(), amount })); return true; } diff --git a/src/neo/SmartContract/StackItemSerializer.cs b/src/neo/SmartContract/StackItemSerializer.cs index 5ed61a1b72..a7f59ad974 100644 --- a/src/neo/SmartContract/StackItemSerializer.cs +++ b/src/neo/SmartContract/StackItemSerializer.cs @@ -13,25 +13,25 @@ namespace Neo.SmartContract { internal static class StackItemSerializer { - public static StackItem Deserialize(byte[] data, uint maxArraySize, uint maxItemSize) + public static StackItem Deserialize(byte[] data, uint maxItemSize, ReferenceCounter referenceCounter = null) { using MemoryStream ms = new MemoryStream(data, false); using BinaryReader reader = new BinaryReader(ms); - return Deserialize(reader, maxArraySize, maxItemSize); + return Deserialize(reader, maxItemSize, referenceCounter); } - public static unsafe StackItem Deserialize(ReadOnlySpan data, uint maxArraySize, uint maxItemSize) + public static unsafe StackItem Deserialize(ReadOnlySpan data, uint maxItemSize, ReferenceCounter referenceCounter = null) { if (data.IsEmpty) throw new FormatException(); fixed (byte* pointer = data) { using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, data.Length); using BinaryReader reader = new BinaryReader(ms); - return Deserialize(reader, maxArraySize, maxItemSize); + return Deserialize(reader, maxItemSize, referenceCounter); } } - private static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uint maxItemSize) + private static StackItem Deserialize(BinaryReader reader, uint maxItemSize, ReferenceCounter referenceCounter) { Stack deserialized = new Stack(); int undeserialized = 1; @@ -52,7 +52,7 @@ private static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uin case StackItemType.Array: case StackItemType.Struct: { - int count = (int)reader.ReadVarInt(maxArraySize); + int count = (int)reader.ReadVarInt(maxItemSize); deserialized.Push(new ContainerPlaceholder { Type = type, @@ -63,7 +63,7 @@ private static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uin break; case StackItemType.Map: { - int count = (int)reader.ReadVarInt(maxArraySize); + int count = (int)reader.ReadVarInt(maxItemSize); deserialized.Push(new ContainerPlaceholder { Type = type, @@ -88,24 +88,24 @@ private static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uin switch (placeholder.Type) { case StackItemType.Array: - Array array = new Array(); + Array array = new Array(referenceCounter); for (int i = 0; i < placeholder.ElementCount; i++) array.Add(stack_temp.Pop()); item = array; break; case StackItemType.Struct: - Struct @struct = new Struct(); + Struct @struct = new Struct(referenceCounter); for (int i = 0; i < placeholder.ElementCount; i++) @struct.Add(stack_temp.Pop()); item = @struct; break; case StackItemType.Map: - Map map = new Map(); + Map map = new Map(referenceCounter); for (int i = 0; i < placeholder.ElementCount; i++) { StackItem key = stack_temp.Pop(); StackItem value = stack_temp.Pop(); - map.Add((PrimitiveType)key, value); + map[(PrimitiveType)key] = value; } item = map; break; diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 5d42b1ae73..fa659246e9 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -7,8 +7,8 @@ using System.Linq; using System.Numerics; using System.Text; -using VMArray = Neo.VM.Types.Array; -using VMBoolean = Neo.VM.Types.Boolean; +using Array = Neo.VM.Types.Array; +using Boolean = Neo.VM.Types.Boolean; namespace Neo.VM { @@ -221,7 +221,7 @@ private static ContractParameter ToParameter(StackItem item, List<(StackItem, Co ContractParameter parameter = null; switch (item) { - case VMArray array: + case Array array: if (context is null) context = new List<(StackItem, ContractParameter)>(); else @@ -245,7 +245,7 @@ private static ContractParameter ToParameter(StackItem item, List<(StackItem, Co parameter.Value = map.Select(p => new KeyValuePair(ToParameter(p.Key, context), ToParameter(p.Value, context))).ToList(); } break; - case VMBoolean _: + case Boolean _: parameter = new ContractParameter { Type = ContractParameterType.Boolean, @@ -295,7 +295,7 @@ private static StackItem ToStackItem(ContractParameter parameter, List<(StackIte (stackItem, _) = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter)); if (stackItem is null) { - stackItem = ((IList)parameter.Value).Select(p => ToStackItem(p, context)).ToList(); + stackItem = new Array(((IList)parameter.Value).Select(p => ToStackItem(p, context))); context.Add((stackItem, parameter)); } break; diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 5897121579..6a3063cfa7 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -29,7 +29,7 @@ - + diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 49d086e42c..772d11edbf 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -687,7 +687,7 @@ internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint[] votes) { - var st = (VM.Types.Struct)StackItemSerializer.Deserialize(trackable.Item.Value, 3, 32); + var st = (VM.Types.Struct)StackItemSerializer.Deserialize(trackable.Item.Value, 32); st.Count.Should().Be(3); st.Select(u => u.GetType()).ToArray().Should().BeEquivalentTo(new Type[] { typeof(VM.Types.Integer), typeof(VM.Types.Integer), typeof(VM.Types.ByteArray) }); // Balance diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 86f62c6ed3..de16dfd3a1 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -387,8 +387,8 @@ public void TestIterator_Create() var map = new Map { - { new Integer(1), new Integer(2) }, - { new Integer(3), new Integer(4) } + [1] = 2, + [3] = 4 }; engine.CurrentContext.EvaluationStack.Push(map); InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeTrue(); diff --git a/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs b/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs index cb27ffcea9..80acd2082d 100644 --- a/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs +++ b/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs @@ -69,7 +69,7 @@ public void TestSerialize() Assert.AreEqual(Encoding.Default.GetString(expectedArray8), Encoding.Default.GetString(result8)); Map stackItem91 = new Map(); - stackItem91.Add(1, stackItem91); + stackItem91[1] = stackItem91; Action action9 = () => StackItemSerializer.Serialize(stackItem91); action9.Should().Throw(); @@ -84,42 +84,42 @@ public void TestDeserializeStackItem() { StackItem stackItem1 = new ByteArray(new byte[5]); byte[] byteArray1 = StackItemSerializer.Serialize(stackItem1); - StackItem result1 = StackItemSerializer.Deserialize(byteArray1, 1, (uint)byteArray1.Length); + StackItem result1 = StackItemSerializer.Deserialize(byteArray1, (uint)byteArray1.Length); Assert.AreEqual(stackItem1, result1); StackItem stackItem2 = new VM.Types.Boolean(true); byte[] byteArray2 = StackItemSerializer.Serialize(stackItem2); - StackItem result2 = StackItemSerializer.Deserialize(byteArray2, 1, (uint)byteArray2.Length); + StackItem result2 = StackItemSerializer.Deserialize(byteArray2, (uint)byteArray2.Length); Assert.AreEqual(stackItem2, result2); StackItem stackItem3 = new Integer(1); byte[] byteArray3 = StackItemSerializer.Serialize(stackItem3); - StackItem result3 = StackItemSerializer.Deserialize(byteArray3, 1, (uint)byteArray3.Length); + StackItem result3 = StackItemSerializer.Deserialize(byteArray3, (uint)byteArray3.Length); Assert.AreEqual(stackItem3, result3); byte[] byteArray4 = StackItemSerializer.Serialize(1); byteArray4[0] = 0x40; - Action action4 = () => StackItemSerializer.Deserialize(byteArray4, 1, (uint)byteArray4.Length); + Action action4 = () => StackItemSerializer.Deserialize(byteArray4, (uint)byteArray4.Length); action4.Should().Throw(); List list5 = new List { 1 }; StackItem stackItem52 = new VM.Types.Array(list5); byte[] byteArray5 = StackItemSerializer.Serialize(stackItem52); - StackItem result5 = StackItemSerializer.Deserialize(byteArray5, 1, (uint)byteArray5.Length); + StackItem result5 = StackItemSerializer.Deserialize(byteArray5, (uint)byteArray5.Length); Assert.AreEqual(((VM.Types.Array)stackItem52).Count, ((VM.Types.Array)result5).Count); Assert.AreEqual(((VM.Types.Array)stackItem52).GetEnumerator().Current, ((VM.Types.Array)result5).GetEnumerator().Current); List list6 = new List { 1 }; StackItem stackItem62 = new Struct(list6); byte[] byteArray6 = StackItemSerializer.Serialize(stackItem62); - StackItem result6 = StackItemSerializer.Deserialize(byteArray6, 1, (uint)byteArray6.Length); + StackItem result6 = StackItemSerializer.Deserialize(byteArray6, (uint)byteArray6.Length); Assert.AreEqual(((Struct)stackItem62).Count, ((Struct)result6).Count); Assert.AreEqual(((Struct)stackItem62).GetEnumerator().Current, ((Struct)result6).GetEnumerator().Current); Dictionary list7 = new Dictionary { [2] = 1 }; StackItem stackItem72 = new Map(list7); byte[] byteArray7 = StackItemSerializer.Serialize(stackItem72); - StackItem result7 = StackItemSerializer.Deserialize(byteArray7, 1, (uint)byteArray7.Length); + StackItem result7 = StackItemSerializer.Deserialize(byteArray7, (uint)byteArray7.Length); Assert.AreEqual(((Map)stackItem72).Count, ((Map)result7).Count); Assert.AreEqual(((Map)stackItem72).Keys.GetEnumerator().Current, ((Map)result7).Keys.GetEnumerator().Current); Assert.AreEqual(((Map)stackItem72).Values.GetEnumerator().Current, ((Map)result7).Values.GetEnumerator().Current); From bdf94bd2599f2030c2fa671655612a56c56b086f Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 5 Dec 2019 00:46:00 +0800 Subject: [PATCH 166/305] Fix ToParameter() (#1323) --- src/neo/VM/Helper.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index fa659246e9..735df63c85 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -272,6 +272,12 @@ private static ContractParameter ToParameter(StackItem item, List<(StackItem, Co Type = ContractParameterType.InteropInterface }; break; + case Null _: + parameter = new ContractParameter + { + Type = ContractParameterType.Any + }; + break; default: // Null included throw new ArgumentException(); } From 573127522e570a72a20edce693f3b24265d9591b Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 5 Dec 2019 20:00:18 +0800 Subject: [PATCH 167/305] Optimize ContractManifest.Clone() (#1328) --- src/neo/SmartContract/Manifest/ContractAbi.cs | 11 +++++++++++ .../Manifest/ContractEventDescriptor.cs | 9 +++++++++ src/neo/SmartContract/Manifest/ContractGroup.cs | 9 +++++++++ src/neo/SmartContract/Manifest/ContractManifest.cs | 13 ++++++++++++- .../Manifest/ContractMethodDescriptor.cs | 10 ++++++++++ .../Manifest/ContractParameterDefinition.cs | 9 +++++++++ .../SmartContract/Manifest/ContractPermission.cs | 9 +++++++++ .../SmartContract/Manifest/UT_ContractManifest.cs | 2 +- 8 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Manifest/ContractAbi.cs b/src/neo/SmartContract/Manifest/ContractAbi.cs index 1d27ae4f1b..e7fb4a5272 100644 --- a/src/neo/SmartContract/Manifest/ContractAbi.cs +++ b/src/neo/SmartContract/Manifest/ContractAbi.cs @@ -28,6 +28,17 @@ public class ContractAbi /// public ContractEventDescriptor[] Events { get; set; } + public ContractAbi Clone() + { + return new ContractAbi + { + Hash = Hash, + EntryPoint = EntryPoint.Clone(), + Methods = Methods.Select(p => p.Clone()).ToArray(), + Events = Events.Select(p => p.Clone()).ToArray() + }; + } + /// /// Parse ContractAbi from json /// diff --git a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs index 00748f9b09..0aa11a3eea 100644 --- a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs @@ -15,6 +15,15 @@ public class ContractEventDescriptor /// public ContractParameterDefinition[] Parameters { get; set; } + public ContractEventDescriptor Clone() + { + return new ContractEventDescriptor + { + Name = Name, + Parameters = Parameters.Select(p => p.Clone()).ToArray() + }; + } + /// /// Parse ContractMethodDescription from json /// diff --git a/src/neo/SmartContract/Manifest/ContractGroup.cs b/src/neo/SmartContract/Manifest/ContractGroup.cs index b138427cfa..c10bcba283 100644 --- a/src/neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/neo/SmartContract/Manifest/ContractGroup.cs @@ -22,6 +22,15 @@ public class ContractGroup /// public byte[] Signature { get; set; } + public ContractGroup Clone() + { + return new ContractGroup + { + PubKey = PubKey, + Signature = Signature + }; + } + /// /// Parse ContractManifestGroup from json /// diff --git a/src/neo/SmartContract/Manifest/ContractManifest.cs b/src/neo/SmartContract/Manifest/ContractManifest.cs index ebbcd61f2c..c376af7d00 100644 --- a/src/neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/neo/SmartContract/Manifest/ContractManifest.cs @@ -136,7 +136,18 @@ public JObject ToJson() /// Clone /// /// Return a copy of this object - public ContractManifest Clone() => FromJson(ToJson()); + public ContractManifest Clone() + { + return new ContractManifest + { + Groups = Groups.Select(p => p.Clone()).ToArray(), + Features = Features, + Abi = Abi.Clone(), + Permissions = Permissions.Select(p => p.Clone()).ToArray(), + Trusts = Trusts, + SafeMethods = SafeMethods + }; + } /// /// String representation diff --git a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs index 476ec546a6..6596f86dd6 100644 --- a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -34,6 +34,16 @@ public class ContractMethodDescriptor : ContractEventDescriptor /// public ContractParameterType ReturnType { get; set; } + public new ContractMethodDescriptor Clone() + { + return new ContractMethodDescriptor + { + Name = Name, + Parameters = Parameters.Select(p => p.Clone()).ToArray(), + ReturnType = ReturnType + }; + } + /// /// Parse ContractMethodDescription from json /// diff --git a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs index 3f184d6ba8..d5b95f5245 100644 --- a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -16,6 +16,15 @@ public class ContractParameterDefinition /// public ContractParameterType Type { get; set; } + public ContractParameterDefinition Clone() + { + return new ContractParameterDefinition + { + Name = Name, + Type = Type + }; + } + /// /// Parse ContractParameterDefinition from json /// diff --git a/src/neo/SmartContract/Manifest/ContractPermission.cs b/src/neo/SmartContract/Manifest/ContractPermission.cs index 890de9d200..ab86157786 100644 --- a/src/neo/SmartContract/Manifest/ContractPermission.cs +++ b/src/neo/SmartContract/Manifest/ContractPermission.cs @@ -27,6 +27,15 @@ public class ContractPermission Methods = WildCardContainer.CreateWildcard() }; + public ContractPermission Clone() + { + return new ContractPermission + { + Contract = Contract, + Methods = Methods + }; + } + /// /// Parse ContractPermission from json /// diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index 5c2a99ff56..2416c60bd5 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -137,7 +137,7 @@ public void TestClone() var expected = ContractManifest.CreateDefault(UInt160.Zero); expected.SafeMethods = WildCardContainer.Create(new string[] { "AAA" }); var actual = expected.Clone(); - Assert.AreEqual(actual.SafeMethods.ToString(), expected.SafeMethods.ToString()); + Assert.AreEqual(actual.ToString(), expected.ToString()); } } } From 23db95b07a10eabe03f691df9beaf2c355a921e6 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 6 Dec 2019 17:01:50 +0800 Subject: [PATCH 168/305] Dispose ApplicationEngine after using (#1336) --- src/neo/Network/RPC/RpcServer.cs | 2 +- src/neo/Wallets/AssetDescriptor.cs | 2 +- src/neo/Wallets/Wallet.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/Network/RPC/RpcServer.cs b/src/neo/Network/RPC/RpcServer.cs index 5649cce92f..e2137d517c 100644 --- a/src/neo/Network/RPC/RpcServer.cs +++ b/src/neo/Network/RPC/RpcServer.cs @@ -111,7 +111,7 @@ public void Dispose() private JObject GetInvokeResult(byte[] script, IVerifiable checkWitnessHashes = null) { - ApplicationEngine engine = ApplicationEngine.Run(script, checkWitnessHashes, extraGAS: MaxGasInvoke); + using ApplicationEngine engine = ApplicationEngine.Run(script, checkWitnessHashes, extraGAS: MaxGasInvoke); JObject json = new JObject(); json["script"] = script.ToHexString(); json["state"] = engine.State; diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index dca0b10b25..46a80a0f5c 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -19,7 +19,7 @@ public AssetDescriptor(UInt160 asset_id) sb.EmitAppCall(asset_id, "name"); script = sb.ToArray(); } - ApplicationEngine engine = ApplicationEngine.Run(script, extraGAS: 3_000_000); + using ApplicationEngine engine = ApplicationEngine.Run(script, extraGAS: 3_000_000); if (engine.State.HasFlag(VMState.FAULT)) throw new ArgumentException(); this.AssetId = asset_id; this.AssetName = engine.ResultStack.Pop().GetString(); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 52fa839b16..954923df8f 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -132,7 +132,7 @@ public BigDecimal GetBalance(UInt160 asset_id, params UInt160[] accounts) sb.EmitAppCall(asset_id, "decimals"); script = sb.ToArray(); } - ApplicationEngine engine = ApplicationEngine.Run(script, extraGAS: 20000000L * accounts.Length); + using ApplicationEngine engine = ApplicationEngine.Run(script, extraGAS: 20000000L * accounts.Length); if (engine.State.HasFlag(VMState.FAULT)) return new BigDecimal(0, 0); byte decimals = (byte)engine.ResultStack.Pop().GetBigInteger(); From c4a57302d5af0c9e9b90917221d54aa092a44894 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 6 Dec 2019 10:17:44 +0100 Subject: [PATCH 169/305] Update NeoVM nuget (#1331) --- src/neo/neo.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 6a3063cfa7..c649d00b64 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -29,7 +29,7 @@ - + From 1b7e6e21b074cf80a2e64a9e4813d637ea6c45a3 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 6 Dec 2019 20:35:53 +0800 Subject: [PATCH 170/305] Update dotnetcore.yml --- .github/workflows/dotnetcore.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/dotnetcore.yml index 2f31794695..dab405a371 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/dotnetcore.yml @@ -33,7 +33,8 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} PublishGithub: - if: github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/') + # Because sometimes this action is not working as expected we will disable it until determine that it's more stable + if: false && github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/') needs: Test runs-on: ubuntu-latest steps: From 1284e15986fb69a6978c53e891af23a656330311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Fri, 6 Dec 2019 21:25:19 -0300 Subject: [PATCH 171/305] Improving UT consensus with new asserts, additional coverage for Recoveries (#1318) * Improving UT consensus with new asserts * Additional subscriber waits before Preparations * Using @shargon tip for snapshot and increasing recovery coverage * Trying to reload commits on Recovery. ping @shargon * Cleaning and fixing wrong multisig being used to sign payload * Checking the promising simple way * Simplifing CreateStorageKey * Fixing error on key and now engine is HALT * Fixing commit Asert after Recovery! * Minor typo fix on asserts * Create function for creating StorageKey for Neo Native * Trying to return Snapshot to original state * Some tricks for modifying committed snaphost * Cleaning * Fixing Assert before subscriber wait * Cleaning and leaving last check comment in case we need * Simplifying mocking of nextvalidators * Cleaning SignPayload * Uncommenting code. Ensuring nondeterministic behavior after last recover --- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 254 ++++++++++++++---- 1 file changed, 195 insertions(+), 59 deletions(-) diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 343a0cece5..06ca0f0cd7 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -18,6 +18,7 @@ using System.Reflection; using System.Security.Cryptography; using ECPoint = Neo.Cryptography.ECC.ECPoint; +using Neo.SmartContract.Native; namespace Neo.UnitTests.Consensus { @@ -46,6 +47,17 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm var mockContext = new Mock(mockWallet.Object, Blockchain.Singleton.Store); mockContext.Object.LastSeenMessage = new int[] { 0, 0, 0, 0, 0, 0, 0 }; + 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) + }; + 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 @@ -129,7 +141,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm RecoveryRequest rrm = (RecoveryRequest)initialRecoveryPayload.ConsensusMessage; rrm.Timestamp.Should().Be(defaultTimestamp); - Console.WriteLine("Waiting for backupChange View... "); + Console.WriteLine("Waiting for backup ChangeView... "); var backupOnAskingChangeView = subscriber.ExpectMsg(); var changeViewPayload = (ConsensusPayload)backupOnAskingChangeView.Inventory; ChangeView cvm = (ChangeView)changeViewPayload.ConsensusMessage; @@ -137,6 +149,13 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm 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("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); + mockContext.Object.Block.NextConsensus.Should().Be(UInt160.Parse("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); + Console.WriteLine("\n=========================="); Console.WriteLine("will trigger OnPersistCompleted again with OnStart flag!"); actorConsensus.Tell(testPersistCompleted); @@ -154,71 +173,90 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm rrm = (RecoveryRequest)recoveryPayload.ConsensusMessage; rrm.Timestamp.Should().Be(defaultTimestamp); - Console.WriteLine("will tell PrepRequest!"); + Console.WriteLine("will create template MakePrepareRequest..."); mockContext.Object.PrevHeader.Timestamp = defaultTimestamp; mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); var prepReq = mockContext.Object.MakePrepareRequest(); var ppToSend = (PrepareRequest)prepReq.ConsensusMessage; - // 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; + Console.WriteLine("will tell prepare request!"); actorConsensus.Tell(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; + 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) // cleaning old try with Self ValidatorIndex mockContext.Object.PreparationPayloads[mockContext.Object.MyIndex] = null; + actorConsensus.Tell(prepReq); var OnPrepResponse = subscriber.ExpectMsg(); var prepResponsePayload = (ConsensusPayload)OnPrepResponse.Inventory; PrepareResponse prm = (PrepareResponse)prepResponsePayload.ConsensusMessage; 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 actorConsensus.Tell(GetPayloadAndModifyValidator(prepResponsePayload, 2)); + //Waiting for RecoveryRequest for a more deterministic UT + backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); + recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; + rrm = (RecoveryRequest)recoveryPayload.ConsensusMessage; + 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 actorConsensus.Tell(GetPayloadAndModifyValidator(prepResponsePayload, 4)); + //Waiting for RecoveryRequest for a more deterministic UT + backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); + recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; + rrm = (RecoveryRequest)recoveryPayload.ConsensusMessage; + 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 actorConsensus.Tell(GetPayloadAndModifyValidator(prepResponsePayload, 3)); - var onCommitPayload = subscriber.ExpectMsg(); var commitPayload = (ConsensusPayload)onCommitPayload.Inventory; Commit cm = (Commit)commitPayload.ConsensusMessage; - - // Original Contract - Contract originalContract = Contract.CreateMultiSigContract(mockContext.Object.M, mockContext.Object.Validators); - Console.WriteLine($"\nORIGINAL Contract is: {originalContract.ScriptHash}"); - originalContract.ScriptHash.Should().Be(UInt160.Parse("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); - mockContext.Object.Block.NextConsensus.Should().Be(UInt160.Parse("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); + 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}"); - //Console.WriteLine($"VALIDATOR[0] {mockContext.Object.Validators[0]}"); - //Console.WriteLine($"VALIDATOR[0]ScriptHash: {mockWallet.Object.GetAccount(mockContext.Object.Validators[0]).ScriptHash}"); - 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) - }; for (int i = 0; i < mockContext.Object.Validators.Length; i++) - Console.WriteLine($"{mockContext.Object.Validators[i]}"); + Console.WriteLine($"{mockContext.Object.Validators[i]}/{Contract.CreateSignatureContract(mockContext.Object.Validators[i]).ScriptHash}"); mockContext.Object.Validators = new ECPoint[7] { - mockContext.Object.Validators[0], + kp_array[0].PublicKey, kp_array[1].PublicKey, kp_array[2].PublicKey, kp_array[3].PublicKey, @@ -228,17 +266,22 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm }; Console.WriteLine($"Generated keypairs PKey:"); for (int i = 0; i < mockContext.Object.Validators.Length; i++) - Console.WriteLine($"{mockContext.Object.Validators[i]}"); - - // update Contract with some random validators + 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}"); + // =============================================================== + mockContext.Object.Snapshot.Storages.Add(CreateStorageKeyForNativeNeo(14), new StorageItem() + { + Value = mockContext.Object.Validators.ToByteArray() + }); + 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; - mockContext.Object.PrevHeader.NextConsensus = updatedContract.ScriptHash; var originalBlockMerkleRoot = mockContext.Object.Block.MerkleRoot; Console.WriteLine($"\noriginalBlockMerkleRoot: {originalBlockMerkleRoot}"); var updatedBlockHashData = mockContext.Object.Block.GetHashData(); @@ -248,72 +291,134 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm 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(commitPayload, 1, kp_array[1], updatedBlockHashData); + var cmPayloadTemp = GetCommitPayloadModifiedAndSignedCopy(commitPayload, 6, kp_array[6], updatedBlockHashData); Crypto.VerifySignature(originalBlockHashData, cm.Signature, mockContext.Object.Validators[0].EncodePoint(false)).Should().BeFalse(); Crypto.VerifySignature(updatedBlockHashData, cm.Signature, mockContext.Object.Validators[0].EncodePoint(false)).Should().BeFalse(); - Crypto.VerifySignature(originalBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[1].EncodePoint(false)).Should().BeFalse(); - Crypto.VerifySignature(updatedBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[1].EncodePoint(false)).Should().BeTrue(); + Crypto.VerifySignature(originalBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[6].EncodePoint(false)).Should().BeFalse(); + Crypto.VerifySignature(updatedBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[6].EncodePoint(false)).Should().BeTrue(); Console.WriteLine("\n=========================="); Console.WriteLine("\n=========================="); - Console.WriteLine("\nCN2 simulation time"); + Console.WriteLine("\nCN7 simulation time"); actorConsensus.Tell(cmPayloadTemp); + var tempPayloadToBlockAndWait = subscriber.ExpectMsg(); + var rmPayload = (ConsensusPayload)tempPayloadToBlockAndWait.Inventory; + RecoveryMessage rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + Console.WriteLine("\nAsserting CountCommitted is 2..."); + mockContext.Object.CountCommitted.Should().Be(2); + Console.WriteLine($"\nAsserting CountFailed is 1..."); + mockContext.Object.CountFailed.Should().Be(1); - Console.WriteLine("\nCN3 simulation time"); - actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 2, kp_array[2], updatedBlockHashData)); + Console.WriteLine("\nCN6 simulation time"); + actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 5, kp_array[5], updatedBlockHashData)); + tempPayloadToBlockAndWait = subscriber.ExpectMsg(); + rmPayload = (ConsensusPayload)tempPayloadToBlockAndWait.Inventory; + rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + Console.WriteLine("\nAsserting CountCommitted is 3..."); + mockContext.Object.CountCommitted.Should().Be(3); + Console.WriteLine($"\nAsserting CountFailed is 0..."); + mockContext.Object.CountFailed.Should().Be(0); - Console.WriteLine("\nCN4 simulation time"); - actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 3, kp_array[3], updatedBlockHashData)); + Console.WriteLine("\nCN5 simulation time"); + actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(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("\nCN7 simulation time. Wrong signature, KeyPair is not known"); - actorConsensus.Tell(GetPayloadAndModifyValidator(commitPayload, 6)); - + Console.WriteLine("\nCN4 simulation time. Wrong signature, KeyPair is not known"); + actorConsensus.Tell(GetPayloadAndModifyValidator(commitPayload, 3)); Console.WriteLine("\nWaiting for recovery due to failed nodes... "); var backupOnRecoveryMessageAfterCommit = subscriber.ExpectMsg(); - var rmPayload = (ConsensusPayload)backupOnRecoveryMessageAfterCommit.Inventory; - RecoveryMessage rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + rmPayload = (ConsensusPayload)backupOnRecoveryMessageAfterCommit.Inventory; + rmm = (RecoveryMessage)rmPayload.ConsensusMessage; + 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); // ============================================= - mockContext.Object.CommitPayloads[0] = null; - Console.WriteLine("\nCN5 simulation time"); - actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 4, kp_array[4], updatedBlockHashData)); - - - Console.WriteLine($"\nFocing block PrevHash to UInt256.Zero {mockContext.Object.Block.GetHashData().ToScriptHash()}"); + 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 + //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 mockContext.Object.Block.Witness = new Witness { }; mockContext.Object.Block.Witness.VerificationScript = updatedContract.Script; - Console.WriteLine($"\nUpdating BlockBase Witness scripthash is {mockContext.Object.Block.Witness.ScriptHash}"); + Console.WriteLine($"\nUpdating BlockBase Witness scripthash to: {mockContext.Object.Block.Witness.ScriptHash}"); Console.WriteLine($"\nNew Hash is {mockContext.Object.Block.GetHashData().ToScriptHash()}"); - Console.WriteLine("\nCN6 simulation time"); - // Here we used modified mockContext.Object.Block.GetHashData().ToScriptHash() for blockhash - actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 5, kp_array[5], mockContext.Object.Block.GetHashData())); + Console.WriteLine("\nCN4 simulation time - Final needed signatures"); + actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 3, kp_array[3], mockContext.Object.Block.GetHashData())); Console.WriteLine("\nWait for subscriber Local.Node Relay"); var onBlockRelay = subscriber.ExpectMsg(); Console.WriteLine("\nAsserting time was Block..."); var utBlock = (Block)onBlockRelay.Inventory; + 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 ConsensusPayload[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(0); + Console.WriteLine($"\nModifying CountFailed and asserting 7..."); + // This will ensure a non-deterministic behavior after last recovery + mockContext.Object.LastSeenMessage = new int[] { -1, -1, -1, -1, -1, -1, -1 }; + mockContext.Object.CountFailed.Should().Be(7); + + actorConsensus.Tell(rmPayload); + + Console.WriteLine("\nWaiting for RecoveryRequest before final asserts..."); + var onRecoveryRequestAfterRecovery = subscriber.ExpectMsg(); + var rrPayload = (ConsensusPayload)onRecoveryRequestAfterRecovery.Inventory; + var rrMessage = (RecoveryRequest)rrPayload.ConsensusMessage; + + // 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("Finalizing consensus service actor and returning states."); + 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)); + mockContext.Object.Snapshot.Commit(); + + Console.WriteLine("mockContext Reset."); + mockContext.Object.Reset(0); + Console.WriteLine("TimeProvider Reset."); TimeProvider.ResetToDefault(); - //Sys.Stop(actorConsensus); + Console.WriteLine("Finalizing consensus service actor."); + Sys.Stop(actorConsensus); + Console.WriteLine("Actor actorConsensus Stopped.\n"); } /// @@ -327,9 +432,13 @@ public ConsensusPayload GetCommitPayloadModifiedAndSignedCopy(ConsensusPayload c { var cpCommitTemp = cpToCopy.ToArray().AsSerializable(); cpCommitTemp.ValidatorIndex = vI; - cpCommitTemp.ConsensusMessage = cpToCopy.ConsensusMessage.ToArray().AsSerializable(); - ((Commit)cpCommitTemp.ConsensusMessage).Signature = Crypto.Sign(blockHashToSign, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()); - // Payload is not being signed by vI, since we are bypassing this check as directly talking to subscriber + var oldViewNumber = ((Commit)cpCommitTemp.ConsensusMessage).ViewNumber; + cpCommitTemp.ConsensusMessage = new Commit + { + ViewNumber = oldViewNumber, + Signature = Crypto.Sign(blockHashToSign, kp.PrivateKey, kp.PublicKey.EncodePoint(false).Skip(1).ToArray()) + }; + SignPayload(cpCommitTemp, kp); return cpCommitTemp; } @@ -345,6 +454,22 @@ public ConsensusPayload GetPayloadAndModifyValidator(ConsensusPayload cpToCopy, return cpTemp; } + private void SignPayload(ConsensusPayload 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() { @@ -785,5 +910,16 @@ private static ConsensusPayload MakeSignedPayload(ConsensusContext context, Cons } }; } + + private StorageKey CreateStorageKeyForNativeNeo(byte prefix) + { + StorageKey storageKey = new StorageKey + { + ScriptHash = NativeContract.NEO.Hash, + Key = new byte[sizeof(byte)] + }; + storageKey.Key[0] = prefix; + return storageKey; + } } } From 83c91a3cd4229eb29cf3e2f400bd5437775e9c0b Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 7 Dec 2019 23:34:01 +0800 Subject: [PATCH 172/305] Remove RPC (#1340) --- src/neo/NeoSystem.cs | 11 - src/neo/Network/RPC/ContractClient.cs | 66 -- src/neo/Network/RPC/Helper.cs | 38 - src/neo/Network/RPC/Models/RpcBlock.cs | 37 - src/neo/Network/RPC/Models/RpcBlockHeader.cs | 37 - src/neo/Network/RPC/Models/RpcInvokeResult.cs | 60 -- src/neo/Network/RPC/Models/RpcNep5Balances.cs | 57 -- .../Network/RPC/Models/RpcNep5TokenInfo.cs | 15 - src/neo/Network/RPC/Models/RpcPeers.cs | 55 -- src/neo/Network/RPC/Models/RpcPlugin.cs | 32 - src/neo/Network/RPC/Models/RpcRawMemPool.cs | 32 - src/neo/Network/RPC/Models/RpcRequest.cs | 37 - src/neo/Network/RPC/Models/RpcResponse.cs | 72 -- src/neo/Network/RPC/Models/RpcTransaction.cs | 49 -- .../RPC/Models/RpcValidateAddressResult.cs | 27 - src/neo/Network/RPC/Models/RpcValidator.cs | 32 - src/neo/Network/RPC/Models/RpcVersion.cs | 35 - src/neo/Network/RPC/Nep5API.cs | 122 ---- src/neo/Network/RPC/PolicyAPI.cs | 57 -- src/neo/Network/RPC/RpcClient.cs | 313 -------- src/neo/Network/RPC/RpcException.cs | 12 - src/neo/Network/RPC/RpcServer.cs | 667 ------------------ src/neo/Network/RPC/TransactionManager.cs | 226 ------ src/neo/Network/RPC/WalletAPI.cs | 192 ----- src/neo/Plugins/IRpcPlugin.cs | 12 - src/neo/Plugins/Plugin.cs | 2 - src/neo/Utility.cs | 54 -- .../Network/RPC/Models/UT_RpcBlock.cs | 29 - .../Network/RPC/Models/UT_RpcBlockHeader.cs | 29 - .../Network/RPC/Models/UT_RpcNep5Balance.cs | 66 -- .../Network/RPC/Models/UT_RpcNep5Balances.cs | 66 -- .../Network/RPC/Models/UT_RpcPeer.cs | 23 - .../Network/RPC/Models/UT_RpcPeers.cs | 44 -- .../Network/RPC/Models/UT_RpcRawMemPool.cs | 29 - .../Network/RPC/Models/UT_RpcRequest.cs | 31 - .../Network/RPC/Models/UT_RpcResponse.cs | 34 - .../Network/RPC/Models/UT_RpcVersion.cs | 27 - .../Network/RPC/UT_ContractClient.cs | 59 -- tests/neo.UnitTests/Network/RPC/UT_Helper.cs | 29 - tests/neo.UnitTests/Network/RPC/UT_Nep5API.cs | 112 --- .../neo.UnitTests/Network/RPC/UT_PolicyAPI.cs | 69 -- .../neo.UnitTests/Network/RPC/UT_RpcClient.cs | 568 --------------- .../neo.UnitTests/Network/RPC/UT_RpcServer.cs | 47 -- .../Network/RPC/UT_TransactionManager.cs | 193 ----- .../neo.UnitTests/Network/RPC/UT_WalletAPI.cs | 116 --- tests/neo.UnitTests/UT_NeoSystem.cs | 3 - tests/neo.UnitTests/UT_Utility.cs | 55 -- 47 files changed, 3978 deletions(-) delete mode 100644 src/neo/Network/RPC/ContractClient.cs delete mode 100644 src/neo/Network/RPC/Helper.cs delete mode 100644 src/neo/Network/RPC/Models/RpcBlock.cs delete mode 100644 src/neo/Network/RPC/Models/RpcBlockHeader.cs delete mode 100644 src/neo/Network/RPC/Models/RpcInvokeResult.cs delete mode 100644 src/neo/Network/RPC/Models/RpcNep5Balances.cs delete mode 100644 src/neo/Network/RPC/Models/RpcNep5TokenInfo.cs delete mode 100644 src/neo/Network/RPC/Models/RpcPeers.cs delete mode 100644 src/neo/Network/RPC/Models/RpcPlugin.cs delete mode 100644 src/neo/Network/RPC/Models/RpcRawMemPool.cs delete mode 100644 src/neo/Network/RPC/Models/RpcRequest.cs delete mode 100644 src/neo/Network/RPC/Models/RpcResponse.cs delete mode 100644 src/neo/Network/RPC/Models/RpcTransaction.cs delete mode 100644 src/neo/Network/RPC/Models/RpcValidateAddressResult.cs delete mode 100644 src/neo/Network/RPC/Models/RpcValidator.cs delete mode 100644 src/neo/Network/RPC/Models/RpcVersion.cs delete mode 100644 src/neo/Network/RPC/Nep5API.cs delete mode 100644 src/neo/Network/RPC/PolicyAPI.cs delete mode 100644 src/neo/Network/RPC/RpcClient.cs delete mode 100644 src/neo/Network/RPC/RpcException.cs delete mode 100644 src/neo/Network/RPC/RpcServer.cs delete mode 100644 src/neo/Network/RPC/TransactionManager.cs delete mode 100644 src/neo/Network/RPC/WalletAPI.cs delete mode 100644 src/neo/Plugins/IRpcPlugin.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/UT_ContractClient.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/UT_Helper.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/UT_Nep5API.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/UT_PolicyAPI.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/UT_RpcClient.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/UT_RpcServer.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs delete mode 100644 tests/neo.UnitTests/Network/RPC/UT_WalletAPI.cs delete mode 100644 tests/neo.UnitTests/UT_Utility.cs diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index e9e8335bb4..b837426d45 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -2,12 +2,10 @@ using Neo.Consensus; using Neo.Ledger; using Neo.Network.P2P; -using Neo.Network.RPC; using Neo.Persistence; using Neo.Plugins; using Neo.Wallets; using System; -using System.Net; namespace Neo { @@ -24,7 +22,6 @@ public class NeoSystem : IDisposable public IActorRef LocalNode { get; } internal IActorRef TaskManager { get; } public IActorRef Consensus { get; private set; } - public RpcServer RpcServer { get; private set; } private readonly IStore store; private ChannelsConfig start_message = null; @@ -47,7 +44,6 @@ public void Dispose() { foreach (var p in Plugin.Plugins) p.Dispose(); - RpcServer?.Dispose(); EnsureStoped(LocalNode); // Dispose will call ActorSystem.Terminate() ActorSystem.Dispose(); @@ -90,13 +86,6 @@ public void StartNode(ChannelsConfig config) } } - public void StartRpc(IPAddress bindAddress, int port, Wallet wallet = null, string sslCert = null, string password = null, - string[] trustedAuthorities = null, long maxGasInvoke = default) - { - RpcServer = new RpcServer(this, wallet, maxGasInvoke); - RpcServer.Start(bindAddress, port, sslCert, password, trustedAuthorities); - } - internal void SuspendNodeStartup() { suspend = true; diff --git a/src/neo/Network/RPC/ContractClient.cs b/src/neo/Network/RPC/ContractClient.cs deleted file mode 100644 index 45fb31d267..0000000000 --- a/src/neo/Network/RPC/ContractClient.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Neo.Network.P2P.Payloads; -using Neo.Network.RPC.Models; -using Neo.SmartContract; -using Neo.SmartContract.Manifest; -using Neo.VM; -using Neo.Wallets; - -namespace Neo.Network.RPC -{ - /// - /// Contract related operations through RPC API - /// - public class ContractClient - { - protected readonly RpcClient rpcClient; - - /// - /// ContractClient Constructor - /// - /// the RPC client to call NEO RPC methods - public ContractClient(RpcClient rpc) - { - rpcClient = rpc; - } - - /// - /// Use RPC method to test invoke operation. - /// - /// contract script hash - /// contract operation - /// operation arguments - /// - public RpcInvokeResult TestInvoke(UInt160 scriptHash, string operation, params object[] args) - { - byte[] script = scriptHash.MakeScript(operation, args); - return rpcClient.InvokeScript(script); - } - - /// - /// Deploy Contract, return signed transaction - /// - /// contract script - /// contract manifest - /// sender KeyPair - /// transaction NetworkFee, set to be 0 if you don't need higher priority - /// - public Transaction CreateDeployContractTx(byte[] contractScript, ContractManifest manifest, KeyPair key, long networkFee = 0) - { - byte[] script; - using (ScriptBuilder sb = new ScriptBuilder()) - { - sb.EmitSysCall(InteropService.Neo_Contract_Create, contractScript, manifest.ToString()); - script = sb.ToArray(); - } - - UInt160 sender = Contract.CreateSignatureRedeemScript(key.PublicKey).ToScriptHash(); - Transaction tx = new TransactionManager(rpcClient, sender) - .MakeTransaction(script, null, null, networkFee) - .AddSignature(key) - .Sign() - .Tx; - - return tx; - } - } -} diff --git a/src/neo/Network/RPC/Helper.cs b/src/neo/Network/RPC/Helper.cs deleted file mode 100644 index 5c5c82e329..0000000000 --- a/src/neo/Network/RPC/Helper.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Numerics; - -namespace Neo.Network.RPC -{ - internal static class Helper - { - /// - /// Convert decimal amount to BigInteger: amount * 10 ^ decimals - /// - /// float value - /// token decimals - /// - public static BigInteger ToBigInteger(this decimal amount, uint decimals) - { - BigInteger factor = BigInteger.Pow(10, (int)decimals); - var (numerator, denominator) = Fraction(amount); - if (factor < denominator) - { - throw new OverflowException("The decimal places is too long."); - } - - BigInteger res = factor * numerator / denominator; - return res; - } - - private static (BigInteger numerator, BigInteger denominator) Fraction(decimal d) - { - int[] bits = decimal.GetBits(d); - BigInteger numerator = (1 - ((bits[3] >> 30) & 2)) * - unchecked(((BigInteger)(uint)bits[2] << 64) | - ((BigInteger)(uint)bits[1] << 32) | - (uint)bits[0]); - BigInteger denominator = BigInteger.Pow(10, (bits[3] >> 16) & 0xff); - return (numerator, denominator); - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcBlock.cs b/src/neo/Network/RPC/Models/RpcBlock.cs deleted file mode 100644 index 1ff485f96e..0000000000 --- a/src/neo/Network/RPC/Models/RpcBlock.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Neo.IO.Json; -using Neo.Network.P2P.Payloads; - -namespace Neo.Network.RPC.Models -{ - public class RpcBlock - { - public Block Block { get; set; } - - public int Confirmations { get; set; } - - public UInt256 NextBlockHash { get; set; } - - public JObject ToJson() - { - JObject json = Block.ToJson(); - json["confirmations"] = Confirmations; - if (NextBlockHash != null) - { - json["nextblockhash"] = NextBlockHash.ToString(); - } - return json; - } - - public static RpcBlock FromJson(JObject json) - { - RpcBlock block = new RpcBlock(); - block.Block = Block.FromJson(json); - block.Confirmations = (int)json["confirmations"].AsNumber(); - if (json["nextblockhash"] != null) - { - block.NextBlockHash = UInt256.Parse(json["nextblockhash"].AsString()); - } - return block; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcBlockHeader.cs b/src/neo/Network/RPC/Models/RpcBlockHeader.cs deleted file mode 100644 index 5346dffd91..0000000000 --- a/src/neo/Network/RPC/Models/RpcBlockHeader.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Neo.IO.Json; -using Neo.Network.P2P.Payloads; - -namespace Neo.Network.RPC.Models -{ - public class RpcBlockHeader - { - public Header Header { get; set; } - - public int Confirmations { get; set; } - - public UInt256 NextBlockHash { get; set; } - - public JObject ToJson() - { - JObject json = Header.ToJson(); - json["confirmations"] = Confirmations; - if (NextBlockHash != null) - { - json["nextblockhash"] = NextBlockHash.ToString(); - } - return json; - } - - public static RpcBlockHeader FromJson(JObject json) - { - RpcBlockHeader block = new RpcBlockHeader(); - block.Header = Header.FromJson(json); - block.Confirmations = (int)json["confirmations"].AsNumber(); - if (json["nextblockhash"] != null) - { - block.NextBlockHash = UInt256.Parse(json["nextblockhash"].AsString()); - } - return block; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcInvokeResult.cs b/src/neo/Network/RPC/Models/RpcInvokeResult.cs deleted file mode 100644 index a9c5f04c48..0000000000 --- a/src/neo/Network/RPC/Models/RpcInvokeResult.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Neo.IO.Json; -using Neo.SmartContract; -using System.Linq; - -namespace Neo.Network.RPC.Models -{ - public class RpcInvokeResult - { - public string Script { get; set; } - - public string State { get; set; } - - public string GasConsumed { get; set; } - - public ContractParameter[] Stack { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["script"] = Script; - json["state"] = State; - json["gas_consumed"] = GasConsumed; - json["stack"] = new JArray(Stack.Select(p => p.ToJson())); - return json; - } - - public static RpcInvokeResult FromJson(JObject json) - { - RpcInvokeResult invokeScriptResult = new RpcInvokeResult(); - invokeScriptResult.Script = json["script"].AsString(); - invokeScriptResult.State = json["state"].AsString(); - invokeScriptResult.GasConsumed = json["gas_consumed"].AsString(); - invokeScriptResult.Stack = ((JArray)json["stack"]).Select(p => ContractParameter.FromJson(p)).ToArray(); - return invokeScriptResult; - } - } - - public class RpcStack - { - public string Type { get; set; } - - public string Value { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["type"] = Type; - json["value"] = Value; - return json; - } - - public static RpcStack FromJson(JObject json) - { - RpcStack stackJson = new RpcStack(); - stackJson.Type = json["type"].AsString(); - stackJson.Value = json["value"].AsString(); - return stackJson; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcNep5Balances.cs b/src/neo/Network/RPC/Models/RpcNep5Balances.cs deleted file mode 100644 index 74fe7be729..0000000000 --- a/src/neo/Network/RPC/Models/RpcNep5Balances.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Neo.IO.Json; -using System.Linq; -using System.Numerics; - -namespace Neo.Network.RPC.Models -{ - public class RpcNep5Balances - { - public string Address { get; set; } - - public RpcNep5Balance[] Balances { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["address"] = Address; - json["balance"] = Balances.Select(p => p.ToJson()).ToArray(); - return json; - } - - public static RpcNep5Balances FromJson(JObject json) - { - RpcNep5Balances nep5Balance = new RpcNep5Balances(); - nep5Balance.Address = json["address"].AsString(); - //List listBalance = new List(); - nep5Balance.Balances = ((JArray)json["balance"]).Select(p => RpcNep5Balance.FromJson(p)).ToArray(); - return nep5Balance; - } - } - - public class RpcNep5Balance - { - public UInt160 AssetHash { get; set; } - - public BigInteger Amount { get; set; } - - public uint LastUpdatedBlock { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["asset_hash"] = AssetHash.ToString(); - json["amount"] = Amount.ToString(); - json["last_updated_block"] = LastUpdatedBlock.ToString(); - return json; - } - - public static RpcNep5Balance FromJson(JObject json) - { - RpcNep5Balance balance = new RpcNep5Balance(); - balance.AssetHash = UInt160.Parse(json["asset_hash"].AsString()); - balance.Amount = BigInteger.Parse(json["amount"].AsString()); - balance.LastUpdatedBlock = uint.Parse(json["last_updated_block"].AsString()); - return balance; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcNep5TokenInfo.cs b/src/neo/Network/RPC/Models/RpcNep5TokenInfo.cs deleted file mode 100644 index 0f251a5a37..0000000000 --- a/src/neo/Network/RPC/Models/RpcNep5TokenInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Numerics; - -namespace Neo.Network.RPC.Models -{ - public class RpcNep5TokenInfo - { - public string Name { get; set; } - - public string Symbol { get; set; } - - public uint Decimals { get; set; } - - public BigInteger TotalSupply { get; set; } - } -} diff --git a/src/neo/Network/RPC/Models/RpcPeers.cs b/src/neo/Network/RPC/Models/RpcPeers.cs deleted file mode 100644 index fac73842de..0000000000 --- a/src/neo/Network/RPC/Models/RpcPeers.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Neo.IO.Json; -using System.Linq; - -namespace Neo.Network.RPC.Models -{ - public class RpcPeers - { - public RpcPeer[] Unconnected { get; set; } - - public RpcPeer[] Bad { get; set; } - - public RpcPeer[] Connected { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["unconnected"] = new JArray(Unconnected.Select(p => p.ToJson())); - json["bad"] = new JArray(Bad.Select(p => p.ToJson())); - json["connected"] = new JArray(Connected.Select(p => p.ToJson())); - return json; - } - - public static RpcPeers FromJson(JObject json) - { - RpcPeers result = new RpcPeers(); - result.Unconnected = ((JArray)json["unconnected"]).Select(p => RpcPeer.FromJson(p)).ToArray(); - result.Bad = ((JArray)json["bad"]).Select(p => RpcPeer.FromJson(p)).ToArray(); - result.Connected = ((JArray)json["connected"]).Select(p => RpcPeer.FromJson(p)).ToArray(); - return result; - } - } - - public class RpcPeer - { - public string Address { get; set; } - - public int Port { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["address"] = Address; - json["port"] = Port; - return json; - } - - public static RpcPeer FromJson(JObject json) - { - RpcPeer peer = new RpcPeer(); - peer.Address = json["address"].AsString(); - peer.Port = int.Parse(json["port"].AsString()); - return peer; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcPlugin.cs b/src/neo/Network/RPC/Models/RpcPlugin.cs deleted file mode 100644 index db03f70eb3..0000000000 --- a/src/neo/Network/RPC/Models/RpcPlugin.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Neo.IO.Json; -using System.Linq; - -namespace Neo.Network.RPC.Models -{ - public class RpcPlugin - { - public string Name { get; set; } - - public string Version { get; set; } - - public string[] Interfaces { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["name"] = Name; - json["version"] = Version; - json["interfaces"] = new JArray(Interfaces.Select(p => (JObject)p)); - return json; - } - - public static RpcPlugin FromJson(JObject json) - { - RpcPlugin plugin = new RpcPlugin(); - plugin.Name = json["name"].AsString(); - plugin.Version = json["version"].AsString(); - plugin.Interfaces = ((JArray)json["interfaces"]).Select(p => p.AsString()).ToArray(); - return plugin; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcRawMemPool.cs b/src/neo/Network/RPC/Models/RpcRawMemPool.cs deleted file mode 100644 index c5ebd63419..0000000000 --- a/src/neo/Network/RPC/Models/RpcRawMemPool.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Neo.IO.Json; -using System.Linq; - -namespace Neo.Network.RPC.Models -{ - public class RpcRawMemPool - { - public uint Height { get; set; } - - public string[] Verified { get; set; } - - public string[] UnVerified { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["height"] = Height; - json["verified"] = new JArray(Verified.Select(p => (JObject)p)); - json["unverified"] = new JArray(UnVerified.Select(p => (JObject)p)); - return json; - } - - public static RpcRawMemPool FromJson(JObject json) - { - RpcRawMemPool rawMemPool = new RpcRawMemPool(); - rawMemPool.Height = uint.Parse(json["height"].AsString()); - rawMemPool.Verified = ((JArray)json["verified"]).Select(p => p.AsString()).ToArray(); - rawMemPool.UnVerified = ((JArray)json["unverified"]).Select(p => p.AsString()).ToArray(); - return rawMemPool; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcRequest.cs b/src/neo/Network/RPC/Models/RpcRequest.cs deleted file mode 100644 index 1970adedbf..0000000000 --- a/src/neo/Network/RPC/Models/RpcRequest.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Neo.IO.Json; -using System.Linq; - -namespace Neo.Network.RPC.Models -{ - public class RpcRequest - { - public int Id { get; set; } - - public string Jsonrpc { get; set; } - - public string Method { get; set; } - - public JObject[] Params { get; set; } - - public static RpcRequest FromJson(JObject json) - { - return new RpcRequest - { - Id = (int)json["id"].AsNumber(), - Jsonrpc = json["jsonrpc"].AsString(), - Method = json["method"].AsString(), - Params = ((JArray)json["params"]).ToArray() - }; - } - - public JObject ToJson() - { - var json = new JObject(); - json["id"] = Id; - json["jsonrpc"] = Jsonrpc; - json["method"] = Method; - json["params"] = new JArray(Params); - return json; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcResponse.cs b/src/neo/Network/RPC/Models/RpcResponse.cs deleted file mode 100644 index e4ebcaed1b..0000000000 --- a/src/neo/Network/RPC/Models/RpcResponse.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Neo.IO.Json; - -namespace Neo.Network.RPC.Models -{ - public class RpcResponse - { - public int? Id { get; set; } - - public string Jsonrpc { get; set; } - - public RpcResponseError Error { get; set; } - - public JObject Result { get; set; } - - public string RawResponse { get; set; } - - public static RpcResponse FromJson(JObject json) - { - var response = new RpcResponse - { - Id = (int?)json["id"]?.AsNumber(), - Jsonrpc = json["jsonrpc"].AsString(), - Result = json["result"] - }; - - if (json["error"] != null) - { - response.Error = RpcResponseError.FromJson(json["error"]); - } - - return response; - } - - public JObject ToJson() - { - var json = new JObject(); - json["id"] = Id; - json["jsonrpc"] = Jsonrpc; - json["error"] = Error.ToJson(); - json["result"] = Result; - return json; - } - } - - public class RpcResponseError - { - public int Code { get; set; } - - public string Message { get; set; } - - public JObject Data { get; set; } - - public static RpcResponseError FromJson(JObject json) - { - return new RpcResponseError - { - Code = (int)json["code"].AsNumber(), - Message = json["message"].AsString(), - Data = json["data"], - }; - } - - public JObject ToJson() - { - var json = new JObject(); - json["code"] = Code; - json["message"] = Message; - json["data"] = Data; - return json; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcTransaction.cs b/src/neo/Network/RPC/Models/RpcTransaction.cs deleted file mode 100644 index 48b1e19bd4..0000000000 --- a/src/neo/Network/RPC/Models/RpcTransaction.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Neo.IO.Json; -using Neo.Network.P2P.Payloads; -using Neo.VM; - -namespace Neo.Network.RPC.Models -{ - public class RpcTransaction - { - public Transaction Transaction { get; set; } - - public UInt256 BlockHash { get; set; } - - public int? Confirmations { get; set; } - - public uint? BlockTime { get; set; } - - public VMState? VMState { get; set; } - - public JObject ToJson() - { - JObject json = Transaction.ToJson(); - if (Confirmations != null) - { - json["blockhash"] = BlockHash.ToString(); - json["confirmations"] = Confirmations; - json["blocktime"] = BlockTime; - if (VMState != null) - { - json["vmState"] = VMState; - } - } - return json; - } - - public static RpcTransaction FromJson(JObject json) - { - RpcTransaction transaction = new RpcTransaction(); - transaction.Transaction = Transaction.FromJson(json); - if (json["confirmations"] != null) - { - transaction.BlockHash = UInt256.Parse(json["blockhash"].AsString()); - transaction.Confirmations = (int)json["confirmations"].AsNumber(); - transaction.BlockTime = (uint)json["blocktime"].AsNumber(); - transaction.VMState = json["vmState"]?.TryGetEnum(); - } - return transaction; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcValidateAddressResult.cs b/src/neo/Network/RPC/Models/RpcValidateAddressResult.cs deleted file mode 100644 index 5e0a704797..0000000000 --- a/src/neo/Network/RPC/Models/RpcValidateAddressResult.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Neo.IO.Json; - -namespace Neo.Network.RPC.Models -{ - public class RpcValidateAddressResult - { - public string Address { get; set; } - - public bool IsValid { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["address"] = Address; - json["isvalid"] = IsValid; - return json; - } - - public static RpcValidateAddressResult FromJson(JObject json) - { - RpcValidateAddressResult validateAddress = new RpcValidateAddressResult(); - validateAddress.Address = json["address"].AsString(); - validateAddress.IsValid = json["isvalid"].AsBoolean(); - return validateAddress; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcValidator.cs b/src/neo/Network/RPC/Models/RpcValidator.cs deleted file mode 100644 index f3116ed2e4..0000000000 --- a/src/neo/Network/RPC/Models/RpcValidator.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Neo.IO.Json; -using System.Numerics; - -namespace Neo.Network.RPC.Models -{ - public class RpcValidator - { - public string PublicKey { get; set; } - - public BigInteger Votes { get; set; } - - public bool Active { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["publickey"] = PublicKey; - json["votes"] = Votes.ToString(); - json["active"] = Active; - return json; - } - - public static RpcValidator FromJson(JObject json) - { - RpcValidator validator = new RpcValidator(); - validator.PublicKey = json["publickey"].AsString(); - validator.Votes = BigInteger.Parse(json["votes"].AsString()); - validator.Active = json["active"].AsBoolean(); - return validator; - } - } -} diff --git a/src/neo/Network/RPC/Models/RpcVersion.cs b/src/neo/Network/RPC/Models/RpcVersion.cs deleted file mode 100644 index 8163875b64..0000000000 --- a/src/neo/Network/RPC/Models/RpcVersion.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Neo.IO.Json; - -namespace Neo.Network.RPC.Models -{ - public class RpcVersion - { - public int TcpPort { get; set; } - - public int WsPort { get; set; } - - public uint Nonce { get; set; } - - public string UserAgent { get; set; } - - public JObject ToJson() - { - JObject json = new JObject(); - json["topPort"] = TcpPort.ToString(); - json["wsPort"] = WsPort.ToString(); - json["nonce"] = Nonce.ToString(); - json["useragent"] = UserAgent; - return json; - } - - public static RpcVersion FromJson(JObject json) - { - RpcVersion version = new RpcVersion(); - version.TcpPort = int.Parse(json["tcpPort"].AsString()); - version.WsPort = int.Parse(json["wsPort"].AsString()); - version.Nonce = uint.Parse(json["nonce"].AsString()); - version.UserAgent = json["useragent"].AsString(); - return version; - } - } -} diff --git a/src/neo/Network/RPC/Nep5API.cs b/src/neo/Network/RPC/Nep5API.cs deleted file mode 100644 index 619c755280..0000000000 --- a/src/neo/Network/RPC/Nep5API.cs +++ /dev/null @@ -1,122 +0,0 @@ -using Neo.Network.P2P.Payloads; -using Neo.Network.RPC.Models; -using Neo.SmartContract; -using Neo.VM; -using Neo.Wallets; -using System.Linq; -using System.Numerics; -using static Neo.Helper; - -namespace Neo.Network.RPC -{ - /// - /// Call NEP5 methods with RPC API - /// - public class Nep5API : ContractClient - { - /// - /// Nep5API Constructor - /// - /// the RPC client to call NEO RPC methods - public Nep5API(RpcClient rpcClient) : base(rpcClient) { } - - /// - /// Get balance of NEP5 token - /// - /// contract script hash - /// account script hash - /// - public BigInteger BalanceOf(UInt160 scriptHash, UInt160 account) - { - BigInteger balance = TestInvoke(scriptHash, "balanceOf", account).Stack.Single().ToStackItem().GetBigInteger(); - return balance; - } - - /// - /// Get name of NEP5 token - /// - /// contract script hash - /// - public string Name(UInt160 scriptHash) - { - return TestInvoke(scriptHash, "name").Stack.Single().ToStackItem().GetString(); - } - - /// - /// Get symbol of NEP5 token - /// - /// contract script hash - /// - public string Symbol(UInt160 scriptHash) - { - return TestInvoke(scriptHash, "symbol").Stack.Single().ToStackItem().GetString(); - } - - /// - /// Get decimals of NEP5 token - /// - /// contract script hash - /// - public uint Decimals(UInt160 scriptHash) - { - return (uint)TestInvoke(scriptHash, "decimals").Stack.Single().ToStackItem().GetBigInteger(); - } - - /// - /// Get total supply of NEP5 token - /// - /// contract script hash - /// - public BigInteger TotalSupply(UInt160 scriptHash) - { - return TestInvoke(scriptHash, "totalSupply").Stack.Single().ToStackItem().GetBigInteger(); - } - - /// - /// Get token information in one rpc call - /// - /// contract script hash - /// - public RpcNep5TokenInfo GetTokenInfo(UInt160 scriptHash) - { - byte[] script = Concat(scriptHash.MakeScript("name"), - scriptHash.MakeScript("symbol"), - scriptHash.MakeScript("decimals"), - scriptHash.MakeScript("totalSupply")); - - var result = rpcClient.InvokeScript(script).Stack; - - return new RpcNep5TokenInfo - { - Name = result[0].ToStackItem().GetString(), - Symbol = result[1].ToStackItem().GetString(), - Decimals = (uint)result[2].ToStackItem().GetBigInteger(), - TotalSupply = result[3].ToStackItem().GetBigInteger() - }; - } - - /// - /// Create NEP5 token transfer transaction - /// - /// contract script hash - /// from KeyPair - /// to account script hash - /// transfer amount - /// netwotk fee, set to be 0 will auto calculate the least fee - /// - public Transaction CreateTransferTx(UInt160 scriptHash, KeyPair fromKey, UInt160 to, BigInteger amount, long networkFee = 0) - { - var sender = Contract.CreateSignatureRedeemScript(fromKey.PublicKey).ToScriptHash(); - Cosigner[] cosigners = new[] { new Cosigner { Scopes = WitnessScope.CalledByEntry, Account = sender } }; - - byte[] script = scriptHash.MakeScript("transfer", sender, to, amount); - Transaction tx = new TransactionManager(rpcClient, sender) - .MakeTransaction(script, null, cosigners, networkFee) - .AddSignature(fromKey) - .Sign() - .Tx; - - return tx; - } - } -} diff --git a/src/neo/Network/RPC/PolicyAPI.cs b/src/neo/Network/RPC/PolicyAPI.cs deleted file mode 100644 index 1b401cd7e5..0000000000 --- a/src/neo/Network/RPC/PolicyAPI.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Neo.SmartContract.Native; -using Neo.VM; -using System.Linq; - -namespace Neo.Network.RPC -{ - /// - /// Get Policy info by RPC API - /// - public class PolicyAPI : ContractClient - { - readonly UInt160 scriptHash = NativeContract.Policy.Hash; - - /// - /// PolicyAPI Constructor - /// - /// the RPC client to call NEO RPC methods - public PolicyAPI(RpcClient rpcClient) : base(rpcClient) { } - - /// - /// Get Max Transactions Count Per Block - /// - /// - public uint GetMaxTransactionsPerBlock() - { - return (uint)TestInvoke(scriptHash, "getMaxTransactionsPerBlock").Stack.Single().ToStackItem().GetBigInteger(); - } - - /// - /// Get Max Block Size - /// - /// - public uint GetMaxBlockSize() - { - return (uint)TestInvoke(scriptHash, "getMaxBlockSize").Stack.Single().ToStackItem().GetBigInteger(); - } - - /// - /// Get Network Fee Per Byte - /// - /// - public long GetFeePerByte() - { - return (long)TestInvoke(scriptHash, "getFeePerByte").Stack.Single().ToStackItem().GetBigInteger(); - } - - /// - /// Get Ploicy Blocked Accounts - /// - /// - public UInt160[] GetBlockedAccounts() - { - var result = (VM.Types.Array)TestInvoke(scriptHash, "getBlockedAccounts").Stack.Single().ToStackItem(); - return result.Select(p => new UInt160(p.GetSpan().ToArray())).ToArray(); - } - } -} diff --git a/src/neo/Network/RPC/RpcClient.cs b/src/neo/Network/RPC/RpcClient.cs deleted file mode 100644 index 0721efcb33..0000000000 --- a/src/neo/Network/RPC/RpcClient.cs +++ /dev/null @@ -1,313 +0,0 @@ -using Neo.IO; -using Neo.IO.Json; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.Network.RPC.Models; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; - -namespace Neo.Network.RPC -{ - /// - /// The RPC client to call NEO RPC methods - /// - public class RpcClient : IDisposable - { - private readonly HttpClient httpClient; - - public RpcClient(string url) - { - httpClient = new HttpClient() { BaseAddress = new Uri(url) }; - } - - public RpcClient(HttpClient client) - { - httpClient = client; - } - - public void Dispose() - { - httpClient?.Dispose(); - } - - public async Task SendAsync(RpcRequest request) - { - var requestJson = request.ToJson().ToString(); - using (var result = await httpClient.PostAsync(httpClient.BaseAddress, new StringContent(requestJson, Encoding.UTF8))) - { - var content = await result.Content.ReadAsStringAsync(); - var response = RpcResponse.FromJson(JObject.Parse(content)); - response.RawResponse = content; - - if (response.Error != null) - { - throw new RpcException(response.Error.Code, response.Error.Message); - } - - return response; - } - } - - public RpcResponse Send(RpcRequest request) - { - try - { - return SendAsync(request).Result; - } - catch (AggregateException ex) - { - throw ex.GetBaseException(); - } - } - - public virtual JObject RpcSend(string method, params JObject[] paraArgs) - { - var request = new RpcRequest - { - Id = 1, - Jsonrpc = "2.0", - Method = method, - Params = paraArgs.Select(p => p).ToArray() - }; - return Send(request).Result; - } - - /// - /// Returns the hash of the tallest block in the main chain. - /// - public string GetBestBlockHash() - { - return RpcSend("getbestblockhash").AsString(); - } - - /// - /// Returns the hash of the tallest block in the main chain. - /// The serialized information of the block is returned, represented by a hexadecimal string. - /// - public string GetBlockHex(string hashOrIndex) - { - if (int.TryParse(hashOrIndex, out int index)) - { - return RpcSend("getblock", index).AsString(); - } - return RpcSend("getblock", hashOrIndex).AsString(); - } - - /// - /// Returns the hash of the tallest block in the main chain. - /// - public RpcBlock GetBlock(string hashOrIndex) - { - if (int.TryParse(hashOrIndex, out int index)) - { - return RpcBlock.FromJson(RpcSend("getblock", index, true)); - } - return RpcBlock.FromJson(RpcSend("getblock", hashOrIndex, true)); - } - - /// - /// Gets the number of blocks in the main chain. - /// - public uint GetBlockCount() - { - return (uint)RpcSend("getblockcount").AsNumber(); - } - - /// - /// Returns the hash value of the corresponding block, based on the specified index. - /// - public string GetBlockHash(int index) - { - return RpcSend("getblockhash", index).AsString(); - } - - /// - /// Returns the corresponding block header information according to the specified script hash. - /// - public string GetBlockHeaderHex(string hashOrIndex) - { - if (int.TryParse(hashOrIndex, out int index)) - { - return RpcSend("getblockheader", index).AsString(); - } - return RpcSend("getblockheader", hashOrIndex).AsString(); - } - - /// - /// Returns the corresponding block header information according to the specified script hash. - /// - public RpcBlockHeader GetBlockHeader(string hashOrIndex) - { - if (int.TryParse(hashOrIndex, out int index)) - { - return RpcBlockHeader.FromJson(RpcSend("getblockheader", index, true)); - } - return RpcBlockHeader.FromJson(RpcSend("getblockheader", hashOrIndex, true)); - } - - /// - /// Returns the system fees of the block, based on the specified index. - /// - public string GetBlockSysFee(int height) - { - return RpcSend("getblocksysfee", height).AsString(); - } - - /// - /// Gets the current number of connections for the node. - /// - public int GetConnectionCount() - { - return (int)RpcSend("getconnectioncount").AsNumber(); - } - - /// - /// Queries contract information, according to the contract script hash. - /// - public ContractState GetContractState(string hash) - { - return ContractState.FromJson(RpcSend("getcontractstate", hash)); - } - - /// - /// Gets the list of nodes that the node is currently connected/disconnected from. - /// - public RpcPeers GetPeers() - { - return RpcPeers.FromJson(RpcSend("getpeers")); - } - - /// - /// Obtains the list of unconfirmed transactions in memory. - /// - public string[] GetRawMempool() - { - return ((JArray)RpcSend("getrawmempool")).Select(p => p.AsString()).ToArray(); - } - - /// - /// Obtains the list of unconfirmed transactions in memory. - /// shouldGetUnverified = true - /// - public RpcRawMemPool GetRawMempoolBoth() - { - return RpcRawMemPool.FromJson(RpcSend("getrawmempool", true)); - } - - /// - /// Returns the corresponding transaction information, based on the specified hash value. - /// - public string GetRawTransactionHex(string txid) - { - return RpcSend("getrawtransaction", txid).AsString(); - } - - /// - /// Returns the corresponding transaction information, based on the specified hash value. - /// verbose = true - /// - public RpcTransaction GetRawTransaction(string txid) - { - return RpcTransaction.FromJson(RpcSend("getrawtransaction", txid, true)); - } - - /// - /// Returns the stored value, according to the contract script hash and the stored key. - /// - public string GetStorage(string script_hash, string key) - { - return RpcSend("getstorage", script_hash, key).AsString(); - } - - /// - /// Returns the block index in which the transaction is found. - /// - public uint GetTransactionHeight(string txid) - { - return uint.Parse(RpcSend("gettransactionheight", txid).AsString()); - } - - /// - /// Returns the current NEO consensus nodes information and voting status. - /// - public RpcValidator[] GetValidators() - { - return ((JArray)RpcSend("getvalidators")).Select(p => RpcValidator.FromJson(p)).ToArray(); - } - - /// - /// Returns the version information about the queried node. - /// - public RpcVersion GetVersion() - { - return RpcVersion.FromJson(RpcSend("getversion")); - } - - /// - /// Returns the result after calling a smart contract at scripthash with the given operation and parameters. - /// This RPC call does not affect the blockchain in any way. - /// - public RpcInvokeResult InvokeFunction(string address, string function, RpcStack[] stacks) - { - return RpcInvokeResult.FromJson(RpcSend("invokefunction", address, function, stacks.Select(p => p.ToJson()).ToArray())); - } - - /// - /// Returns the result after passing a script through the VM. - /// This RPC call does not affect the blockchain in any way. - /// - public RpcInvokeResult InvokeScript(byte[] script, params UInt160[] scriptHashesForVerifying) - { - List parameters = new List - { - script.ToHexString() - }; - parameters.AddRange(scriptHashesForVerifying.Select(p => (JObject)p.ToString())); - return RpcInvokeResult.FromJson(RpcSend("invokescript", parameters.ToArray())); - } - - /// - /// Returns a list of plugins loaded by the node. - /// - public RpcPlugin[] ListPlugins() - { - return ((JArray)RpcSend("listplugins")).Select(p => RpcPlugin.FromJson(p)).ToArray(); - } - - /// - /// Broadcasts a serialized transaction over the NEO network. - /// - public bool SendRawTransaction(byte[] rawTransaction) - { - return RpcSend("sendrawtransaction", rawTransaction.ToHexString()).AsBoolean(); - } - - /// - /// Broadcasts a transaction over the NEO network. - /// - public bool SendRawTransaction(Transaction transaction) - { - return SendRawTransaction(transaction.ToArray()); - } - - /// - /// Broadcasts a serialized block over the NEO network. - /// - public bool SubmitBlock(byte[] block) - { - return RpcSend("submitblock", block.ToHexString()).AsBoolean(); - } - - /// - /// Verifies that the address is a correct NEO address. - /// - public RpcValidateAddressResult ValidateAddress(string address) - { - return RpcValidateAddressResult.FromJson(RpcSend("validateaddress", address)); - } - } -} diff --git a/src/neo/Network/RPC/RpcException.cs b/src/neo/Network/RPC/RpcException.cs deleted file mode 100644 index b5030750ca..0000000000 --- a/src/neo/Network/RPC/RpcException.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Neo.Network.RPC -{ - public class RpcException : Exception - { - public RpcException(int code, string message) : base(message) - { - HResult = code; - } - } -} diff --git a/src/neo/Network/RPC/RpcServer.cs b/src/neo/Network/RPC/RpcServer.cs deleted file mode 100644 index e2137d517c..0000000000 --- a/src/neo/Network/RPC/RpcServer.cs +++ /dev/null @@ -1,667 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; -using Akka.Actor; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.ResponseCompression; -using Microsoft.AspNetCore.Server.Kestrel.Https; -using Microsoft.Extensions.DependencyInjection; -using Neo.IO; -using Neo.IO.Json; -using Neo.Ledger; -using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; -using Neo.Persistence; -using Neo.Plugins; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.Wallets; - -namespace Neo.Network.RPC -{ - public sealed class RpcServer : IDisposable - { - private class CheckWitnessHashes : IVerifiable - { - private readonly UInt160[] _scriptHashesForVerifying; - public Witness[] Witnesses { get; set; } - public int Size { get; } - - public CheckWitnessHashes(UInt160[] scriptHashesForVerifying) - { - _scriptHashesForVerifying = scriptHashesForVerifying; - } - - public void Serialize(BinaryWriter writer) - { - throw new NotImplementedException(); - } - - public void Deserialize(BinaryReader reader) - { - throw new NotImplementedException(); - } - - public void DeserializeUnsigned(BinaryReader reader) - { - throw new NotImplementedException(); - } - - public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) - { - return _scriptHashesForVerifying; - } - - public void SerializeUnsigned(BinaryWriter writer) - { - throw new NotImplementedException(); - } - } - - public Wallet Wallet { get; set; } - public long MaxGasInvoke { get; } - - private IWebHost host; - private readonly NeoSystem system; - - public RpcServer(NeoSystem system, Wallet wallet = null, long maxGasInvoke = default) - { - this.system = system; - this.Wallet = wallet; - this.MaxGasInvoke = maxGasInvoke; - } - - private static JObject CreateErrorResponse(JObject id, int code, string message, JObject data = null) - { - JObject response = CreateResponse(id); - response["error"] = new JObject(); - response["error"]["code"] = code; - response["error"]["message"] = message; - if (data != null) - response["error"]["data"] = data; - return response; - } - - private static JObject CreateResponse(JObject id) - { - JObject response = new JObject(); - response["jsonrpc"] = "2.0"; - response["id"] = id; - return response; - } - - public void Dispose() - { - if (host != null) - { - host.Dispose(); - host = null; - } - } - - private JObject GetInvokeResult(byte[] script, IVerifiable checkWitnessHashes = null) - { - using ApplicationEngine engine = ApplicationEngine.Run(script, checkWitnessHashes, extraGAS: MaxGasInvoke); - JObject json = new JObject(); - json["script"] = script.ToHexString(); - json["state"] = engine.State; - json["gas_consumed"] = engine.GasConsumed.ToString(); - try - { - json["stack"] = new JArray(engine.ResultStack.Select(p => p.ToParameter().ToJson())); - } - catch (InvalidOperationException) - { - json["stack"] = "error: recursive reference"; - } - return json; - } - - private static JObject GetRelayResult(RelayResultReason reason, UInt256 hash) - { - if (reason == RelayResultReason.Succeed) - { - var ret = new JObject(); - ret["hash"] = hash.ToString(); - return ret; - } - else - { - throw new RpcException(-500, reason.ToString()); - } - } - - private JObject Process(string method, JArray _params) - { - switch (method) - { - case "getbestblockhash": - { - return GetBestBlockHash(); - } - case "getblock": - { - JObject key = _params[0]; - bool verbose = _params.Count >= 2 && _params[1].AsBoolean(); - return GetBlock(key, verbose); - } - case "getblockcount": - { - return GetBlockCount(); - } - case "getblockhash": - { - uint height = uint.Parse(_params[0].AsString()); - return GetBlockHash(height); - } - case "getblockheader": - { - JObject key = _params[0]; - bool verbose = _params.Count >= 2 && _params[1].AsBoolean(); - return GetBlockHeader(key, verbose); - } - case "getblocksysfee": - { - uint height = uint.Parse(_params[0].AsString()); - return GetBlockSysFee(height); - } - case "getconnectioncount": - { - return GetConnectionCount(); - } - case "getcontractstate": - { - UInt160 script_hash = UInt160.Parse(_params[0].AsString()); - return GetContractState(script_hash); - } - case "getpeers": - { - return GetPeers(); - } - case "getrawmempool": - { - bool shouldGetUnverified = _params.Count >= 1 && _params[0].AsBoolean(); - return GetRawMemPool(shouldGetUnverified); - } - case "getrawtransaction": - { - UInt256 hash = UInt256.Parse(_params[0].AsString()); - bool verbose = _params.Count >= 2 && _params[1].AsBoolean(); - return GetRawTransaction(hash, verbose); - } - case "getstorage": - { - UInt160 script_hash = UInt160.Parse(_params[0].AsString()); - byte[] key = _params[1].AsString().HexToBytes(); - return GetStorage(script_hash, key); - } - case "gettransactionheight": - { - UInt256 hash = UInt256.Parse(_params[0].AsString()); - return GetTransactionHeight(hash); - } - case "getvalidators": - { - return GetValidators(); - } - case "getversion": - { - return GetVersion(); - } - case "invokefunction": - { - UInt160 script_hash = UInt160.Parse(_params[0].AsString()); - string operation = _params[1].AsString(); - ContractParameter[] args = _params.Count >= 3 ? ((JArray)_params[2]).Select(p => ContractParameter.FromJson(p)).ToArray() : new ContractParameter[0]; - return InvokeFunction(script_hash, operation, args); - } - case "invokescript": - { - byte[] script = _params[0].AsString().HexToBytes(); - CheckWitnessHashes checkWitnessHashes = null; - if (_params.Count > 1) - { - UInt160[] scriptHashesForVerifying = _params.Skip(1).Select(u => UInt160.Parse(u.AsString())).ToArray(); - checkWitnessHashes = new CheckWitnessHashes(scriptHashesForVerifying); - } - return GetInvokeResult(script, checkWitnessHashes); - } - case "listplugins": - { - return ListPlugins(); - } - case "sendrawtransaction": - { - Transaction tx = _params[0].AsString().HexToBytes().AsSerializable(); - return SendRawTransaction(tx); - } - case "submitblock": - { - Block block = _params[0].AsString().HexToBytes().AsSerializable(); - return SubmitBlock(block); - } - case "validateaddress": - { - string address = _params[0].AsString(); - return ValidateAddress(address); - } - default: - throw new RpcException(-32601, "Method not found"); - } - } - - private async Task ProcessAsync(HttpContext context) - { - context.Response.Headers["Access-Control-Allow-Origin"] = "*"; - context.Response.Headers["Access-Control-Allow-Methods"] = "GET, POST"; - context.Response.Headers["Access-Control-Allow-Headers"] = "Content-Type"; - context.Response.Headers["Access-Control-Max-Age"] = "31536000"; - if (context.Request.Method != "GET" && context.Request.Method != "POST") return; - JObject request = null; - if (context.Request.Method == "GET") - { - string jsonrpc = context.Request.Query["jsonrpc"]; - string id = context.Request.Query["id"]; - string method = context.Request.Query["method"]; - string _params = context.Request.Query["params"]; - if (!string.IsNullOrEmpty(id) && !string.IsNullOrEmpty(method) && !string.IsNullOrEmpty(_params)) - { - try - { - _params = Encoding.UTF8.GetString(Convert.FromBase64String(_params)); - } - catch (FormatException) { } - request = new JObject(); - if (!string.IsNullOrEmpty(jsonrpc)) - request["jsonrpc"] = jsonrpc; - request["id"] = id; - request["method"] = method; - request["params"] = JObject.Parse(_params); - } - } - else if (context.Request.Method == "POST") - { - using (StreamReader reader = new StreamReader(context.Request.Body)) - { - try - { - request = JObject.Parse(reader); - } - catch (FormatException) { } - } - } - JObject response; - if (request == null) - { - response = CreateErrorResponse(null, -32700, "Parse error"); - } - else if (request is JArray array) - { - if (array.Count == 0) - { - response = CreateErrorResponse(request["id"], -32600, "Invalid Request"); - } - else - { - response = array.Select(p => ProcessRequest(context, p)).Where(p => p != null).ToArray(); - } - } - else - { - response = ProcessRequest(context, request); - } - if (response == null || (response as JArray)?.Count == 0) return; - context.Response.ContentType = "application/json-rpc"; - await context.Response.WriteAsync(response.ToString(), Encoding.UTF8); - } - - private JObject ProcessRequest(HttpContext context, JObject request) - { - if (!request.ContainsProperty("id")) return null; - if (!request.ContainsProperty("method") || !request.ContainsProperty("params") || !(request["params"] is JArray)) - { - return CreateErrorResponse(request["id"], -32600, "Invalid Request"); - } - JObject result = null; - try - { - string method = request["method"].AsString(); - JArray _params = (JArray)request["params"]; - foreach (IRpcPlugin plugin in Plugin.RpcPlugins) - plugin.PreProcess(context, method, _params); - foreach (IRpcPlugin plugin in Plugin.RpcPlugins) - { - result = plugin.OnProcess(context, method, _params); - if (result != null) break; - } - if (result == null) - result = Process(method, _params); - foreach (IRpcPlugin plugin in Plugin.RpcPlugins) - plugin.PostProcess(context, method, _params, result); - } - catch (FormatException) - { - return CreateErrorResponse(request["id"], -32602, "Invalid params"); - } - catch (IndexOutOfRangeException) - { - return CreateErrorResponse(request["id"], -32602, "Invalid params"); - } - catch (Exception ex) - { -#if DEBUG - return CreateErrorResponse(request["id"], ex.HResult, ex.Message, ex.StackTrace); -#else - return CreateErrorResponse(request["id"], ex.HResult, ex.Message); -#endif - } - JObject response = CreateResponse(request["id"]); - response["result"] = result; - return response; - } - - public void Start(IPAddress bindAddress, int port, string sslCert = null, string password = null, string[] trustedAuthorities = null) - { - host = new WebHostBuilder().UseKestrel(options => options.Listen(bindAddress, port, listenOptions => - { - if (string.IsNullOrEmpty(sslCert)) return; - listenOptions.UseHttps(sslCert, password, httpsConnectionAdapterOptions => - { - if (trustedAuthorities is null || trustedAuthorities.Length == 0) - return; - httpsConnectionAdapterOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate; - httpsConnectionAdapterOptions.ClientCertificateValidation = (cert, chain, err) => - { - if (err != SslPolicyErrors.None) - return false; - X509Certificate2 authority = chain.ChainElements[chain.ChainElements.Count - 1].Certificate; - return trustedAuthorities.Contains(authority.Thumbprint); - }; - }); - })) - .Configure(app => - { - app.UseResponseCompression(); - app.Run(ProcessAsync); - }) - .ConfigureServices(services => - { - services.AddResponseCompression(options => - { - // options.EnableForHttps = false; - options.Providers.Add(); - options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Append("application/json-rpc"); - }); - - services.Configure(options => - { - options.Level = CompressionLevel.Fastest; - }); - }) - .Build(); - - host.Start(); - } - - private JObject GetBestBlockHash() - { - return Blockchain.Singleton.CurrentBlockHash.ToString(); - } - - private JObject GetBlock(JObject key, bool verbose) - { - Block block; - if (key is JNumber) - { - uint index = uint.Parse(key.AsString()); - block = Blockchain.Singleton.GetBlock(index); - } - else - { - UInt256 hash = UInt256.Parse(key.AsString()); - block = Blockchain.Singleton.View.GetBlock(hash); - } - if (block == null) - throw new RpcException(-100, "Unknown block"); - if (verbose) - { - JObject json = block.ToJson(); - json["confirmations"] = Blockchain.Singleton.Height - block.Index + 1; - UInt256 hash = Blockchain.Singleton.GetNextBlockHash(block.Hash); - if (hash != null) - json["nextblockhash"] = hash.ToString(); - return json; - } - return block.ToArray().ToHexString(); - } - - private JObject GetBlockCount() - { - return Blockchain.Singleton.Height + 1; - } - - private JObject GetBlockHash(uint height) - { - if (height <= Blockchain.Singleton.Height) - { - return Blockchain.Singleton.GetBlockHash(height).ToString(); - } - throw new RpcException(-100, "Invalid Height"); - } - - private JObject GetBlockHeader(JObject key, bool verbose) - { - Header header; - if (key is JNumber) - { - uint height = uint.Parse(key.AsString()); - header = Blockchain.Singleton.GetHeader(height); - } - else - { - UInt256 hash = UInt256.Parse(key.AsString()); - header = Blockchain.Singleton.View.GetHeader(hash); - } - if (header == null) - throw new RpcException(-100, "Unknown block"); - - if (verbose) - { - JObject json = header.ToJson(); - json["confirmations"] = Blockchain.Singleton.Height - header.Index + 1; - UInt256 hash = Blockchain.Singleton.GetNextBlockHash(header.Hash); - if (hash != null) - json["nextblockhash"] = hash.ToString(); - return json; - } - - return header.ToArray().ToHexString(); - } - - private JObject GetBlockSysFee(uint height) - { - if (height <= Blockchain.Singleton.Height) - using (ApplicationEngine engine = NativeContract.GAS.TestCall("getSysFeeAmount", height)) - { - return engine.ResultStack.Peek().GetBigInteger().ToString(); - } - throw new RpcException(-100, "Invalid Height"); - } - - private JObject GetConnectionCount() - { - return LocalNode.Singleton.ConnectedCount; - } - - private JObject GetContractState(UInt160 script_hash) - { - ContractState contract = Blockchain.Singleton.View.Contracts.TryGet(script_hash); - return contract?.ToJson() ?? throw new RpcException(-100, "Unknown contract"); - } - - private JObject GetPeers() - { - JObject json = new JObject(); - json["unconnected"] = new JArray(LocalNode.Singleton.GetUnconnectedPeers().Select(p => - { - JObject peerJson = new JObject(); - peerJson["address"] = p.Address.ToString(); - peerJson["port"] = p.Port; - return peerJson; - })); - json["bad"] = new JArray(); //badpeers has been removed - json["connected"] = new JArray(LocalNode.Singleton.GetRemoteNodes().Select(p => - { - JObject peerJson = new JObject(); - peerJson["address"] = p.Remote.Address.ToString(); - peerJson["port"] = p.ListenerTcpPort; - return peerJson; - })); - return json; - } - - private JObject GetRawMemPool(bool shouldGetUnverified) - { - if (!shouldGetUnverified) - return new JArray(Blockchain.Singleton.MemPool.GetVerifiedTransactions().Select(p => (JObject)p.Hash.ToString())); - - JObject json = new JObject(); - json["height"] = Blockchain.Singleton.Height; - Blockchain.Singleton.MemPool.GetVerifiedAndUnverifiedTransactions( - out IEnumerable verifiedTransactions, - out IEnumerable unverifiedTransactions); - json["verified"] = new JArray(verifiedTransactions.Select(p => (JObject)p.Hash.ToString())); - json["unverified"] = new JArray(unverifiedTransactions.Select(p => (JObject)p.Hash.ToString())); - return json; - } - - private JObject GetRawTransaction(UInt256 hash, bool verbose) - { - Transaction tx = Blockchain.Singleton.GetTransaction(hash); - if (tx == null) - throw new RpcException(-100, "Unknown transaction"); - if (verbose) - { - JObject json = tx.ToJson(); - TransactionState txState = Blockchain.Singleton.View.Transactions.TryGet(hash); - if (txState != null) - { - Header header = Blockchain.Singleton.GetHeader(txState.BlockIndex); - json["blockhash"] = header.Hash.ToString(); - json["confirmations"] = Blockchain.Singleton.Height - header.Index + 1; - json["blocktime"] = header.Timestamp; - json["vmState"] = txState.VMState; - } - return json; - } - return tx.ToArray().ToHexString(); - } - - private JObject GetStorage(UInt160 script_hash, byte[] key) - { - StorageItem item = Blockchain.Singleton.View.Storages.TryGet(new StorageKey - { - ScriptHash = script_hash, - Key = key - }) ?? new StorageItem(); - return item.Value?.ToHexString(); - } - - private JObject GetTransactionHeight(UInt256 hash) - { - uint? height = Blockchain.Singleton.View.Transactions.TryGet(hash)?.BlockIndex; - if (height.HasValue) return height.Value; - throw new RpcException(-100, "Unknown transaction"); - } - - private JObject GetValidators() - { - using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) - { - var validators = NativeContract.NEO.GetValidators(snapshot); - return NativeContract.NEO.GetRegisteredValidators(snapshot).Select(p => - { - JObject validator = new JObject(); - validator["publickey"] = p.PublicKey.ToString(); - validator["votes"] = p.Votes.ToString(); - validator["active"] = validators.Contains(p.PublicKey); - return validator; - }).ToArray(); - } - } - - private JObject GetVersion() - { - JObject json = new JObject(); - json["tcpPort"] = LocalNode.Singleton.ListenerTcpPort; - json["wsPort"] = LocalNode.Singleton.ListenerWsPort; - json["nonce"] = LocalNode.Nonce; - json["useragent"] = LocalNode.UserAgent; - return json; - } - - private JObject InvokeFunction(UInt160 script_hash, string operation, ContractParameter[] args) - { - byte[] script; - using (ScriptBuilder sb = new ScriptBuilder()) - { - script = sb.EmitAppCall(script_hash, operation, args).ToArray(); - } - return GetInvokeResult(script); - } - - private JObject ListPlugins() - { - return new JArray(Plugin.Plugins - .OrderBy(u => u.Name) - .Select(u => new JObject - { - ["name"] = u.Name, - ["version"] = u.Version.ToString(), - ["interfaces"] = new JArray(u.GetType().GetInterfaces() - .Select(p => p.Name) - .Where(p => p.EndsWith("Plugin")) - .Select(p => (JObject)p)) - })); - } - - private JObject SendRawTransaction(Transaction tx) - { - RelayResultReason reason = system.Blockchain.Ask(tx).Result; - return GetRelayResult(reason, tx.Hash); - } - - private JObject SubmitBlock(Block block) - { - RelayResultReason reason = system.Blockchain.Ask(block).Result; - return GetRelayResult(reason, block.Hash); - } - - private JObject ValidateAddress(string address) - { - JObject json = new JObject(); - UInt160 scriptHash; - try - { - scriptHash = address.ToScriptHash(); - } - catch - { - scriptHash = null; - } - json["address"] = address; - json["isvalid"] = scriptHash != null; - return json; - } - } -} diff --git a/src/neo/Network/RPC/TransactionManager.cs b/src/neo/Network/RPC/TransactionManager.cs deleted file mode 100644 index 7841ba8527..0000000000 --- a/src/neo/Network/RPC/TransactionManager.cs +++ /dev/null @@ -1,226 +0,0 @@ -using Neo.Cryptography.ECC; -using Neo.IO; -using Neo.Network.P2P.Payloads; -using Neo.Network.RPC.Models; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.Wallets; -using System; - -namespace Neo.Network.RPC -{ - /// - /// This class helps to create transaction with RPC API. - /// - public class TransactionManager - { - private readonly RpcClient rpcClient; - private readonly PolicyAPI policyAPI; - private readonly Nep5API nep5API; - private readonly UInt160 sender; - - /// - /// The Transaction context to manage the witnesses - /// - private ContractParametersContext context; - - /// - /// The Transaction managed by this class - /// - public Transaction Tx { get; private set; } - - /// - /// TransactionManager Constructor - /// - /// the RPC client to call NEO RPC API - /// the account script hash of sender - public TransactionManager(RpcClient rpc, UInt160 sender) - { - rpcClient = rpc; - policyAPI = new PolicyAPI(rpc); - nep5API = new Nep5API(rpc); - this.sender = sender; - } - - /// - /// Create an unsigned Transaction object with given parameters. - /// - /// Transaction Script - /// Transaction Attributes - /// Transaction Cosigners - /// Transaction NetworkFee, will set to estimate value(with only basic signatures) when networkFee is 0 - /// - public TransactionManager MakeTransaction(byte[] script, TransactionAttribute[] attributes = null, Cosigner[] cosigners = null, long networkFee = 0) - { - var random = new Random(); - uint height = rpcClient.GetBlockCount() - 1; - Tx = new Transaction - { - Version = 0, - Nonce = (uint)random.Next(), - Script = script, - Sender = sender, - ValidUntilBlock = height + Transaction.MaxValidUntilBlockIncrement, - Attributes = attributes ?? new TransactionAttribute[0], - Cosigners = cosigners ?? new Cosigner[0], - Witnesses = new Witness[0] - }; - - // Add witness hashes parameter to pass CheckWitness - UInt160[] hashes = Tx.GetScriptHashesForVerifying(null); - RpcInvokeResult result = rpcClient.InvokeScript(script, hashes); - Tx.SystemFee = Math.Max(long.Parse(result.GasConsumed) - ApplicationEngine.GasFree, 0); - if (Tx.SystemFee > 0) - { - long d = (long)NativeContract.GAS.Factor; - long remainder = Tx.SystemFee % d; - if (remainder > 0) - Tx.SystemFee += d - remainder; - else if (remainder < 0) - Tx.SystemFee -= remainder; - } - - context = new ContractParametersContext(Tx); - - // set networkfee to estimate value when networkFee is 0 - Tx.NetworkFee = networkFee == 0 ? EstimateNetworkFee() : networkFee; - - var gasBalance = nep5API.BalanceOf(NativeContract.GAS.Hash, sender); - if (gasBalance >= Tx.SystemFee + Tx.NetworkFee) return this; - throw new InvalidOperationException($"Insufficient GAS in address: {sender.ToAddress()}"); - } - - /// - /// Estimate NetworkFee, assuming the witnesses are basic Signature Contract - /// - private long EstimateNetworkFee() - { - long networkFee = 0; - UInt160[] hashes = Tx.GetScriptHashesForVerifying(null); - int size = Transaction.HeaderSize + Tx.Attributes.GetVarSize() + Tx.Script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); - - // assume the hashes are single Signature - foreach (var hash in hashes) - { - size += 166; - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null); - } - - networkFee += size * policyAPI.GetFeePerByte(); - return networkFee; - } - - /// - /// Calculate NetworkFee with context items - /// - private long CalculateNetworkFee() - { - long networkFee = 0; - UInt160[] hashes = Tx.GetScriptHashesForVerifying(null); - int size = Transaction.HeaderSize + Tx.Attributes.GetVarSize() + Tx.Script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); - foreach (UInt160 hash in hashes) - { - byte[] witness_script = context.GetScript(hash); - if (witness_script is null || witness_script.Length == 0) - { - try - { - witness_script = rpcClient.GetContractState(hash.ToString())?.Script; - } - catch { } - } - - if (witness_script is null) continue; - - networkFee += Wallet.CalculateNetWorkFee(witness_script, ref size); - } - networkFee += size * policyAPI.GetFeePerByte(); - return networkFee; - } - - /// - /// Add Signature - /// - /// The KeyPair to sign transction - /// - public TransactionManager AddSignature(KeyPair key) - { - var contract = Contract.CreateSignatureContract(key.PublicKey); - - byte[] signature = Tx.Sign(key); - if (!context.AddSignature(contract, key.PublicKey, signature)) - { - throw new Exception("AddSignature failed!"); - } - - return this; - } - - /// - /// Add Multi-Signature - /// - /// The KeyPair to sign transction - /// The least count of signatures needed for multiple signature contract - /// The Public Keys construct the multiple signature contract - public TransactionManager AddMultiSig(KeyPair key, int m, params ECPoint[] publicKeys) - { - Contract contract = Contract.CreateMultiSigContract(m, publicKeys); - - byte[] signature = Tx.Sign(key); - if (!context.AddSignature(contract, key.PublicKey, signature)) - { - throw new Exception("AddMultiSig failed!"); - } - - return this; - } - - /// - /// Add Witness with contract - /// - /// The witness verification contract - /// The witness invocation parameters - public TransactionManager AddWitness(Contract contract, params object[] parameters) - { - if (!context.Add(contract, parameters)) - { - throw new Exception("AddWitness failed!"); - }; - return this; - } - - /// - /// Add Witness with scriptHash - /// - /// The witness verification contract hash - /// The witness invocation parameters - public TransactionManager AddWitness(UInt160 scriptHash, params object[] parameters) - { - var contract = Contract.Create(scriptHash); - return AddWitness(contract, parameters); - } - - /// - /// Verify Witness count and add witnesses - /// - public TransactionManager Sign() - { - // Verify witness count - if (!context.Completed) - { - throw new Exception($"Please add signature or witness first!"); - } - - // Calculate NetworkFee - long leastNetworkFee = CalculateNetworkFee(); - if (Tx.NetworkFee < leastNetworkFee) - { - throw new InvalidOperationException("Insufficient NetworkFee"); - } - - Tx.Witnesses = context.GetWitnesses(); - return this; - } - } -} diff --git a/src/neo/Network/RPC/WalletAPI.cs b/src/neo/Network/RPC/WalletAPI.cs deleted file mode 100644 index e1ab3b777f..0000000000 --- a/src/neo/Network/RPC/WalletAPI.cs +++ /dev/null @@ -1,192 +0,0 @@ -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.Network.RPC.Models; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.Wallets; -using System; -using System.Linq; -using System.Numerics; -using System.Threading.Tasks; - -namespace Neo.Network.RPC -{ - /// - /// Wallet Common APIs - /// - public class WalletAPI - { - private readonly RpcClient rpcClient; - private readonly Nep5API nep5API; - - /// - /// WalletAPI Constructor - /// - /// the RPC client to call NEO RPC methods - public WalletAPI(RpcClient rpc) - { - rpcClient = rpc; - nep5API = new Nep5API(rpc); - } - - /// - /// Get unclaimed gas with address, scripthash or public key string - /// - /// address, scripthash or public key string - /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") - /// - public decimal GetUnclaimedGas(string account) - { - UInt160 accountHash = Utility.GetScriptHash(account); - return GetUnclaimedGas(accountHash); - } - - /// - /// Get unclaimed gas - /// - /// account scripthash - /// - public decimal GetUnclaimedGas(UInt160 account) - { - UInt160 scriptHash = NativeContract.NEO.Hash; - BigInteger balance = nep5API.TestInvoke(scriptHash, "unclaimedGas", account, rpcClient.GetBlockCount() - 1) - .Stack.Single().ToStackItem().GetBigInteger(); - return ((decimal)balance) / (long)NativeContract.GAS.Factor; - } - - /// - /// Get Neo Balance - /// - /// address, scripthash or public key string - /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") - /// - public uint GetNeoBalance(string account) - { - BigInteger balance = GetTokenBalance(NativeContract.NEO.Hash.ToString(), account); - return (uint)balance; - } - - /// - /// Get Gas Balance - /// - /// address, scripthash or public key string - /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") - /// - public decimal GetGasBalance(string account) - { - BigInteger balance = GetTokenBalance(NativeContract.GAS.Hash.ToString(), account); - return ((decimal)balance) / (long)NativeContract.GAS.Factor; - } - - /// - /// Get token balance with string parameters - /// - /// token script hash, Example: "0x43cf98eddbe047e198a3e5d57006311442a0ca15"(NEO) - /// address, scripthash or public key string - /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") - /// - public BigInteger GetTokenBalance(string tokenHash, string account) - { - UInt160 scriptHash = Utility.GetScriptHash(tokenHash); - UInt160 accountHash = Utility.GetScriptHash(account); - return nep5API.BalanceOf(scriptHash, accountHash); - } - - /// - /// The GAS is claimed when doing NEO transfer - /// This function will transfer NEO balance from account to itself - /// - /// wif or private key - /// Example: WIF ("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"), PrivateKey ("450d6c2a04b5b470339a745427bae6828400cf048400837d73c415063835e005") - /// The transaction sended - public Transaction ClaimGas(string key) - { - KeyPair keyPair = Utility.GetKeyPair(key); - return ClaimGas(keyPair); - } - - /// - /// The GAS is claimed when doing NEO transfer - /// This function will transfer NEO balance from account to itself - /// - /// keyPair - /// The transaction sended - public Transaction ClaimGas(KeyPair keyPair) - { - UInt160 toHash = Contract.CreateSignatureRedeemScript(keyPair.PublicKey).ToScriptHash(); - BigInteger balance = nep5API.BalanceOf(NativeContract.NEO.Hash, toHash); - Transaction transaction = nep5API.CreateTransferTx(NativeContract.NEO.Hash, keyPair, toHash, balance); - rpcClient.SendRawTransaction(transaction); - return transaction; - } - - /// - /// Transfer NEP5 token balance, with common data types - /// - /// nep5 token script hash, Example: scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91") - /// wif or private key - /// Example: WIF ("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"), PrivateKey ("450d6c2a04b5b470339a745427bae6828400cf048400837d73c415063835e005") - /// address or account script hash - /// token amount - /// netwotk fee, set to be 0 will auto calculate the least fee - /// - public Transaction Transfer(string tokenHash, string fromKey, string toAddress, decimal amount, decimal networkFee = 0) - { - UInt160 scriptHash = Utility.GetScriptHash(tokenHash); - var decimals = nep5API.Decimals(scriptHash); - - KeyPair from = Utility.GetKeyPair(fromKey); - UInt160 to = Utility.GetScriptHash(toAddress); - BigInteger amountInteger = amount.ToBigInteger(decimals); - BigInteger networkFeeInteger = networkFee.ToBigInteger(NativeContract.GAS.Decimals); - return Transfer(scriptHash, from, to, amountInteger, (long)networkFeeInteger); - } - - /// - /// Transfer NEP5 token balance - /// - /// contract script hash - /// from KeyPair - /// to account script hash - /// transfer amount - /// netwotk fee, set to be 0 will auto calculate the least fee - /// - public Transaction Transfer(UInt160 scriptHash, KeyPair from, UInt160 to, BigInteger amountInteger, BigInteger networkFeeInteger = default) - { - Transaction transaction = nep5API.CreateTransferTx(scriptHash, from, to, amountInteger, (long)networkFeeInteger); - rpcClient.SendRawTransaction(transaction); - return transaction; - } - - /// - /// Wait until the transaction is observable block chain - /// - /// the transaction to observe - /// TimeoutException throws after "timeout" seconds - /// the Transaction state, including vmState and blockhash - public async Task WaitTransaction(Transaction transaction, int timeout = 60) - { - DateTime deadline = DateTime.UtcNow.AddSeconds(timeout); - RpcTransaction rpcTx = null; - while (rpcTx == null || rpcTx.Confirmations == null) - { - if (deadline < DateTime.UtcNow) - { - throw new TimeoutException(); - } - - try - { - rpcTx = rpcClient.GetRawTransaction(transaction.Hash.ToString()); - if (rpcTx == null || rpcTx.Confirmations == null) - { - await Task.Delay((int)Blockchain.MillisecondsPerBlock / 2); - } - } - catch (Exception) { } - } - return rpcTx; - } - } -} diff --git a/src/neo/Plugins/IRpcPlugin.cs b/src/neo/Plugins/IRpcPlugin.cs deleted file mode 100644 index 92e7a0fa48..0000000000 --- a/src/neo/Plugins/IRpcPlugin.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Neo.IO.Json; - -namespace Neo.Plugins -{ - public interface IRpcPlugin - { - void PreProcess(HttpContext context, string method, JArray _params); - JObject OnProcess(HttpContext context, string method, JArray _params); - void PostProcess(HttpContext context, string method, JArray _params, JObject result); - } -} diff --git a/src/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs index 918a1e4ae3..36adac6a77 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -14,7 +14,6 @@ public abstract class Plugin : IDisposable public static readonly List Plugins = new List(); internal static readonly List Loggers = new List(); internal static readonly Dictionary Storages = new Dictionary(); - internal static readonly List RpcPlugins = new List(); internal static readonly List PersistencePlugins = new List(); internal static readonly List P2PPlugins = new List(); internal static readonly List TxObserverPlugins = new List(); @@ -52,7 +51,6 @@ protected Plugin() if (this is ILogPlugin logger) Loggers.Add(logger); if (this is IStoragePlugin storage) Storages.Add(Name, storage); if (this is IP2PPlugin p2p) P2PPlugins.Add(p2p); - if (this is IRpcPlugin rpc) RpcPlugins.Add(rpc); if (this is IPersistencePlugin persistence) PersistencePlugins.Add(persistence); if (this is IMemoryPoolTxObserverPlugin txObserver) TxObserverPlugins.Add(txObserver); diff --git a/src/neo/Utility.cs b/src/neo/Utility.cs index b6402a91c6..984422512e 100644 --- a/src/neo/Utility.cs +++ b/src/neo/Utility.cs @@ -1,65 +1,11 @@ using Microsoft.Extensions.Configuration; -using Neo.Cryptography.ECC; using Neo.Plugins; -using Neo.SmartContract; -using Neo.Wallets; using System; namespace Neo { public static class Utility { - /// - /// Parse WIF or private key hex string to KeyPair - /// - /// WIF or private key hex string - /// Example: WIF ("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"), PrivateKey ("450d6c2a04b5b470339a745427bae6828400cf048400837d73c415063835e005") - /// - public static KeyPair GetKeyPair(string key) - { - if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException(nameof(key)); } - if (key.StartsWith("0x")) { key = key.Substring(2); } - - if (key.Length == 52) - { - return new KeyPair(Wallet.GetPrivateKeyFromWIF(key)); - } - else if (key.Length == 64) - { - return new KeyPair(key.HexToBytes()); - } - - throw new FormatException(); - } - - /// - /// Parse address, scripthash or public key string to UInt160 - /// - /// account address, scripthash or public key string - /// Example: address ("AV556nYUwyJKNv8Xy7hVMLQnkmKPukw6x5"), scripthash ("0x6a38cd693b615aea24dd00de12a9f5836844da91"), public key ("02f9ec1fd0a98796cf75b586772a4ddd41a0af07a1dbdf86a7238f74fb72503575") - /// - public static UInt160 GetScriptHash(string account) - { - if (string.IsNullOrEmpty(account)) { throw new ArgumentNullException(nameof(account)); } - if (account.StartsWith("0x")) { account = account.Substring(2); } - - if (account.Length == 34) - { - return Wallets.Helper.ToScriptHash(account); - } - else if (account.Length == 40) - { - return UInt160.Parse(account); - } - else if (account.Length == 66) - { - var pubKey = ECPoint.Parse(account, ECCurve.Secp256r1); - return Contract.CreateSignatureRedeemScript(pubKey).ToScriptHash(); - } - - throw new FormatException(); - } - /// /// Load configuration with different Environment Variable /// diff --git a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs deleted file mode 100644 index 860eafa076..0000000000 --- a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlock.cs +++ /dev/null @@ -1,29 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.P2P.Payloads; -using Neo.Network.RPC.Models; -using System; - -namespace Neo.UnitTests.Network.RPC.Models -{ - [TestClass] - public class UT_RpcBlock - { - [TestMethod] - public void TestToJson() - { - var rpcBlock = new RpcBlock(); - var block = new Block(); - TestUtils.SetupBlockWithValues(block, UInt256.Zero, out UInt256 _, out UInt160 _, out ulong _, out uint _, out Witness _, out Transaction[] _, 1); - rpcBlock.Block = block; - var json = rpcBlock.ToJson(); - json["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - json.Should().NotBeNull(); - - rpcBlock.Confirmations = 1; - rpcBlock.NextBlockHash = UInt256.Zero; - json = rpcBlock.ToJson(); - json["confirmations"].AsNumber().Should().Be(1); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs deleted file mode 100644 index 85a604d3e8..0000000000 --- a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcBlockHeader.cs +++ /dev/null @@ -1,29 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.P2P.Payloads; -using Neo.Network.RPC.Models; -using System; - -namespace Neo.UnitTests.Network.RPC.Models -{ - [TestClass] - public class UT_RpcBlockHeader - { - [TestMethod] - public void TestToJson() - { - var rpcBlockHeader = new RpcBlockHeader(); - var header = new Header(); - TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 _, out UInt160 _, out ulong _, out uint _, out Witness _); - rpcBlockHeader.Header = header; - var json = rpcBlockHeader.ToJson(); - json["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - json.Should().NotBeNull(); - - rpcBlockHeader.Confirmations = 1; - rpcBlockHeader.NextBlockHash = UInt256.Zero; - json = rpcBlockHeader.ToJson(); - json["confirmations"].AsNumber().Should().Be(1); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs deleted file mode 100644 index 9131db811a..0000000000 --- a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balance.cs +++ /dev/null @@ -1,66 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.Network.RPC.Models; -using System.Numerics; - -namespace Neo.UnitTests.Network.RPC.Models -{ - [TestClass] - public class UT_RpcNep5Balance - { - private RpcNep5Balance balance; - - [TestInitialize] - public void Setup() - { - balance = new RpcNep5Balance(); - } - - [TestMethod] - public void TestAssetHash() - { - balance.AssetHash = UInt160.Zero; - balance.AssetHash.Should().Be(UInt160.Zero); - } - - [TestMethod] - public void TestAmount() - { - balance.Amount = BigInteger.Zero; - balance.Amount.Should().Be(BigInteger.Zero); - } - - [TestMethod] - public void TestLastUpdatedBlock() - { - balance.LastUpdatedBlock = 0; - balance.LastUpdatedBlock.Should().Be(0); - } - - [TestMethod] - public void TestToJson() - { - balance.AssetHash = UInt160.Zero; - balance.Amount = BigInteger.Zero; - balance.LastUpdatedBlock = 0; - var json = balance.ToJson(); - json["asset_hash"].AsString().Should().Be("0x0000000000000000000000000000000000000000"); - json["amount"].AsNumber().Should().Be(0); - json["last_updated_block"].AsNumber().Should().Be(0); - } - - [TestMethod] - public void TestFromJson() - { - var json = new JObject(); - json["asset_hash"] = "0x0000000000000000000000000000000000000000"; - json["amount"] = "0"; - json["last_updated_block"] = "0"; - var rpcNep5Balance = RpcNep5Balance.FromJson(json); - rpcNep5Balance.AssetHash.Should().Be(UInt160.Zero); - rpcNep5Balance.Amount.Should().Be(BigInteger.Zero); - rpcNep5Balance.LastUpdatedBlock.Should().Be(0); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs deleted file mode 100644 index 57601626ab..0000000000 --- a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcNep5Balances.cs +++ /dev/null @@ -1,66 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.Network.RPC.Models; -using System.Numerics; - -namespace Neo.UnitTests.Network.RPC.Models -{ - [TestClass] - public class UT_RpcNep5Balances - { - private RpcNep5Balances balances; - - [TestInitialize] - public void Setup() - { - balances = new RpcNep5Balances() - { - Address = "abc", - Balances = new RpcNep5Balance[] { - new RpcNep5Balance() - { - AssetHash = UInt160.Zero, - Amount = BigInteger.Zero, - LastUpdatedBlock = 0 - }, - new RpcNep5Balance() - { - AssetHash = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), - Amount = new BigInteger(1), - LastUpdatedBlock = 1 - } - } - }; - } - - [TestMethod] - public void TestAddress() - { - balances.Address.Should().Be("abc"); - } - - [TestMethod] - public void TestBalances() - { - balances.Balances.Length.Should().Be(2); - } - - [TestMethod] - public void TestToJson() - { - var json = balances.ToJson(); - json["address"].AsString().Should().Be("abc"); - ((JArray)json["balance"]).Count.Should().Be(2); - } - - [TestMethod] - public void TestFromJson() - { - var json = balances.ToJson(); - var rpcNep5Balances = RpcNep5Balances.FromJson(json); - rpcNep5Balances.Address.Should().Be("abc"); - rpcNep5Balances.Balances.Length.Should().Be(2); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs deleted file mode 100644 index b5c5044d13..0000000000 --- a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeer.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.RPC.Models; - -namespace Neo.UnitTests.Network.RPC.Models -{ - [TestClass] - public class UT_RpcPeer - { - [TestMethod] - public void TestToJson() - { - var rpcPeer = new RpcPeer() - { - Address = "abc", - Port = 800 - }; - var json = rpcPeer.ToJson(); - json["address"].AsString().Should().Be("abc"); - json["port"].AsNumber().Should().Be(800); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs deleted file mode 100644 index cb6f6ff611..0000000000 --- a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcPeers.cs +++ /dev/null @@ -1,44 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.Network.RPC.Models; - -namespace Neo.UnitTests.Network.RPC.Models -{ - [TestClass] - public class UT_RpcPeers - { - [TestMethod] - public void TestToJson() - { - var rpcPeers = new RpcPeers() - { - Unconnected = new RpcPeer[] { - new RpcPeer() - { - Address = "Unconnected", - Port = 600 - } - }, - Bad = new RpcPeer[] { - new RpcPeer() - { - Address = "Bad", - Port = 700 - } - }, - Connected = new RpcPeer[] { - new RpcPeer() - { - Address = "Connected", - Port = 800 - } - } - }; - var json = rpcPeers.ToJson(); - ((JArray)json["unconnected"]).Count.Should().Be(1); - ((JArray)json["bad"]).Count.Should().Be(1); - ((JArray)json["connected"]).Count.Should().Be(1); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs deleted file mode 100644 index e8cb9bad7e..0000000000 --- a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcRawMemPool.cs +++ /dev/null @@ -1,29 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.RPC.Models; - -namespace Neo.UnitTests.Network.RPC.Models -{ - [TestClass] - public class UT_RpcRawMemPool - { - [TestMethod] - public void TestToJson() - { - var pool = new RpcRawMemPool - { - Height = 1, - Verified = new string[] { - "a", "b" - }, - UnVerified = new string[] { - "c", "d" - } - }; - var json = pool.ToJson(); - json["height"].AsNumber().Should().Be(1); - json["verified"].AsString().Should().Be("a,b"); - json["unverified"].AsString().Should().Be("c,d"); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs deleted file mode 100644 index 8f2a3b4f74..0000000000 --- a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.Network.RPC.Models; - -namespace Neo.UnitTests.Network.RPC.Models -{ - [TestClass] - public class UT_RpcRequest - { - [TestMethod] - public void TestFromJson() - { - var req = new RpcRequest() - { - Id = 1, - Jsonrpc = "myrpc", - Method = "get", - Params = new JObject[] { - new JBoolean(true) - } - }; - var json = req.ToJson(); - var rpcRequest = RpcRequest.FromJson(json); - rpcRequest.Jsonrpc.Should().Be("myrpc"); - rpcRequest.Method.Should().Be("get"); - rpcRequest.Id.Should().Be(1); - rpcRequest.Params.Length.Should().Be(1); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs deleted file mode 100644 index 5c90eed92d..0000000000 --- a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcResponse.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO.Json; -using Neo.Network.RPC.Models; - -namespace Neo.UnitTests.Network.RPC.Models -{ - [TestClass] - public class UT_RpcResponse - { - [TestMethod] - public void TestToJson() - { - var error = new RpcResponseError() - { - Code = 0, - Message = "msg", - Data = new JBoolean(true) - }; - var rep = new RpcResponse() - { - Id = 1, - Jsonrpc = "rpc", - Error = error, - Result = new JBoolean(true) - }; - var json = rep.ToJson(); - json["id"].AsNumber().Should().Be(1); - json["jsonrpc"].AsString().Should().Be("rpc"); - json["error"].AsString().Should().Be(error.ToJson().AsString()); - json["result"].AsBoolean().Should().BeTrue(); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs b/tests/neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs deleted file mode 100644 index cbb9603907..0000000000 --- a/tests/neo.UnitTests/Network/RPC/Models/UT_RpcVersion.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.RPC.Models; - -namespace Neo.UnitTests.Network.RPC.Models -{ - [TestClass] - public class UT_RpcVersion - { - [TestMethod] - public void TestToJson() - { - var version = new RpcVersion() - { - TcpPort = 800, - WsPort = 900, - Nonce = 1, - UserAgent = "agent" - }; - var json = version.ToJson(); - json["topPort"].AsNumber().Should().Be(800); - json["wsPort"].AsNumber().Should().Be(900); - json["nonce"].AsNumber().Should().Be(1); - json["useragent"].AsString().Should().Be("agent"); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/UT_ContractClient.cs b/tests/neo.UnitTests/Network/RPC/UT_ContractClient.cs deleted file mode 100644 index 13a02b5b9d..0000000000 --- a/tests/neo.UnitTests/Network/RPC/UT_ContractClient.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Neo.Network.RPC; -using Neo.SmartContract; -using Neo.SmartContract.Manifest; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.Wallets; - -namespace Neo.UnitTests.Network.RPC -{ - [TestClass] - public class UT_ContractClient - { - Mock rpcClientMock; - KeyPair keyPair1; - UInt160 sender; - - [TestInitialize] - public void TestSetup() - { - keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); - sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); - rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]); - } - - [TestMethod] - public void TestInvoke() - { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.ByteArray, Value = "00e057eb481b".HexToBytes() }); - - ContractClient contractClient = new ContractClient(rpcClientMock.Object); - var result = contractClient.TestInvoke(NativeContract.GAS.Hash, "balanceOf", UInt160.Zero); - - Assert.AreEqual(30000000000000L, (long)result.Stack[0].ToStackItem().GetBigInteger()); - } - - [TestMethod] - public void TestDeployContract() - { - byte[] script; - var manifest = ContractManifest.CreateDefault(new byte[1].ToScriptHash()); - manifest.Features = ContractFeatures.HasStorage | ContractFeatures.Payable; - using (ScriptBuilder sb = new ScriptBuilder()) - { - sb.EmitSysCall(InteropService.Neo_Contract_Create, new byte[1], manifest.ToString()); - script = sb.ToArray(); - } - - UT_TransactionManager.MockInvokeScript(rpcClientMock, script, new ContractParameter()); - - ContractClient contractClient = new ContractClient(rpcClientMock.Object); - var result = contractClient.CreateDeployContractTx(new byte[1], manifest, keyPair1); - - Assert.IsNotNull(result); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/UT_Helper.cs b/tests/neo.UnitTests/Network/RPC/UT_Helper.cs deleted file mode 100644 index cb791fa6ce..0000000000 --- a/tests/neo.UnitTests/Network/RPC/UT_Helper.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.RPC; -using System; -using System.Numerics; - -namespace Neo.UnitTests.Network.RPC -{ - [TestClass] - public class UT_Helper - { - [TestMethod] - public void TestToBigInteger() - { - decimal amount = 1.23456789m; - uint decimals = 9; - var result = amount.ToBigInteger(decimals); - Assert.AreEqual(1234567890, result); - - amount = 1.23456789m; - decimals = 18; - result = amount.ToBigInteger(decimals); - Assert.AreEqual(BigInteger.Parse("1234567890000000000"), result); - - amount = 1.23456789m; - decimals = 4; - Assert.ThrowsException(() => result = amount.ToBigInteger(decimals)); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/UT_Nep5API.cs b/tests/neo.UnitTests/Network/RPC/UT_Nep5API.cs deleted file mode 100644 index 48fd711638..0000000000 --- a/tests/neo.UnitTests/Network/RPC/UT_Nep5API.cs +++ /dev/null @@ -1,112 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Neo.Network.RPC; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.Wallets; -using System.Linq; -using System.Numerics; - -namespace Neo.UnitTests.Network.RPC -{ - [TestClass] - public class UT_Nep5API - { - Mock rpcClientMock; - KeyPair keyPair1; - UInt160 sender; - Nep5API nep5API; - - [TestInitialize] - public void TestSetup() - { - keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); - sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); - rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]); - nep5API = new Nep5API(rpcClientMock.Object); - } - - [TestMethod] - public void TestBalanceOf() - { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(10000) }); - - var balance = nep5API.BalanceOf(NativeContract.GAS.Hash, UInt160.Zero); - Assert.AreEqual(10000, (int)balance); - } - - [TestMethod] - public void TestGetName() - { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("name"); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.String, Value = NativeContract.GAS.Name }); - - var result = nep5API.Name(NativeContract.GAS.Hash); - Assert.AreEqual(NativeContract.GAS.Name, result); - } - - [TestMethod] - public void TestGetSymbol() - { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("symbol"); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.String, Value = NativeContract.GAS.Symbol }); - - var result = nep5API.Symbol(NativeContract.GAS.Hash); - Assert.AreEqual(NativeContract.GAS.Symbol, result); - } - - [TestMethod] - public void TestGetDecimals() - { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("decimals"); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(NativeContract.GAS.Decimals) }); - - var result = nep5API.Decimals(NativeContract.GAS.Hash); - Assert.AreEqual(NativeContract.GAS.Decimals, (byte)result); - } - - [TestMethod] - public void TestGetTotalSupply() - { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("totalSupply"); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_00000000) }); - - var result = nep5API.TotalSupply(NativeContract.GAS.Hash); - Assert.AreEqual(1_00000000, (int)result); - } - - [TestMethod] - public void TestGetTokenInfo() - { - UInt160 scriptHash = NativeContract.GAS.Hash; - byte[] testScript = scriptHash.MakeScript("name") - .Concat(scriptHash.MakeScript("symbol")) - .Concat(scriptHash.MakeScript("decimals")) - .Concat(scriptHash.MakeScript("totalSupply")) - .ToArray(); ; - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, - new ContractParameter { Type = ContractParameterType.String, Value = NativeContract.GAS.Name }, - new ContractParameter { Type = ContractParameterType.String, Value = NativeContract.GAS.Symbol }, - new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(NativeContract.GAS.Decimals) }, - new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_00000000) }); - - var result = nep5API.GetTokenInfo(NativeContract.GAS.Hash); - Assert.AreEqual(NativeContract.GAS.Name, result.Name); - Assert.AreEqual(NativeContract.GAS.Symbol, result.Symbol); - Assert.AreEqual(8, (int)result.Decimals); - Assert.AreEqual(1_00000000, (int)result.TotalSupply); - } - - [TestMethod] - public void TestTransfer() - { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, new BigInteger(1_00000000)); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter()); - - var result = nep5API.CreateTransferTx(NativeContract.GAS.Hash, keyPair1, UInt160.Zero, new BigInteger(1_00000000)); - Assert.IsNotNull(result); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/UT_PolicyAPI.cs b/tests/neo.UnitTests/Network/RPC/UT_PolicyAPI.cs deleted file mode 100644 index 6b6c449111..0000000000 --- a/tests/neo.UnitTests/Network/RPC/UT_PolicyAPI.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Neo.Network.RPC; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.Wallets; -using System.Numerics; - -namespace Neo.UnitTests.Network.RPC -{ - [TestClass] - public class UT_PolicyAPI - { - Mock rpcClientMock; - KeyPair keyPair1; - UInt160 sender; - PolicyAPI policyAPI; - - [TestInitialize] - public void TestSetup() - { - keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); - sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); - rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]); - policyAPI = new PolicyAPI(rpcClientMock.Object); - } - - [TestMethod] - public void TestGetMaxTransactionsPerBlock() - { - byte[] testScript = NativeContract.Policy.Hash.MakeScript("getMaxTransactionsPerBlock"); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(512) }); - - var result = policyAPI.GetMaxTransactionsPerBlock(); - Assert.AreEqual(512u, result); - } - - [TestMethod] - public void TestGetMaxBlockSize() - { - byte[] testScript = NativeContract.Policy.Hash.MakeScript("getMaxBlockSize"); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1024u * 256u) }); - - var result = policyAPI.GetMaxBlockSize(); - Assert.AreEqual(1024u * 256u, result); - } - - [TestMethod] - public void TestGetFeePerByte() - { - byte[] testScript = NativeContract.Policy.Hash.MakeScript("getFeePerByte"); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1000) }); - - var result = policyAPI.GetFeePerByte(); - Assert.AreEqual(1000L, result); - } - - [TestMethod] - public void TestGetBlockedAccounts() - { - byte[] testScript = NativeContract.Policy.Hash.MakeScript("getBlockedAccounts"); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Array, Value = new[] { new ContractParameter { Type = ContractParameterType.Hash160, Value = UInt160.Zero } } }); - - var result = policyAPI.GetBlockedAccounts(); - Assert.AreEqual(UInt160.Zero, result[0]); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/UT_RpcClient.cs b/tests/neo.UnitTests/Network/RPC/UT_RpcClient.cs deleted file mode 100644 index a8b212fe37..0000000000 --- a/tests/neo.UnitTests/Network/RPC/UT_RpcClient.cs +++ /dev/null @@ -1,568 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Moq.Protected; -using Neo.IO; -using Neo.IO.Json; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.Network.RPC; -using Neo.Network.RPC.Models; -using Neo.SmartContract; -using Neo.SmartContract.Manifest; -using Neo.VM; -using System; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace Neo.UnitTests.Network.RPC -{ - [TestClass] - public class UT_RpcClient - { - RpcClient rpc; - Mock handlerMock; - - [TestInitialize] - public void TestSetup() - { - handlerMock = new Mock(MockBehavior.Strict); - - // use real http client with mocked handler here - var httpClient = new HttpClient(handlerMock.Object) - { - BaseAddress = new Uri("http://seed1.neo.org:10331"), - }; - - rpc = new RpcClient(httpClient); - } - - private void MockResponse(string content) - { - handlerMock.Protected() - // Setup the PROTECTED method to mock - .Setup>( - "SendAsync", - ItExpr.IsAny(), - ItExpr.IsAny() - ) - // prepare the expected response of the mocked http call - .ReturnsAsync(new HttpResponseMessage() - { - StatusCode = HttpStatusCode.OK, - Content = new StringContent(content), - }) - .Verifiable(); - } - - private JObject CreateErrorResponse(JObject id, int code, string message, JObject data = null) - { - JObject response = CreateResponse(id); - response["error"] = new JObject(); - response["error"]["code"] = code; - response["error"]["message"] = message; - if (data != null) - response["error"]["data"] = data; - return response; - } - - private JObject CreateResponse(JObject id) - { - JObject response = new JObject(); - response["jsonrpc"] = "2.0"; - response["id"] = id; - return response; - } - - [TestMethod] - public void TestErrorResponse() - { - JObject response = CreateErrorResponse(null, -32700, "Parse error"); - MockResponse(response.ToString()); - try - { - var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); - } - catch (RpcException ex) - { - Assert.AreEqual(-32700, ex.HResult); - Assert.AreEqual("Parse error", ex.Message); - } - } - - [TestMethod] - public void TestGetBestBlockHash() - { - JObject response = CreateResponse(1); - response["result"] = "000000002deadfa82cbc4682f5800"; - MockResponse(response.ToString()); - - var result = rpc.GetBestBlockHash(); - Assert.AreEqual("000000002deadfa82cbc4682f5800", result); - } - - [TestMethod] - public void TestGetBlockHex() - { - JObject response = CreateResponse(1); - response["result"] = "000000002deadfa82cbc4682f5800"; - MockResponse(response.ToString()); - - var result = rpc.GetBlockHex("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); - Assert.AreEqual("000000002deadfa82cbc4682f5800", result); - } - - [TestMethod] - public void TestGetBlock() - { - // create block - var block = new Block(); - TestUtils.SetupBlockWithValues(block, UInt256.Zero, out _, out UInt160 _, out ulong _, out uint _, out Witness _, out Transaction[] _, 0); - - block.Transactions = new[] - { - TestUtils.GetTransaction(), - TestUtils.GetTransaction(), - TestUtils.GetTransaction() - }; - - JObject json = block.ToJson(); - json["confirmations"] = 20; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.GetBlock("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); - Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); - Assert.IsNull(result.NextBlockHash); - Assert.AreEqual(20, result.Confirmations); - Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); - Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); - - // verbose with confirmations - json["confirmations"] = 20; - json["nextblockhash"] = "773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"; - MockResponse(response.ToString()); - result = rpc.GetBlock("773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e"); - Assert.AreEqual(block.Hash.ToString(), result.Block.Hash.ToString()); - Assert.AreEqual(20, result.Confirmations); - Assert.AreEqual("0x773dd2dae4a9c9275290f89b56e67d7363ea4826dfd4fc13cc01cf73a44b0d0e", result.NextBlockHash.ToString()); - Assert.AreEqual(block.Transactions.Length, result.Block.Transactions.Length); - Assert.AreEqual(block.Transactions[0].Hash.ToString(), result.Block.Transactions[0].Hash.ToString()); - } - - [TestMethod] - public void TestGetBlockCount() - { - JObject response = CreateResponse(1); - response["result"] = 100; - MockResponse(response.ToString()); - - var result = rpc.GetBlockCount(); - Assert.AreEqual(100u, result); - } - - [TestMethod] - public void TestGetBlockHash() - { - JObject response = CreateResponse(1); - response["result"] = "0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; - MockResponse(response.ToString()); - - var result = rpc.GetBlockHash(100); - Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); - } - - [TestMethod] - public void TestGetBlockHeaderHex() - { - JObject response = CreateResponse(1); - response["result"] = "0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; - MockResponse(response.ToString()); - - var result = rpc.GetBlockHeaderHex("100"); - Assert.AreEqual("0x4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2", result); - } - - [TestMethod] - public void TestGetBlockHeader() - { - Header header = new Header(); - TestUtils.SetupHeaderWithValues(header, UInt256.Zero, out UInt256 _, out UInt160 _, out ulong _, out uint _, out Witness _); - - JObject json = header.ToJson(); - json["confirmations"] = 20; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.GetBlockHeader("100"); - Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); - Assert.IsNull(result.NextBlockHash); - Assert.AreEqual(20, result.Confirmations); - - json["confirmations"] = 20; - json["nextblockhash"] = "4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; - MockResponse(response.ToString()); - result = rpc.GetBlockHeader("100"); - Assert.AreEqual(header.Hash.ToString(), result.Header.Hash.ToString()); - Assert.AreEqual(20, result.Confirmations); - } - - [TestMethod] - public void TestGetBlockSysFee() - { - JObject response = CreateResponse(1); - response["result"] = "195500"; - MockResponse(response.ToString()); - - var result = rpc.GetBlockSysFee(100); - Assert.AreEqual("195500", result); - } - - [TestMethod] - public void TestGetConnectionCount() - { - JObject response = CreateResponse(1); - response["result"] = 9; - MockResponse(response.ToString()); - - var result = rpc.GetConnectionCount(); - Assert.AreEqual(9, result); - } - - [TestMethod] - public void TestGetContractState() - { - byte[] script; - using (var sb = new ScriptBuilder()) - { - sb.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); - script = sb.ToArray(); - } - - ContractState state = new ContractState - { - Script = new byte[] { (byte)OpCode.DROP, (byte)OpCode.DROP }.Concat(script).ToArray(), - Manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")) - }; - - JObject response = CreateResponse(1); - response["result"] = state.ToJson(); - MockResponse(response.ToString()); - - var result = rpc.GetContractState("17694b31cc7ee215cea2ded146e0b2b28768fc46"); - - Assert.AreEqual(state.Script.ToHexString(), result.Script.ToHexString()); - Assert.AreEqual(state.Manifest.Abi.EntryPoint.Name, result.Manifest.Abi.EntryPoint.Name); - } - - [TestMethod] - public void TestGetPeers() - { - JObject response = CreateResponse(1); - response["result"] = JObject.Parse(@"{ - ""unconnected"": [ - { - ""address"": ""::ffff:70.73.16.236"", - ""port"": 10333 - }, - { - ""address"": ""::ffff:82.95.77.148"", - ""port"": 10333 - }, - { - ""address"": ""::ffff:49.50.215.166"", - ""port"": 10333 - } - ], - ""bad"": [], - ""connected"": [ - { - ""address"": ""::ffff:139.219.106.33"", - ""port"": 10333 - }, - { - ""address"": ""::ffff:47.88.53.224"", - ""port"": 10333 - } - ] - }"); - MockResponse(response.ToString()); - - var result = rpc.GetPeers(); - Assert.AreEqual("::ffff:139.219.106.33", result.Connected[0].Address); - Assert.AreEqual("::ffff:82.95.77.148", result.Unconnected[1].Address); - } - - [TestMethod] - public void TestGetRawMempool() - { - JObject response = CreateResponse(1); - response["result"] = JObject.Parse(@"[ - ""0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"", - ""0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7"", - ""0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6"" - ]"); - MockResponse(response.ToString()); - - var result = rpc.GetRawMempool(); - Assert.AreEqual("0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7", result[1]); - } - - [TestMethod] - public void TestGetRawMempoolBoth() - { - JObject json = new JObject(); - json["height"] = 65535; - json["verified"] = new JArray(new[] { "0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e" }.Select(p => (JObject)p)); - json["unverified"] = new JArray(new[] { "0xb488ad25eb474f89d5ca3f985cc047ca96bc7373a6d3da8c0f192722896c1cd7", "0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6" }.Select(p => (JObject)p)); - - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.GetRawMempoolBoth(); - Assert.AreEqual(65535u, result.Height); - Assert.AreEqual("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e", result.Verified[0]); - Assert.AreEqual("0xf86f6f2c08fbf766ebe59dc84bc3b8829f1053f0a01deb26bf7960d99fa86cd6", result.UnVerified[1]); - } - - [TestMethod] - public void TestGetRawTransactionHex() - { - var json = TestUtils.GetTransaction().ToArray().ToHexString(); - - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - //var result = rpc.GetRawTransactionHex("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); - var result = rpc.GetRawTransactionHex(TestUtils.GetTransaction().Hash.ToString()); - Assert.AreEqual(json, result); - } - - [TestMethod] - public void TestGetRawTransaction() - { - var transaction = TestUtils.GetTransaction(); - JObject json = transaction.ToJson(); - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); - Assert.AreEqual(transaction.Hash, result.Transaction.Hash); - Assert.AreEqual(json.ToString(), result.ToJson().ToString()); - - // make the code compatible with the old version - json["blockhash"] = UInt256.Zero.ToString(); - json["confirmations"] = 100; - json["blocktime"] = 10; - MockResponse(response.ToString()); - - result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); - Assert.AreEqual(transaction.Hash, result.Transaction.Hash); - Assert.AreEqual(100, result.Confirmations); - Assert.AreEqual(null, result.VMState); - Assert.AreEqual(json.ToString(), result.ToJson().ToString()); - - json["vmState"] = VMState.HALT; - MockResponse(response.ToString()); - - result = rpc.GetRawTransaction("0x9786cce0dddb524c40ddbdd5e31a41ed1f6b5c8a683c122f627ca4a007a7cf4e"); - Assert.AreEqual(transaction.Hash, result.Transaction.Hash); - Assert.AreEqual(100, result.Confirmations); - Assert.AreEqual(VMState.HALT, result.VMState); - Assert.AreEqual(json.ToString(), result.ToJson().ToString()); - } - - [TestMethod] - public void TestGetStorage() - { - JObject json = "4c696e"; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.GetStorage("03febccf81ac85e3d795bc5cbd4e84e907812aa3", "5065746572"); - Assert.AreEqual("4c696e", result); - } - - [TestMethod] - public void TestGetTransactionHeight() - { - JObject json = 10000; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.GetTransactionHeight("9c909e1e3ba03290553a68d862e002c7a21ba302e043fc492fe069bf6a134d29"); - Assert.AreEqual(json.ToString(), result.ToString()); - } - - [TestMethod] - public void TestGetValidators() - { - JObject json = JObject.Parse(@"[ - { - ""publickey"": ""02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"", - ""votes"": ""46632420"", - ""active"": true - }, - { - ""publickey"": ""024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"", - ""votes"": ""46632420"", - ""active"": true - } - ]"); - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.GetValidators(); - Assert.AreEqual(((JArray)json)[0].ToString(), (result[0]).ToJson().ToString()); - } - - [TestMethod] - public void TestGetVersion() - { - JObject json = new JObject(); - json["tcpPort"] = 30001; - json["wsPort"] = 30002; - json["nonce"] = 1546258664; - json["useragent"] = "/NEO:2.7.5/"; - - var json1 = JObject.Parse(@"{ - ""tcpPort"": 30001, - ""wsPort"": 30002, - ""nonce"": 1546258664, - ""useragent"": ""/NEO:2.7.5/"" - }"); - Assert.AreEqual(json.ToString(), json1.ToString()); - - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.GetVersion(); - Assert.AreEqual(30001, result.TcpPort); - Assert.AreEqual("/NEO:2.7.5/", result.UserAgent); - } - - [TestMethod] - public void TestInvokeFunction() - { - JObject json = JObject.Parse(@" - { - ""script"": ""1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf"", - ""state"": ""HALT"", - ""gas_consumed"": ""0.311"", - ""stack"": [ - { - ""type"": ""ByteArray"", - ""value"": ""262bec084432"" - } - ] - }"); - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.InvokeFunction("af7c7328eee5a275a3bcaee2bf0cf662b5e739be", "balanceOf", new[] { new RpcStack { Type = "Hash160", Value = "91b83e96f2a7c4fdf0c1688441ec61986c7cae26" } }); - Assert.AreEqual(json.ToString(), result.ToJson().ToString()); - } - - [TestMethod] - public void TestInvokeScript() - { - JObject json = JObject.Parse(@" - { - ""script"": ""1426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf"", - ""state"": ""HALT"", - ""gas_consumed"": ""0.311"", - ""stack"": [ - { - ""type"": ""ByteArray"", - ""value"": ""262bec084432"" - } - ] - }"); - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.InvokeScript("00046e616d656724058e5e1b6008847cd662728549088a9ee82191".HexToBytes()); - Assert.AreEqual(json.ToString(), result.ToJson().ToString()); - } - - [TestMethod] - public void TestListPlugins() - { - JObject json = JObject.Parse(@"[{ - ""name"": ""SimplePolicyPlugin"", - ""version"": ""2.10.1.0"", - ""interfaces"": [ - ""ILogPlugin"", - ""IPolicyPlugin"" - ] - }]"); - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.ListPlugins(); - Assert.AreEqual(((JArray)json)[0].ToString(), result[0].ToJson().ToString()); - } - - [TestMethod] - public void TestSendRawTransaction() - { - JObject json = true; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.SendRawTransaction("80000001195876cb34364dc38b730077156c6bc3a7fc570044a66fbfeeea56f71327e8ab0000029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500c65eaf440000000f9a23e06f74cf86b8827a9108ec2e0f89ad956c9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50092e14b5e00000030aab52ad93f6ce17ca07fa88fc191828c58cb71014140915467ecd359684b2dc358024ca750609591aa731a0b309c7fb3cab5cd0836ad3992aa0a24da431f43b68883ea5651d548feb6bd3c8e16376e6e426f91f84c58232103322f35c7819267e721335948d385fae5be66e7ba8c748ac15467dcca0693692dac".HexToBytes()); - Assert.AreEqual(json.ToString(), ((JObject)result).ToString()); - } - - [TestMethod] - public void TestSubmitBlock() - { - JObject json = true; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.SubmitBlock("03febccf81ac85e3d795bc5cbd4e84e907812aa3".HexToBytes()); - Assert.AreEqual(json.ToString(), ((JObject)result).ToString()); - } - - [TestMethod] - public void TestValidateAddress() - { - JObject json = new JObject(); - json["address"] = "AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"; - json["isvalid"] = false; - JObject response = CreateResponse(1); - response["result"] = json; - MockResponse(response.ToString()); - - var result = rpc.ValidateAddress("AQVh2pG732YvtNaxEGkQUei3YA4cvo7d2i"); - Assert.AreEqual(json.ToString(), result.ToJson().ToString()); - } - - [TestMethod] - public void TestConstructorByUrlAndDispose() - { - //dummy url for test - var client = new RpcClient("http://www.xxx.yyy"); - Action action = () => client.Dispose(); - action.Should().NotThrow(); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/UT_RpcServer.cs b/tests/neo.UnitTests/Network/RPC/UT_RpcServer.cs deleted file mode 100644 index cfdfe7fbd8..0000000000 --- a/tests/neo.UnitTests/Network/RPC/UT_RpcServer.cs +++ /dev/null @@ -1,47 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.RPC; -using System; -using System.Net; - -namespace Neo.UnitTests.Network.RPC -{ - [TestClass] - public class UT_RpcServer - { - private RpcServer server; - - [TestInitialize] - public void Setup() - { - server = new RpcServer(TestBlockchain.TheNeoSystem); - } - - [TestCleanup] - public void TestDispose() - { - server.Dispose(); - } - - [TestMethod] - public void TestWallet() - { - var wallet = TestUtils.GenerateTestWallet(); - server.Wallet = wallet; - server.Wallet.Should().Be(wallet); - } - - [TestMethod] - public void TestMaxGasInvoke() - { - server.MaxGasInvoke.Should().Be(0); - } - - [TestMethod] - public void TestStart() - { - Action action = () => server.Start(IPAddress.Parse("127.0.0.1"), 8999); - action.Should().NotThrow(); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs b/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs deleted file mode 100644 index 6fb69800cb..0000000000 --- a/tests/neo.UnitTests/Network/RPC/UT_TransactionManager.cs +++ /dev/null @@ -1,193 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Neo.Cryptography; -using Neo.IO; -using Neo.IO.Json; -using Neo.Ledger; -using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; -using Neo.Network.RPC; -using Neo.Network.RPC.Models; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.Wallets; -using System; -using System.Linq; -using System.Numerics; - -namespace Neo.UnitTests.Network.RPC -{ - [TestClass] - public class UT_TransactionManager - { - TransactionManager txManager; - Mock rpcClientMock; - KeyPair keyPair1; - KeyPair keyPair2; - UInt160 sender; - - [TestInitialize] - public void TestSetup() - { - keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); - keyPair2 = new KeyPair(Wallet.GetPrivateKeyFromWIF("L2LGkrwiNmUAnWYb1XGd5mv7v2eDf6P4F3gHyXSrNJJR4ArmBp7Q")); - sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); - rpcClientMock = MockRpcClient(sender, new byte[1]); - } - - public static Mock MockRpcClient(UInt160 sender, byte[] script) - { - var mockRpc = new Mock(MockBehavior.Strict, "http://seed1.neo.org:10331"); - - // MockHeight - mockRpc.Setup(p => p.RpcSend("getblockcount")).Returns(100).Verifiable(); - - // MockGasBalance - byte[] balanceScript = NativeContract.GAS.Hash.MakeScript("balanceOf", sender); - var balanceResult = new ContractParameter() { Type = ContractParameterType.Integer, Value = BigInteger.Parse("10000000000000000") }; - - MockInvokeScript(mockRpc, balanceScript, balanceResult); - - // MockFeePerByte - byte[] policyScript = NativeContract.Policy.Hash.MakeScript("getFeePerByte"); - var policyResult = new ContractParameter() { Type = ContractParameterType.Integer, Value = BigInteger.Parse("1000") }; - - MockInvokeScript(mockRpc, policyScript, policyResult); - - // MockGasConsumed - var result = new ContractParameter(); - MockInvokeScript(mockRpc, script, result); - - return mockRpc; - } - - public static void MockInvokeScript(Mock mockClient, byte[] script, params ContractParameter[] parameters) - { - var result = new RpcInvokeResult() - { - Stack = parameters, - GasConsumed = "100", - Script = script.ToHexString(), - State = "" - }; - - mockClient.Setup(p => p.RpcSend("invokescript", It.Is(j => j[0].AsString() == script.ToHexString()))) - .Returns(result.ToJson()) - .Verifiable(); - } - - [TestMethod] - public void TestMakeTransaction() - { - txManager = new TransactionManager(rpcClientMock.Object, sender); - - TransactionAttribute[] attributes = new TransactionAttribute[1] - { - new TransactionAttribute - { - Usage = TransactionAttributeUsage.Url, - Data = "53616d706c6555726c".HexToBytes() // "SampleUrl" - } - }; - - byte[] script = new byte[1]; - txManager.MakeTransaction(script, attributes, null, 60000); - - var tx = txManager.Tx; - Assert.AreEqual("53616d706c6555726c", tx.Attributes[0].Data.ToHexString()); - Assert.AreEqual(0, tx.SystemFee % (long)NativeContract.GAS.Factor); - Assert.AreEqual(60000, tx.NetworkFee); - } - - [TestMethod] - public void TestSign() - { - txManager = new TransactionManager(rpcClientMock.Object, sender); - - TransactionAttribute[] attributes = new TransactionAttribute[1] - { - new TransactionAttribute - { - Usage = TransactionAttributeUsage.Url, - Data = "53616d706c6555726c".HexToBytes() // "SampleUrl" - } - }; - - byte[] script = new byte[1]; - txManager.MakeTransaction(script, attributes) - .AddSignature(keyPair1) - .Sign(); - - // get signature from Witnesses - var tx = txManager.Tx; - byte[] signature = tx.Witnesses[0].InvocationScript.Skip(1).ToArray(); - - Assert.IsTrue(Crypto.VerifySignature(tx.GetHashData(), signature, keyPair1.PublicKey.EncodePoint(false).Skip(1).ToArray())); - - // duplicate sign should not add new witness - txManager.AddSignature(keyPair1).Sign(); - Assert.AreEqual(1, txManager.Tx.Witnesses.Length); - - // throw exception when the KeyPair is wrong - Assert.ThrowsException(() => txManager.AddSignature(keyPair2)); - } - - [TestMethod] - public void TestSignMulti() - { - txManager = new TransactionManager(rpcClientMock.Object, sender); - - var multiContract = Contract.CreateMultiSigContract(2, keyPair1.PublicKey, keyPair2.PublicKey); - - // Cosigner needs multi signature - Cosigner[] cosigners = new Cosigner[1] - { - new Cosigner - { - Account = multiContract.ScriptHash, - Scopes = WitnessScope.Global - } - }; - - byte[] script = new byte[1]; - txManager.MakeTransaction(script, null, cosigners, 0_10000000) - .AddMultiSig(keyPair1, 2, keyPair1.PublicKey, keyPair2.PublicKey) - .AddMultiSig(keyPair2, 2, keyPair1.PublicKey, keyPair2.PublicKey) - .AddSignature(keyPair1) - .Sign(); - - var snapshot = Blockchain.Singleton.GetSnapshot(); - - var tx = txManager.Tx; - Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); - } - - [TestMethod] - public void TestAddWitness() - { - txManager = new TransactionManager(rpcClientMock.Object, sender); - - // Cosigner as contract scripthash - Cosigner[] cosigners = new Cosigner[1] - { - new Cosigner - { - Account = UInt160.Zero, - Scopes = WitnessScope.Global - } - }; - - byte[] script = new byte[1]; - txManager.MakeTransaction(script, null, cosigners, 0_10000000); - txManager.AddWitness(UInt160.Zero); - txManager.AddSignature(keyPair1); - txManager.Sign(); - - var tx = txManager.Tx; - Assert.AreEqual(2, tx.Witnesses.Length); - Assert.AreEqual(0, tx.Witnesses[0].VerificationScript.Length); - Assert.AreEqual(0, tx.Witnesses[0].InvocationScript.Length); - } - } -} diff --git a/tests/neo.UnitTests/Network/RPC/UT_WalletAPI.cs b/tests/neo.UnitTests/Network/RPC/UT_WalletAPI.cs deleted file mode 100644 index bba6a787e1..0000000000 --- a/tests/neo.UnitTests/Network/RPC/UT_WalletAPI.cs +++ /dev/null @@ -1,116 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using Neo.IO.Json; -using Neo.Network.P2P.Payloads; -using Neo.Network.RPC; -using Neo.Network.RPC.Models; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.Wallets; -using System.Numerics; - -namespace Neo.UnitTests.Network.RPC -{ - [TestClass] - public class UT_WalletAPI - { - Mock rpcClientMock; - KeyPair keyPair1; - string address1; - UInt160 sender; - WalletAPI walletAPI; - - [TestInitialize] - public void TestSetup() - { - keyPair1 = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); - sender = Contract.CreateSignatureRedeemScript(keyPair1.PublicKey).ToScriptHash(); - address1 = Neo.Wallets.Helper.ToAddress(sender); - rpcClientMock = UT_TransactionManager.MockRpcClient(sender, new byte[0]); - walletAPI = new WalletAPI(rpcClientMock.Object); - } - - [TestMethod] - public void TestGetUnclaimedGas() - { - byte[] testScript = NativeContract.NEO.Hash.MakeScript("unclaimedGas", sender, 99); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); - - var balance = walletAPI.GetUnclaimedGas(address1); - Assert.AreEqual(1.1m, balance); - } - - [TestMethod] - public void TestGetNeoBalance() - { - byte[] testScript = NativeContract.NEO.Hash.MakeScript("balanceOf", sender); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_00000000) }); - - var balance = walletAPI.GetNeoBalance(address1); - Assert.AreEqual(1_00000000u, balance); - } - - [TestMethod] - public void TestGetGasBalance() - { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", sender); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); - - var balance = walletAPI.GetGasBalance(address1); - Assert.AreEqual(1.1m, balance); - } - - [TestMethod] - public void TestGetTokenBalance() - { - byte[] testScript = UInt160.Zero.MakeScript("balanceOf", sender); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); - - var balance = walletAPI.GetTokenBalance(UInt160.Zero.ToString(), address1); - Assert.AreEqual(1_10000000, balance); - } - - [TestMethod] - public void TestClaimGas() - { - byte[] balanceScript = NativeContract.NEO.Hash.MakeScript("balanceOf", sender); - UT_TransactionManager.MockInvokeScript(rpcClientMock, balanceScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_00000000) }); - - byte[] testScript = NativeContract.NEO.Hash.MakeScript("transfer", sender, sender, new BigInteger(1_00000000)); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); - - rpcClientMock.Setup(p => p.RpcSend("sendrawtransaction", It.IsAny())).Returns(true); - - var tranaction = walletAPI.ClaimGas(keyPair1.Export()); - Assert.AreEqual(testScript.ToHexString(), tranaction.Script.ToHexString()); - } - - [TestMethod] - public void TestTransfer() - { - byte[] decimalsScript = NativeContract.GAS.Hash.MakeScript("decimals"); - UT_TransactionManager.MockInvokeScript(rpcClientMock, decimalsScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(8) }); - - byte[] testScript = NativeContract.GAS.Hash.MakeScript("transfer", sender, UInt160.Zero, NativeContract.GAS.Factor * 100); - UT_TransactionManager.MockInvokeScript(rpcClientMock, testScript, new ContractParameter { Type = ContractParameterType.Integer, Value = new BigInteger(1_10000000) }); - - rpcClientMock.Setup(p => p.RpcSend("sendrawtransaction", It.IsAny())).Returns(true); - - var tranaction = walletAPI.Transfer(NativeContract.GAS.Hash.ToString(), keyPair1.Export(), UInt160.Zero.ToAddress(), 100, 1.1m); - Assert.AreEqual(testScript.ToHexString(), tranaction.Script.ToHexString()); - } - - [TestMethod] - public void TestWaitTransaction() - { - Transaction transaction = TestUtils.GetTransaction(); - rpcClientMock.Setup(p => p.RpcSend("getrawtransaction", It.Is(j => j[0].AsString() == transaction.Hash.ToString()))) - .Returns(new RpcTransaction { Transaction = transaction, VMState = VMState.HALT, BlockHash = UInt256.Zero, BlockTime = 100, Confirmations = 1 }.ToJson()); - - var tx = walletAPI.WaitTransaction(transaction).Result; - Assert.AreEqual(VMState.HALT, tx.VMState); - Assert.AreEqual(UInt256.Zero, tx.BlockHash); - } - } -} diff --git a/tests/neo.UnitTests/UT_NeoSystem.cs b/tests/neo.UnitTests/UT_NeoSystem.cs index 1ed00464fa..5e440d8374 100644 --- a/tests/neo.UnitTests/UT_NeoSystem.cs +++ b/tests/neo.UnitTests/UT_NeoSystem.cs @@ -25,8 +25,5 @@ public void Setup() [TestMethod] public void TestGetConsensus() => neoSystem.Consensus.Should().BeNull(); - - [TestMethod] - public void TestGetRpcServer() => neoSystem.RpcServer.Should().BeNull(); } } diff --git a/tests/neo.UnitTests/UT_Utility.cs b/tests/neo.UnitTests/UT_Utility.cs deleted file mode 100644 index dd68da017c..0000000000 --- a/tests/neo.UnitTests/UT_Utility.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract; -using Neo.Wallets; -using System; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_Utility - { - private KeyPair keyPair; - private UInt160 scriptHash; - - [TestInitialize] - public void TestSetup() - { - keyPair = new KeyPair(Wallet.GetPrivateKeyFromWIF("KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p")); - scriptHash = Contract.CreateSignatureRedeemScript(keyPair.PublicKey).ToScriptHash(); - } - - [TestMethod] - public void TestGetKeyPair() - { - string nul = null; - Assert.ThrowsException(() => Utility.GetKeyPair(nul)); - - string wif = "KyXwTh1hB76RRMquSvnxZrJzQx7h9nQP2PCRL38v6VDb5ip3nf1p"; - var result = Utility.GetKeyPair(wif); - Assert.AreEqual(keyPair, result); - - string privateKey = keyPair.PrivateKey.ToHexString(); - result = Utility.GetKeyPair(privateKey); - Assert.AreEqual(keyPair, result); - } - - [TestMethod] - public void TestGetScriptHash() - { - string nul = null; - Assert.ThrowsException(() => Utility.GetScriptHash(nul)); - - string addr = scriptHash.ToAddress(); - var result = Utility.GetScriptHash(addr); - Assert.AreEqual(scriptHash, result); - - string hash = scriptHash.ToString(); - result = Utility.GetScriptHash(hash); - Assert.AreEqual(scriptHash, result); - - string publicKey = keyPair.PublicKey.ToString(); - result = Utility.GetScriptHash(publicKey); - Assert.AreEqual(scriptHash, result); - } - } -} From 24b982bfc66ee2fddbf4a7ee747c667ee4f7c8f9 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 8 Dec 2019 00:18:19 +0800 Subject: [PATCH 173/305] Remove some dependencies --- src/neo/neo.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index c649d00b64..593839600d 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -23,14 +23,11 @@ - - - From c192be958b9409f4d719333200409c409558f341 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 9 Dec 2019 20:07:39 +0800 Subject: [PATCH 174/305] Update and rename dotnetcore.yml to main.yml --- .../workflows/{dotnetcore.yml => main.yml} | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) rename .github/workflows/{dotnetcore.yml => main.yml} (62%) diff --git a/.github/workflows/dotnetcore.yml b/.github/workflows/main.yml similarity index 62% rename from .github/workflows/dotnetcore.yml rename to .github/workflows/main.yml index dab405a371..f53a6b53f2 100644 --- a/.github/workflows/dotnetcore.yml +++ b/.github/workflows/main.yml @@ -72,3 +72,41 @@ jobs: run: dotnet nuget push out/*.nupkg -s https://www.myget.org/F/neo/api/v2/package -k ${MYGET_TOKEN} -ss https://www.myget.org/F/neo/symbols/api/v2/package -sk ${MYGET_TOKEN} env: MYGET_TOKEN: ${{ secrets.MYGET_TOKEN }} + + Release: + if: github.ref == 'refs/heads/master' && startsWith(github.repository, 'neo-project/') + needs: Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Get version + id: get_version + run: | + sudo apt install xmlstarlet + find src -name *.csproj | xargs xmlstarlet sel -t -v "concat('::set-output name=version::v',//VersionPrefix/text(),'-',//VersionSuffix/text())" | xargs echo + - name: Check tag + id: check_tag + run: curl -s -I ${{ format('https://github.com/{0}/releases/tag/{1}', github.repository, steps.get_version.outputs.version) }} | head -n 1 | cut -d$' ' -f2 | xargs printf "::set-output name=statusCode::%s" | xargs echo + - name: Create release + if: steps.check_tag.outputs.statusCode == '404' + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.get_version.outputs.version }} + release_name: ${{ steps.get_version.outputs.version }} + prerelease: ${{ contains(steps.get_version.outputs.version, '-') }} + - name: Setup .NET Core + if: steps.check_tag.outputs.statusCode == '404' + uses: actions/setup-dotnet@v1 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + - name: Publish to NuGet + if: steps.check_tag.outputs.statusCode == '404' + run: | + dotnet pack -o out -c Release + dotnet nuget push out/*.nupkg -s https://api.nuget.org/v3/index.json -k ${NUGET_TOKEN} + env: + NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }} From 5b03551e31184bf84bc8e1811d8193e89dea4b2d Mon Sep 17 00:00:00 2001 From: Ricardo Prado <38396062+lock9@users.noreply.github.com> Date: Mon, 9 Dec 2019 18:00:20 -0300 Subject: [PATCH 175/305] Readme update (#1341) * text update * Remaining images * Using tables, moving status * Neo modules * Fixing typo * Fixing typo 2 * Fixing URL * Using same name on titles --- .github/images/discord-logo.png | Bin 0 -> 5135 bytes .github/images/medium-logo.png | Bin 0 -> 11661 bytes .github/images/nnt-logo.jpg | Bin 0 -> 24913 bytes .github/images/reddit-logo.png | Bin 0 -> 21168 bytes .github/images/telegram-logo.png | Bin 0 -> 23291 bytes .github/images/twitter-logo.png | Bin 0 -> 6683 bytes .github/images/we-chat-logo.png | Bin 0 -> 12301 bytes .github/images/weibo-logo.png | Bin 0 -> 136165 bytes .github/images/youtube-logo.png | Bin 0 -> 18463 bytes README.md | 212 ++++++++++++++++++------------- 10 files changed, 121 insertions(+), 91 deletions(-) create mode 100644 .github/images/discord-logo.png create mode 100644 .github/images/medium-logo.png create mode 100644 .github/images/nnt-logo.jpg create mode 100644 .github/images/reddit-logo.png create mode 100644 .github/images/telegram-logo.png create mode 100644 .github/images/twitter-logo.png create mode 100644 .github/images/we-chat-logo.png create mode 100644 .github/images/weibo-logo.png create mode 100644 .github/images/youtube-logo.png diff --git a/.github/images/discord-logo.png b/.github/images/discord-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f53cf1b2e2776dd69f3838a0071e6e43f047bf75 GIT binary patch literal 5135 zcmb_g2{@Ep-=DFQWf(g#jk1(6GuD~0uSNEq3S(w47&B%jhAe}T6xt+3gyg9tr5I7h znmrX+pG+w2XrU*{H`=cE{jTTzmiPPa>%Qll`<&nT{nzuq&o$ZZt~({fl*Ir5fP~{N zJ5OF)%X@l6gm}LhXW4!L0MLYJYwK=}Cx;Rso;z(I+DJ4Sr2_zHA3t@p#i`w0HmgO& zzO&3%WQI%8@^FtHY0Yuma~R@(#rDKiN83}OGBGu&UqE>=oA*v7^J(pDwO$fU;_tka zVapO1JTm?9+%#Hs5Lgj9dhVdd!4Ljd;vOBZneQ7{2rJ}cx)xT(UD*!+X~lLZPA`Zs zM_roU8FsCv>Z@OL;@1diQXy{i62gfw=Fo+v7AS;>H;4KexgcC9wuA`c zu0$%qE78>(ml%aZ9YT*XhyDnt zzl%G>mP{o;jP#6QIHaKw#00H}G&V3qqje$r2qX%QK*5m)Fr+cY5P{Lxhy1*tJZV&X z7{=4i;ioL#n>jRsPN!ht@c8(6y?6sXGBq5|OBOU7p%2&Bhw%_FT0#sR%YenuG=4GI z5okCnkwPbuV;~!h*iiC*x;d1m=?@b~l;5;5w4ZL`ISkIgQs78E#D+=Vfq2|+9A!T> z`nzyE4o-+BkO(n!8V`&7jip48>10|2`Cm}~p8h8RJlDFo{Fd>zx{ye}MbPN>aXd4A zI^=KBH17lo0q#klk@r(^1p7Fini?C{P%ySs0+vpudXve~zXIj{OJ#_y?M7hG5N$tV z44xcM)A@%r1UoF9U=H1whavT02t#kA0frYlqzMdRfI%RBKwZdqVpzi8K@E&BMn?Yx zXOUM4VU_1^JMy8UmJe!FmY&Zc@f52$uFnT`w(|Sj zKkVFJa9%ms=>D!lyqDjVj1a>sUsPV5a>@#>;(cyX9qp{W8O3kUS;rN*%5=5N4W!UB zJdC3=+(3T`__~5t1zg8!>CO0zw_tw@>wIuwF{Hm>pmbgIAiA}`e7D#MYa377{S^x< zB_$-$+kGc3^0!D#uG%_(+W@UA~qvCG5_=kV*wi-)1Qu5Y!D? zQR1xGRfwST?-@$oeLE3S#jLm>g6&Y5sLMW<(G5s)ojo7@Mwepz_z5s+r_pWIv~a4HQ6k3E@?FB|-t9L)lG zc=m_P1ljDZcm$N+Bkaeu$^RX`LfE7+ddNeB3KQa(#3 z+H)*T#s#ZMMXh5d?Ml8%$?*qwM?FKRks1Rp6IB8hpNl!%Jc;d(tmLZ|STxAZcvd43 zmHsW=U}Gj% zwgJBJlC<;f;ML0^&8u5jsBie5rTGihaU{iUoksmVXww_;aHT6u1wEHSS)^6z9EYRB)6JrtZP+}A3C zh#uS8o(FDxJZRMaz72ffu|%JuK#~;qx{hSN;tG(Qxufw&o(-Q6rf zraer3&D81e<1LYe3yTRNtFE0UQfpnShrdc8D!evHR3udtI2<6Z4kj(N-F2y&TD&VW z@A3%6KF?@6+0{BBJuKv2pFpdmNtc>YO1fGz>!s&eQbA){Qfn7T9HsX+Qo;L@q$xch z>&%^nIeB%Tp&|3;t%}FRyFABp&V7b*car!A6{~Db)CHPDT>YeaBpS4fWvzM-$}57WIR5z)3E2)`u(i7KXR>TP0w5)IWiqZ;^a zFIo~hEx6`233x9w-(1_r7Zj;t^`v&#I7G!yFv-`=?Q=Sd-grYvg}puGpwzn=Z`U!O z?>tv|oPW%g^W>s(qOhzZd|{ut7H8^#JNC*_E|LJ_M+{3 zjA$25^^a5mu(i0(lYob3f>%vms-G4s@}BV0s9#kqI^|4PYj|#+>EphAA?H1PJSAW4h`W}TZ`)Y7J72b?tXV#5_m&Rs z=?mIAnjW5J?Zuor^P=GPeEjWq;}?~fEwupwb=e>eEnoH;lp&#CnKH`W%8l+GoIBqV z8h>k0Y#S5Zw`FyzOAaWNtdm$f)Yai>dSwV3S$1hpraM%H z-6FA9nquL-b0)c};B3*J$oWA}Q*dz#0iB;JH8m|%BZ5+?y|&{B$z_g(I_s0|uhJ!A zyxyWW$G_5$wu!GsNNuag=BS0?uMKAcV#rz(8E$$aD0Ozr5cPn%+f-^~`yq^eIcn=) zoUd17tvRn6JHTfmRE9Wo?zi5w)jhN6%-tk@hh0XL)JPNRyf(L&dZ6*yva18uU7M$x z(JMY*wrK62)0duT)nV;&A5stO`$P7NzNagc07MCuNpM1r7&i9f> zk=a=vJJL@q>Wng>179;n>%#|CP2f^Dq4V@1A0fKe!3+ck@N3tJ?_Qg^PQE0AlEiH_yP-be=O<64VI{Z!yTCu!)Mu=XhOUv3%bsM_ymXI7>Qa zthVP)KjZZgb$5o%WG3I}QKG8h_7j!9MnvK9=4Wr6oWLlR4qc@W^YQCuLD2C#I^+@} z(4-7GBN^%AmgB)`(tc*-b*ns8-%zSJM{+up>vd*ETylF?YI;rVen`eK*Z-fh(!V%} zHD~GM?L2h4nV+ex^R0>`;!&A?NlD>yV64CuQ2DT7`X2_sMTv{<^?ua?#Zk)E#SoUx zv2(A_#wzXru6y4~XLEd%g?YCEVv}ZKs}pCpi^d^vKmHQ1+vemZpD4_GHNXYkbrccjOj?WbBvXs%}%Sx zB3_=lYu|qxbipsuVEnShM3t~+1|;B$U!m6dtJYG^LS@FO(1vk6bA*!YWJTOZ4&FWb z=)?nluu-%)V`!r>`{XR;JdEDv=L`gEp^OK*Kqm8Xm@1UvfP<8Cad|=5p1AQ6 zwL;YH(}pVMh}Z%>qH$u#j9JseXkOZ+*Bh$Xip&U-z7NVx{%Eo;j*G+=XzQqblX6ha WioAaP<@SyLn;h+3?P_d7(*FZ0MuqhN literal 0 HcmV?d00001 diff --git a/.github/images/medium-logo.png b/.github/images/medium-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8ff3fb659ad66840678d714954fe9e7c8b23fa03 GIT binary patch literal 11661 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�)pCShTx;vV> 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 MDMiT=aYO(A1=V<*NdN!< literal 0 HcmV?d00001 diff --git a/.github/images/nnt-logo.jpg b/.github/images/nnt-logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dfa91f569549946f52ec394fb8ec55f139566a41 GIT binary patch literal 24913 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 SKj(j6$)8vMC)LGYpZ)<$_(6XF literal 0 HcmV?d00001 diff --git a/.github/images/reddit-logo.png b/.github/images/reddit-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..be704a89e75076773def78197407e237a50c00ae GIT binary patch literal 21168 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 L+bK6}eNO!!A~Z@a literal 0 HcmV?d00001 diff --git a/.github/images/telegram-logo.png b/.github/images/telegram-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7c86be4be68f21b7da1825362e3cf8585dcc940b GIT binary patch literal 23291 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 literal 0 HcmV?d00001 diff --git a/.github/images/twitter-logo.png b/.github/images/twitter-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..cb90d622911e35cc9ed2c2cc73e269876942a7ef GIT binary patch literal 6683 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 PzXz1Hqg92)@#OykoSw>6 literal 0 HcmV?d00001 diff --git a/.github/images/we-chat-logo.png b/.github/images/we-chat-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..201e08c10667536a2ff345e5ccb6b539df4a81af GIT binary patch literal 12301 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 literal 0 HcmV?d00001 diff --git a/.github/images/weibo-logo.png b/.github/images/weibo-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..364b4bc823589c51684277fc27fd2a085e1bb66d GIT binary patch literal 136165 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 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index d567717e71..82664ab4f0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,85 @@

- + + neo-logo +

+

Neo Blockchain

+ +

+ A modern distributed network for the Smart Economy. +
+ Documentation » +
+
+ Neo + · + Neo VM + · + Neo Modules + · + Neo DevPack + · + Neo Node +

+

+ + + +   + + + +   + + + +   + + + +   + + + +   + + + +   + + + +   + + + + +   + + + +

Current TravisCI build status. @@ -19,110 +95,64 @@

-NEO 3.0 (under development): A distributed network for the Smart Economy -================ - -NEO uses digital identity and blockchain technology to digitize assets and leverages smart contracts for autonomously managed digital assets to create a "smart economy" within a decentralized network. - -To learn more about NEO, please read the [White Paper](https://docs.neo.org/en-us/whitepaper.html)|[白皮书](https://docs.neo.org/zh-cn/whitepaper.html). - -NEO 2.x: Reference template -================ - -NEO adopts a model of continuous improvement and evolution. -We believe that the research and development of blockchain technology will be kept in continuous transformation for the next years. -From 2017 until the middle of 2020 (estimated), NEO 2.x will still be supported and the compatibility of new packages will be maintained. - -A link to the essential C# reference implementation can be seen below: - -* [neo](https://github.com/neo-project/neo/tree/master-2.x): core implementation (branch `master-2.x`) -* [neo-vm](https://github.com/neo-project/neo-vm/tree/master-2.x): virtual machine (branch `master-2.x`) -* [neo-cli](https://github.com/neo-project/neo-cli/tree/master-2.x): command-line interface (branch `master-2.x`) -* [neo-plugins](https://github.com/neo-project/neo-plugins/tree/master-2.x): plugins (branch `master-2.x`) -* [neo-devpack-dotnet](https://github.com/neo-project/neo-devpack-dotnet/tree/master-2.x): NeoContract compiler and development pack for .NET (branch `master-2.x`) - -NEO 1.x was known as Antshares and the roots of the code can be found historically in this current repository. - -Supported Platforms --------- - -We already support the following platforms: - -* CentOS 7 -* Docker -* macOS 10 + -* Red Hat Enterprise Linux 7.0 + -* Ubuntu 14.04, Ubuntu 14.10, Ubuntu 15.04, Ubuntu 15.10, Ubuntu 16.04, Ubuntu 16.10 -* Windows 7 SP1 +, Windows Server 2008 R2 + - -We will support the following platforms in the future: -* Debian -* Fedora -* FreeBSD -* Linux Mint -* OpenSUSE -* Oracle Linux -Development --------- -To start building peer applications for NEO on Windows, you need to download [Visual Studio 2017](https://www.visualstudio.com/products/visual-studio-community-vs), install the [.NET Framework 4.7 Developer Pack](https://www.microsoft.com/en-us/download/details.aspx?id=55168) and the [.NET Core SDK](https://www.microsoft.com/net/core). +## Table of Contents +1. [Overview](#overview) +2. [Project structure](#project-structure) +3. [Related projects](#related-projects) +4. [Opening a new issue](#opening-a-new-issue) +5. [Bounty program](#bounty-program) +6. [License](#license) -If you need to develop on Linux or macOS, just install the [.NET Core SDK](https://www.microsoft.com/net/core). +## Overview +This repository contain main classes of the +[Neo](https://www.neo.org) blockchain. +Visit the [documentation](https://docs.neo.org/docs/en-us/index.html) to get started. -To install Neo SDK to your project, run the following command in the [Package Manager Console](https://docs.nuget.org/ndocs/tools/package-manager-console): -``` -PM> Install-Package Neo -``` +*Note: This is Neo 3 branch, currently under development. For the current stable version, please [click here](https://github.com/neo-project/neo/tree/master-2.x)* -For more information about how to build DAPPs for NEO, please read the [documentation](http://docs.neo.org/en-us/sc/introduction.html)|[文档](http://docs.neo.org/zh-cn/sc/introduction.html). -Daily builds --------- -If you want to use the [latest daily build](https://www.myget.org/feed/neo/package/nuget/Neo) then you need to add a NuGet.Config to your app with the following content: +## Project structure +An overview of the project folders can be seen below. -```xml - - - - - - - - -``` +|Folder|Content| +|---|---| +|Consensus| Classes used in the dBFT consensus algorithm, including the `ConsensusService` actor.| +|Cryptography|General cryptography classes including ECC implementation.| +|IO|Data structures used for caching and collection interaction.| +|Ledger|Classes responsible for the state control, including the `MemoryPool` and `Blockchain` classes.| +|Network|Peer-to-peer protocol implementation classes.| +|Persistence|Classes used to allow other classes to access application state.| +|Plugins|Interfaces used to extend Neo, including the storage interface.| +|SmartContract|Native contracts, `ApplicationEngine`, `InteropService` and other smart-contract related classes.| +|VM|Helper methods used to interact with the VM.| +|Wallet|Wallet and account implementation. | -*NOTE: This NuGet.Config should be with your application unless you want nightly packages to potentially start being restored for other apps on the machine.* -How to Contribute --------- +## Related projects +Code references are provided for all platform building blocks. That includes the base library, the VM, a command line application and the compiler. -You can contribute to NEO with [issues](https://github.com/neo-project/neo/issues) and [PRs](https://github.com/neo-project/neo/pulls). Simply filing issues for problems you encounter is a great way to contribute. Contributing implementations is greatly appreciated. +* [**neo:**](https://github.com/neo-project/neo/tree/) Neo core library, contains base classes, including ledger, p2p and IO modules. +* [neo-vm:](https://github.com/neo-project/neo-vm/) Neo Virtual Machine is a decoupled VM that Neo uses to execute its scripts. It also uses the `InteropService` layer to extend its functionalities. +* [neo-node:](https://github.com/neo-project/neo-node/) Executable version of the Neo library, exposing features using a command line application or GUI. +* [neo-modules:](https://github.com/neo-project/neo-modules/) Neo modules include additional tools and plugins to be used with Neo. +* [neo-devpack-dotnet:](https://github.com/neo-project/neo-devpack-dotnet/) These are the official tools used to convert a C# smart-contract into a *neo executable file*. -We use and recommend the following workflow: +## Opening a new issue +Please feel free to create new issues to suggest features or ask questions. -1. Create an issue for your work. - * You can skip this step for trivial changes. - * Reuse an existing issue on the topic, if there is one. - * Clearly state that you are going to take on implementing it, if that's the case. You can request that the issue be assigned to you. Note: The issue filer and the implementer don't have to be the same person. -1. Create a personal fork of the repository on GitHub (if you don't already have one). -1. Create a branch off of master(`git checkout -b mybranch`). - * Name the branch so that it clearly communicates your intentions, such as issue-123 or githubhandle-issue. - * Branches are useful since they isolate your changes from incoming changes from upstream. They also enable you to create multiple PRs from the same fork. -1. Make and commit your changes. -1. Add new tests corresponding to your change, if applicable. -1. Build the repository with your changes. - * Make sure that the builds are clean. - * Make sure that the tests are all passing, including your new tests. -1. Create a pull request (PR) against the upstream repository's master branch. - * Push your changes to your fork on GitHub. +- [Feature request](https://github.com/neo-project/neo/issues/new?assignees=&labels=discussion&template=feature-or-enhancement-request.md&title=) +- [Bug report](https://github.com/neo-project/neo/issues/new?assignees=&labels=&template=bug_report.md&title=) +- [Questions](https://github.com/neo-project/neo/issues/new?assignees=&labels=question&template=questions.md&title=) -Note: It is OK for your PR to include a large number of commits. Once your change is accepted, you will be asked to squash your commits into one or some appropriately small number of commits before your PR is merged. +If you found a security issue, please refer to our [security policy](https://github.com/neo-project/neo/security/policy). -License ------- +## Bounty program +You can be rewarded by finding security issues. Please refer to our [bounty program page](https://neo.org/bounty) for more information. +## License The NEO project is licensed under the [MIT license](LICENSE). From 5e1333b215518f48f1387abe288e3dd051899d3a Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 10 Dec 2019 14:55:18 +0800 Subject: [PATCH 176/305] Improve JSON (#1315) --- src/neo/IO/Json/JArray.cs | 59 ++--- src/neo/IO/Json/JBoolean.cs | 34 +-- src/neo/IO/Json/JNumber.cs | 80 ++----- src/neo/IO/Json/JObject.cs | 204 +++++++++--------- src/neo/IO/Json/JString.cs | 63 ++---- src/neo/Wallets/NEP6/NEP6Wallet.cs | 6 +- tests/neo.UnitTests/IO/Json/UT_JBoolean.cs | 50 ----- tests/neo.UnitTests/IO/Json/UT_JNumber.cs | 14 +- tests/neo.UnitTests/IO/Json/UT_JObject.cs | 52 ++--- tests/neo.UnitTests/IO/Json/UT_JString.cs | 42 ++-- .../SmartContract/UT_JsonSerializer.cs | 3 - 11 files changed, 193 insertions(+), 414 deletions(-) diff --git a/src/neo/IO/Json/JArray.cs b/src/neo/IO/Json/JArray.cs index 6de623d29a..8b7dab524d 100644 --- a/src/neo/IO/Json/JArray.cs +++ b/src/neo/IO/Json/JArray.cs @@ -1,9 +1,7 @@ -using System; using System.Collections; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text; +using System.Text.Json; namespace Neo.IO.Json { @@ -55,7 +53,7 @@ public void Add(JObject item) public override string AsString() { - return string.Join(VALUE_SEPARATOR.ToString(), items.Select(p => p?.AsString())); + return string.Join(",", items.Select(p => p?.AsString())); } public void Clear() @@ -93,32 +91,6 @@ public void Insert(int index, JObject item) items.Insert(index, item); } - internal new static JArray Parse(TextReader reader, int max_nest) - { - SkipSpace(reader); - if (reader.Read() != BEGIN_ARRAY) throw new FormatException(); - JArray array = new JArray(); - SkipSpace(reader); - if (reader.Peek() != END_ARRAY) - { - while (true) - { - JObject obj = JObject.Parse(reader, max_nest - 1); - array.items.Add(obj); - SkipSpace(reader); - char nextchar = (char)reader.Read(); - if (nextchar == VALUE_SEPARATOR) continue; - if (nextchar == END_ARRAY) break; - throw new FormatException(); - } - } - else - { - reader.Read(); - } - return array; - } - public bool Remove(JObject item) { return items.Remove(item); @@ -129,27 +101,22 @@ public void RemoveAt(int index) items.RemoveAt(index); } - public override string ToString() + internal override void Write(Utf8JsonWriter writer) { - StringBuilder sb = new StringBuilder(); - sb.Append(BEGIN_ARRAY); + writer.WriteStartArray(); foreach (JObject item in items) { - if (item == null) - sb.Append(LITERAL_NULL); + if (item is null) + writer.WriteNullValue(); else - sb.Append(item); - sb.Append(VALUE_SEPARATOR); + item.Write(writer); } - if (items.Count == 0) - { - sb.Append(END_ARRAY); - } - else - { - sb[sb.Length - 1] = END_ARRAY; - } - return sb.ToString(); + writer.WriteEndArray(); + } + + public static implicit operator JArray(JObject[] value) + { + return new JArray(value); } } } diff --git a/src/neo/IO/Json/JBoolean.cs b/src/neo/IO/Json/JBoolean.cs index 6b7c7a34c4..fa1554ab56 100644 --- a/src/neo/IO/Json/JBoolean.cs +++ b/src/neo/IO/Json/JBoolean.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System.Text.Json; namespace Neo.IO.Json { @@ -27,38 +26,19 @@ public override string AsString() return Value.ToString().ToLowerInvariant(); } - internal static JBoolean Parse(TextReader reader) - { - SkipSpace(reader); - char firstChar = (char)reader.Peek(); - if (firstChar == LITERAL_FALSE[0]) - return ParseFalse(reader); - else if (firstChar == LITERAL_TRUE[0]) - return ParseTrue(reader); - throw new FormatException(); - } - - internal static JBoolean ParseFalse(TextReader reader) + public override string ToString() { - SkipSpace(reader); - for (int i = 0; i < LITERAL_FALSE.Length; i++) - if ((char)reader.Read() != LITERAL_FALSE[i]) - throw new FormatException(); - return new JBoolean(false); + return AsString(); } - internal static JBoolean ParseTrue(TextReader reader) + internal override void Write(Utf8JsonWriter writer) { - SkipSpace(reader); - for (int i = 0; i < LITERAL_TRUE.Length; i++) - if ((char)reader.Read() != LITERAL_TRUE[i]) - throw new FormatException(); - return new JBoolean(true); + writer.WriteBooleanValue(Value); } - public override string ToString() + public static implicit operator JBoolean(bool value) { - return AsString(); + return new JBoolean(value); } } } diff --git a/src/neo/IO/Json/JNumber.cs b/src/neo/IO/Json/JNumber.cs index daac440094..4f933aa3ad 100644 --- a/src/neo/IO/Json/JNumber.cs +++ b/src/neo/IO/Json/JNumber.cs @@ -1,7 +1,6 @@ using System; using System.Globalization; -using System.IO; -using System.Text; +using System.Text.Json; namespace Neo.IO.Json { @@ -14,12 +13,13 @@ public class JNumber : JObject public JNumber(double value = 0) { + if (!double.IsFinite(value)) throw new FormatException(); this.Value = value; } public override bool AsBoolean() { - return Value != 0 && !double.IsNaN(Value); + return Value != 0; } public override double AsNumber() @@ -29,73 +29,9 @@ public override double AsNumber() public override string AsString() { - if (double.IsPositiveInfinity(Value)) throw new FormatException("Positive infinity number"); - if (double.IsNegativeInfinity(Value)) throw new FormatException("Negative infinity number"); return Value.ToString(CultureInfo.InvariantCulture); } - internal static JNumber Parse(TextReader reader) - { - SkipSpace(reader); - StringBuilder sb = new StringBuilder(); - char nextchar = (char)reader.Read(); - if (nextchar == '-') - { - sb.Append(nextchar); - nextchar = (char)reader.Read(); - } - if (nextchar < '0' || nextchar > '9') throw new FormatException(); - sb.Append(nextchar); - if (nextchar > '0') - { - while (true) - { - char c = (char)reader.Peek(); - if (c < '0' || c > '9') break; - sb.Append((char)reader.Read()); - } - } - nextchar = (char)reader.Peek(); - if (nextchar == '.') - { - sb.Append((char)reader.Read()); - nextchar = (char)reader.Read(); - if (nextchar < '0' || nextchar > '9') throw new FormatException(); - sb.Append(nextchar); - while (true) - { - nextchar = (char)reader.Peek(); - if (nextchar < '0' || nextchar > '9') break; - sb.Append((char)reader.Read()); - } - } - if (nextchar == 'e' || nextchar == 'E') - { - sb.Append((char)reader.Read()); - nextchar = (char)reader.Read(); - if (nextchar == '-' || nextchar == '+') - { - sb.Append(nextchar); - nextchar = (char)reader.Read(); - } - if (nextchar < '0' || nextchar > '9') throw new FormatException(); - sb.Append(nextchar); - while (true) - { - nextchar = (char)reader.Peek(); - if (nextchar < '0' || nextchar > '9') break; - sb.Append((char)reader.Read()); - } - } - - var value = double.Parse(sb.ToString(), NumberStyles.Float, CultureInfo.InvariantCulture); - - if (value > MAX_SAFE_INTEGER || value < MIN_SAFE_INTEGER) - throw new FormatException(); - - return new JNumber(value); - } - public override string ToString() { return AsString(); @@ -116,5 +52,15 @@ public override T TryGetEnum(T defaultValue = default, bool ignoreCase = fals object result = Enum.ToObject(enumType, value); return Enum.IsDefined(enumType, result) ? (T)result : defaultValue; } + + internal override void Write(Utf8JsonWriter writer) + { + writer.WriteNumberValue(Value); + } + + public static implicit operator JNumber(double value) + { + return new JNumber(value); + } } } diff --git a/src/neo/IO/Json/JObject.cs b/src/neo/IO/Json/JObject.cs index cf6f68cbc7..11e477704d 100644 --- a/src/neo/IO/Json/JObject.cs +++ b/src/neo/IO/Json/JObject.cs @@ -3,23 +3,12 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Text.Json; namespace Neo.IO.Json { public class JObject { - protected const char BEGIN_ARRAY = '['; - protected const char BEGIN_OBJECT = '{'; - protected const char END_ARRAY = ']'; - protected const char END_OBJECT = '}'; - protected const char NAME_SEPARATOR = ':'; - protected const char VALUE_SEPARATOR = ','; - protected const char QUOTATION_MARK = '"'; - protected const string WS = " \t\n\r"; - protected const string LITERAL_FALSE = "false"; - protected const string LITERAL_NULL = "null"; - protected const string LITERAL_TRUE = "true"; - public static readonly JObject Null = null; public IDictionary Properties { get; } = new OrderedDictionary(); @@ -27,8 +16,9 @@ public class JObject { get { - Properties.TryGetValue(name, out JObject value); - return value; + if (Properties.TryGetValue(name, out JObject value)) + return value; + return null; } set { @@ -56,112 +46,114 @@ public bool ContainsProperty(string key) return Properties.ContainsKey(key); } - public static JObject Parse(TextReader reader, int max_nest = 100) - { - if (max_nest < 0) throw new FormatException(); - SkipSpace(reader); - char firstChar = (char)reader.Peek(); - if (firstChar == LITERAL_FALSE[0]) - return JBoolean.ParseFalse(reader); - if (firstChar == LITERAL_NULL[0]) - return ParseNull(reader); - if (firstChar == LITERAL_TRUE[0]) - return JBoolean.ParseTrue(reader); - if (firstChar == BEGIN_OBJECT) - return ParseObject(reader, max_nest); - if (firstChar == BEGIN_ARRAY) - return JArray.Parse(reader, max_nest); - if ((firstChar >= '0' && firstChar <= '9') || firstChar == '-') - return JNumber.Parse(reader); - if (firstChar == QUOTATION_MARK) - return JString.Parse(reader); - throw new FormatException(); + private static string GetString(ref Utf8JsonReader reader) + { + try + { + return reader.GetString(); + } + catch (InvalidOperationException ex) + { + throw new FormatException(ex.Message, ex); + } } - public static JObject Parse(string value, int max_nest = 100) + public static JObject Parse(ReadOnlySpan value, int max_nest = 100) { - using (StringReader reader = new StringReader(value)) + Utf8JsonReader reader = new Utf8JsonReader(value, new JsonReaderOptions + { + AllowTrailingCommas = false, + CommentHandling = JsonCommentHandling.Skip, + MaxDepth = max_nest + }); + try { - JObject json = Parse(reader, max_nest); - SkipSpace(reader); - if (reader.Read() != -1) throw new FormatException(); + JObject json = Read(ref reader); + if (reader.Read()) throw new FormatException(); return json; } + catch (JsonException ex) + { + throw new FormatException(ex.Message, ex); + } } - private static JObject ParseNull(TextReader reader) + public static JObject Parse(string value, int max_nest = 100) { - for (int i = 0; i < LITERAL_NULL.Length; i++) - if ((char)reader.Read() != LITERAL_NULL[i]) - throw new FormatException(); - return null; + return Parse(Encoding.UTF8.GetBytes(value), max_nest); } - private static JObject ParseObject(TextReader reader, int max_nest) + private static JObject Read(ref Utf8JsonReader reader, bool skipReading = false) { - SkipSpace(reader); - if (reader.Read() != BEGIN_OBJECT) throw new FormatException(); - JObject obj = new JObject(); - SkipSpace(reader); - if (reader.Peek() != END_OBJECT) - { - while (true) - { - string name = JString.Parse(reader).Value; - if (obj.Properties.ContainsKey(name)) throw new FormatException(); - SkipSpace(reader); - if (reader.Read() != NAME_SEPARATOR) throw new FormatException(); - JObject value = Parse(reader, max_nest - 1); - obj.Properties.Add(name, value); - SkipSpace(reader); - char nextchar = (char)reader.Read(); - if (nextchar == VALUE_SEPARATOR) continue; - if (nextchar == END_OBJECT) break; - throw new FormatException(); - } - } - else + if (!skipReading && !reader.Read()) throw new FormatException(); + return reader.TokenType switch { - reader.Read(); - } - return obj; + JsonTokenType.False => false, + JsonTokenType.Null => Null, + JsonTokenType.Number => reader.GetDouble(), + JsonTokenType.StartArray => ReadArray(ref reader), + JsonTokenType.StartObject => ReadObject(ref reader), + JsonTokenType.String => GetString(ref reader), + JsonTokenType.True => true, + _ => throw new FormatException(), + }; } - protected static void SkipSpace(TextReader reader) + private static JArray ReadArray(ref Utf8JsonReader reader) { - while (WS.IndexOf((char)reader.Peek()) >= 0) + JArray array = new JArray(); + while (reader.Read()) { - reader.Read(); + switch (reader.TokenType) + { + case JsonTokenType.EndArray: + return array; + default: + array.Add(Read(ref reader, skipReading: true)); + break; + } } + throw new FormatException(); } - public override string ToString() + private static JObject ReadObject(ref Utf8JsonReader reader) { - StringBuilder sb = new StringBuilder(); - sb.Append(BEGIN_OBJECT); - foreach (KeyValuePair pair in Properties) + JObject obj = new JObject(); + while (reader.Read()) { - sb.Append((JObject)pair.Key); - sb.Append(NAME_SEPARATOR); - if (pair.Value == null) - { - sb.Append(LITERAL_NULL); - } - else + switch (reader.TokenType) { - sb.Append(pair.Value); + case JsonTokenType.EndObject: + return obj; + case JsonTokenType.PropertyName: + string name = GetString(ref reader); + if (obj.Properties.ContainsKey(name)) throw new FormatException(); + JObject value = Read(ref reader); + obj.Properties.Add(name, value); + break; + default: + throw new FormatException(); } - sb.Append(VALUE_SEPARATOR); - } - if (Properties.Count == 0) - { - sb.Append(END_OBJECT); } - else + throw new FormatException(); + } + + public override string ToString() + { + return ToString(false); + } + + public string ToString(bool indented) + { + using MemoryStream ms = new MemoryStream(); + using Utf8JsonWriter writer = new Utf8JsonWriter(ms, new JsonWriterOptions { - sb[sb.Length - 1] = END_OBJECT; - } - return sb.ToString(); + Indented = indented, + SkipValidation = true + }); + Write(writer); + writer.Flush(); + return Encoding.UTF8.GetString(ms.ToArray()); } public virtual T TryGetEnum(T defaultValue = default, bool ignoreCase = false) where T : Enum @@ -169,29 +161,43 @@ public override string ToString() return defaultValue; } + internal virtual void Write(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + foreach (KeyValuePair pair in Properties) + { + writer.WritePropertyName(pair.Key); + if (pair.Value is null) + writer.WriteNullValue(); + else + pair.Value.Write(writer); + } + writer.WriteEndObject(); + } + public static implicit operator JObject(Enum value) { - return new JString(value.ToString()); + return (JString)value; } public static implicit operator JObject(JObject[] value) { - return new JArray(value); + return (JArray)value; } public static implicit operator JObject(bool value) { - return new JBoolean(value); + return (JBoolean)value; } public static implicit operator JObject(double value) { - return new JNumber(value); + return (JNumber)value; } public static implicit operator JObject(string value) { - return value == null ? null : new JString(value); + return (JString)value; } } } diff --git a/src/neo/IO/Json/JString.cs b/src/neo/IO/Json/JString.cs index 032470cf4e..a54284c3f0 100644 --- a/src/neo/IO/Json/JString.cs +++ b/src/neo/IO/Json/JString.cs @@ -1,8 +1,6 @@ using System; using System.Globalization; -using System.IO; -using System.Text; -using System.Text.Encodings.Web; +using System.Text.Json; namespace Neo.IO.Json { @@ -31,50 +29,6 @@ public override string AsString() return Value; } - internal static JString Parse(TextReader reader) - { - SkipSpace(reader); - if (reader.Read() != QUOTATION_MARK) throw new FormatException(); - char[] buffer = new char[4]; - StringBuilder sb = new StringBuilder(); - while (true) - { - int c = reader.Read(); - if (c == QUOTATION_MARK) break; - if (c == '\\') - { - c = (char)reader.Read(); - switch (c) - { - case QUOTATION_MARK: c = QUOTATION_MARK; break; - case '\\': c = '\\'; break; - case '/': c = '/'; break; - case 'b': c = '\b'; break; - case 'f': c = '\f'; break; - case 'n': c = '\n'; break; - case 'r': c = '\r'; break; - case 't': c = '\t'; break; - case 'u': - reader.Read(buffer, 0, buffer.Length); - c = short.Parse(new string(buffer), NumberStyles.HexNumber); - break; - default: throw new FormatException(); - } - } - else if (c < ' ' || c == -1) - { - throw new FormatException(); - } - sb.Append((char)c); - } - return new JString(sb.ToString()); - } - - public override string ToString() - { - return $"{QUOTATION_MARK}{JavaScriptEncoder.Default.Encode(Value)}{QUOTATION_MARK}"; - } - public override T TryGetEnum(T defaultValue = default, bool ignoreCase = false) { try @@ -86,5 +40,20 @@ public override T TryGetEnum(T defaultValue = default, bool ignoreCase = fals return defaultValue; } } + + internal override void Write(Utf8JsonWriter writer) + { + writer.WriteStringValue(Value); + } + + public static implicit operator JString(Enum value) + { + return new JString(value.ToString()); + } + + public static implicit operator JString(string value) + { + return value == null ? null : new JString(value); + } } } diff --git a/src/neo/Wallets/NEP6/NEP6Wallet.cs b/src/neo/Wallets/NEP6/NEP6Wallet.cs index 3668287b3e..daf1b9158d 100644 --- a/src/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/neo/Wallets/NEP6/NEP6Wallet.cs @@ -28,11 +28,7 @@ public NEP6Wallet(string path, string name = null) this.path = path; if (File.Exists(path)) { - JObject wallet; - using (StreamReader reader = new StreamReader(path)) - { - wallet = JObject.Parse(reader); - } + JObject wallet = JObject.Parse(File.ReadAllBytes(path)); LoadFromJson(wallet, out Scrypt, out accounts, out extra); } else diff --git a/tests/neo.UnitTests/IO/Json/UT_JBoolean.cs b/tests/neo.UnitTests/IO/Json/UT_JBoolean.cs index 302d48c52a..ea080cd423 100644 --- a/tests/neo.UnitTests/IO/Json/UT_JBoolean.cs +++ b/tests/neo.UnitTests/IO/Json/UT_JBoolean.cs @@ -1,8 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; -using System; -using System.IO; namespace Neo.UnitTests.IO.Json { @@ -25,53 +23,5 @@ public void TestAsNumber() jFalse.AsNumber().Should().Be(0); jTrue.AsNumber().Should().Be(1); } - - [TestMethod] - public void TestParse() - { - TextReader tr1 = new StringReader("true"); - JBoolean ret1 = JBoolean.Parse(tr1); - ret1.AsBoolean().Should().BeTrue(); - - TextReader tr2 = new StringReader("false"); - JBoolean ret2 = JBoolean.Parse(tr2); - ret2.AsBoolean().Should().BeFalse(); - - TextReader tr3 = new StringReader("aaa"); - Action action = () => JBoolean.Parse(tr3); - action.Should().Throw(); - } - - [TestMethod] - public void TestParseFalse() - { - TextReader tr1 = new StringReader("false"); - JBoolean ret1 = JBoolean.ParseFalse(tr1); - ret1.AsBoolean().Should().BeFalse(); - - TextReader tr2 = new StringReader("aaa"); - Action action = () => JBoolean.ParseFalse(tr2); - action.Should().Throw(); - - TextReader tr3 = new StringReader("\t\rfalse"); - JBoolean ret3 = JBoolean.ParseFalse(tr3); - ret3.AsBoolean().Should().BeFalse(); - } - - [TestMethod] - public void TestParseTrue() - { - TextReader tr1 = new StringReader("true"); - JBoolean ret1 = JBoolean.ParseTrue(tr1); - ret1.AsBoolean().Should().BeTrue(); - - TextReader tr2 = new StringReader("aaa"); - Action action = () => JBoolean.ParseTrue(tr2); - action.Should().Throw(); - - TextReader tr3 = new StringReader(" true"); - JBoolean ret3 = JBoolean.ParseTrue(tr3); - ret3.AsBoolean().Should().BeTrue(); - } } } diff --git a/tests/neo.UnitTests/IO/Json/UT_JNumber.cs b/tests/neo.UnitTests/IO/Json/UT_JNumber.cs index e3cbe6d300..677157a1f0 100644 --- a/tests/neo.UnitTests/IO/Json/UT_JNumber.cs +++ b/tests/neo.UnitTests/IO/Json/UT_JNumber.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using System; -using System.IO; namespace Neo.UnitTests.IO.Json { @@ -43,6 +42,9 @@ public void TestAsString() Action action2 = () => new JNumber(double.NegativeInfinity).AsString(); action2.Should().Throw(); + + Action action3 = () => new JNumber(double.NaN).AsString(); + action3.Should().Throw(); } [TestMethod] @@ -53,15 +55,5 @@ public void TestTryGetEnum() new JNumber(2).TryGetEnum().Should().Be(Woo.James); new JNumber(3).TryGetEnum().Should().Be(Woo.Tom); } - - [TestMethod] - public void TestParse() - { - Action action1 = () => JNumber.Parse(new StringReader("100.a")); - action1.Should().Throw(); - - Action action2 = () => JNumber.Parse(new StringReader("100.+")); - action2.Should().Throw(); - } } } diff --git a/tests/neo.UnitTests/IO/Json/UT_JObject.cs b/tests/neo.UnitTests/IO/Json/UT_JObject.cs index 9645b04011..fe835016f0 100644 --- a/tests/neo.UnitTests/IO/Json/UT_JObject.cs +++ b/tests/neo.UnitTests/IO/Json/UT_JObject.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using System; -using System.IO; namespace Neo.UnitTests.IO.Json { @@ -53,35 +52,28 @@ public void TestAsNumber() [TestMethod] public void TestParse() { - Action action = () => JObject.Parse(new StringReader(""), -1); - action.Should().Throw(); - } - - [TestMethod] - public void TestParseNull() - { - Action action = () => JObject.Parse(new StringReader("naaa")); - action.Should().Throw(); - - JObject.Parse(new StringReader("null")).Should().BeNull(); - } - - [TestMethod] - public void TestParseObject() - { - Action action1 = () => JObject.Parse(new StringReader("{\"k1\":\"v1\",\"k1\":\"v2\"}"), 100); - action1.Should().Throw(); - - Action action2 = () => JObject.Parse(new StringReader("{\"k1\",\"k1\"}"), 100); - action2.Should().Throw(); - - Action action3 = () => JObject.Parse(new StringReader("{\"k1\":\"v1\""), 100); - action3.Should().Throw(); - - Action action4 = () => JObject.Parse(new StringReader("aaa"), 100); - action4.Should().Throw(); - - JObject.Parse(new StringReader("{\"k1\":\"v1\"}"), 100).ToString().Should().Be("{\"k1\":\"v1\"}"); + Assert.ThrowsException(() => JObject.Parse("", -1)); + Assert.ThrowsException(() => JObject.Parse("aaa")); + Assert.ThrowsException(() => JObject.Parse("hello world")); + Assert.ThrowsException(() => JObject.Parse("100.a")); + Assert.ThrowsException(() => JObject.Parse("100.+")); + Assert.ThrowsException(() => JObject.Parse("\"\\s\"")); + Assert.ThrowsException(() => JObject.Parse("\"a")); + Assert.ThrowsException(() => JObject.Parse("{\"k1\":\"v1\",\"k1\":\"v2\"}")); + Assert.ThrowsException(() => JObject.Parse("{\"k1\",\"k1\"}")); + Assert.ThrowsException(() => JObject.Parse("{\"k1\":\"v1\"")); + Assert.ThrowsException(() => JObject.Parse(new byte[] { 0x22, 0x01, 0x22 })); + Assert.ThrowsException(() => JObject.Parse("{\"color\":\"red\",\"\\uDBFF\\u0DFFF\":\"#f00\"}")); + Assert.ThrowsException(() => JObject.Parse("{\"color\":\"\\uDBFF\\u0DFFF\"}")); + Assert.ThrowsException(() => JObject.Parse("\"\\uDBFF\\u0DFFF\"")); + + JObject.Parse("null").Should().BeNull(); + JObject.Parse("true").AsBoolean().Should().BeTrue(); + JObject.Parse("false").AsBoolean().Should().BeFalse(); + JObject.Parse("\"hello world\"").AsString().Should().Be("hello world"); + JObject.Parse("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"").AsString().Should().Be("\"\\/\b\f\n\r\t"); + JObject.Parse("\"\\u0030\"").AsString().Should().Be("0"); + JObject.Parse("{\"k1\":\"v1\"}", 100).ToString().Should().Be("{\"k1\":\"v1\"}"); } [TestMethod] diff --git a/tests/neo.UnitTests/IO/Json/UT_JString.cs b/tests/neo.UnitTests/IO/Json/UT_JString.cs index 5d20b30fa0..0b6df4c087 100644 --- a/tests/neo.UnitTests/IO/Json/UT_JString.cs +++ b/tests/neo.UnitTests/IO/Json/UT_JString.cs @@ -2,7 +2,6 @@ using Neo.IO.Json; using Neo.SmartContract; using System; -using System.IO; namespace Neo.UnitTests.IO { @@ -12,7 +11,7 @@ public class UT_JString [TestMethod] public void TestConstructor() { - String s = "hello world"; + string s = "hello world"; JString jstring = new JString(s); Assert.AreEqual(s, jstring.Value); Assert.ThrowsException(() => new JString(null)); @@ -21,8 +20,8 @@ public void TestConstructor() [TestMethod] public void TestAsBoolean() { - String s1 = "hello world"; - String s2 = ""; + string s1 = "hello world"; + string s2 = ""; JString jstring1 = new JString(s1); JString jstring2 = new JString(s2); Assert.AreEqual(true, jstring1.AsBoolean()); @@ -30,32 +29,17 @@ public void TestAsBoolean() } [TestMethod] - public void TestParse() + public void TestAsNumber() { - TextReader tr = new StringReader("\"hello world\""); - String s = JString.Parse(tr).Value; - Assert.AreEqual("hello world", s); - - tr = new StringReader("hello world"); - Assert.ThrowsException(() => JString.Parse(tr)); - - tr = new StringReader("\"\\s\""); - Assert.ThrowsException(() => JString.Parse(tr)); - - tr = new StringReader("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\""); - s = JString.Parse(tr).Value; - Assert.AreEqual("\"\\/\b\f\n\r\t", s); - - tr = new StringReader("\"\\u0030\""); - s = JString.Parse(tr).Value; - Assert.AreEqual("0", s); - - tr = new StringReader("\"a"); - Assert.ThrowsException(() => JString.Parse(tr)); - - byte[] byteArray = new byte[] { 0x22, 0x01, 0x22 }; - tr = new StringReader(System.Text.Encoding.ASCII.GetString(byteArray)); - Assert.ThrowsException(() => JString.Parse(tr)); + string s1 = "hello world"; + string s2 = "123"; + string s3 = ""; + JString jstring1 = new JString(s1); + JString jstring2 = new JString(s2); + JString jstring3 = new JString(s3); + Assert.AreEqual(double.NaN, jstring1.AsNumber()); + Assert.AreEqual(123, jstring2.AsNumber()); + Assert.AreEqual(0, jstring3.AsNumber()); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs index 788868622c..5f2758ad36 100644 --- a/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs +++ b/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs @@ -32,9 +32,6 @@ public void JsonTest_WrongJson() json = @"{""length"":99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999}"; Assert.ThrowsException(() => JObject.Parse(json)); - - json = $"{{\"length\":{long.MaxValue}}}"; - Assert.ThrowsException(() => JObject.Parse(json)); } [TestMethod] From a586c575647c1359a70afa7b0f6571d0f7c61d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Tue, 10 Dec 2019 05:51:38 -0300 Subject: [PATCH 177/305] Adding some comments to P2P classes (#1212) * Adding random hashes for OnGetDataMessageReceived * Adding static readonly Random * Adding some comments to Peers class * Reverting change on ProtocolHandler * dotnet format * Additional comments * Adding extra comments * Fixing typo * Fixing typo * Fixing typo * Adding more comments * adding more comments * Add some comments of P2P (#1303) * add some comments of P2P * fix * Minor changes * Minor changes * Minor Changes * Minor changes * Minor changes * dotnet format * Minor changes * Minor changes * Additional comments * Minor changes * Reverting variable change * Dotnet format * Minor changes * Minor changes * Minor changes * Minor changes * Minor changes --- src/neo/Helper.cs | 8 ++++++ src/neo/Network/P2P/LocalNode.cs | 28 ++++++++++++++++++ src/neo/Network/P2P/Message.cs | 4 +++ src/neo/Network/P2P/Peer.cs | 39 ++++++++++++++++++++++++++ src/neo/Network/P2P/ProtocolHandler.cs | 24 ++++++++++++++++ src/neo/Network/P2P/RemoteNode.cs | 10 +++++++ src/neo/Network/P2P/TaskManager.cs | 14 +++++++++ 7 files changed, 127 insertions(+) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index ebafdc16cc..b8a0a7f79e 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -237,6 +237,10 @@ public static ulong ToTimestampMS(this DateTime time) return (ulong)(time.ToUniversalTime() - unixEpoch).TotalMilliseconds; } + /// + /// Checks if address is IPv4 Maped to IPv6 format, if so, Map to IPv4. + /// Otherwise, return current address. + /// internal static IPAddress Unmap(this IPAddress address) { if (address.IsIPv4MappedToIPv6) @@ -244,6 +248,10 @@ internal static IPAddress Unmap(this IPAddress address) return address; } + /// + /// Checks if IPEndPoint is IPv4 Maped to IPv6 format, if so, unmap to IPv4. + /// Otherwise, return current endpoint. + /// internal static IPEndPoint Unmap(this IPEndPoint endPoint) { if (!endPoint.Address.IsIPv4MappedToIPv6) diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index 0efd8a3287..a651d46a16 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -59,11 +59,21 @@ public LocalNode(NeoSystem system) } } + /// + /// Packs a MessageCommand to a full Message with an optional ISerializable payload. + /// Forwards it to . + /// + /// The message command to be packed. + /// Optional payload to be Serialized along the message. private void BroadcastMessage(MessageCommand command, ISerializable payload = null) { BroadcastMessage(Message.Create(command, payload)); } + /// + /// Broadcast a message to all connected nodes, namely . + /// + /// The message to be broadcasted. private void BroadcastMessage(Message message) { Connections.Tell(message); @@ -87,6 +97,10 @@ private static IPEndPoint GetIPEndpointFromHostPort(string hostNameOrAddress, in return new IPEndPoint(ipAddress, port); } + /// + /// Return an amount of random seeds nodes from the default SeedList file defined on . + /// + /// Limit of random seed nodes to be obtained, also limited by the available seeds from file. private static IEnumerable GetIPEndPointsFromSeedList(int seedsToTake) { if (seedsToTake > 0) @@ -122,6 +136,12 @@ public IEnumerable GetUnconnectedPeers() return UnconnectedPeers; } + /// + /// Override of abstract class that is triggered when is empty. + /// Performs a BroadcastMessage with the command `MessageCommand.GetAddr`, which, eventually, tells all known connections. + /// If there are no connected peers it will try with the default, respecting MaxCountFromSeedList limit. + /// + /// The count of peers required protected override void NeedMorePeers(int count) { count = Math.Max(count, MaxCountFromSeedList); @@ -131,6 +151,8 @@ protected override void NeedMorePeers(int count) } else { + // Will call AddPeers with default SeedList set cached on . + // It will try to add those, sequentially, to the list of currently uncconected ones. AddPeers(GetIPEndPointsFromSeedList(count)); } } @@ -157,6 +179,12 @@ protected override void OnReceive(object message) } } + /// + /// For Transaction type of IInventory, it will tell Transaction to the actor of Consensus. + /// Otherwise, tell the inventory to the actor of Blockchain. + /// There are, currently, three implementations of IInventory: TX, Block and ConsensusPayload. + /// + /// The inventory to be relayed. private void OnRelay(IInventory inventory) { if (inventory is Transaction transaction) diff --git a/src/neo/Network/P2P/Message.cs b/src/neo/Network/P2P/Message.cs index 2e5c1f8258..2f395ce01f 100644 --- a/src/neo/Network/P2P/Message.cs +++ b/src/neo/Network/P2P/Message.cs @@ -14,6 +14,10 @@ public class Message : ISerializable private const int CompressionMinSize = 128; private const int CompressionThreshold = 64; + /// + /// Flags that represents whether a message is compressed. + /// 0 for None, 1 for Compressed. + /// public MessageFlags Flags; public MessageCommand Command; public ISerializable Payload; diff --git a/src/neo/Network/P2P/Peer.cs b/src/neo/Network/P2P/Peer.cs index bc852fb8fa..8e7eb21bd2 100644 --- a/src/neo/Network/P2P/Peer.cs +++ b/src/neo/Network/P2P/Peer.cs @@ -36,8 +36,19 @@ private class WsConnected { public WebSocket Socket; public IPEndPoint Remote; p private static readonly HashSet localAddresses = new HashSet(); private readonly Dictionary ConnectedAddresses = new Dictionary(); + /// + /// A dictionary that stores the connected nodes. + /// protected readonly ConcurrentDictionary ConnectedPeers = new ConcurrentDictionary(); + /// + /// An ImmutableHashSet that stores the Peers received: 1) from other nodes or 2) from default file. + /// If the number of desired connections is not enough, first try to connect with the peers from this set. + /// protected ImmutableHashSet UnconnectedPeers = ImmutableHashSet.Empty; + /// + /// When a TCP connection request is sent to a peer, the peer will be added to the ImmutableHashSet. + /// If a Tcp.Connected or a Tcp.CommandFailed (with TCP.Command of type Tcp.Connect) is received, the related peer will be removed. + /// protected ImmutableHashSet ConnectingPeers = ImmutableHashSet.Empty; protected HashSet TrustedIpAddresses { get; } = new HashSet(); @@ -63,10 +74,16 @@ static Peer() localAddresses.UnionWith(NetworkInterface.GetAllNetworkInterfaces().SelectMany(p => p.GetIPProperties().UnicastAddresses).Select(p => p.Address.Unmap())); } + /// + /// Tries to add a set of peers to the immutable ImmutableHashSet of UnconnectedPeers. + /// + /// Peers that the method will try to add (union) to (with) UnconnectedPeers. protected void AddPeers(IEnumerable peers) { if (UnconnectedPeers.Count < UnconnectedMax) { + // Do not select peers to be added that are already on the ConnectedPeers + // If the address is the same, the ListenerTcpPort should be different peers = peers.Where(p => (p.Port != ListenerTcpPort || !localAddresses.Contains(p.Address)) && !ConnectedPeers.Values.Contains(p)); ImmutableInterlocked.Update(ref UnconnectedPeers, p => p.Union(peers)); } @@ -75,9 +92,11 @@ protected void AddPeers(IEnumerable peers) protected void ConnectToPeer(IPEndPoint endPoint, bool isTrusted = false) { endPoint = endPoint.Unmap(); + // If the address is the same, the ListenerTcpPort should be different, otherwise, return if (endPoint.Port == ListenerTcpPort && localAddresses.Contains(endPoint.Address)) return; if (isTrusted) TrustedIpAddresses.Add(endPoint.Address); + // If connections with the peer greater than or equal to MaxConnectionsPerAddress, return. if (ConnectedAddresses.TryGetValue(endPoint.Address, out int count) && count >= MaxConnectionsPerAddress) return; if (ConnectedPeers.Values.Contains(endPoint)) return; @@ -96,6 +115,10 @@ private static bool IsIntranetAddress(IPAddress address) return (value & 0xff000000) == 0x0a000000 || (value & 0xff000000) == 0x7f000000 || (value & 0xfff00000) == 0xac100000 || (value & 0xffff0000) == 0xc0a80000 || (value & 0xffff0000) == 0xa9fe0000; } + /// + /// Abstract method for asking for more peers. Currently triggered when UnconnectedPeers is empty. + /// + /// Number of peers that are being requested. protected abstract void NeedMorePeers(int count); protected override void OnReceive(object message) @@ -141,6 +164,7 @@ private void OnStart(ChannelsConfig config) MaxConnections = config.MaxConnections; MaxConnectionsPerAddress = config.MaxConnectionsPerAddress; + // schedule time to trigger `OnTimer` event every TimerMillisecondsInterval ms timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(0, 5000, Context.Self, new Timer(), ActorRefs.NoSender); if ((ListenerTcpPort > 0 || ListenerWsPort > 0) && localAddresses.All(p => !p.IsIPv4MappedToIPv6 || IsIntranetAddress(p)) @@ -174,6 +198,13 @@ private void OnStart(ChannelsConfig config) } } + /// + /// Will be triggered when a Tcp.Connected message is received. + /// If the conditions are met, the remote endpoint will be added to ConnectedPeers. + /// Increase the connection number with the remote endpoint by one. + /// + /// The remote endpoint of TCP connection. + /// The local endpoint of TCP connection. private void OnTcpConnected(IPEndPoint remote, IPEndPoint local) { ImmutableInterlocked.Update(ref ConnectingPeers, p => p.Remove(remote)); @@ -198,6 +229,11 @@ private void OnTcpConnected(IPEndPoint remote, IPEndPoint local) } } + /// + /// Will be triggered when a Tcp.CommandFailed message is received. + /// If it's a Tcp.Connect command, remove the related endpoint from ConnectingPeers. + /// + /// Tcp.Command message/event. private void OnTcpCommandFailed(Tcp.Command cmd) { switch (cmd) @@ -223,7 +259,10 @@ private void OnTerminated(IActorRef actorRef) private void OnTimer() { + // Check if the number of desired connections is already enough if (ConnectedPeers.Count >= MinDesiredConnections) return; + + // If there aren't available UnconnectedPeers, it triggers an abstract implementation of NeedMorePeers if (UnconnectedPeers.Count == 0) NeedMorePeers(MinDesiredConnections - ConnectedPeers.Count); IPEndPoint[] endpoints = UnconnectedPeers.Take(MinDesiredConnections - ConnectedPeers.Count).ToArray(); diff --git a/src/neo/Network/P2P/ProtocolHandler.cs b/src/neo/Network/P2P/ProtocolHandler.cs index 12aaae48d9..cecbb02796 100644 --- a/src/neo/Network/P2P/ProtocolHandler.cs +++ b/src/neo/Network/P2P/ProtocolHandler.cs @@ -174,6 +174,11 @@ private void OnFilterLoadMessageReceived(FilterLoadPayload payload) Context.Parent.Tell(new SetFilter { Filter = bloom_filter }); } + /// + /// Will be triggered when a MessageCommand.GetAddr message is received. + /// Randomly select nodes from the local RemoteNodes and tells to RemoteNode actors a MessageCommand.Addr message. + /// The message contains a list of networkAddresses from those selected random peers. + /// private void OnGetAddrMessageReceived() { Random rand = new Random(); @@ -187,9 +192,16 @@ private void OnGetAddrMessageReceived() Context.Parent.Tell(Message.Create(MessageCommand.Addr, AddrPayload.Create(networkAddresses))); } + /// + /// Will be triggered when a MessageCommand.GetBlocks message is received. + /// Tell the specified number of blocks' hashes starting with the requested HashStart until payload.Count or MaxHashesCount + /// Responses are sent to RemoteNode actor as MessageCommand.Inv Message. + /// + /// 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; @@ -227,6 +239,12 @@ private void OnGetBlockDataMessageReceived(GetBlockDataPayload payload) } } + /// + /// Will be triggered when a MessageCommand.GetData message is received. + /// The payload includes an array of hash values. + /// For different payload.Type (Tx, Block, Consensus), get the corresponding (Txs, Blocks, Consensus) and tell them to RemoteNode actor. + /// + /// The payload containing the requested information. private void OnGetDataMessageReceived(InvPayload payload) { UInt256[] hashes = payload.Hashes.Where(p => sentHashes.Add(p)).ToArray(); @@ -262,6 +280,12 @@ private void OnGetDataMessageReceived(InvPayload payload) } } + /// + /// Will be triggered when a MessageCommand.GetHeaders message is received. + /// Tell the specified number of blocks' headers starting with the requested HashStart 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 Hash and number of blocks' headers requested. private void OnGetHeadersMessageReceived(GetBlocksPayload payload) { UInt256 hash = payload.HashStart; diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index 5d4e712729..44bb2ef107 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -50,6 +50,12 @@ public RemoteNode(NeoSystem system, object connection, IPEndPoint remote, IPEndP SendMessage(Message.Create(MessageCommand.Version, VersionPayload.Create(LocalNode.Nonce, LocalNode.UserAgent, capabilities.ToArray()))); } + /// + /// It defines the message queue to be used for dequeuing. + /// If the high-priority message queue is not empty, choose the high-priority message queue. + /// Otherwise, choose the low-priority message queue. + /// Finally, it sends the first message of the queue. + /// private void CheckMessageQueue() { if (!verack || !ack) return; @@ -67,6 +73,10 @@ private void EnqueueMessage(MessageCommand command, ISerializable payload = null EnqueueMessage(Message.Create(command, payload)); } + /// + /// Add message to high priority queue or low priority queue depending on the message type. + /// + /// The message to be added. private void EnqueueMessage(Message message) { bool is_single = false; diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 4fb1a2fd09..818973ea4b 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -27,7 +27,11 @@ private class Timer { } private readonly NeoSystem system; private const int MaxConncurrentTasks = 3; + private const int PingCoolingOffPeriod = 60; // in secconds. + /// + /// A set of known hashes, of inventories or payloads, already received. + /// private readonly FIFOSet knownHashes; private readonly Dictionary globalTasks = new Dictionary(); private readonly Dictionary sessions = new Dictionary(); @@ -55,16 +59,20 @@ 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 < Blockchain.Singleton.HeaderHeight) { RequestTasks(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 => globalTasks.ContainsKey(p))); + // Remove those that are already in process by other sessions hashes.Remove(globalTasks); if (hashes.Count == 0) { @@ -72,6 +80,7 @@ private void OnNewTasks(InvPayload payload) return; } + // Update globalTasks with the ones that will be requested within this current session foreach (UInt256 hash in hashes) { IncrementGlobalTask(hash); @@ -214,9 +223,11 @@ public static Props Props(NeoSystem system) private void RequestTasks(TaskSession session) { if (session.HasTask) return; + // If there are pending tasks of InventoryType.Block we should process them if (session.AvailableTasks.Count > 0) { 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 => Blockchain.Singleton.ContainsBlock(p)); HashSet hashes = new HashSet(session.AvailableTasks); if (hashes.Count > 0) @@ -234,6 +245,9 @@ private void RequestTasks(TaskSession session) return; } } + + // 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 || globalTasks[HeaderTaskHash] < MaxConncurrentTasks) && Blockchain.Singleton.HeaderHeight < session.LastBlockIndex) { session.Tasks[HeaderTaskHash] = DateTime.UtcNow; From 8ec8a20599c40e5fe41302954f41bdfe83b6d147 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 11 Dec 2019 16:19:53 +0800 Subject: [PATCH 178/305] More optimizations about Span (#1352) --- src/neo/Cryptography/Crypto.cs | 8 +++--- src/neo/SmartContract/InteropService.NEO.cs | 4 +-- src/neo/SmartContract/InteropService.cs | 25 ++++++++----------- .../SmartContract/Native/PolicyContract.cs | 4 +-- .../SmartContract/Native/Tokens/NeoToken.cs | 4 +-- .../SmartContract/Native/Tokens/Nep5Token.cs | 6 ++--- src/neo/UInt160.cs | 2 +- src/neo/Wallets/Helper.cs | 2 +- 8 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/neo/Cryptography/Crypto.cs b/src/neo/Cryptography/Crypto.cs index bef425a044..ee7b1418c8 100644 --- a/src/neo/Cryptography/Crypto.cs +++ b/src/neo/Cryptography/Crypto.cs @@ -32,13 +32,13 @@ public static byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey) } } - public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, byte[] pubkey) + public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ReadOnlySpan pubkey) { if (pubkey.Length == 33 && (pubkey[0] == 0x02 || pubkey[0] == 0x03)) { try { - pubkey = ECC.ECPoint.DecodePoint(pubkey, ECC.ECCurve.Secp256r1).EncodePoint(false)[1..]; + pubkey = ECC.ECPoint.DecodePoint(pubkey, ECC.ECCurve.Secp256r1).EncodePoint(false).AsSpan(1); } catch { @@ -58,8 +58,8 @@ public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan engine.ScriptContainer.GetHashData(), _ => item0.GetSpan() }; - byte[] pubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + ReadOnlySpan pubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); ReadOnlySpan signature = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); try { @@ -162,7 +162,7 @@ private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) private static bool Account_IsStandard(ApplicationEngine engine) { - UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray()); + UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan()); ContractState contract = engine.Snapshot.Contracts.TryGet(hash); bool isStandard = contract is null || contract.Script.IsStandardContract(); engine.CurrentContext.EvaluationStack.Push(isStandard); diff --git a/src/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs index cd30deca8f..def6184619 100644 --- a/src/neo/SmartContract/InteropService.cs +++ b/src/neo/SmartContract/InteropService.cs @@ -13,7 +13,6 @@ using System.Numerics; using System.Text; using Array = Neo.VM.Types.Array; -using Boolean = Neo.VM.Types.Boolean; namespace Neo.SmartContract { @@ -77,10 +76,8 @@ private static bool CheckItemForNotification(StackItem state) items_unchecked.Enqueue(item); } break; - case Boolean _: - case ByteArray _: - case Integer _: - size += state.GetByteLength(); + case PrimitiveType primitive: + size += primitive.GetByteLength(); break; case Null _: break; @@ -231,7 +228,7 @@ private static bool Runtime_CheckWitness(ApplicationEngine engine) ReadOnlySpan hashOrPubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); bool result; if (hashOrPubkey.Length == 20) - result = CheckWitness(engine, new UInt160(hashOrPubkey.ToArray())); + result = CheckWitness(engine, new UInt160(hashOrPubkey)); else if (hashOrPubkey.Length == 33) result = CheckWitness(engine, ECPoint.DecodePoint(hashOrPubkey, ECCurve.Secp256r1)); else @@ -250,7 +247,7 @@ private static bool Runtime_Notify(ApplicationEngine engine) private static bool Runtime_Log(ApplicationEngine engine) { - byte[] state = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + ReadOnlySpan state = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); if (state.Length > MaxNotificationSize) return false; string message = Encoding.UTF8.GetString(state); engine.SendLog(engine.CurrentScriptHash, message); @@ -287,7 +284,7 @@ private static bool Runtime_GetNotifications(ApplicationEngine engine) IEnumerable notifications = engine.Notifications; if (!item.IsNull) // must filter by scriptHash { - var hash = new UInt160(item.GetSpan().ToArray()); + var hash = new UInt160(item.GetSpan()); notifications = notifications.Where(p => p.ScriptHash == hash); } @@ -334,7 +331,7 @@ private static bool Blockchain_GetHeight(ApplicationEngine engine) private static bool Blockchain_GetBlock(ApplicationEngine engine) { - byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + ReadOnlySpan data = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); UInt256 hash; if (data.Length <= 5) hash = Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); @@ -353,7 +350,7 @@ private static bool Blockchain_GetBlock(ApplicationEngine engine) private static bool Blockchain_GetTransaction(ApplicationEngine engine) { - byte[] hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + ReadOnlySpan hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); Transaction tx = engine.Snapshot.GetTransaction(new UInt256(hash)); if (tx == null) engine.CurrentContext.EvaluationStack.Push(StackItem.Null); @@ -364,7 +361,7 @@ private static bool Blockchain_GetTransaction(ApplicationEngine engine) private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine) { - byte[] hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + ReadOnlySpan hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); var tx = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); engine.CurrentContext.EvaluationStack.Push(tx != null ? new BigInteger(tx.BlockIndex) : BigInteger.MinusOne); return true; @@ -372,7 +369,7 @@ private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine) private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) { - byte[] data = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + ReadOnlySpan data = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); UInt256 hash; if (data.Length <= 5) hash = Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); @@ -402,7 +399,7 @@ private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) private static bool Blockchain_GetContract(ApplicationEngine engine) { - UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray()); + UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan()); ContractState contract = engine.Snapshot.Contracts.TryGet(hash); if (contract == null) engine.CurrentContext.EvaluationStack.Push(StackItem.Null); @@ -470,7 +467,7 @@ private static bool Contract_Call(ApplicationEngine engine) { StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop(); - ContractState contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractHash.GetSpan().ToArray())); + ContractState contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractHash.GetSpan())); if (contract is null) return false; StackItem method = engine.CurrentContext.EvaluationStack.Pop(); diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index d51e1a75b3..e9b7984bce 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -145,7 +145,7 @@ private StackItem SetFeePerByte(ApplicationEngine engine, Array args) private StackItem BlockAccount(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; - UInt160 account = new UInt160(args[0].GetSpan().ToArray()); + UInt160 account = new UInt160(args[0].GetSpan()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; SortedSet accounts = new SortedSet(storage.Value.AsSerializableArray()); @@ -159,7 +159,7 @@ private StackItem BlockAccount(ApplicationEngine engine, Array args) private StackItem UnblockAccount(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; - UInt160 account = new UInt160(args[0].GetSpan().ToArray()); + UInt160 account = new UInt160(args[0].GetSpan()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; SortedSet accounts = new SortedSet(storage.Value.AsSerializableArray()); diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 3e8f79ad96..546cb17032 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -119,7 +119,7 @@ protected override bool OnPersist(ApplicationEngine engine) [ContractMethod(0_03000000, ContractParameterType.Integer, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "account", "end" }, SafeMethod = true)] private StackItem UnclaimedGas(ApplicationEngine engine, Array args) { - UInt160 account = new UInt160(args[0].GetSpan().ToArray()); + UInt160 account = new UInt160(args[0].GetSpan()); uint end = (uint)args[1].GetBigInteger(); return UnclaimedGas(engine.Snapshot, account, end); } @@ -153,7 +153,7 @@ private bool RegisterValidator(StoreView snapshot, ECPoint pubkey) [ContractMethod(5_00000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" })] private StackItem Vote(ApplicationEngine engine, Array args) { - UInt160 account = new UInt160(args[0].GetSpan().ToArray()); + UInt160 account = new UInt160(args[0].GetSpan()); ECPoint[] pubkeys = ((Array)args[1]).Select(p => p.GetSpan().AsSerializable()).ToArray(); if (!InteropService.CheckWitness(engine, account)) return false; StorageKey key_account = CreateAccountKey(account); diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index 0ee3952350..fa9d779ace 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -149,7 +149,7 @@ public virtual BigInteger TotalSupply(StoreView snapshot) [ContractMethod(0_01000000, ContractParameterType.Integer, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" }, SafeMethod = true)] protected StackItem BalanceOf(ApplicationEngine engine, Array args) { - return BalanceOf(engine.Snapshot, new UInt160(args[0].GetSpan().ToArray())); + return BalanceOf(engine.Snapshot, new UInt160(args[0].GetSpan())); } public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) @@ -163,8 +163,8 @@ public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) [ContractMethod(0_08000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" })] protected StackItem Transfer(ApplicationEngine engine, Array args) { - UInt160 from = new UInt160(args[0].GetSpan().ToArray()); - UInt160 to = new UInt160(args[1].GetSpan().ToArray()); + UInt160 from = new UInt160(args[0].GetSpan()); + UInt160 to = new UInt160(args[1].GetSpan()); BigInteger amount = args[2].GetBigInteger(); return Transfer(engine, from, to, amount); } diff --git a/src/neo/UInt160.cs b/src/neo/UInt160.cs index e2ce4e5bcf..69c2f85ed1 100644 --- a/src/neo/UInt160.cs +++ b/src/neo/UInt160.cs @@ -22,7 +22,7 @@ public UInt160() { } - public unsafe UInt160(byte[] value) + public unsafe UInt160(ReadOnlySpan value) { fixed (ulong* p = &value1) { diff --git a/src/neo/Wallets/Helper.cs b/src/neo/Wallets/Helper.cs index c5b203df90..712c2585de 100644 --- a/src/neo/Wallets/Helper.cs +++ b/src/neo/Wallets/Helper.cs @@ -28,7 +28,7 @@ public static UInt160 ToScriptHash(this string address) throw new FormatException(); if (data[0] != ProtocolSettings.Default.AddressVersion) throw new FormatException(); - return new UInt160(data[1..]); + return new UInt160(data.AsSpan(1)); } internal static byte[] XOR(byte[] x, byte[] y) From d472578022b782cd7466f99d574862f057779ac6 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 12 Dec 2019 18:59:43 +0800 Subject: [PATCH 179/305] Restrict the serialization (#1353) --- src/neo/IO/Json/JObject.cs | 19 +-- src/neo/SmartContract/InteropService.NEO.cs | 32 +++-- src/neo/SmartContract/InteropService.cs | 6 +- src/neo/SmartContract/JsonSerializer.cs | 69 ++++++++++ .../Native/Tokens/Nep5AccountState.cs | 2 +- src/neo/SmartContract/StackItemSerializer.cs | 8 +- .../SmartContract/UT_StackItemSerializer.cs | 35 +++--- .../SmartContract/UT_Syscalls.cs | 118 ++++++++++++++++++ 8 files changed, 247 insertions(+), 42 deletions(-) diff --git a/src/neo/IO/Json/JObject.cs b/src/neo/IO/Json/JObject.cs index 11e477704d..9e1f1d62e0 100644 --- a/src/neo/IO/Json/JObject.cs +++ b/src/neo/IO/Json/JObject.cs @@ -138,12 +138,7 @@ private static JObject ReadObject(ref Utf8JsonReader reader) throw new FormatException(); } - public override string ToString() - { - return ToString(false); - } - - public string ToString(bool indented) + public byte[] ToByteArray(bool indented) { using MemoryStream ms = new MemoryStream(); using Utf8JsonWriter writer = new Utf8JsonWriter(ms, new JsonWriterOptions @@ -153,7 +148,17 @@ public string ToString(bool indented) }); Write(writer); writer.Flush(); - return Encoding.UTF8.GetString(ms.ToArray()); + return ms.ToArray(); + } + + public override string ToString() + { + return ToString(false); + } + + public string ToString(bool indented) + { + return Encoding.UTF8.GetString(ToByteArray(indented)); } public virtual T TryGetEnum(T defaultValue = default, bool ignoreCase = false) where T : Enum diff --git a/src/neo/SmartContract/InteropService.NEO.cs b/src/neo/SmartContract/InteropService.NEO.cs index 1900512cba..5ba3675090 100644 --- a/src/neo/SmartContract/InteropService.NEO.cs +++ b/src/neo/SmartContract/InteropService.NEO.cs @@ -376,21 +376,33 @@ private static bool Iterator_Concat(ApplicationEngine engine) private static bool Json_Deserialize(ApplicationEngine engine) { - var json = engine.CurrentContext.EvaluationStack.Pop().GetString(); - var obj = JObject.Parse(json, 10); - var item = JsonSerializer.Deserialize(obj, engine.ReferenceCounter); - - engine.CurrentContext.EvaluationStack.Push(item); - return true; + var json = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); + try + { + var obj = JObject.Parse(json, 10); + var item = JsonSerializer.Deserialize(obj, engine.ReferenceCounter); + engine.CurrentContext.EvaluationStack.Push(item); + return true; + } + catch + { + return false; + } } private static bool Json_Serialize(ApplicationEngine engine) { var item = engine.CurrentContext.EvaluationStack.Pop(); - var json = JsonSerializer.Serialize(item); - - engine.CurrentContext.EvaluationStack.Push(json.ToString()); - return true; + try + { + var json = JsonSerializer.SerializeToByteArray(item, engine.MaxItemSize); + engine.CurrentContext.EvaluationStack.Push(json); + return true; + } + catch + { + return false; + } } } } diff --git a/src/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs index def6184619..93583f4851 100644 --- a/src/neo/SmartContract/InteropService.cs +++ b/src/neo/SmartContract/InteropService.cs @@ -265,14 +265,12 @@ private static bool Runtime_Serialize(ApplicationEngine engine) byte[] serialized; try { - serialized = StackItemSerializer.Serialize(engine.CurrentContext.EvaluationStack.Pop()); + serialized = StackItemSerializer.Serialize(engine.CurrentContext.EvaluationStack.Pop(), engine.MaxItemSize); } - catch (NotSupportedException) + catch { return false; } - if (serialized.Length > engine.MaxItemSize) - return false; engine.CurrentContext.EvaluationStack.Push(serialized); return true; } diff --git a/src/neo/SmartContract/JsonSerializer.cs b/src/neo/SmartContract/JsonSerializer.cs index b0ed09383c..8bf3e6758f 100644 --- a/src/neo/SmartContract/JsonSerializer.cs +++ b/src/neo/SmartContract/JsonSerializer.cs @@ -2,8 +2,11 @@ using Neo.VM; using Neo.VM.Types; using System; +using System.Collections; +using System.IO; using System.Linq; using System.Numerics; +using System.Text.Json; using Array = Neo.VM.Types.Array; using Boolean = Neo.VM.Types.Boolean; @@ -61,6 +64,72 @@ public static JObject Serialize(StackItem item) } } + public static byte[] SerializeToByteArray(StackItem item, uint maxSize) + { + using MemoryStream ms = new MemoryStream(); + using Utf8JsonWriter writer = new Utf8JsonWriter(ms, new JsonWriterOptions + { + Indented = false, + SkipValidation = false + }); + Stack stack = new Stack(); + stack.Push(item); + while (stack.Count > 0) + { + switch (stack.Pop()) + { + case Array array: + writer.WriteStartArray(); + stack.Push(JsonTokenType.EndArray); + for (int i = array.Count - 1; i >= 0; i--) + stack.Push(array[i]); + break; + case JsonTokenType.EndArray: + writer.WriteEndArray(); + break; + case ByteArray buffer: + writer.WriteStringValue(Convert.ToBase64String(buffer.GetSpan())); + break; + case Integer num: + { + var integer = num.GetBigInteger(); + if (integer > JNumber.MAX_SAFE_INTEGER || integer < JNumber.MIN_SAFE_INTEGER) + throw new InvalidOperationException(); + writer.WriteNumberValue((double)num.GetBigInteger()); + break; + } + case Boolean boolean: + writer.WriteBooleanValue(boolean.ToBoolean()); + break; + case Map map: + writer.WriteStartObject(); + stack.Push(JsonTokenType.EndObject); + foreach (var pair in map.Reverse()) + { + stack.Push(pair.Value); + stack.Push(pair.Key); + stack.Push(JsonTokenType.PropertyName); + } + break; + case JsonTokenType.EndObject: + writer.WriteEndObject(); + break; + case JsonTokenType.PropertyName: + writer.WritePropertyName(((PrimitiveType)stack.Pop()).GetSpan()); + break; + case Null _: + writer.WriteNullValue(); + break; + default: + throw new InvalidOperationException(); + } + if (ms.Position > maxSize) throw new InvalidOperationException(); + } + writer.Flush(); + if (ms.Position > maxSize) throw new InvalidOperationException(); + return ms.ToArray(); + } + /// /// Convert json object to stack item /// diff --git a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs index a7bd3fffc0..b42e93814b 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs @@ -29,7 +29,7 @@ protected virtual void FromStruct(Struct @struct) public byte[] ToByteArray() { - return StackItemSerializer.Serialize(ToStruct()); + return StackItemSerializer.Serialize(ToStruct(), 4096); } protected virtual Struct ToStruct() diff --git a/src/neo/SmartContract/StackItemSerializer.cs b/src/neo/SmartContract/StackItemSerializer.cs index a7f59ad974..8301afcb60 100644 --- a/src/neo/SmartContract/StackItemSerializer.cs +++ b/src/neo/SmartContract/StackItemSerializer.cs @@ -116,16 +116,16 @@ private static StackItem Deserialize(BinaryReader reader, uint maxItemSize, Refe return stack_temp.Peek(); } - public static byte[] Serialize(StackItem item) + public static byte[] Serialize(StackItem item, uint maxSize) { using MemoryStream ms = new MemoryStream(); using BinaryWriter writer = new BinaryWriter(ms); - Serialize(item, writer); + Serialize(item, writer, maxSize); writer.Flush(); return ms.ToArray(); } - private static void Serialize(StackItem item, BinaryWriter writer) + private static void Serialize(StackItem item, BinaryWriter writer, uint maxSize) { List serialized = new List(); Stack unserialized = new Stack(); @@ -177,6 +177,8 @@ private static void Serialize(StackItem item, BinaryWriter writer) writer.Write((byte)StackItemType.Null); break; } + if (writer.BaseStream.Position > maxSize) + throw new InvalidOperationException(); } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs b/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs index 80acd2082d..75ab173460 100644 --- a/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs +++ b/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs @@ -11,33 +11,34 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_StackItemSerializer { + private const int MaxItemSize = 1024 * 1024; [TestMethod] public void TestSerialize() { - byte[] result1 = StackItemSerializer.Serialize(new byte[5]); + byte[] result1 = StackItemSerializer.Serialize(new byte[5], MaxItemSize); byte[] expectedArray1 = new byte[] { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray1), Encoding.Default.GetString(result1)); - byte[] result2 = StackItemSerializer.Serialize(true); + byte[] result2 = StackItemSerializer.Serialize(true, MaxItemSize); byte[] expectedArray2 = new byte[] { 0x01, 0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray2), Encoding.Default.GetString(result2)); - byte[] result3 = StackItemSerializer.Serialize(1); + byte[] result3 = StackItemSerializer.Serialize(1, MaxItemSize); byte[] expectedArray3 = new byte[] { 0x02, 0x01, 0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray3), Encoding.Default.GetString(result3)); StackItem stackItem4 = new InteropInterface(new object()); - Action action4 = () => StackItemSerializer.Serialize(stackItem4); + Action action4 = () => StackItemSerializer.Serialize(stackItem4, MaxItemSize); action4.Should().Throw(); - byte[] result5 = StackItemSerializer.Serialize(1); + byte[] result5 = StackItemSerializer.Serialize(1, MaxItemSize); byte[] expectedArray5 = new byte[] { 0x02, 0x01, 0x01 }; @@ -46,7 +47,7 @@ public void TestSerialize() List list6 = new List { 1 }; StackItem stackItem62 = new VM.Types.Array(list6); - byte[] result6 = StackItemSerializer.Serialize(stackItem62); + byte[] result6 = StackItemSerializer.Serialize(stackItem62, MaxItemSize); byte[] expectedArray6 = new byte[] { 0x80,0x01,0x02,0x01,0x01 }; @@ -54,7 +55,7 @@ public void TestSerialize() List list7 = new List { 1 }; StackItem stackItem72 = new Struct(list7); - byte[] result7 = StackItemSerializer.Serialize(stackItem72); + byte[] result7 = StackItemSerializer.Serialize(stackItem72, MaxItemSize); byte[] expectedArray7 = new byte[] { 0x81,0x01,0x02,0x01,0x01 }; @@ -62,7 +63,7 @@ public void TestSerialize() Dictionary list8 = new Dictionary { [2] = 1 }; StackItem stackItem82 = new Map(list8); - byte[] result8 = StackItemSerializer.Serialize(stackItem82); + byte[] result8 = StackItemSerializer.Serialize(stackItem82, MaxItemSize); byte[] expectedArray8 = new byte[] { 0x82,0x01,0x02,0x01,0x02,0x02,0x01,0x01 }; @@ -70,12 +71,12 @@ public void TestSerialize() Map stackItem91 = new Map(); stackItem91[1] = stackItem91; - Action action9 = () => StackItemSerializer.Serialize(stackItem91); + Action action9 = () => StackItemSerializer.Serialize(stackItem91, MaxItemSize); action9.Should().Throw(); VM.Types.Array stackItem10 = new VM.Types.Array(); stackItem10.Add(stackItem10); - Action action10 = () => StackItemSerializer.Serialize(stackItem10); + Action action10 = () => StackItemSerializer.Serialize(stackItem10, MaxItemSize); action10.Should().Throw(); } @@ -83,42 +84,42 @@ public void TestSerialize() public void TestDeserializeStackItem() { StackItem stackItem1 = new ByteArray(new byte[5]); - byte[] byteArray1 = StackItemSerializer.Serialize(stackItem1); + byte[] byteArray1 = StackItemSerializer.Serialize(stackItem1, MaxItemSize); StackItem result1 = StackItemSerializer.Deserialize(byteArray1, (uint)byteArray1.Length); Assert.AreEqual(stackItem1, result1); StackItem stackItem2 = new VM.Types.Boolean(true); - byte[] byteArray2 = StackItemSerializer.Serialize(stackItem2); + byte[] byteArray2 = StackItemSerializer.Serialize(stackItem2, MaxItemSize); StackItem result2 = StackItemSerializer.Deserialize(byteArray2, (uint)byteArray2.Length); Assert.AreEqual(stackItem2, result2); StackItem stackItem3 = new Integer(1); - byte[] byteArray3 = StackItemSerializer.Serialize(stackItem3); + byte[] byteArray3 = StackItemSerializer.Serialize(stackItem3, MaxItemSize); StackItem result3 = StackItemSerializer.Deserialize(byteArray3, (uint)byteArray3.Length); Assert.AreEqual(stackItem3, result3); - byte[] byteArray4 = StackItemSerializer.Serialize(1); + byte[] byteArray4 = StackItemSerializer.Serialize(1, MaxItemSize); byteArray4[0] = 0x40; Action action4 = () => StackItemSerializer.Deserialize(byteArray4, (uint)byteArray4.Length); action4.Should().Throw(); List list5 = new List { 1 }; StackItem stackItem52 = new VM.Types.Array(list5); - byte[] byteArray5 = StackItemSerializer.Serialize(stackItem52); + byte[] byteArray5 = StackItemSerializer.Serialize(stackItem52, MaxItemSize); StackItem result5 = StackItemSerializer.Deserialize(byteArray5, (uint)byteArray5.Length); Assert.AreEqual(((VM.Types.Array)stackItem52).Count, ((VM.Types.Array)result5).Count); Assert.AreEqual(((VM.Types.Array)stackItem52).GetEnumerator().Current, ((VM.Types.Array)result5).GetEnumerator().Current); List list6 = new List { 1 }; StackItem stackItem62 = new Struct(list6); - byte[] byteArray6 = StackItemSerializer.Serialize(stackItem62); + byte[] byteArray6 = StackItemSerializer.Serialize(stackItem62, MaxItemSize); StackItem result6 = StackItemSerializer.Deserialize(byteArray6, (uint)byteArray6.Length); Assert.AreEqual(((Struct)stackItem62).Count, ((Struct)result6).Count); Assert.AreEqual(((Struct)stackItem62).GetEnumerator().Current, ((Struct)result6).GetEnumerator().Current); Dictionary list7 = new Dictionary { [2] = 1 }; StackItem stackItem72 = new Map(list7); - byte[] byteArray7 = StackItemSerializer.Serialize(stackItem72); + byte[] byteArray7 = StackItemSerializer.Serialize(stackItem72, MaxItemSize); StackItem result7 = StackItemSerializer.Deserialize(byteArray7, (uint)byteArray7.Length); Assert.AreEqual(((Map)stackItem72).Count, ((Map)result7).Count); Assert.AreEqual(((Map)stackItem72).Keys.GetEnumerator().Current, ((Map)result7).Keys.GetEnumerator().Current); diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 6485ba0961..e6c0d006c2 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -12,6 +12,12 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_Syscalls { + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + } + [TestMethod] public void System_Blockchain_GetBlock() { @@ -86,6 +92,118 @@ public void System_Blockchain_GetBlock() } } + [TestMethod] + public void Json_Deserialize() + { + // Good + + using (var script = new ScriptBuilder()) + { + script.EmitPush("123"); + script.EmitSysCall(InteropService.Neo_Json_Deserialize); + script.EmitPush("null"); + script.EmitSysCall(InteropService.Neo_Json_Deserialize); + + using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(2, engine.ResultStack.Count); + + Assert.IsTrue(engine.ResultStack.TryPop(out _)); + Assert.IsTrue(engine.ResultStack.TryPop(out var i) && i.GetBigInteger() == 123); + } + } + + // Error 1 - Wrong Json + + using (var script = new ScriptBuilder()) + { + script.EmitPush("***"); + script.EmitSysCall(InteropService.Neo_Json_Deserialize); + + using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.FAULT); + Assert.AreEqual(0, engine.ResultStack.Count); + } + } + + // Error 2 - No decimals + + using (var script = new ScriptBuilder()) + { + script.EmitPush("123.45"); + script.EmitSysCall(InteropService.Neo_Json_Deserialize); + + using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.FAULT); + Assert.AreEqual(0, engine.ResultStack.Count); + } + } + } + + [TestMethod] + public void Json_Serialize() + { + // Good + + using (var script = new ScriptBuilder()) + { + script.EmitPush(5); + script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.Emit(OpCode.PUSH0); + script.Emit(OpCode.NOT); + script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.EmitPush("test"); + script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.Emit(OpCode.PUSHNULL); + script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.Emit(OpCode.NEWMAP); + script.Emit(OpCode.DUP); + script.EmitPush("key"); + script.EmitPush("value"); + script.Emit(OpCode.SETITEM); + script.EmitSysCall(InteropService.Neo_Json_Serialize); + + using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(5, engine.ResultStack.Count); + + Assert.IsTrue(engine.ResultStack.TryPop(out var m) && m.GetString() == "{\"key\":\"dmFsdWU=\"}"); + Assert.IsTrue(engine.ResultStack.TryPop(out var n) && n.GetString() == "null"); + Assert.IsTrue(engine.ResultStack.TryPop(out var s) && s.GetString() == "\"dGVzdA==\""); + Assert.IsTrue(engine.ResultStack.TryPop(out var b) && b.GetString() == "true"); + Assert.IsTrue(engine.ResultStack.TryPop(out var i) && i.GetString() == "5"); + } + } + + // Error + + using (var script = new ScriptBuilder()) + { + script.EmitSysCall(InteropService.System_Storage_GetContext); + script.EmitSysCall(InteropService.Neo_Json_Serialize); + + using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.FAULT); + Assert.AreEqual(0, engine.ResultStack.Count); + } + } + } + [TestMethod] public void System_ExecutionEngine_GetScriptContainer() { From ed6990aa850f8f821497497300bd20f6dfaaef5e Mon Sep 17 00:00:00 2001 From: Ricardo Prado <38396062+lock9@users.noreply.github.com> Date: Fri, 13 Dec 2019 14:55:13 -0300 Subject: [PATCH 180/305] Adding contact information to manifest (#1246) * Adding neo 2 metadata * Double check * Adding website and description * Solution tests * Dotnet format * Using generic extra object * Fix clone * Removing manifest to test push * Removing wrong file, renaming test * Revert "Removing manifest to test push" This reverts commit 4f57da4f757944314dbd2eabac29c0c2f98b62e8. * Update src/neo/SmartContract/Manifest/ContractManifest.cs Co-Authored-By: Erik Zhang * Update src/neo/SmartContract/Manifest/ContractManifest.cs Co-Authored-By: Erik Zhang * Fixing tests * JObject clone * Dotnet format * Null check * Removing unecessary instantiation of primitive types * Removing refenrece check, returning this --- src/neo/IO/Json/JArray.cs | 12 +++++++ src/neo/IO/Json/JBoolean.cs | 5 +++ src/neo/IO/Json/JNumber.cs | 5 +++ src/neo/IO/Json/JObject.cs | 12 +++++++ src/neo/IO/Json/JString.cs | 5 +++ .../Manifest/ContractManifest.cs | 25 ++++++++----- .../Manifest/ContractPermission.cs | 6 ++-- .../Manifest/WildCardContainer.cs | 10 +++--- .../SmartContract/Native/NativeContract.cs | 2 +- tests/neo.UnitTests/IO/Json/UT_JObject.cs | 12 +++++++ .../neo.UnitTests/Ledger/UT_ContractState.cs | 2 +- .../Manifest/UT_ContractManifest.cs | 36 ++++++++++++------- .../Manifest/UT_WildCardContainer.cs | 20 +++++------ .../SmartContract/UT_InteropService.cs | 4 +-- 14 files changed, 113 insertions(+), 43 deletions(-) diff --git a/src/neo/IO/Json/JArray.cs b/src/neo/IO/Json/JArray.cs index 8b7dab524d..9519c98b3e 100644 --- a/src/neo/IO/Json/JArray.cs +++ b/src/neo/IO/Json/JArray.cs @@ -114,6 +114,18 @@ internal override void Write(Utf8JsonWriter writer) writer.WriteEndArray(); } + public override JObject Clone() + { + var cloned = new JArray(); + + foreach (JObject item in items) + { + cloned.Add(item.Clone()); + } + + return cloned; + } + public static implicit operator JArray(JObject[] value) { return new JArray(value); diff --git a/src/neo/IO/Json/JBoolean.cs b/src/neo/IO/Json/JBoolean.cs index fa1554ab56..7cfd7ee025 100644 --- a/src/neo/IO/Json/JBoolean.cs +++ b/src/neo/IO/Json/JBoolean.cs @@ -36,6 +36,11 @@ internal override void Write(Utf8JsonWriter writer) writer.WriteBooleanValue(Value); } + public override JObject Clone() + { + return this; + } + public static implicit operator JBoolean(bool value) { return new JBoolean(value); diff --git a/src/neo/IO/Json/JNumber.cs b/src/neo/IO/Json/JNumber.cs index 4f933aa3ad..165d8138ec 100644 --- a/src/neo/IO/Json/JNumber.cs +++ b/src/neo/IO/Json/JNumber.cs @@ -58,6 +58,11 @@ internal override void Write(Utf8JsonWriter writer) writer.WriteNumberValue(Value); } + public override JObject Clone() + { + return this; + } + public static implicit operator JNumber(double value) { return new JNumber(value); diff --git a/src/neo/IO/Json/JObject.cs b/src/neo/IO/Json/JObject.cs index 9e1f1d62e0..ef681d5410 100644 --- a/src/neo/IO/Json/JObject.cs +++ b/src/neo/IO/Json/JObject.cs @@ -204,5 +204,17 @@ internal virtual void Write(Utf8JsonWriter writer) { return (JString)value; } + + public virtual JObject Clone() + { + var cloned = new JObject(); + + foreach (KeyValuePair pair in Properties) + { + cloned[pair.Key] = pair.Value != null ? pair.Value.Clone() : Null; + } + + return cloned; + } } } diff --git a/src/neo/IO/Json/JString.cs b/src/neo/IO/Json/JString.cs index a54284c3f0..7c625875b3 100644 --- a/src/neo/IO/Json/JString.cs +++ b/src/neo/IO/Json/JString.cs @@ -46,6 +46,11 @@ internal override void Write(Utf8JsonWriter writer) writer.WriteStringValue(Value); } + public override JObject Clone() + { + return this; + } + public static implicit operator JString(Enum value) { return new JString(value.ToString()); diff --git a/src/neo/SmartContract/Manifest/ContractManifest.cs b/src/neo/SmartContract/Manifest/ContractManifest.cs index c376af7d00..112a608030 100644 --- a/src/neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/neo/SmartContract/Manifest/ContractManifest.cs @@ -1,5 +1,6 @@ using Neo.IO; using Neo.IO.Json; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -50,13 +51,18 @@ public class ContractManifest : ISerializable /// The trusts field is an array containing a set of contract hashes or group public keys. It can also be assigned with a wildcard *. If it is a wildcard *, then it means that it trusts any contract. /// If a contract is trusted, the user interface will not give any warnings when called by the contract. /// - public WildCardContainer Trusts { get; set; } + public WildcardContainer Trusts { get; set; } /// /// The safemethods field is an array containing a set of method names. It can also be assigned with a wildcard *. If it is a wildcard *, then it means that all methods of the contract are safe. /// If a method is marked as safe, the user interface will not give any warnings when it is called by any other contract. /// - public WildCardContainer SafeMethods { get; set; } + public WildcardContainer SafeMethods { get; set; } + + /// + /// Custom user data + /// + public JObject Extra { get; set; } /// /// Create Default Contract manifest @@ -77,8 +83,9 @@ public static ContractManifest CreateDefault(UInt160 hash) }, Features = ContractFeatures.NoProperty, Groups = new ContractGroup[0], - SafeMethods = WildCardContainer.Create(), - Trusts = WildCardContainer.Create() + SafeMethods = WildcardContainer.Create(), + Trusts = WildcardContainer.Create(), + Extra = null, }; } @@ -128,6 +135,7 @@ public JObject ToJson() json["permissions"] = Permissions.Select(p => p.ToJson()).ToArray(); json["trusts"] = Trusts.ToJson(); json["safeMethods"] = SafeMethods.ToJson(); + json["extra"] = Extra; return json; } @@ -145,7 +153,8 @@ public ContractManifest Clone() Abi = Abi.Clone(), Permissions = Permissions.Select(p => p.Clone()).ToArray(), Trusts = Trusts, - SafeMethods = SafeMethods + SafeMethods = SafeMethods, + Extra = Extra?.Clone() }; } @@ -171,9 +180,9 @@ private void DeserializeFromJson(JObject json) Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson(u)).ToArray(); Features = ContractFeatures.NoProperty; Permissions = ((JArray)json["permissions"]).Select(u => ContractPermission.FromJson(u)).ToArray(); - Trusts = WildCardContainer.FromJson(json["trusts"], u => UInt160.Parse(u.AsString())); - SafeMethods = WildCardContainer.FromJson(json["safeMethods"], u => u.AsString()); - + Trusts = WildcardContainer.FromJson(json["trusts"], u => UInt160.Parse(u.AsString())); + SafeMethods = WildcardContainer.FromJson(json["safeMethods"], u => u.AsString()); + Extra = json["extra"]; if (json["features"]["storage"].AsBoolean()) Features |= ContractFeatures.HasStorage; if (json["features"]["payable"].AsBoolean()) Features |= ContractFeatures.Payable; } diff --git a/src/neo/SmartContract/Manifest/ContractPermission.cs b/src/neo/SmartContract/Manifest/ContractPermission.cs index ab86157786..f4084eaf0f 100644 --- a/src/neo/SmartContract/Manifest/ContractPermission.cs +++ b/src/neo/SmartContract/Manifest/ContractPermission.cs @@ -19,12 +19,12 @@ public class ContractPermission /// The methods field is an array containing a set of methods to be called. It can also be assigned with a wildcard *. If it is a wildcard *, then it means that any method can be called. /// If a contract invokes a contract or method that is not declared in the manifest at runtime, the invocation will fail. /// - public WildCardContainer Methods { get; set; } + public WildcardContainer Methods { get; set; } public static readonly ContractPermission DefaultPermission = new ContractPermission { Contract = ContractPermissionDescriptor.CreateWildcard(), - Methods = WildCardContainer.CreateWildcard() + Methods = WildcardContainer.CreateWildcard() }; public ContractPermission Clone() @@ -46,7 +46,7 @@ public static ContractPermission FromJson(JObject json) return new ContractPermission { Contract = ContractPermissionDescriptor.FromJson(json["contract"]), - Methods = WildCardContainer.FromJson(json["methods"], u => u.AsString()), + Methods = WildcardContainer.FromJson(json["methods"], u => u.AsString()), }; } diff --git a/src/neo/SmartContract/Manifest/WildCardContainer.cs b/src/neo/SmartContract/Manifest/WildCardContainer.cs index 12dbac60fe..d85f23fbd8 100644 --- a/src/neo/SmartContract/Manifest/WildCardContainer.cs +++ b/src/neo/SmartContract/Manifest/WildCardContainer.cs @@ -6,7 +6,7 @@ namespace Neo.SmartContract.Manifest { - public class WildCardContainer : IReadOnlyList + public class WildcardContainer : IReadOnlyList { private readonly T[] _data; @@ -26,7 +26,7 @@ public class WildCardContainer : IReadOnlyList /// Constructor /// /// Data - private WildCardContainer(T[] data) + private WildcardContainer(T[] data) { _data = data; } @@ -36,15 +36,15 @@ private WildCardContainer(T[] data) /// /// Data /// WildCardContainer - public static WildCardContainer Create(params T[] data) => new WildCardContainer(data); + public static WildcardContainer Create(params T[] data) => new WildcardContainer(data); /// /// Create a wildcard /// /// WildCardContainer - public static WildCardContainer CreateWildcard() => new WildCardContainer(null); + public static WildcardContainer CreateWildcard() => new WildcardContainer(null); - public static WildCardContainer FromJson(JObject json, Func elementSelector) + public static WildcardContainer FromJson(JObject json, Func elementSelector) { switch (json) { diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 80edebd11b..cb2863fda2 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -62,7 +62,7 @@ protected NativeContract() }); } this.Manifest.Abi.Methods = descriptors.ToArray(); - this.Manifest.SafeMethods = WildCardContainer.Create(safeMethods.ToArray()); + this.Manifest.SafeMethods = WildcardContainer.Create(safeMethods.ToArray()); contracts.Add(this); } diff --git a/tests/neo.UnitTests/IO/Json/UT_JObject.cs b/tests/neo.UnitTests/IO/Json/UT_JObject.cs index fe835016f0..f32bf968dc 100644 --- a/tests/neo.UnitTests/IO/Json/UT_JObject.cs +++ b/tests/neo.UnitTests/IO/Json/UT_JObject.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; +using Neo.SmartContract.Manifest; using System; namespace Neo.UnitTests.IO.Json @@ -106,5 +107,16 @@ public void TestGetNull() { JObject.Null.Should().BeNull(); } + + [TestMethod] + public void TestClone() + { + var bobClone = bob.Clone(); + bobClone.Should().NotBeSameAs(bob); + foreach (var key in bobClone.Properties.Keys) + { + bobClone[key].Should().BeEquivalentTo(bob[key]); + } + } } } diff --git a/tests/neo.UnitTests/Ledger/UT_ContractState.cs b/tests/neo.UnitTests/Ledger/UT_ContractState.cs index 097e9c3e04..670f31d757 100644 --- a/tests/neo.UnitTests/Ledger/UT_ContractState.cs +++ b/tests/neo.UnitTests/Ledger/UT_ContractState.cs @@ -84,7 +84,7 @@ public void TestDeserialize() public void TestGetSize() { ISerializable newContract = contract; - newContract.Size.Should().Be(355); + newContract.Size.Should().Be(368); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index 2416c60bd5..6bab91a808 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; +using Neo.IO.Json; using Neo.SmartContract.Manifest; using System.IO; @@ -11,7 +12,7 @@ public class UT_ContractManifest [TestMethod] public void ParseFromJson_Default() { - var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[]}"; + var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); @@ -22,7 +23,7 @@ public void ParseFromJson_Default() [TestMethod] public void ParseFromJson_Features() { - var json = @"{""groups"":[],""features"":{""storage"":true,""payable"":true},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[]}"; + var json = @"{""groups"":[],""features"":{""storage"":true,""payable"":true},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToJson().ToString(), json); @@ -34,7 +35,7 @@ public void ParseFromJson_Features() [TestMethod] public void ParseFromJson_Permissions() { - var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""safeMethods"":[]}"; + var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""safeMethods"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); @@ -44,7 +45,7 @@ public void ParseFromJson_Permissions() new ContractPermission() { Contract = ContractPermissionDescriptor.Create(UInt160.Zero), - Methods = WildCardContainer.Create("method1", "method2") + Methods = WildcardContainer.Create("method1", "method2") } }; Assert.AreEqual(manifest.ToString(), check.ToString()); @@ -53,31 +54,31 @@ public void ParseFromJson_Permissions() [TestMethod] public void ParseFromJson_SafeMethods() { - var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[""balanceOf""]}"; + var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[""balanceOf""],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); var check = ContractManifest.CreateDefault(UInt160.Zero); - check.SafeMethods = WildCardContainer.Create("balanceOf"); + check.SafeMethods = WildcardContainer.Create("balanceOf"); Assert.AreEqual(manifest.ToString(), check.ToString()); } [TestMethod] public void ParseFromJson_Trust() { - var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""safeMethods"":[]}"; + var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""safeMethods"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); var check = ContractManifest.CreateDefault(UInt160.Zero); - check.Trusts = WildCardContainer.Create(UInt160.Parse("0x0000000000000000000000000000000000000001")); + check.Trusts = WildcardContainer.Create(UInt160.Parse("0x0000000000000000000000000000000000000001")); Assert.AreEqual(manifest.ToString(), check.ToString()); } [TestMethod] public void ParseFromJson_Groups() { - var json = @"{""groups"":[{""pubKey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[]}"; + var json = @"{""groups"":[{""pubKey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); @@ -86,6 +87,15 @@ public void ParseFromJson_Groups() Assert.AreEqual(manifest.ToString(), check.ToString()); } + [TestMethod] + public void ParseFromJson_Extra() + { + var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":{""key"":""value""}}"; + var manifest = ContractManifest.Parse(json); + Assert.AreEqual(json, json); + Assert.AreEqual("value", manifest.Extra["key"].AsString(), false); + } + [TestMethod] public void TestDeserializeAndSerialize() { @@ -93,7 +103,7 @@ public void TestDeserializeAndSerialize() BinaryWriter writer = new BinaryWriter(stream); BinaryReader reader = new BinaryReader(stream); var expected = ContractManifest.CreateDefault(UInt160.Zero); - expected.SafeMethods = WildCardContainer.Create(new string[] { "AAA" }); + expected.SafeMethods = WildcardContainer.Create(new string[] { "AAA" }); expected.Serialize(writer); stream.Seek(0, SeekOrigin.Begin); var actual = ContractManifest.CreateDefault(UInt160.Zero); @@ -113,7 +123,7 @@ public void TestGetHash() public void TestGetSize() { var temp = ContractManifest.CreateDefault(UInt160.Zero); - Assert.AreEqual(353, temp.Size); + Assert.AreEqual(366, temp.Size); } [TestMethod] @@ -127,7 +137,7 @@ public void TestGenerator() public void TestCanCall() { var temp = ContractManifest.CreateDefault(UInt160.Zero); - temp.SafeMethods = WildCardContainer.Create(new string[] { "AAA" }); + temp.SafeMethods = WildcardContainer.Create(new string[] { "AAA" }); Assert.AreEqual(true, temp.CanCall(ContractManifest.CreateDefault(UInt160.Zero), "AAA")); } @@ -135,7 +145,7 @@ public void TestCanCall() public void TestClone() { var expected = ContractManifest.CreateDefault(UInt160.Zero); - expected.SafeMethods = WildCardContainer.Create(new string[] { "AAA" }); + expected.SafeMethods = WildcardContainer.Create(new string[] { "AAA" }); var actual = expected.Clone(); Assert.AreEqual(actual.ToString(), expected.ToString()); } diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs index 96bec21d2d..9a6743e4a5 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs @@ -15,22 +15,22 @@ public class UT_WildCardContainer public void TestFromJson() { JString jstring = new JString("*"); - WildCardContainer s = WildCardContainer.FromJson(jstring, u => u.AsString()); + WildcardContainer s = WildcardContainer.FromJson(jstring, u => u.AsString()); s.Should().BeEmpty(); jstring = new JString("hello world"); - Action action = () => WildCardContainer.FromJson(jstring, u => u.AsString()); + Action action = () => WildcardContainer.FromJson(jstring, u => u.AsString()); action.Should().Throw(); JObject alice = new JObject(); alice["name"] = "alice"; alice["age"] = 30; JArray jarray = new JArray { alice }; - WildCardContainer r = WildCardContainer.FromJson(jarray, u => u.AsString()); + WildcardContainer r = WildcardContainer.FromJson(jarray, u => u.AsString()); r[0].Should().Be("{\"name\":\"alice\",\"age\":30}"); JBoolean jbool = new JBoolean(); - action = () => WildCardContainer.FromJson(jbool, u => u.AsString()); + action = () => WildcardContainer.FromJson(jbool, u => u.AsString()); action.Should().Throw(); } @@ -38,11 +38,11 @@ public void TestFromJson() public void TestGetCount() { string[] s = new string[] { "hello", "world" }; - WildCardContainer container = WildCardContainer.Create(s); + WildcardContainer container = WildcardContainer.Create(s); container.Count.Should().Be(2); s = null; - container = WildCardContainer.Create(s); + container = WildcardContainer.Create(s); container.Count.Should().Be(0); } @@ -50,7 +50,7 @@ public void TestGetCount() public void TestGetItem() { string[] s = new string[] { "hello", "world" }; - WildCardContainer container = WildCardContainer.Create(s); + WildcardContainer container = WildcardContainer.Create(s); container[0].Should().Be("hello"); container[1].Should().Be("world"); } @@ -60,12 +60,12 @@ public void TestGetEnumerator() { string[] s = null; IReadOnlyList rs = (IReadOnlyList)new string[0]; - WildCardContainer container = WildCardContainer.Create(s); + WildcardContainer container = WildcardContainer.Create(s); IEnumerator enumerator = container.GetEnumerator(); enumerator.Should().Be(rs.GetEnumerator()); s = new string[] { "hello", "world" }; - container = WildCardContainer.Create(s); + container = WildcardContainer.Create(s); enumerator = container.GetEnumerator(); foreach (string _ in s) { @@ -78,7 +78,7 @@ public void TestGetEnumerator() public void TestIEnumerableGetEnumerator() { string[] s = new string[] { "hello", "world" }; - WildCardContainer container = WildCardContainer.Create(s); + WildcardContainer container = WildcardContainer.Create(s); IEnumerable enumerable = container; var enumerator = enumerable.GetEnumerator(); foreach (string _ in s) diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index e1f29ad4ad..d7e376def3 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -748,12 +748,12 @@ public void TestContract_Call() engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(method.ToHexString()); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(args.ToHexString()); - state.Manifest.Permissions[0].Methods = WildCardContainer.Create("a"); + state.Manifest.Permissions[0].Methods = WildcardContainer.Create("a"); engine.CurrentContext.EvaluationStack.Push(args); engine.CurrentContext.EvaluationStack.Push(method); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); InteropService.Invoke(engine, InteropService.System_Contract_Call).Should().BeFalse(); - state.Manifest.Permissions[0].Methods = WildCardContainer.CreateWildcard(); + state.Manifest.Permissions[0].Methods = WildcardContainer.CreateWildcard(); engine.CurrentContext.EvaluationStack.Push(args); engine.CurrentContext.EvaluationStack.Push(method); From 3aaa3822624f68539e94726cfdb3673809666ed8 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 13 Dec 2019 19:04:24 +0100 Subject: [PATCH 181/305] Prevent Timer's lock with wrong dns (#1358) * Prevent to lock Timer with wrong dns * Parse dns en ProtocolSettings * Update LocalNode.cs * Update LocalNode.cs * Update ProtocolSettings.cs * Fix ut * Update ProtocolSettings.cs * Update UT_ProtocolSettings.cs * dotnet format * Process dns seeds in parallel * Revert UT * Revert protocol settings * Update UT_ProtocolSettings.cs * Update ProtocolSettings.cs * Add comment * Update LocalNode.cs * Update LocalNode.cs * Update LocalNode.cs * Update LocalNode.cs * Update LocalNode.cs --- src/neo/Network/P2P/LocalNode.cs | 44 ++++++++++++++------------------ 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index a651d46a16..680437e430 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -10,6 +10,7 @@ using System.Net.Sockets; using System.Reflection; using System.Threading; +using System.Threading.Tasks; namespace Neo.Network.P2P { @@ -21,6 +22,7 @@ internal class SendDirectly { public IInventory Inventory; } public const uint ProtocolVersion = 0; private const int MaxCountFromSeedList = 5; + private readonly IPEndPoint[] SeedList = new IPEndPoint[ProtocolSettings.Default.SeedList.Length]; private static readonly object lockObj = new object(); private readonly NeoSystem system; @@ -56,6 +58,11 @@ public LocalNode(NeoSystem system) throw new InvalidOperationException(); this.system = system; singleton = this; + + // Start dns resolution in parallel + + for (int i = 0; i < ProtocolSettings.Default.SeedList.Length; i++) + Task.Run(() => SeedList[i] = GetIpEndPoint(ProtocolSettings.Default.SeedList[i])); } } @@ -97,33 +104,18 @@ private static IPEndPoint GetIPEndpointFromHostPort(string hostNameOrAddress, in return new IPEndPoint(ipAddress, port); } - /// - /// Return an amount of random seeds nodes from the default SeedList file defined on . - /// - /// Limit of random seed nodes to be obtained, also limited by the available seeds from file. - private static IEnumerable GetIPEndPointsFromSeedList(int seedsToTake) + internal static IPEndPoint GetIpEndPoint(string hostAndPort) { - if (seedsToTake > 0) + if (string.IsNullOrEmpty(hostAndPort)) return null; + + try { - Random rand = new Random(); - foreach (string hostAndPort in ProtocolSettings.Default.SeedList.OrderBy(p => rand.Next())) - { - if (seedsToTake == 0) break; - string[] p = hostAndPort.Split(':'); - IPEndPoint seed; - try - { - seed = GetIPEndpointFromHostPort(p[0], int.Parse(p[1])); - } - catch (AggregateException) - { - continue; - } - if (seed == null) continue; - seedsToTake--; - yield return seed; - } + string[] p = hostAndPort.Split(':'); + return GetIPEndpointFromHostPort(p[0], int.Parse(p[1])); } + catch { } + + return null; } public IEnumerable GetRemoteNodes() @@ -153,7 +145,9 @@ protected override void NeedMorePeers(int count) { // Will call AddPeers with default SeedList set cached on . // It will try to add those, sequentially, to the list of currently uncconected ones. - AddPeers(GetIPEndPointsFromSeedList(count)); + + Random rand = new Random(); + AddPeers(SeedList.Where(u => u != null).OrderBy(p => rand.Next()).Take(count)); } } From dcf0d0ac370064b82e6a83885ca9d769e30b533f Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 14 Dec 2019 21:13:46 +0800 Subject: [PATCH 182/305] Neo.VM.3.0.0-CI00190 (#1357) --- src/neo/Ledger/Blockchain.cs | 6 +- src/neo/Network/P2P/Payloads/Witness.cs | 4 +- .../ApplicationEngine.OpCodePrices.cs | 134 ++++------- src/neo/SmartContract/ApplicationEngine.cs | 7 +- .../ContractParametersContext.cs | 8 +- .../SmartContract/ExecutionContextState.cs | 7 +- src/neo/SmartContract/Helper.cs | 42 ++-- src/neo/SmartContract/InteropService.cs | 2 + src/neo/SmartContract/StackItemSerializer.cs | 2 +- src/neo/VM/Helper.cs | 5 +- src/neo/Wallets/Wallet.cs | 10 +- src/neo/neo.csproj | 2 +- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 6 +- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 6 +- tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs | 2 +- .../Network/P2P/Payloads/UT_Block.cs | 10 +- .../Network/P2P/Payloads/UT_Header.cs | 4 +- .../Network/P2P/Payloads/UT_Transaction.cs | 50 ++-- .../Network/P2P/Payloads/UT_Witness.cs | 6 +- .../SmartContract/UT_Contract.cs | 109 +++++---- .../UT_ContractParameterContext.cs | 21 +- .../SmartContract/UT_InteropPrices.cs | 10 +- .../SmartContract/UT_InteropService.cs | 45 +++- .../SmartContract/UT_StackItemSerializer.cs | 11 +- tests/neo.UnitTests/TestUtils.cs | 6 +- tests/neo.UnitTests/VM/UT_Helper.cs | 223 +++++++++--------- .../Wallets/SQLite/UT_VerificationContract.cs | 10 +- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 4 +- 28 files changed, 380 insertions(+), 372 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index d6c2840526..819289b333 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -45,7 +45,7 @@ private class ParallelVerified { public Transaction Transaction; public bool Sho Witness = new Witness { InvocationScript = Array.Empty(), - VerificationScript = new[] { (byte)OpCode.PUSHT } + VerificationScript = new[] { (byte)OpCode.PUSH1 } }, ConsensusData = new ConsensusData { @@ -164,7 +164,7 @@ private static Transaction DeployNativeContracts() { Version = 0, Script = script, - Sender = (new[] { (byte)OpCode.PUSHT }).ToScriptHash(), + Sender = (new[] { (byte)OpCode.PUSH1 }).ToScriptHash(), SystemFee = 0, Attributes = new TransactionAttribute[0], Cosigners = new Cosigner[0], @@ -173,7 +173,7 @@ private static Transaction DeployNativeContracts() new Witness { InvocationScript = Array.Empty(), - VerificationScript = new[] { (byte)OpCode.PUSHT } + VerificationScript = new[] { (byte)OpCode.PUSH1 } } } }; diff --git a/src/neo/Network/P2P/Payloads/Witness.cs b/src/neo/Network/P2P/Payloads/Witness.cs index 1cd5bf7ee8..55b2c7ae4b 100644 --- a/src/neo/Network/P2P/Payloads/Witness.cs +++ b/src/neo/Network/P2P/Payloads/Witness.cs @@ -30,9 +30,9 @@ void ISerializable.Deserialize(BinaryReader reader) { // This is designed to allow a MultiSig 10/10 (around 1003 bytes) ~1024 bytes // Invocation = 10 * 64 + 10 = 650 ~ 664 (exact is 653) - InvocationScript = reader.ReadVarBytes(664); + InvocationScript = reader.ReadVarBytes(663); // Verification = 10 * 33 + 10 = 340 ~ 360 (exact is 351) - VerificationScript = reader.ReadVarBytes(360); + VerificationScript = reader.ReadVarBytes(361); } void ISerializable.Serialize(BinaryWriter writer) diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index b6b65b37da..a1a2ffd400 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -7,87 +7,19 @@ partial class ApplicationEngine { public static readonly IReadOnlyDictionary OpCodePrices = new Dictionary { - [OpCode.PUSH0] = 30, - [OpCode.PUSHBYTES1] = 120, - [OpCode.PUSHBYTES2] = 120, - [OpCode.PUSHBYTES3] = 120, - [OpCode.PUSHBYTES4] = 120, - [OpCode.PUSHBYTES5] = 120, - [OpCode.PUSHBYTES6] = 120, - [OpCode.PUSHBYTES7] = 120, - [OpCode.PUSHBYTES8] = 120, - [OpCode.PUSHBYTES9] = 120, - [OpCode.PUSHBYTES10] = 120, - [OpCode.PUSHBYTES11] = 120, - [OpCode.PUSHBYTES12] = 120, - [OpCode.PUSHBYTES13] = 120, - [OpCode.PUSHBYTES14] = 120, - [OpCode.PUSHBYTES15] = 120, - [OpCode.PUSHBYTES16] = 120, - [OpCode.PUSHBYTES17] = 120, - [OpCode.PUSHBYTES18] = 120, - [OpCode.PUSHBYTES19] = 120, - [OpCode.PUSHBYTES20] = 120, - [OpCode.PUSHBYTES21] = 120, - [OpCode.PUSHBYTES22] = 120, - [OpCode.PUSHBYTES23] = 120, - [OpCode.PUSHBYTES24] = 120, - [OpCode.PUSHBYTES25] = 120, - [OpCode.PUSHBYTES26] = 120, - [OpCode.PUSHBYTES27] = 120, - [OpCode.PUSHBYTES28] = 120, - [OpCode.PUSHBYTES29] = 120, - [OpCode.PUSHBYTES30] = 120, - [OpCode.PUSHBYTES31] = 120, - [OpCode.PUSHBYTES32] = 120, - [OpCode.PUSHBYTES33] = 120, - [OpCode.PUSHBYTES34] = 120, - [OpCode.PUSHBYTES35] = 120, - [OpCode.PUSHBYTES36] = 120, - [OpCode.PUSHBYTES37] = 120, - [OpCode.PUSHBYTES38] = 120, - [OpCode.PUSHBYTES39] = 120, - [OpCode.PUSHBYTES40] = 120, - [OpCode.PUSHBYTES41] = 120, - [OpCode.PUSHBYTES42] = 120, - [OpCode.PUSHBYTES43] = 120, - [OpCode.PUSHBYTES44] = 120, - [OpCode.PUSHBYTES45] = 120, - [OpCode.PUSHBYTES46] = 120, - [OpCode.PUSHBYTES47] = 120, - [OpCode.PUSHBYTES48] = 120, - [OpCode.PUSHBYTES49] = 120, - [OpCode.PUSHBYTES50] = 120, - [OpCode.PUSHBYTES51] = 120, - [OpCode.PUSHBYTES52] = 120, - [OpCode.PUSHBYTES53] = 120, - [OpCode.PUSHBYTES54] = 120, - [OpCode.PUSHBYTES55] = 120, - [OpCode.PUSHBYTES56] = 120, - [OpCode.PUSHBYTES57] = 120, - [OpCode.PUSHBYTES58] = 120, - [OpCode.PUSHBYTES59] = 120, - [OpCode.PUSHBYTES60] = 120, - [OpCode.PUSHBYTES61] = 120, - [OpCode.PUSHBYTES62] = 120, - [OpCode.PUSHBYTES63] = 120, - [OpCode.PUSHBYTES64] = 120, - [OpCode.PUSHBYTES65] = 120, - [OpCode.PUSHBYTES66] = 120, - [OpCode.PUSHBYTES67] = 120, - [OpCode.PUSHBYTES68] = 120, - [OpCode.PUSHBYTES69] = 120, - [OpCode.PUSHBYTES70] = 120, - [OpCode.PUSHBYTES71] = 120, - [OpCode.PUSHBYTES72] = 120, - [OpCode.PUSHBYTES73] = 120, - [OpCode.PUSHBYTES74] = 120, - [OpCode.PUSHBYTES75] = 120, + [OpCode.PUSHINT8] = 30, + [OpCode.PUSHINT16] = 30, + [OpCode.PUSHINT32] = 30, + [OpCode.PUSHINT64] = 30, + [OpCode.PUSHINT128] = 120, + [OpCode.PUSHINT256] = 120, + [OpCode.PUSHA] = 120, + [OpCode.PUSHNULL] = 30, [OpCode.PUSHDATA1] = 180, [OpCode.PUSHDATA2] = 13000, [OpCode.PUSHDATA4] = 110000, [OpCode.PUSHM1] = 30, - [OpCode.PUSHNULL] = 30, + [OpCode.PUSH0] = 30, [OpCode.PUSH1] = 30, [OpCode.PUSH2] = 30, [OpCode.PUSH3] = 30, @@ -106,29 +38,51 @@ partial class ApplicationEngine [OpCode.PUSH16] = 30, [OpCode.NOP] = 30, [OpCode.JMP] = 70, + [OpCode.JMP_L] = 70, [OpCode.JMPIF] = 70, + [OpCode.JMPIF_L] = 70, [OpCode.JMPIFNOT] = 70, + [OpCode.JMPIFNOT_L] = 70, + [OpCode.JMPEQ] = 70, + [OpCode.JMPEQ_L] = 70, + [OpCode.JMPNE] = 70, + [OpCode.JMPNE_L] = 70, + [OpCode.JMPGT] = 70, + [OpCode.JMPGT_L] = 70, + [OpCode.JMPGE] = 70, + [OpCode.JMPGE_L] = 70, + [OpCode.JMPLT] = 70, + [OpCode.JMPLT_L] = 70, + [OpCode.JMPLE] = 70, + [OpCode.JMPLE_L] = 70, [OpCode.CALL] = 22000, + [OpCode.CALL_L] = 22000, + [OpCode.CALLA] = 22000, + [OpCode.THROW] = 30, + [OpCode.THROWIF] = 30, + [OpCode.THROWIFNOT] = 30, [OpCode.RET] = 40, [OpCode.SYSCALL] = 0, - [OpCode.DUPFROMALTSTACKBOTTOM] = 60, - [OpCode.DUPFROMALTSTACK] = 60, - [OpCode.TOALTSTACK] = 60, - [OpCode.FROMALTSTACK] = 60, - [OpCode.ISNULL] = 60, - [OpCode.XDROP] = 400, - [OpCode.XSWAP] = 60, - [OpCode.XTUCK] = 400, [OpCode.DEPTH] = 60, [OpCode.DROP] = 60, - [OpCode.DUP] = 60, [OpCode.NIP] = 60, + [OpCode.XDROP] = 400, + [OpCode.CLEAR] = 400, + [OpCode.DUP] = 60, [OpCode.OVER] = 60, [OpCode.PICK] = 60, - [OpCode.ROLL] = 400, - [OpCode.ROT] = 60, - [OpCode.SWAP] = 60, [OpCode.TUCK] = 60, + [OpCode.SWAP] = 60, + [OpCode.ROT] = 60, + [OpCode.ROLL] = 400, + [OpCode.REVERSE3] = 60, + [OpCode.REVERSE4] = 60, + [OpCode.REVERSEN] = 400, + [OpCode.TOALTSTACK] = 60, + [OpCode.FROMALTSTACK] = 60, + [OpCode.DUPFROMALTSTACK] = 60, + [OpCode.DUPFROMALTSTACKBOTTOM] = 60, + [OpCode.ISNULL] = 60, [OpCode.CAT] = 80000, [OpCode.SUBSTR] = 80000, [OpCode.LEFT] = 80000, @@ -178,8 +132,6 @@ partial class ApplicationEngine [OpCode.HASKEY] = 270000, [OpCode.KEYS] = 500, [OpCode.VALUES] = 7000, - [OpCode.THROW] = 30, - [OpCode.THROWIFNOT] = 30 }; } } diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index bc3694f712..4439012f9c 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -25,7 +25,7 @@ public partial class ApplicationEngine : ExecutionEngine public StoreView Snapshot { get; } public long GasConsumed { get; private set; } = 0; public UInt160 CurrentScriptHash => CurrentContext?.GetState().ScriptHash; - public UInt160 CallingScriptHash => InvocationStack.Count > 1 ? InvocationStack.Peek(1).GetState().ScriptHash : null; + public UInt160 CallingScriptHash => CurrentContext?.GetState().CallingScriptHash; public UInt160 EntryScriptHash => EntryContext?.GetState().ScriptHash; public IReadOnlyList Notifications => notifications; internal Dictionary InvocationCounter { get; } = new Dictionary(); @@ -55,10 +55,7 @@ protected override void LoadContext(ExecutionContext context) { // Set default execution context state - context.SetState(new ExecutionContextState() - { - ScriptHash = ((byte[])context.Script).ToScriptHash() - }); + context.GetState().ScriptHash ??= ((byte[])context.Script).ToScriptHash(); base.LoadContext(context); } diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index 2b0b99623f..f57f1f9679 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -144,16 +144,16 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) int i = 0; switch (contract.Script[i++]) { - case 1: + case (byte)OpCode.PUSHINT8: ++i; break; - case 2: + case (byte)OpCode.PUSHINT16: i += 2; break; } - while (contract.Script[i++] == 33) + while (contract.Script[i++] == (byte)OpCode.PUSHDATA1) { - points.Add(ECPoint.DecodePoint(contract.Script.AsSpan(i, 33), ECCurve.Secp256r1)); + points.Add(ECPoint.DecodePoint(contract.Script.AsSpan(++i, 33), ECCurve.Secp256r1)); i += 33; } } diff --git a/src/neo/SmartContract/ExecutionContextState.cs b/src/neo/SmartContract/ExecutionContextState.cs index d26641f7dd..929048fee1 100644 --- a/src/neo/SmartContract/ExecutionContextState.cs +++ b/src/neo/SmartContract/ExecutionContextState.cs @@ -1,10 +1,15 @@ namespace Neo.SmartContract { - public class ExecutionContextState + internal class ExecutionContextState { /// /// Script hash /// public UInt160 ScriptHash { get; set; } + + /// + /// Calling script hash + /// + public UInt160 CallingScriptHash { get; set; } } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 28c6045dcd..0bb328bd0a 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -15,44 +15,49 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n) { m = 0; n = 0; int i = 0; - if (script.Length < 42) return false; - if (script[i] > (byte)OpCode.PUSH16) return false; - if (script[i] < (byte)OpCode.PUSH1 && script[i] != 1 && script[i] != 2) return false; + if (script.Length < 43) return false; switch (script[i]) { - case 1: + case (byte)OpCode.PUSHINT8: m = script[++i]; ++i; break; - case 2: + case (byte)OpCode.PUSHINT16: m = BinaryPrimitives.ReadUInt16LittleEndian(script.AsSpan(++i)); i += 2; break; - default: - m = script[i++] - 80; + case byte b when b >= (byte)OpCode.PUSH1 && b <= (byte)OpCode.PUSH16: + m = b - (byte)OpCode.PUSH0; + ++i; break; + default: + return false; } if (m < 1 || m > 1024) return false; - while (script[i] == 33) + while (script[i] == (byte)OpCode.PUSHDATA1) { + if (script.Length <= i + 35) return false; + if (script[++i] != 33) return false; i += 34; - if (script.Length <= i) return false; ++n; } if (n < m || n > 1024) return false; switch (script[i]) { - case 1: + case (byte)OpCode.PUSHINT8: if (n != script[++i]) return false; ++i; break; - case 2: + case (byte)OpCode.PUSHINT16: if (script.Length < i + 3 || n != BinaryPrimitives.ReadUInt16LittleEndian(script.AsSpan(++i))) return false; i += 2; break; - default: - if (n != script[i++] - 80) return false; + case byte b when b >= (byte)OpCode.PUSH1 && b <= (byte)OpCode.PUSH16: + if (n != b - (byte)OpCode.PUSH0) return false; + ++i; break; + default: + return false; } if (script[i++] != (byte)OpCode.PUSHNULL) return false; if (script[i++] != (byte)OpCode.SYSCALL) return false; @@ -64,11 +69,12 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n) public static bool IsSignatureContract(this byte[] script) { - if (script.Length != 40) return false; - if (script[0] != (byte)OpCode.PUSHBYTES33 - || script[34] != (byte)OpCode.PUSHNULL - || script[35] != (byte)OpCode.SYSCALL - || BitConverter.ToUInt32(script, 36) != InteropService.Neo_Crypto_ECDsaVerify) + if (script.Length != 41) return false; + if (script[0] != (byte)OpCode.PUSHDATA1 + || script[1] != 33 + || script[35] != (byte)OpCode.PUSHNULL + || script[36] != (byte)OpCode.SYSCALL + || BitConverter.ToUInt32(script, 37) != InteropService.Neo_Crypto_ECDsaVerify) return false; return true; } diff --git a/src/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs index 93583f4851..40e1d77c5b 100644 --- a/src/neo/SmartContract/InteropService.cs +++ b/src/neo/SmartContract/InteropService.cs @@ -484,7 +484,9 @@ private static bool Contract_Call(ApplicationEngine engine) engine.InvocationCounter[contract.ScriptHash] = 1; } + UInt160 callingScriptHash = engine.CurrentScriptHash; ExecutionContext context_new = engine.LoadScript(contract.Script, 1); + context_new.GetState().CallingScriptHash = callingScriptHash; context_new.EvaluationStack.Push(args); context_new.EvaluationStack.Push(method); return true; diff --git a/src/neo/SmartContract/StackItemSerializer.cs b/src/neo/SmartContract/StackItemSerializer.cs index 8301afcb60..24f5128ff1 100644 --- a/src/neo/SmartContract/StackItemSerializer.cs +++ b/src/neo/SmartContract/StackItemSerializer.cs @@ -47,7 +47,7 @@ private static StackItem Deserialize(BinaryReader reader, uint maxItemSize, Refe deserialized.Push(new Boolean(reader.ReadBoolean())); break; case StackItemType.Integer: - deserialized.Push(new Integer(new BigInteger(reader.ReadVarBytes(ExecutionEngine.MaxSizeForBigInteger)))); + deserialized.Push(new Integer(new BigInteger(reader.ReadVarBytes(Integer.MaxSize)))); break; case StackItemType.Array: case StackItemType.Struct: diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 735df63c85..a921f4411a 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -312,7 +312,10 @@ private static StackItem ToStackItem(ContractParameter parameter, List<(StackIte (stackItem, _) = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter)); if (stackItem is null) { - stackItem = new Map(((IList>)parameter.Value).ToDictionary(p => (PrimitiveType)ToStackItem(p.Key, context), p => ToStackItem(p.Value, context))); + Map map = new Map(); + foreach (var pair in (IList>)parameter.Value) + map[(PrimitiveType)ToStackItem(pair.Key, context)] = ToStackItem(pair.Value, context); + stackItem = map; context.Add((stackItem, parameter)); } break; diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 954923df8f..a616c971db 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -353,17 +353,17 @@ public static long CalculateNetWorkFee(byte[] witness_script, ref int size) if (witness_script.IsSignatureContract()) { - size += 66 + witness_script.GetVarSize(); - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] + ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null); + size += 67 + witness_script.GetVarSize(); + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null); } else if (witness_script.IsMultiSigContract(out int m, out int n)) { - int size_inv = 65 * m; + int size_inv = 66 * m; size += IO.Helper.GetVarSize(size_inv) + size_inv + witness_script.GetVarSize(); - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES64] * m; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * m; using (ScriptBuilder sb = new ScriptBuilder()) networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHBYTES33] * n; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * n; using (ScriptBuilder sb = new ScriptBuilder()) networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null) * n; diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 593839600d..ebac81c4bc 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 06ca0f0cd7..97ebacfdbe 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -153,8 +153,8 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm 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("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); - mockContext.Object.Block.NextConsensus.Should().Be(UInt160.Parse("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); + originalContract.ScriptHash.Should().Be(UInt160.Parse("0x9412c3107a59fa732ccd94866976f7bbb3d9c372")); + mockContext.Object.Block.NextConsensus.Should().Be(UInt160.Parse("0x9412c3107a59fa732ccd94866976f7bbb3d9c372")); Console.WriteLine("\n=========================="); Console.WriteLine("will trigger OnPersistCompleted again with OnStart flag!"); @@ -175,7 +175,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("will create template MakePrepareRequest..."); mockContext.Object.PrevHeader.Timestamp = defaultTimestamp; - mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0xbdbe3ca30e9d74df12ce57ebc95a302dfaa0828c")); + mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0x9412c3107a59fa732ccd94866976f7bbb3d9c372")); var prepReq = mockContext.Object.MakePrepareRequest(); var ppToSend = (PrepareRequest)prepReq.ConsensusMessage; // Forcing hashes to 0 because mempool is currently shared diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index d93555262a..622f156b16 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -70,13 +70,13 @@ public void TestContainsTransaction() [TestMethod] public void TestGetCurrentBlockHash() { - Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0x0d492ce0f38090a65b2b01af50f7a6d685b6b76fbc41672762e96b05d15d742c")); + Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0x2b8a21dfaf989dc1a5f2694517aefdbda1dd340f3cf177187d73e038a58ad2bb")); } [TestMethod] public void TestGetCurrentHeaderHash() { - Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0x0d492ce0f38090a65b2b01af50f7a6d685b6b76fbc41672762e96b05d15d742c")); + Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0x2b8a21dfaf989dc1a5f2694517aefdbda1dd340f3cf177187d73e038a58ad2bb")); } [TestMethod] @@ -88,7 +88,7 @@ public void TestGetBlock() [TestMethod] public void TestGetBlockHash() { - Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0x0d492ce0f38090a65b2b01af50f7a6d685b6b76fbc41672762e96b05d15d742c")); + Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0x2b8a21dfaf989dc1a5f2694517aefdbda1dd340f3cf177187d73e038a58ad2bb")); Blockchain.Singleton.GetBlockHash(10).Should().BeNull(); } diff --git a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs index 482297f7ab..b74a0b2a9e 100644 --- a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs +++ b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs @@ -25,7 +25,7 @@ public static TrimmedBlock GetTrimmedBlockWithNoTransaction() Witness = new Witness { InvocationScript = new byte[0], - VerificationScript = new[] { (byte)OpCode.PUSHT } + VerificationScript = new[] { (byte)OpCode.PUSH1 } }, Hashes = new UInt256[0] }; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index fd8f821c6a..2090acda87 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 = "0000000000000000000000000000000000000000000000000000000000000000000000000f29b0d748a9ccf8c5af3cde10db3e36ec9a5f720643a2bcb4add76b3daf41d8e913ff854c000000000000000000000000000000000000000000000000000000010001510200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c000000000000000000000000000000000000000000000000000000010001110200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; 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 = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c000000000000000000000000000000000000000000000000000000010001510200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c000000000000000000000000000000000000000000000000000000010001110200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) using (BinaryReader reader = new BinaryReader(ms)) @@ -199,18 +199,18 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0x4e1d8392e7c44830e7e45c18e5e0e3ef3c36af883868846d3691a436a62494b2"); + jObj["hash"].AsString().Should().Be("0x865b348426952d2eaa978cf5d37dc85ec5f95bc56c57c1379d1be21898e74c1e"); jObj["size"].AsNumber().Should().Be(166); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - jObj["merkleroot"].AsString().Should().Be("0xd841af3d6bd7adb4bca24306725f9aec363edb10de3cafc5f8cca948d7b0290f"); + jObj["merkleroot"].AsString().Should().Be("0x6226416a0e5aca42b5566f5a19ab467692688ba9d47986f6981a7f747bba2772"); jObj["time"].AsNumber().Should().Be(328665601001); jObj["index"].AsNumber().Should().Be(0); jObj["nextconsensus"].AsString().Should().Be("NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf"); JObject scObj = ((JArray)jObj["witnesses"])[0]; scObj["invocation"].AsString().Should().Be(""); - scObj["verification"].AsString().Should().Be("UQ=="); + scObj["verification"].AsString().Should().Be("EQ=="); jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index 3eabd054b6..4db51e9bef 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -35,7 +35,7 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - var hex = "0000000000000000000000000000000000000000000000000000000000000000000000000f29b0d748a9ccf8c5af3cde10db3e36ec9a5f720643a2bcb4add76b3daf41d8e913ff854c0000000000000000000000000000000000000000000000000000000100015100"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c0000000000000000000000000000000000000000000000000000000100011100"; using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) { @@ -96,7 +96,7 @@ public void Serialize() UInt256 val256 = UInt256.Zero; TestUtils.SetupHeaderWithValues(uut, val256, out _, out _, out _, out _, out _); - var hex = "0000000000000000000000000000000000000000000000000000000000000000000000000f29b0d748a9ccf8c5af3cde10db3e36ec9a5f720643a2bcb4add76b3daf41d8e913ff854c0000000000000000000000000000000000000000000000000000000100015100"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c0000000000000000000000000000000000000000000000000000000100011100"; uut.ToArray().ToHexString().Should().Be(hex); } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 3430d84390..679103a470 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -164,9 +164,9 @@ public void FeeIsMultiSigContract() } var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); - Assert.AreEqual(verificationGas, 2000570); - Assert.AreEqual(sizeGas, 359000); - Assert.AreEqual(tx.NetworkFee, 2359570); + Assert.AreEqual(2000810, verificationGas); + Assert.AreEqual(367000, sizeGas); + Assert.AreEqual(2367810, tx.NetworkFee); } } @@ -214,7 +214,7 @@ public void FeeIsSignatureContractDetailed() Assert.IsNull(tx.Witnesses); // check pre-computed network fee (already guessing signature sizes) - tx.NetworkFee.Should().Be(1258270); + tx.NetworkFee.Should().Be(1264390); // ---- // Sign @@ -251,37 +251,37 @@ public void FeeIsSignatureContractDetailed() verificationGas += engine.GasConsumed; } } - Assert.AreEqual(verificationGas, 1000270); + Assert.AreEqual(verificationGas, 1000390); // ------------------ // check tx_size cost // ------------------ - Assert.AreEqual(tx.Size, 258); + Assert.AreEqual(264, tx.Size); // will verify tx size, step by step // Part I - Assert.AreEqual(Transaction.HeaderSize, 45); + Assert.AreEqual(45, Transaction.HeaderSize); // Part II - Assert.AreEqual(tx.Attributes.GetVarSize(), 1); - Assert.AreEqual(tx.Attributes.Length, 0); - Assert.AreEqual(tx.Cosigners.Length, 1); - Assert.AreEqual(tx.Cosigners.GetVarSize(), 22); + Assert.AreEqual(1, tx.Attributes.GetVarSize()); + Assert.AreEqual(0, tx.Attributes.Length); + Assert.AreEqual(1, tx.Cosigners.Length); + Assert.AreEqual(22, tx.Cosigners.GetVarSize()); // Note that Data size and Usage size are different (because of first byte on GetVarSize()) - Assert.AreEqual(tx.Cosigners[0].Size, 21); + Assert.AreEqual(21, tx.Cosigners[0].Size); // Part III - Assert.AreEqual(tx.Script.GetVarSize(), 82); + Assert.AreEqual(86, tx.Script.GetVarSize()); // Part IV - Assert.AreEqual(tx.Witnesses.GetVarSize(), 108); + Assert.AreEqual(110, tx.Witnesses.GetVarSize()); // I + II + III + IV - Assert.AreEqual(tx.Size, 45 + 23 + 82 + 108); + Assert.AreEqual(45 + 23 + 86 + 110, tx.Size); - Assert.AreEqual(NativeContract.Policy.GetFeePerByte(snapshot), 1000); + Assert.AreEqual(1000, NativeContract.Policy.GetFeePerByte(snapshot)); var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); - Assert.AreEqual(sizeGas, 258000); + Assert.AreEqual(264000, sizeGas); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1258270); + Assert.AreEqual(1264390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -374,7 +374,7 @@ public void FeeIsSignatureContract_TestScope_Global() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1258270); + Assert.AreEqual(1264390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -468,7 +468,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1279270); + Assert.AreEqual(1285390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -565,7 +565,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1279270); + Assert.AreEqual(1285390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -722,7 +722,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1299270); + Assert.AreEqual(1305390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -838,7 +838,7 @@ public void Transaction_Serialize_Deserialize_Simple() "04030201" + // timelimit "00" + // no attributes "00" + // no cosigners - "0151" + // push1 script + "0111" + // push1 script "00"); // no witnesses // try to deserialize @@ -889,7 +889,7 @@ public void Transaction_Serialize_Deserialize_DistinctCosigners() byte[] sTx = txDoubleCosigners.ToArray(); // no need for detailed hexstring here (see basic tests for it) - sTx.ToHexString().Should().Be("0004030201000000000000000000000000000000000000000000e1f505000000000100000000000000040302010002090807060504030201000908070605040302010000090807060504030201000908070605040302010001015100"); + sTx.ToHexString().Should().Be("0004030201000000000000000000000000000000000000000000e1f505000000000100000000000000040302010002090807060504030201000908070605040302010000090807060504030201000908070605040302010001011100"); // back to transaction (should fail, due to non-distinct cosigners) Transaction tx2 = null; @@ -1072,7 +1072,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(verificationGas + sizeGas, 1258270); + Assert.AreEqual(1264390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index 87c82124b5..aebda4d9da 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -82,9 +82,9 @@ public void MaxSize_OK() // Check max size - witness.Size.Should().Be(1004); - witness.InvocationScript.GetVarSize().Should().Be(653); - witness.VerificationScript.GetVarSize().Should().Be(351); + witness.Size.Should().Be(1024); + witness.InvocationScript.GetVarSize().Should().Be(663); + witness.VerificationScript.GetVarSize().Should().Be(361); Assert.IsTrue(witness.Size <= 1024); diff --git a/tests/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/neo.UnitTests/SmartContract/UT_Contract.cs index c0f4a86cf1..36efd787cb 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Contract.cs @@ -1,11 +1,11 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; +using Neo.VM; using Neo.Wallets; using System; using System.Linq; using System.Security.Cryptography; -using System.Text; namespace Neo.UnitTests.SmartContract { @@ -20,13 +20,13 @@ public void TestGetAddress() rng.GetBytes(privateKey); KeyPair key = new KeyPair(privateKey); Contract contract = Contract.CreateSignatureContract(key.PublicKey); - byte[] script = contract.Script; - byte[] expectedArray = new byte[40]; - expectedArray[0] = 0x21; - Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); - expectedArray[34] = 0x50; - expectedArray[35] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 36, 4); + byte[] expectedArray = new byte[41]; + expectedArray[0] = (byte)OpCode.PUSHDATA1; + expectedArray[1] = 0x21; + Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); + expectedArray[35] = (byte)OpCode.PUSHNULL; + expectedArray[36] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 37, 4); Assert.AreEqual(expectedArray.ToScriptHash().ToAddress(), contract.Address); } @@ -38,13 +38,13 @@ public void TestGetScriptHash() rng.GetBytes(privateKey); KeyPair key = new KeyPair(privateKey); Contract contract = Contract.CreateSignatureContract(key.PublicKey); - byte[] script = contract.Script; - byte[] expectedArray = new byte[40]; - expectedArray[0] = 0x21; - Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); - expectedArray[34] = 0x50; - expectedArray[35] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 36, 4); + byte[] expectedArray = new byte[41]; + expectedArray[0] = (byte)OpCode.PUSHDATA1; + expectedArray[1] = 0x21; + Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); + expectedArray[35] = (byte)OpCode.PUSHNULL; + expectedArray[36] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 37, 4); Assert.AreEqual(expectedArray.ToScriptHash(), contract.ScriptHash); } @@ -75,17 +75,19 @@ public void TestCreateMultiSigContract() publicKeys[1] = key2.PublicKey; publicKeys = publicKeys.OrderBy(p => p).ToArray(); Contract contract = Contract.CreateMultiSigContract(2, publicKeys); - byte[] expectedArray = new byte[76]; - expectedArray[0] = 0x52; - expectedArray[1] = 0x21; - Array.Copy(publicKeys[0].EncodePoint(true), 0, expectedArray, 2, 33); - expectedArray[35] = 0x21; - Array.Copy(publicKeys[1].EncodePoint(true), 0, expectedArray, 36, 33); - expectedArray[69] = 0x52; - expectedArray[70] = 0x50; - expectedArray[71] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaCheckMultiSig), 0, expectedArray, 72, 4); - Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(contract.Script)); + byte[] expectedArray = new byte[78]; + expectedArray[0] = (byte)OpCode.PUSH2; + expectedArray[1] = (byte)OpCode.PUSHDATA1; + expectedArray[2] = 0x21; + Array.Copy(publicKeys[0].EncodePoint(true), 0, expectedArray, 3, 33); + expectedArray[36] = (byte)OpCode.PUSHDATA1; + expectedArray[37] = 0x21; + Array.Copy(publicKeys[1].EncodePoint(true), 0, expectedArray, 38, 33); + expectedArray[71] = (byte)OpCode.PUSH2; + expectedArray[72] = (byte)OpCode.PUSHNULL; + expectedArray[73] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaCheckMultiSig), 0, expectedArray, 74, 4); + CollectionAssert.AreEqual(expectedArray, contract.Script); Assert.AreEqual(2, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[1]); @@ -109,17 +111,19 @@ public void TestCreateMultiSigRedeemScript() Action action = () => Contract.CreateMultiSigRedeemScript(0, publicKeys); action.Should().Throw(); byte[] script = Contract.CreateMultiSigRedeemScript(2, publicKeys); - byte[] expectedArray = new byte[76]; - expectedArray[0] = 0x52; - expectedArray[1] = 0x21; - Array.Copy(publicKeys[0].EncodePoint(true), 0, expectedArray, 2, 33); - expectedArray[35] = 0x21; - Array.Copy(publicKeys[1].EncodePoint(true), 0, expectedArray, 36, 33); - expectedArray[69] = 0x52; - expectedArray[70] = 0x50; - expectedArray[71] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaCheckMultiSig), 0, expectedArray, 72, 4); - Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(script)); + byte[] expectedArray = new byte[78]; + expectedArray[0] = (byte)OpCode.PUSH2; + expectedArray[1] = (byte)OpCode.PUSHDATA1; + expectedArray[2] = 0x21; + Array.Copy(publicKeys[0].EncodePoint(true), 0, expectedArray, 3, 33); + expectedArray[36] = (byte)OpCode.PUSHDATA1; + expectedArray[37] = 0x21; + Array.Copy(publicKeys[1].EncodePoint(true), 0, expectedArray, 38, 33); + expectedArray[71] = (byte)OpCode.PUSH2; + expectedArray[72] = (byte)OpCode.PUSHNULL; + expectedArray[73] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaCheckMultiSig), 0, expectedArray, 74, 4); + CollectionAssert.AreEqual(expectedArray, script); } [TestMethod] @@ -130,14 +134,14 @@ public void TestCreateSignatureContract() rng.GetBytes(privateKey); KeyPair key = new KeyPair(privateKey); Contract contract = Contract.CreateSignatureContract(key.PublicKey); - byte[] script = contract.Script; - byte[] expectedArray = new byte[40]; - expectedArray[0] = 0x21; - Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); - expectedArray[34] = 0x50; - expectedArray[35] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 36, 4); - Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(script)); + byte[] expectedArray = new byte[41]; + expectedArray[0] = (byte)OpCode.PUSHDATA1; + expectedArray[1] = 0x21; + Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); + expectedArray[35] = (byte)OpCode.PUSHNULL; + expectedArray[36] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 37, 4); + CollectionAssert.AreEqual(expectedArray, contract.Script); Assert.AreEqual(1, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); } @@ -150,13 +154,14 @@ public void TestCreateSignatureRedeemScript() rng.GetBytes(privateKey); KeyPair key = new KeyPair(privateKey); byte[] script = Contract.CreateSignatureRedeemScript(key.PublicKey); - byte[] expectedArray = new byte[40]; - expectedArray[0] = 0x21; - Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 1, 33); - expectedArray[34] = 0x50; - expectedArray[35] = 0x68; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 36, 4); - Assert.AreEqual(Encoding.Default.GetString(expectedArray), Encoding.Default.GetString(script)); + byte[] expectedArray = new byte[41]; + expectedArray[0] = (byte)OpCode.PUSHDATA1; + expectedArray[1] = 0x21; + Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); + expectedArray[35] = (byte)OpCode.PUSHNULL; + expectedArray[36] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 37, 4); + CollectionAssert.AreEqual(expectedArray, script); } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index f5fd304243..f5110b00ea 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -3,6 +3,7 @@ using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; using Neo.SmartContract; +using Neo.VM; using Neo.Wallets; using System; @@ -33,7 +34,7 @@ public static void ClassSetUp(TestContext context) public void TestGetComplete() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); + tx.Sender = UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); var context = new ContractParametersContext(tx); context.Completed.Should().BeFalse(); } @@ -42,11 +43,11 @@ public void TestGetComplete() public void TestToString() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); + tx.Sender = UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); 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"":""AAAAAAB8l3ZIRRctgn08hjdDKTkxppEnGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA=="",""items"":{""0x1a2791a63139294337863c7d822d17454876977c"":{""script"":""IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFlBoCpBq1A=="",""parameters"":[{""type"":""Signature"",""value"":""AQ==""}]}}}"); + str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""hex"":""AAAAAABmUJDLobcPtqo9vZKIdjXsd8fVGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA=="",""items"":{""0x1bd5c777ec35768892bd3daab60fb7a1cb905066"":{""script"":""DCECb/A7lJJBzh2t1DUZ5pYOCoW0GmmgXDKBA6orzhWUyhYLQQqQatQ="",""parameters"":[{""type"":""Signature"",""value"":""AQ==""}]}}}"); } [TestMethod] @@ -60,7 +61,7 @@ public void TestParse() [TestMethod] public void TestFromJson() { - Action action = () => ContractParametersContext.Parse("{\"type\":\"wrongType\",\"hex\":\"00000000007c97764845172d827d3c863743293931a691271a0000000000000000000000000000000000000000000100\",\"items\":{\"0x1a2791a63139294337863c7d822d17454876977c\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1650680a906ad4\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}"); + Action action = () => ContractParametersContext.Parse("{\"type\":\"wrongType\",\"hex\":\"00000000007c97764845172d827d3c863743293931a691271a0000000000000000000000000000000000000000000100\",\"items\":{\"0x1bd5c777ec35768892bd3daab60fb7a1cb905066\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1650680a906ad4\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}"); action.Should().Throw(); } @@ -71,7 +72,7 @@ public void TestAdd() var context1 = new ContractParametersContext(tx); context1.Add(contract, 0, new byte[] { 0x01 }).Should().BeFalse(); - tx.Sender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); + tx.Sender = UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); var context2 = new ContractParametersContext(tx); context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); //test repeatlly createItem @@ -82,7 +83,7 @@ public void TestAdd() public void TestGetParameter() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); + tx.Sender = UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); var context = new ContractParametersContext(tx); context.GetParameter(tx.Sender, 0).Should().BeNull(); @@ -95,12 +96,12 @@ public void TestGetParameter() public void TestGetWitnesses() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); + tx.Sender = UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); var context = new ContractParametersContext(tx); context.Add(contract, 0, new byte[] { 0x01 }); Witness[] witnesses = context.GetWitnesses(); witnesses.Length.Should().Be(1); - witnesses[0].InvocationScript.ToHexString().Should().Be(new byte[] { 0x01, 0x01 }.ToHexString()); + witnesses[0].InvocationScript.ToHexString().Should().Be(new byte[] { (byte)OpCode.PUSHDATA1, 0x01, 0x01 }.ToHexString()); witnesses[0].VerificationScript.ToHexString().Should().Be(contract.Script.ToHexString()); } @@ -108,7 +109,7 @@ public void TestGetWitnesses() public void TestAddSignature() { Transaction tx = TestUtils.GetTransaction(); - var singleSender = UInt160.Parse("0x1a2791a63139294337863c7d822d17454876977c"); + var singleSender = UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); tx.Sender = singleSender; //singleSign @@ -138,7 +139,7 @@ public void TestAddSignature() key.PublicKey, key2.PublicKey }); - var multiSender = UInt160.Parse("0xfc8b59f1a337dcc17b1a201d327a2081d41fac8d"); + var multiSender = UInt160.Parse("0xd8e21c5f8b2e48c409220a3aff34a7fc4c87fbe9"); tx.Sender = multiSender; context = new ContractParametersContext(tx); context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index d6b9b8254b..ac8a017d1f 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -40,18 +40,18 @@ public void ApplicationEngineFixedPrices() public void ApplicationEngineVariablePrices() { // Neo.Contract.Create: f66ca56e (requires push properties on fourth position) - byte[] SyscallContractCreateHash00 = new byte[] { 0x01, 0x00, 0x02, 0x00, 0x00, 0x68, 0xf6, 0x6c, 0xa5, 0x6e }; + byte[] SyscallContractCreateHash00 = new byte[] { (byte)OpCode.PUSHDATA1, 0x01, 0x00, (byte)OpCode.PUSHDATA1, 0x02, 0x00, 0x00, (byte)OpCode.SYSCALL, 0xf6, 0x6c, 0xa5, 0x6e }; using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0, testMode: true)) { Debugger debugger = new Debugger(ae); ae.LoadScript(SyscallContractCreateHash00); - debugger.StepInto(); // PUSHBYTES1 - debugger.StepInto(); // PUSHBYTES2 + debugger.StepInto(); // PUSHDATA1 + debugger.StepInto(); // PUSHDATA1 InteropService.GetPrice(InteropService.Neo_Contract_Create, ae.CurrentContext.EvaluationStack).Should().Be(0_00300000L); } // System.Storage.Put: e63f1884 (requires push key and value) - byte[] SyscallStoragePutHash = new byte[] { 0x53, 0x53, 0x00, 0x68, 0xe6, 0x3f, 0x18, 0x84 }; + byte[] SyscallStoragePutHash = new byte[] { (byte)OpCode.PUSH3, (byte)OpCode.PUSH3, (byte)OpCode.PUSH0, (byte)OpCode.SYSCALL, 0xe6, 0x3f, 0x18, 0x84 }; using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0, testMode: true)) { Debugger debugger = new Debugger(ae); @@ -63,7 +63,7 @@ public void ApplicationEngineVariablePrices() } // System.Storage.PutEx: 73e19b3a (requires push key and value) - byte[] SyscallStoragePutExHash = new byte[] { 0x53, 0x53, 0x00, 0x68, 0x73, 0xe1, 0x9b, 0x3a }; + byte[] SyscallStoragePutExHash = new byte[] { (byte)OpCode.PUSH3, (byte)OpCode.PUSH3, (byte)OpCode.PUSH0, (byte)OpCode.SYSCALL, 0x73, 0xe1, 0x9b, 0x3a }; using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0, testMode: true)) { Debugger debugger = new Debugger(ae); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index d7e376def3..4e4b05b0c9 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -223,15 +223,35 @@ public void TestExecutionEngine_GetExecutingScriptHash() [TestMethod] public void TestExecutionEngine_GetCallingScriptHash() { + // Test without + var engine = GetEngine(true); InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetCallingScriptHash).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); - engine = GetEngine(true); - engine.LoadScript(new byte[] { 0x01 }); - InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetCallingScriptHash).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() - .Should().Be(engine.CallingScriptHash.ToArray().ToHexString()); + // Test real + + using ScriptBuilder scriptA = new ScriptBuilder(); + scriptA.Emit(OpCode.DROP); // Drop arguments + scriptA.Emit(OpCode.DROP); // Drop method + scriptA.EmitSysCall(InteropService.System_ExecutionEngine_GetCallingScriptHash); + + var contract = new ContractState() + { + Manifest = ContractManifest.CreateDefault(scriptA.ToArray().ToScriptHash()), + Script = scriptA.ToArray() + }; + + engine = GetEngine(true, true, false); + engine.Snapshot.Contracts.Add(contract.ScriptHash, contract); + + using ScriptBuilder scriptB = new ScriptBuilder(); + scriptB.EmitAppCall(contract.ScriptHash, ""); + engine.LoadScript(scriptB.ToArray()); + + Assert.AreEqual(VMState.HALT, engine.Execute()); + + engine.ResultStack.Pop().GetSpan().ToHexString().Should().Be(scriptB.ToArray().ToScriptHash().ToArray().ToHexString()); } [TestMethod] @@ -808,28 +828,31 @@ 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) + private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSnapshot = false, bool addScript = true) { var tx = TestUtils.GetTransaction(); var snapshot = Blockchain.Singleton.GetSnapshot(); ApplicationEngine engine; if (hasContainer && hasSnapshot) { - engine = new ApplicationEngine(TriggerType.Application, tx, snapshot, 0); + engine = new ApplicationEngine(TriggerType.Application, tx, snapshot, 0, true); } else if (hasContainer && !hasSnapshot) { - engine = new ApplicationEngine(TriggerType.Application, tx, null, 0); + engine = new ApplicationEngine(TriggerType.Application, tx, null, 0, true); } else if (!hasContainer && hasSnapshot) { - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); } else { - engine = new ApplicationEngine(TriggerType.Application, null, null, 0); + engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true); + } + if (addScript) + { + engine.LoadScript(new byte[] { 0x01 }); } - engine.LoadScript(new byte[] { 0x01 }); return engine; } } diff --git a/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs b/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs index 75ab173460..ef92f57016 100644 --- a/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs +++ b/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs @@ -4,6 +4,7 @@ using Neo.VM.Types; using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace Neo.UnitTests.SmartContract @@ -61,8 +62,7 @@ public void TestSerialize() }; Assert.AreEqual(Encoding.Default.GetString(expectedArray7), Encoding.Default.GetString(result7)); - Dictionary list8 = new Dictionary { [2] = 1 }; - StackItem stackItem82 = new Map(list8); + StackItem stackItem82 = new Map { [2] = 1 }; byte[] result8 = StackItemSerializer.Serialize(stackItem82, MaxItemSize); byte[] expectedArray8 = new byte[] { 0x82,0x01,0x02,0x01,0x02,0x02,0x01,0x01 @@ -117,13 +117,12 @@ public void TestDeserializeStackItem() Assert.AreEqual(((Struct)stackItem62).Count, ((Struct)result6).Count); Assert.AreEqual(((Struct)stackItem62).GetEnumerator().Current, ((Struct)result6).GetEnumerator().Current); - Dictionary list7 = new Dictionary { [2] = 1 }; - StackItem stackItem72 = new Map(list7); + StackItem stackItem72 = new Map { [2] = 1 }; byte[] byteArray7 = StackItemSerializer.Serialize(stackItem72, MaxItemSize); StackItem result7 = StackItemSerializer.Deserialize(byteArray7, (uint)byteArray7.Length); Assert.AreEqual(((Map)stackItem72).Count, ((Map)result7).Count); - Assert.AreEqual(((Map)stackItem72).Keys.GetEnumerator().Current, ((Map)result7).Keys.GetEnumerator().Current); - Assert.AreEqual(((Map)stackItem72).Values.GetEnumerator().Current, ((Map)result7).Values.GetEnumerator().Current); + CollectionAssert.AreEqual(((Map)stackItem72).Keys.ToArray(), ((Map)result7).Keys.ToArray()); + CollectionAssert.AreEqual(((Map)stackItem72).Values.ToArray(), ((Map)result7).Values.ToArray()); } } } diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index 83f3d6515e..a68eba49a0 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -8,6 +8,7 @@ using Neo.Wallets.NEP6; using System; using System.IO; +using System.Linq; namespace Neo.UnitTests { @@ -83,12 +84,13 @@ public static void SetupBlockWithValues(Block block, UInt256 val256, out UInt256 block.ConsensusData = new ConsensusData(); block.Transactions = transactionsVal; + block.MerkleRoot = merkRootVal = Block.CalculateMerkleRoot(block.ConsensusData.Hash, block.Transactions.Select(p => p.Hash)); } private static void setupBlockBaseWithValues(BlockBase bb, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal) { bb.PrevHash = val256; - merkRootVal = UInt256.Parse("0xd841af3d6bd7adb4bca24306725f9aec363edb10de3cafc5f8cca948d7b0290f"); + merkRootVal = UInt256.Parse("0x6226416a0e5aca42b5566f5a19ab467692688ba9d47986f6981a7f747bba2772"); bb.MerkleRoot = merkRootVal; timestampVal = new DateTime(1980, 06, 01, 0, 0, 1, 001, DateTimeKind.Utc).ToTimestampMS(); // GMT: Sunday, June 1, 1980 12:00:01.001 AM bb.Timestamp = timestampVal; @@ -99,7 +101,7 @@ private static void setupBlockBaseWithValues(BlockBase bb, UInt256 val256, out U scriptVal = new Witness { InvocationScript = new byte[0], - VerificationScript = new[] { (byte)OpCode.PUSHT } + VerificationScript = new[] { (byte)OpCode.PUSH1 } }; bb.Witness = scriptVal; } diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 5f1632824d..1923f48a66 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -23,27 +23,28 @@ public void TestEmit() { ScriptBuilder sb = new ScriptBuilder(); sb.Emit(new OpCode[] { OpCode.PUSH0 }); - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00 }), Encoding.Default.GetString(sb.ToArray())); + CollectionAssert.AreEqual(new[] { (byte)OpCode.PUSH0 }, sb.ToArray()); } [TestMethod] public void TestEmitAppCall1() { - //format:(byte)0x00+(byte)OpCode.NEWARRAY+(string)operation+(Uint160)scriptHash+(uint)InteropService.System_Contract_Call + //format:(byte)0x10+(byte)OpCode.NEWARRAY+(string)operation+(Uint160)scriptHash+(uint)InteropService.System_Contract_Call ScriptBuilder sb = new ScriptBuilder(); sb.EmitAppCall(UInt160.Zero, "AAAAA"); - byte[] tempArray = new byte[34]; - tempArray[0] = 0x00;//0 - tempArray[1] = 0xC5;//OpCode.NEWARRAY - tempArray[2] = 5;//operation.Length - Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 3, 5);//operation.data - tempArray[8] = 0x14;//scriptHash.Length - Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 9, 20);//operation.data + byte[] tempArray = new byte[36]; + tempArray[0] = (byte)OpCode.PUSH0; + tempArray[1] = (byte)OpCode.NEWARRAY; + 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 = InteropService.System_Contract_Call; - tempArray[29] = 0x68;//OpCode.SYSCALL - Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 30, 4);//api.data - byte[] resultArray = sb.ToArray(); - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(resultArray)); + tempArray[31] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 32, 4);//api.data + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } [TestMethod] @@ -52,19 +53,20 @@ 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) }); - byte[] tempArray = new byte[35]; - tempArray[0] = 0x00;//0 - tempArray[1] = 0x51;//ContractParameter.Length - tempArray[2] = 0xC1;//OpCode.PACK - tempArray[3] = 0x05;//operation.Length - Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 4, 5);//operation.data - tempArray[9] = 0x14;//scriptHash.Length - Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 10, 20);//operation.data + byte[] tempArray = new byte[37]; + tempArray[0] = (byte)OpCode.PUSH0; + tempArray[1] = (byte)OpCode.PUSH1; + tempArray[2] = (byte)OpCode.PACK; + tempArray[3] = (byte)OpCode.PUSHDATA1; + tempArray[4] = 0x05;//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 = InteropService.System_Contract_Call; - tempArray[30] = 0x68;//OpCode.SYSCALL - Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 31, 4);//api.data - byte[] resultArray = sb.ToArray(); - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(resultArray)); + tempArray[32] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } [TestMethod] @@ -73,19 +75,20 @@ 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); - byte[] tempArray = new byte[35]; - tempArray[0] = 0x51;//arg - tempArray[1] = 0x51;//args.Length - tempArray[2] = 0xC1;//OpCode.PACK - tempArray[3] = 0x05;//operation.Length - Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 4, 5);//operation.data - tempArray[9] = 0x14;//scriptHash.Length - Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 10, 20);//operation.data + byte[] tempArray = new byte[37]; + tempArray[0] = (byte)OpCode.PUSH1;//arg + tempArray[1] = (byte)OpCode.PUSH1;//args.Length + tempArray[2] = (byte)OpCode.PACK; + tempArray[3] = (byte)OpCode.PUSHDATA1; + tempArray[4] = 0x05;//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 = InteropService.System_Contract_Call; - tempArray[30] = 0x68;//OpCode.SYSCALL - Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 31, 4);//api.data - byte[] resultArray = sb.ToArray(); - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(resultArray)); + tempArray[32] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } [TestMethod] @@ -93,7 +96,7 @@ public void TestMakeScript() { byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - Assert.AreEqual("14000000000000000000000000000000000000000051c10962616c616e63654f66142582d1b275e86c8f0e93a9b2facd5fdb760976a168627d5b52", + Assert.AreEqual("0c14000000000000000000000000000000000000000011c10c0962616c616e63654f660c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b52", testScript.ToHexString()); } @@ -115,7 +118,7 @@ public void TestToParameter() StackItem arrayItem = new VM.Types.Array(new[] { byteItem, boolItem, intItem, interopItem }); Assert.AreEqual(1000, (BigInteger)(arrayItem.ToParameter().Value as List)[2].Value); - StackItem mapItem = new Map(new Dictionary { [(PrimitiveType)byteItem] = intItem }); + StackItem mapItem = new Map { [(PrimitiveType)byteItem] = intItem }; Assert.AreEqual(1000, (BigInteger)(mapItem.ToParameter().Value as List>)[0].Value.Value); } @@ -158,9 +161,10 @@ public void TestEmitPush1() { ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(UInt160.Zero); - byte[] tempArray = new byte[21]; - tempArray[0] = 0x14; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + byte[] tempArray = new byte[22]; + tempArray[0] = (byte)OpCode.PUSHDATA1; + tempArray[1] = 0x14; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } [TestMethod] @@ -192,47 +196,51 @@ private void TestEmitPush2Array() parameter.Value = values; sb.EmitPush(parameter); byte[] tempArray = new byte[4]; - tempArray[0] = 0x00; - tempArray[1] = 0x00; - tempArray[2] = 0x52; - tempArray[3] = 0xC1; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + tempArray[1] = (byte)OpCode.PUSH0; + tempArray[2] = (byte)OpCode.PUSH2; + tempArray[3] = (byte)OpCode.PACK; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush2String() { ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(new ContractParameter(ContractParameterType.String)); - byte[] tempArray = new byte[1]; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + byte[] tempArray = new byte[2]; + tempArray[0] = (byte)OpCode.PUSHDATA1; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush2PublicKey() { ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(new ContractParameter(ContractParameterType.PublicKey)); - byte[] tempArray = new byte[34]; - tempArray[0] = 0x21; - Array.Copy(ECCurve.Secp256r1.G.EncodePoint(true), 0, tempArray, 1, 33); - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + byte[] tempArray = new byte[35]; + tempArray[0] = (byte)OpCode.PUSHDATA1; + tempArray[1] = 0x21; + Array.Copy(ECCurve.Secp256r1.G.EncodePoint(true), 0, tempArray, 2, 33); + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush2Hash256() { ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(new ContractParameter(ContractParameterType.Hash256)); - byte[] tempArray = new byte[33]; - tempArray[0] = 0x20; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + byte[] tempArray = new byte[34]; + tempArray[0] = (byte)OpCode.PUSHDATA1; + tempArray[1] = 0x20; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush2Hash160() { ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(new ContractParameter(ContractParameterType.Hash160)); - byte[] tempArray = new byte[21]; - tempArray[0] = 0x14; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + byte[] tempArray = new byte[22]; + tempArray[0] = (byte)OpCode.PUSHDATA1; + tempArray[1] = 0x14; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush2BigInteger() @@ -244,8 +252,8 @@ private void TestEmitPush2BigInteger() }; sb.EmitPush(parameter); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush2Integer() @@ -254,8 +262,8 @@ private void TestEmitPush2Integer() ContractParameter parameter = new ContractParameter(ContractParameterType.Integer); sb.EmitPush(parameter); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush2Boolean() @@ -263,26 +271,28 @@ private void TestEmitPush2Boolean() ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(new ContractParameter(ContractParameterType.Boolean)); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush2ByteArray() { ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(new ContractParameter(ContractParameterType.ByteArray)); - byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + byte[] tempArray = new byte[2]; + tempArray[0] = (byte)OpCode.PUSHDATA1; + tempArray[1] = 0x00; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush2Signature() { ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(new ContractParameter(ContractParameterType.Signature)); - byte[] tempArray = new byte[65]; - tempArray[0] = 0x40; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + byte[] tempArray = new byte[66]; + tempArray[0] = (byte)OpCode.PUSHDATA1; + tempArray[1] = 0x40; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } enum TestEnum : byte @@ -319,8 +329,8 @@ private void TestEmitPush3Enum() ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(TestEnum.case1); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3Ulong() @@ -329,8 +339,8 @@ private void TestEmitPush3Ulong() ulong temp = 0; VM.Helper.EmitPush(sb, temp); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3Long() @@ -339,8 +349,8 @@ private void TestEmitPush3Long() long temp = 0; VM.Helper.EmitPush(sb, temp); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3Uint() @@ -349,8 +359,8 @@ private void TestEmitPush3Uint() uint temp = 0; VM.Helper.EmitPush(sb, temp); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3Int() @@ -359,8 +369,8 @@ private void TestEmitPush3Int() int temp = 0; VM.Helper.EmitPush(sb, temp); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3Ushort() @@ -369,8 +379,8 @@ private void TestEmitPush3Ushort() ushort temp = 0; VM.Helper.EmitPush(sb, temp); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3Short() @@ -379,8 +389,8 @@ private void TestEmitPush3Short() short temp = 0; VM.Helper.EmitPush(sb, temp); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3Byte() @@ -389,8 +399,8 @@ private void TestEmitPush3Byte() byte temp = 0; VM.Helper.EmitPush(sb, temp); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3Sbyte() @@ -399,17 +409,18 @@ private void TestEmitPush3Sbyte() sbyte temp = 0; VM.Helper.EmitPush(sb, temp); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3ISerializable() { ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(UInt160.Zero); - byte[] tempArray = new byte[21]; - tempArray[0] = 0x14; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + byte[] tempArray = new byte[22]; + tempArray[0] = (byte)OpCode.PUSHDATA1; + tempArray[1] = 0x14; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3BigInteger() @@ -417,27 +428,29 @@ private void TestEmitPush3BigInteger() ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(BigInteger.Zero); byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH0; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3String() { ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(""); - byte[] tempArray = new byte[1]; - tempArray[0] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + byte[] tempArray = new byte[2]; + tempArray[0] = (byte)OpCode.PUSHDATA1; + tempArray[1] = 0x00; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3ByteArray() { ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(new byte[] { 0x01 }); - byte[] tempArray = new byte[2]; - tempArray[0] = 0x01; + byte[] tempArray = new byte[3]; + tempArray[0] = (byte)OpCode.PUSHDATA1; tempArray[1] = 0x01; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[2] = 0x01; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } private void TestEmitPush3Bool() @@ -445,8 +458,8 @@ private void TestEmitPush3Bool() ScriptBuilder sb = new ScriptBuilder(); sb.EmitPush(true); byte[] tempArray = new byte[1]; - tempArray[0] = 0x51; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + tempArray[0] = (byte)OpCode.PUSH1; + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } [TestMethod] @@ -455,13 +468,13 @@ public void TestEmitSysCall() ScriptBuilder sb = new ScriptBuilder(); sb.EmitSysCall(0, true); byte[] tempArray = new byte[6]; - tempArray[0] = 0x51; - tempArray[1] = 0x68; + tempArray[0] = (byte)OpCode.PUSH1; + tempArray[1] = (byte)OpCode.SYSCALL; tempArray[2] = 0x00; tempArray[3] = 0x00; tempArray[4] = 0x00; tempArray[5] = 0x00; - Assert.AreEqual(Encoding.Default.GetString(tempArray), Encoding.Default.GetString(sb.ToArray())); + CollectionAssert.AreEqual(tempArray, sb.ToArray()); } [TestMethod] diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs index 70d0ba2e4b..19eaa434dc 100644 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs +++ b/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs @@ -120,12 +120,12 @@ public void TestSerialize() byte[] byteArray = new byte[stream.Length]; stream.Read(byteArray, 0, (int)stream.Length); byte[] script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey); - byte[] result = new byte[63]; + byte[] result = new byte[64]; result[20] = 0x01; result[21] = 0x00; - result[22] = 0x28; - Array.Copy(script, 0, result, 23, 40); - Assert.AreEqual(Encoding.Default.GetString(result), Encoding.Default.GetString(byteArray)); + result[22] = 0x29; + Array.Copy(script, 0, result, 23, 41); + CollectionAssert.AreEqual(result, byteArray); } [TestMethod] @@ -142,7 +142,7 @@ public void TestGetSize() Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), ParameterList = new[] { ContractParameterType.Signature } }; - Assert.AreEqual(63, contract1.Size); + Assert.AreEqual(64, contract1.Size); } } } diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index ed02f4d61e..a210cf581e 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -166,9 +166,9 @@ public void TestGetVersion() public void TestGetAccount1() { MyWallet wallet = new MyWallet(); - wallet.CreateAccount(UInt160.Parse("0xc43d04da83afcf7df3b2908c169cfbfbf7512d7f")); + wallet.CreateAccount(UInt160.Parse("0x7e471cf52f27edc291e29ec8f2d1ea2d210d6725")); WalletAccount account = wallet.GetAccount(ECCurve.Secp256r1.G); - account.ScriptHash.Should().Be(UInt160.Parse("0xc43d04da83afcf7df3b2908c169cfbfbf7512d7f")); + account.ScriptHash.Should().Be(UInt160.Parse("0x7e471cf52f27edc291e29ec8f2d1ea2d210d6725")); } [TestMethod] From fd951e6cf7dea91446bdad8630505dd676be4dfe Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 15 Dec 2019 17:11:17 +0800 Subject: [PATCH 183/305] Fix SeedList initialization (#1363) --- src/neo/Network/P2P/LocalNode.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index 680437e430..bda237c219 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -62,7 +62,10 @@ public LocalNode(NeoSystem system) // Start dns resolution in parallel for (int i = 0; i < ProtocolSettings.Default.SeedList.Length; i++) - Task.Run(() => SeedList[i] = GetIpEndPoint(ProtocolSettings.Default.SeedList[i])); + { + int index = i; + Task.Run(() => SeedList[index] = GetIpEndPoint(ProtocolSettings.Default.SeedList[index])); + } } } From 203aa9f3257dec76b160f89df2b2ebd420e8ab7f Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 15 Dec 2019 17:20:28 +0800 Subject: [PATCH 184/305] AssemblyResolve (#1349) --- src/neo/Plugins/Plugin.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs index 36adac6a77..61af339936 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -85,16 +85,23 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven if (args.Name.Contains(".resources")) return null; + AssemblyName an = new AssemblyName(args.Name); + Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name); - if (assembly != null) - return assembly; + if (assembly is null) + assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == an.Name); + if (assembly != null) return assembly; - AssemblyName an = new AssemblyName(args.Name); string filename = an.Name + ".dll"; + string path = filename; + if (!File.Exists(path)) path = Combine(GetDirectoryName(Assembly.GetEntryAssembly().Location), filename); + if (!File.Exists(path)) path = Combine(PluginsDirectory, filename); + if (!File.Exists(path)) path = Combine(PluginsDirectory, args.RequestingAssembly.GetName().Name, filename); + if (!File.Exists(path)) return null; try { - return Assembly.Load(File.ReadAllBytes(filename)); + return Assembly.Load(File.ReadAllBytes(path)); } catch (Exception ex) { From ae66b594646b36c7b0c86eba6a47e55dc50e1be1 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 15 Dec 2019 18:42:13 +0800 Subject: [PATCH 185/305] Rename SYSCALLs (#1362) --- src/neo/Ledger/Blockchain.cs | 2 +- ...kItemSerializer.cs => BinarySerializer.cs} | 2 +- src/neo/SmartContract/Contract.cs | 4 +- src/neo/SmartContract/Helper.cs | 4 +- src/neo/SmartContract/InteropDescriptor.cs | 13 +- .../SmartContract/InteropService.Binary.cs | 50 ++ .../InteropService.Blockchain.cs | 107 ++++ .../SmartContract/InteropService.Contract.cs | 153 +++++ .../SmartContract/InteropService.Crypto.cs | 116 ++++ .../InteropService.Enumerator.cs | 69 +++ .../SmartContract/InteropService.Iterator.cs | 84 +++ src/neo/SmartContract/InteropService.Json.cs | 45 ++ src/neo/SmartContract/InteropService.NEO.cs | 408 ------------- .../SmartContract/InteropService.Native.cs | 34 ++ .../SmartContract/InteropService.Runtime.cs | 215 +++++++ .../SmartContract/InteropService.Storage.cs | 180 ++++++ src/neo/SmartContract/InteropService.cs | 545 +----------------- .../SmartContract/Native/PolicyContract.cs | 2 +- .../SmartContract/Native/Tokens/NeoToken.cs | 2 +- .../Native/Tokens/Nep5AccountState.cs | 4 +- .../SmartContract/Native/Tokens/Nep5Token.cs | 2 +- src/neo/VM/Helper.cs | 6 +- src/neo/Wallets/Wallet.cs | 4 +- .../Native/Tokens/UT_NeoToken.cs | 2 +- ...emSerializer.cs => UT_BinarySerializer.cs} | 50 +- .../SmartContract/UT_Contract.cs | 12 +- .../SmartContract/UT_InteropPrices.cs | 12 +- .../SmartContract/UT_InteropService.NEO.cs | 82 +-- .../SmartContract/UT_InteropService.cs | 132 ++--- .../SmartContract/UT_Syscalls.cs | 40 +- tests/neo.UnitTests/VM/UT_Helper.cs | 6 +- 31 files changed, 1256 insertions(+), 1131 deletions(-) rename src/neo/SmartContract/{StackItemSerializer.cs => BinarySerializer.cs} (99%) create mode 100644 src/neo/SmartContract/InteropService.Binary.cs create mode 100644 src/neo/SmartContract/InteropService.Blockchain.cs create mode 100644 src/neo/SmartContract/InteropService.Contract.cs create mode 100644 src/neo/SmartContract/InteropService.Crypto.cs create mode 100644 src/neo/SmartContract/InteropService.Enumerator.cs create mode 100644 src/neo/SmartContract/InteropService.Iterator.cs create mode 100644 src/neo/SmartContract/InteropService.Json.cs delete mode 100644 src/neo/SmartContract/InteropService.NEO.cs create mode 100644 src/neo/SmartContract/InteropService.Native.cs create mode 100644 src/neo/SmartContract/InteropService.Runtime.cs create mode 100644 src/neo/SmartContract/InteropService.Storage.cs rename tests/neo.UnitTests/SmartContract/{UT_StackItemSerializer.cs => UT_BinarySerializer.cs} (67%) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 819289b333..74d06806e4 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -157,7 +157,7 @@ private static Transaction DeployNativeContracts() byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitSysCall(InteropService.Neo_Native_Deploy); + sb.EmitSysCall(InteropService.Native.Deploy); script = sb.ToArray(); } return new Transaction diff --git a/src/neo/SmartContract/StackItemSerializer.cs b/src/neo/SmartContract/BinarySerializer.cs similarity index 99% rename from src/neo/SmartContract/StackItemSerializer.cs rename to src/neo/SmartContract/BinarySerializer.cs index 24f5128ff1..1c8f2f9088 100644 --- a/src/neo/SmartContract/StackItemSerializer.cs +++ b/src/neo/SmartContract/BinarySerializer.cs @@ -11,7 +11,7 @@ namespace Neo.SmartContract { - internal static class StackItemSerializer + internal static class BinarySerializer { public static StackItem Deserialize(byte[] data, uint maxItemSize, ReferenceCounter referenceCounter = null) { diff --git a/src/neo/SmartContract/Contract.cs b/src/neo/SmartContract/Contract.cs index 63384af675..022e6a4c3f 100644 --- a/src/neo/SmartContract/Contract.cs +++ b/src/neo/SmartContract/Contract.cs @@ -82,7 +82,7 @@ public static byte[] CreateMultiSigRedeemScript(int m, params ECPoint[] publicKe } sb.EmitPush(publicKeys.Length); sb.Emit(OpCode.PUSHNULL); - sb.EmitSysCall(InteropService.Neo_Crypto_ECDsaCheckMultiSig); + sb.EmitSysCall(InteropService.Crypto.ECDsaCheckMultiSig); return sb.ToArray(); } } @@ -102,7 +102,7 @@ public static byte[] CreateSignatureRedeemScript(ECPoint publicKey) { sb.EmitPush(publicKey.EncodePoint(true)); sb.Emit(OpCode.PUSHNULL); - sb.EmitSysCall(InteropService.Neo_Crypto_ECDsaVerify); + sb.EmitSysCall(InteropService.Crypto.ECDsaVerify); return sb.ToArray(); } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 0bb328bd0a..1e6b29e7bb 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -62,7 +62,7 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n) if (script[i++] != (byte)OpCode.PUSHNULL) return false; if (script[i++] != (byte)OpCode.SYSCALL) return false; if (script.Length != i + 4) return false; - if (BitConverter.ToUInt32(script, i) != InteropService.Neo_Crypto_ECDsaCheckMultiSig) + if (BitConverter.ToUInt32(script, i) != InteropService.Crypto.ECDsaCheckMultiSig) return false; return true; } @@ -74,7 +74,7 @@ public static bool IsSignatureContract(this byte[] script) || script[1] != 33 || script[35] != (byte)OpCode.PUSHNULL || script[36] != (byte)OpCode.SYSCALL - || BitConverter.ToUInt32(script, 37) != InteropService.Neo_Crypto_ECDsaVerify) + || BitConverter.ToUInt32(script, 37) != InteropService.Crypto.ECDsaVerify) return false; return true; } diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index 9ae9e5c992..5cdcf6024a 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -3,22 +3,22 @@ namespace Neo.SmartContract { - internal class InteropDescriptor + public class InteropDescriptor { public string Method { get; } public uint Hash { get; } - public Func Handler { get; } + internal Func Handler { get; } public long Price { get; } public Func PriceCalculator { get; } public TriggerType AllowedTriggers { get; } - public InteropDescriptor(string method, Func handler, long price, TriggerType allowedTriggers) + internal InteropDescriptor(string method, Func handler, long price, TriggerType allowedTriggers) : this(method, handler, allowedTriggers) { this.Price = price; } - public InteropDescriptor(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers) + internal InteropDescriptor(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers) : this(method, handler, allowedTriggers) { this.PriceCalculator = priceCalculator; @@ -36,5 +36,10 @@ public long GetPrice(EvaluationStack stack) { return PriceCalculator is null ? Price : PriceCalculator(stack); } + + public static implicit operator uint(InteropDescriptor descriptor) + { + return descriptor.Hash; + } } } diff --git a/src/neo/SmartContract/InteropService.Binary.cs b/src/neo/SmartContract/InteropService.Binary.cs new file mode 100644 index 0000000000..450e4744c5 --- /dev/null +++ b/src/neo/SmartContract/InteropService.Binary.cs @@ -0,0 +1,50 @@ +using Neo.VM; +using Neo.VM.Types; +using System; +using System.IO; + +namespace Neo.SmartContract +{ + partial class InteropService + { + public static class Binary + { + public static readonly InteropDescriptor Serialize = Register("System.Binary.Serialize", Binary_Serialize, 0_00100000, TriggerType.All); + public static readonly InteropDescriptor Deserialize = Register("System.Binary.Deserialize", Binary_Deserialize, 0_00500000, TriggerType.All); + + private static bool Binary_Serialize(ApplicationEngine engine) + { + byte[] serialized; + try + { + serialized = BinarySerializer.Serialize(engine.CurrentContext.EvaluationStack.Pop(), engine.MaxItemSize); + } + catch + { + return false; + } + engine.CurrentContext.EvaluationStack.Push(serialized); + return true; + } + + private static bool Binary_Deserialize(ApplicationEngine engine) + { + StackItem item; + try + { + item = BinarySerializer.Deserialize(engine.CurrentContext.EvaluationStack.Pop().GetSpan(), engine.MaxItemSize, engine.ReferenceCounter); + } + catch (FormatException) + { + return false; + } + catch (IOException) + { + return false; + } + engine.CurrentContext.EvaluationStack.Push(item); + return true; + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.Blockchain.cs b/src/neo/SmartContract/InteropService.Blockchain.cs new file mode 100644 index 0000000000..d5cdad0769 --- /dev/null +++ b/src/neo/SmartContract/InteropService.Blockchain.cs @@ -0,0 +1,107 @@ +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Numerics; + +namespace Neo.SmartContract +{ + partial class InteropService + { + public static class Blockchain + { + public static readonly InteropDescriptor GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); + public static readonly InteropDescriptor GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application); + public static readonly InteropDescriptor GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application); + public static readonly InteropDescriptor GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application); + public static readonly InteropDescriptor GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application); + public static readonly InteropDescriptor GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application); + + private static bool Blockchain_GetHeight(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.Height); + return true; + } + + private static bool Blockchain_GetBlock(ApplicationEngine engine) + { + ReadOnlySpan data = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); + UInt256 hash; + if (data.Length <= 5) + hash = Ledger.Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); + else if (data.Length == 32) + hash = new UInt256(data); + else + return false; + + Block block = hash != null ? engine.Snapshot.GetBlock(hash) : null; + if (block == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + else + engine.CurrentContext.EvaluationStack.Push(block.ToStackItem(engine.ReferenceCounter)); + return true; + } + + private static bool Blockchain_GetTransaction(ApplicationEngine engine) + { + ReadOnlySpan hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); + Transaction tx = engine.Snapshot.GetTransaction(new UInt256(hash)); + if (tx == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + else + engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem(engine.ReferenceCounter)); + return true; + } + + private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine) + { + ReadOnlySpan hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); + var tx = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); + engine.CurrentContext.EvaluationStack.Push(tx != null ? new BigInteger(tx.BlockIndex) : BigInteger.MinusOne); + return true; + } + + private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) + { + ReadOnlySpan data = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); + UInt256 hash; + if (data.Length <= 5) + hash = Ledger.Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); + else if (data.Length == 32) + hash = new UInt256(data); + else + return false; + + TrimmedBlock block = hash != null ? engine.Snapshot.Blocks.TryGet(hash) : null; + if (block == null) + { + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + } + else + { + int index = (int)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger(); + if (index < 0 || index >= block.Hashes.Length - 1) return false; + + Transaction tx = engine.Snapshot.GetTransaction(block.Hashes[index + 1]); + if (tx == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + else + engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem(engine.ReferenceCounter)); + } + return true; + } + + private static bool Blockchain_GetContract(ApplicationEngine engine) + { + UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan()); + ContractState contract = engine.Snapshot.Contracts.TryGet(hash); + if (contract == null) + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + else + engine.CurrentContext.EvaluationStack.Push(contract.ToStackItem(engine.ReferenceCounter)); + return true; + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs new file mode 100644 index 0000000000..0e50ea8f6d --- /dev/null +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -0,0 +1,153 @@ +using Neo.IO; +using Neo.Ledger; +using Neo.SmartContract.Manifest; +using Neo.VM; +using Neo.VM.Types; +using System.Linq; + +namespace Neo.SmartContract +{ + partial class InteropService + { + public static class Contract + { + public static readonly InteropDescriptor Create = Register("System.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application); + public static readonly InteropDescriptor Update = Register("System.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application); + public static readonly InteropDescriptor Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application); + public static readonly InteropDescriptor Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application); + public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All); + + private static long GetDeploymentPrice(EvaluationStack stack) + { + int size = stack.Peek(0).GetByteLength() + stack.Peek(1).GetByteLength(); + return Storage.GasPerByte * size; + } + + private static bool Contract_Create(ApplicationEngine engine) + { + byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + if (script.Length > 1024 * 1024) return false; + + var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString(); + if (manifest.Length > ContractManifest.MaxLength) return false; + + UInt160 hash = script.ToScriptHash(); + ContractState contract = engine.Snapshot.Contracts.TryGet(hash); + if (contract != null) return false; + contract = new ContractState + { + Script = script, + Manifest = ContractManifest.Parse(manifest) + }; + + if (!contract.Manifest.IsValid(hash)) return false; + + engine.Snapshot.Contracts.Add(hash, contract); + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(contract)); + return true; + } + + private static bool Contract_Update(ApplicationEngine engine) + { + byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + if (script.Length > 1024 * 1024) return false; + var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString(); + if (manifest.Length > ContractManifest.MaxLength) return false; + + var contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash); + if (contract is null) return false; + + if (script.Length > 0) + { + UInt160 hash_new = script.ToScriptHash(); + if (hash_new.Equals(engine.CurrentScriptHash)) return false; + if (engine.Snapshot.Contracts.TryGet(hash_new) != null) return false; + contract = new ContractState + { + Script = script, + Manifest = contract.Manifest + }; + contract.Manifest.Abi.Hash = hash_new; + engine.Snapshot.Contracts.Add(hash_new, contract); + if (contract.HasStorage) + { + foreach (var (key, value) in engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).ToArray()) + { + engine.Snapshot.Storages.Add(new StorageKey + { + ScriptHash = hash_new, + Key = key.Key + }, new StorageItem + { + Value = value.Value, + IsConstant = false + }); + } + } + Contract_Destroy(engine); + } + if (manifest.Length > 0) + { + contract = engine.Snapshot.Contracts.GetAndChange(contract.ScriptHash); + contract.Manifest = ContractManifest.Parse(manifest); + if (!contract.Manifest.IsValid(contract.ScriptHash)) return false; + if (!contract.HasStorage && engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).Any()) return false; + } + + return true; + } + + private static bool Contract_Destroy(ApplicationEngine engine) + { + UInt160 hash = engine.CurrentScriptHash; + ContractState contract = engine.Snapshot.Contracts.TryGet(hash); + if (contract == null) return true; + engine.Snapshot.Contracts.Delete(hash); + if (contract.HasStorage) + foreach (var (key, _) in engine.Snapshot.Storages.Find(hash.ToArray())) + engine.Snapshot.Storages.Delete(key); + return true; + } + + private static bool Contract_Call(ApplicationEngine engine) + { + StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop(); + + ContractState contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractHash.GetSpan())); + if (contract is null) return false; + + StackItem method = engine.CurrentContext.EvaluationStack.Pop(); + StackItem args = engine.CurrentContext.EvaluationStack.Pop(); + ContractManifest currentManifest = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash)?.Manifest; + + if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method.GetString())) + return false; + + if (engine.InvocationCounter.TryGetValue(contract.ScriptHash, out var counter)) + { + engine.InvocationCounter[contract.ScriptHash] = counter + 1; + } + else + { + engine.InvocationCounter[contract.ScriptHash] = 1; + } + + UInt160 callingScriptHash = engine.CurrentScriptHash; + ExecutionContext context_new = engine.LoadScript(contract.Script, 1); + context_new.GetState().CallingScriptHash = callingScriptHash; + context_new.EvaluationStack.Push(args); + context_new.EvaluationStack.Push(method); + return true; + } + + private static bool Contract_IsStandard(ApplicationEngine engine) + { + UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan()); + ContractState contract = engine.Snapshot.Contracts.TryGet(hash); + bool isStandard = contract is null || contract.Script.IsStandardContract(); + engine.CurrentContext.EvaluationStack.Push(isStandard); + return true; + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.Crypto.cs b/src/neo/SmartContract/InteropService.Crypto.cs new file mode 100644 index 0000000000..f1c2f491e0 --- /dev/null +++ b/src/neo/SmartContract/InteropService.Crypto.cs @@ -0,0 +1,116 @@ +using Neo.Cryptography; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Linq; +using Array = Neo.VM.Types.Array; + +namespace Neo.SmartContract +{ + partial class InteropService + { + public static class Crypto + { + public static readonly InteropDescriptor ECDsaVerify = Register("Neo.Crypto.ECDsaVerify", Crypto_ECDsaVerify, 0_01000000, TriggerType.All); + public static readonly InteropDescriptor ECDsaCheckMultiSig = Register("Neo.Crypto.ECDsaCheckMultiSig", Crypto_ECDsaCheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All); + + private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack) + { + if (stack.Count < 2) return 0; + var item = stack.Peek(1); + int n; + if (item is Array array) n = array.Count; + else n = (int)item.GetBigInteger(); + if (n < 1) return 0; + return ECDsaVerify.Price * n; + } + + private static bool Crypto_ECDsaVerify(ApplicationEngine engine) + { + StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); + ReadOnlySpan message = item0 switch + { + InteropInterface _interface => _interface.GetInterface().GetHashData(), + Null _ => engine.ScriptContainer.GetHashData(), + _ => item0.GetSpan() + }; + ReadOnlySpan pubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); + ReadOnlySpan signature = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); + try + { + engine.CurrentContext.EvaluationStack.Push(Cryptography.Crypto.VerifySignature(message, signature, pubkey)); + } + catch (ArgumentException) + { + engine.CurrentContext.EvaluationStack.Push(false); + } + return true; + } + + private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) + { + StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); + ReadOnlySpan message = item0 switch + { + InteropInterface _interface => _interface.GetInterface().GetHashData(), + Null _ => engine.ScriptContainer.GetHashData(), + _ => item0.GetSpan() + }; + int n; + byte[][] pubkeys; + StackItem item = engine.CurrentContext.EvaluationStack.Pop(); + if (item is Array array1) + { + pubkeys = array1.Select(p => p.GetSpan().ToArray()).ToArray(); + n = pubkeys.Length; + if (n == 0) return false; + } + else + { + n = (int)item.GetBigInteger(); + if (n < 1 || n > engine.CurrentContext.EvaluationStack.Count) return false; + pubkeys = new byte[n][]; + for (int i = 0; i < n; i++) + pubkeys[i] = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + } + int m; + byte[][] signatures; + item = engine.CurrentContext.EvaluationStack.Pop(); + if (item is Array array2) + { + signatures = array2.Select(p => p.GetSpan().ToArray()).ToArray(); + m = signatures.Length; + if (m == 0 || m > n) return false; + } + else + { + m = (int)item.GetBigInteger(); + if (m < 1 || m > n || m > engine.CurrentContext.EvaluationStack.Count) return false; + signatures = new byte[m][]; + for (int i = 0; i < m; i++) + signatures[i] = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + } + bool fSuccess = true; + try + { + for (int i = 0, j = 0; fSuccess && i < m && j < n;) + { + if (Cryptography.Crypto.VerifySignature(message, signatures[i], pubkeys[j])) + i++; + j++; + if (m - i > n - j) + fSuccess = false; + } + } + catch (ArgumentException) + { + fSuccess = false; + } + engine.CurrentContext.EvaluationStack.Push(fSuccess); + return true; + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.Enumerator.cs b/src/neo/SmartContract/InteropService.Enumerator.cs new file mode 100644 index 0000000000..143e040ba4 --- /dev/null +++ b/src/neo/SmartContract/InteropService.Enumerator.cs @@ -0,0 +1,69 @@ +using Neo.SmartContract.Enumerators; +using Neo.SmartContract.Iterators; +using Neo.VM.Types; +using Array = Neo.VM.Types.Array; + +namespace Neo.SmartContract +{ + partial class InteropService + { + public static class Enumerator + { + public static readonly InteropDescriptor Create = Register("System.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor Next = Register("System.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All); + public static readonly InteropDescriptor Value = Register("System.Enumerator.Value", Enumerator_Value, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor Concat = Register("System.Enumerator.Concat", Enumerator_Concat, 0_00000400, TriggerType.All); + + private static bool Enumerator_Create(ApplicationEngine engine) + { + IEnumerator enumerator; + switch (engine.CurrentContext.EvaluationStack.Pop()) + { + case Array array: + enumerator = new ArrayWrapper(array); + break; + case PrimitiveType primitive: + enumerator = new ByteArrayWrapper(primitive); + break; + default: + return false; + } + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(enumerator)); + return true; + } + + private static bool Enumerator_Next(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + IEnumerator enumerator = _interface.GetInterface(); + engine.CurrentContext.EvaluationStack.Push(enumerator.Next()); + return true; + } + return false; + } + + private static bool Enumerator_Value(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + IEnumerator enumerator = _interface.GetInterface(); + engine.CurrentContext.EvaluationStack.Push(enumerator.Value()); + return true; + } + return false; + } + + private static bool Enumerator_Concat(ApplicationEngine engine) + { + if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface1)) return false; + if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface2)) return false; + IEnumerator first = _interface1.GetInterface(); + IEnumerator second = _interface2.GetInterface(); + IEnumerator result = new ConcatenatedEnumerator(first, second); + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(result)); + return true; + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.Iterator.cs b/src/neo/SmartContract/InteropService.Iterator.cs new file mode 100644 index 0000000000..0a99e2848b --- /dev/null +++ b/src/neo/SmartContract/InteropService.Iterator.cs @@ -0,0 +1,84 @@ +using Neo.SmartContract.Enumerators; +using Neo.SmartContract.Iterators; +using Neo.VM.Types; +using Array = Neo.VM.Types.Array; + +namespace Neo.SmartContract +{ + partial class InteropService + { + public static class Iterator + { + public static readonly InteropDescriptor Create = Register("System.Iterator.Create", Iterator_Create, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor Key = Register("System.Iterator.Key", Iterator_Key, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor Keys = Register("System.Iterator.Keys", Iterator_Keys, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor Values = Register("System.Iterator.Values", Iterator_Values, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor Concat = Register("System.Iterator.Concat", Iterator_Concat, 0_00000400, TriggerType.All); + + private static bool Iterator_Create(ApplicationEngine engine) + { + IIterator iterator; + switch (engine.CurrentContext.EvaluationStack.Pop()) + { + case Array array: + iterator = new ArrayWrapper(array); + break; + case Map map: + iterator = new MapWrapper(map); + break; + case PrimitiveType primitive: + iterator = new ByteArrayWrapper(primitive); + break; + default: + return false; + } + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator)); + return true; + } + + private static bool Iterator_Key(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + IIterator iterator = _interface.GetInterface(); + engine.CurrentContext.EvaluationStack.Push(iterator.Key()); + return true; + } + return false; + } + + private static bool Iterator_Keys(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + IIterator iterator = _interface.GetInterface(); + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new IteratorKeysWrapper(iterator))); + return true; + } + return false; + } + + private static bool Iterator_Values(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + IIterator iterator = _interface.GetInterface(); + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new IteratorValuesWrapper(iterator))); + return true; + } + return false; + } + + private static bool Iterator_Concat(ApplicationEngine engine) + { + if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface1)) return false; + if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface2)) return false; + IIterator first = _interface1.GetInterface(); + IIterator second = _interface2.GetInterface(); + IIterator result = new ConcatenatedIterator(first, second); + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(result)); + return true; + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.Json.cs b/src/neo/SmartContract/InteropService.Json.cs new file mode 100644 index 0000000000..45e12db545 --- /dev/null +++ b/src/neo/SmartContract/InteropService.Json.cs @@ -0,0 +1,45 @@ +using Neo.IO.Json; +using Neo.VM; + +namespace Neo.SmartContract +{ + partial class InteropService + { + public static class Json + { + public static readonly InteropDescriptor Serialize = Register("System.Json.Serialize", Json_Serialize, 0_00100000, TriggerType.All); + public static readonly InteropDescriptor Deserialize = Register("System.Json.Deserialize", Json_Deserialize, 0_00500000, TriggerType.All); + + private static bool Json_Serialize(ApplicationEngine engine) + { + var item = engine.CurrentContext.EvaluationStack.Pop(); + try + { + var json = JsonSerializer.SerializeToByteArray(item, engine.MaxItemSize); + engine.CurrentContext.EvaluationStack.Push(json); + return true; + } + catch + { + return false; + } + } + + private static bool Json_Deserialize(ApplicationEngine engine) + { + var json = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); + try + { + var obj = JObject.Parse(json, 10); + var item = JsonSerializer.Deserialize(obj, engine.ReferenceCounter); + engine.CurrentContext.EvaluationStack.Push(item); + return true; + } + catch + { + return false; + } + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.NEO.cs b/src/neo/SmartContract/InteropService.NEO.cs deleted file mode 100644 index 5ba3675090..0000000000 --- a/src/neo/SmartContract/InteropService.NEO.cs +++ /dev/null @@ -1,408 +0,0 @@ -using Neo.Cryptography; -using Neo.IO; -using Neo.IO.Json; -using Neo.Ledger; -using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; -using Neo.SmartContract.Enumerators; -using Neo.SmartContract.Iterators; -using Neo.SmartContract.Manifest; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.VM.Types; -using System; -using System.Linq; -using VMArray = Neo.VM.Types.Array; - -namespace Neo.SmartContract -{ - static partial class InteropService - { - public static readonly uint Neo_Native_Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application); - public static readonly uint Neo_Crypto_ECDsaVerify = Register("Neo.Crypto.ECDsaVerify", Crypto_ECDsaVerify, 0_01000000, TriggerType.All); - public static readonly uint Neo_Crypto_ECDsaCheckMultiSig = Register("Neo.Crypto.ECDsaCheckMultiSig", Crypto_ECDsaCheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All); - public static readonly uint Neo_Account_IsStandard = Register("Neo.Account.IsStandard", Account_IsStandard, 0_00030000, TriggerType.All); - public static readonly uint Neo_Contract_Create = Register("Neo.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application); - public static readonly uint Neo_Contract_Update = Register("Neo.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application); - public static readonly uint Neo_Storage_Find = Register("Neo.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application); - public static readonly uint Neo_Enumerator_Create = Register("Neo.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All); - public static readonly uint Neo_Enumerator_Next = Register("Neo.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All); - public static readonly uint Neo_Enumerator_Value = Register("Neo.Enumerator.Value", Enumerator_Value, 0_00000400, TriggerType.All); - public static readonly uint Neo_Enumerator_Concat = Register("Neo.Enumerator.Concat", Enumerator_Concat, 0_00000400, TriggerType.All); - public static readonly uint Neo_Iterator_Create = Register("Neo.Iterator.Create", Iterator_Create, 0_00000400, TriggerType.All); - public static readonly uint Neo_Iterator_Key = Register("Neo.Iterator.Key", Iterator_Key, 0_00000400, TriggerType.All); - public static readonly uint Neo_Iterator_Keys = Register("Neo.Iterator.Keys", Iterator_Keys, 0_00000400, TriggerType.All); - public static readonly uint Neo_Iterator_Values = Register("Neo.Iterator.Values", Iterator_Values, 0_00000400, TriggerType.All); - public static readonly uint Neo_Iterator_Concat = Register("Neo.Iterator.Concat", Iterator_Concat, 0_00000400, TriggerType.All); - public static readonly uint Neo_Json_Serialize = Register("Neo.Json.Serialize", Json_Serialize, 0_00100000, TriggerType.All); - public static readonly uint Neo_Json_Deserialize = Register("Neo.Json.Deserialize", Json_Deserialize, 0_00500000, TriggerType.All); - - static InteropService() - { - foreach (NativeContract contract in NativeContract.Contracts) - Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application); - } - - private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack) - { - if (stack.Count < 2) return 0; - var item = stack.Peek(1); - int n; - if (item is VMArray array) n = array.Count; - else n = (int)item.GetBigInteger(); - if (n < 1) return 0; - return GetPrice(Neo_Crypto_ECDsaVerify, stack) * n; - } - - private static long GetDeploymentPrice(EvaluationStack stack) - { - int size = stack.Peek(0).GetByteLength() + stack.Peek(1).GetByteLength(); - return GasPerByte * size; - } - - private static bool Native_Deploy(ApplicationEngine engine) - { - if (engine.Snapshot.PersistingBlock.Index != 0) return false; - foreach (NativeContract contract in NativeContract.Contracts) - { - engine.Snapshot.Contracts.Add(contract.Hash, new ContractState - { - Script = contract.Script, - Manifest = contract.Manifest - }); - contract.Initialize(engine); - } - return true; - } - - private static bool Crypto_ECDsaVerify(ApplicationEngine engine) - { - StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); - ReadOnlySpan message = item0 switch - { - InteropInterface _interface => _interface.GetInterface().GetHashData(), - Null _ => engine.ScriptContainer.GetHashData(), - _ => item0.GetSpan() - }; - ReadOnlySpan pubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - ReadOnlySpan signature = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - try - { - engine.CurrentContext.EvaluationStack.Push(Crypto.VerifySignature(message, signature, pubkey)); - } - catch (ArgumentException) - { - engine.CurrentContext.EvaluationStack.Push(false); - } - return true; - } - - private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) - { - StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); - ReadOnlySpan message = item0 switch - { - InteropInterface _interface => _interface.GetInterface().GetHashData(), - Null _ => engine.ScriptContainer.GetHashData(), - _ => item0.GetSpan() - }; - int n; - byte[][] pubkeys; - StackItem item = engine.CurrentContext.EvaluationStack.Pop(); - if (item is VMArray array1) - { - pubkeys = array1.Select(p => p.GetSpan().ToArray()).ToArray(); - n = pubkeys.Length; - if (n == 0) return false; - } - else - { - n = (int)item.GetBigInteger(); - if (n < 1 || n > engine.CurrentContext.EvaluationStack.Count) return false; - pubkeys = new byte[n][]; - for (int i = 0; i < n; i++) - pubkeys[i] = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - } - int m; - byte[][] signatures; - item = engine.CurrentContext.EvaluationStack.Pop(); - if (item is VMArray array2) - { - signatures = array2.Select(p => p.GetSpan().ToArray()).ToArray(); - m = signatures.Length; - if (m == 0 || m > n) return false; - } - else - { - m = (int)item.GetBigInteger(); - if (m < 1 || m > n || m > engine.CurrentContext.EvaluationStack.Count) return false; - signatures = new byte[m][]; - for (int i = 0; i < m; i++) - signatures[i] = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - } - bool fSuccess = true; - try - { - for (int i = 0, j = 0; fSuccess && i < m && j < n;) - { - if (Crypto.VerifySignature(message, signatures[i], pubkeys[j])) - i++; - j++; - if (m - i > n - j) - fSuccess = false; - } - } - catch (ArgumentException) - { - fSuccess = false; - } - engine.CurrentContext.EvaluationStack.Push(fSuccess); - return true; - } - - private static bool Account_IsStandard(ApplicationEngine engine) - { - UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan()); - ContractState contract = engine.Snapshot.Contracts.TryGet(hash); - bool isStandard = contract is null || contract.Script.IsStandardContract(); - engine.CurrentContext.EvaluationStack.Push(isStandard); - return true; - } - - private static bool Contract_Create(ApplicationEngine engine) - { - byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - if (script.Length > 1024 * 1024) return false; - - var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString(); - if (manifest.Length > ContractManifest.MaxLength) return false; - - UInt160 hash = script.ToScriptHash(); - ContractState contract = engine.Snapshot.Contracts.TryGet(hash); - if (contract != null) return false; - contract = new ContractState - { - Script = script, - Manifest = ContractManifest.Parse(manifest) - }; - - if (!contract.Manifest.IsValid(hash)) return false; - - engine.Snapshot.Contracts.Add(hash, contract); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(contract)); - return true; - } - - private static bool Contract_Update(ApplicationEngine engine) - { - byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - if (script.Length > 1024 * 1024) return false; - var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString(); - if (manifest.Length > ContractManifest.MaxLength) return false; - - var contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash); - if (contract is null) return false; - - if (script.Length > 0) - { - UInt160 hash_new = script.ToScriptHash(); - if (hash_new.Equals(engine.CurrentScriptHash)) return false; - if (engine.Snapshot.Contracts.TryGet(hash_new) != null) return false; - contract = new ContractState - { - Script = script, - Manifest = contract.Manifest - }; - contract.Manifest.Abi.Hash = hash_new; - engine.Snapshot.Contracts.Add(hash_new, contract); - if (contract.HasStorage) - { - foreach (var (key, value) in engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).ToArray()) - { - engine.Snapshot.Storages.Add(new StorageKey - { - ScriptHash = hash_new, - Key = key.Key - }, new StorageItem - { - Value = value.Value, - IsConstant = false - }); - } - } - Contract_Destroy(engine); - } - if (manifest.Length > 0) - { - contract = engine.Snapshot.Contracts.GetAndChange(contract.ScriptHash); - contract.Manifest = ContractManifest.Parse(manifest); - if (!contract.Manifest.IsValid(contract.ScriptHash)) return false; - if (!contract.HasStorage && engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).Any()) return false; - } - - return true; - } - - private static bool Storage_Find(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - StorageContext context = _interface.GetInterface(); - if (!CheckStorageContext(engine, context)) return false; - byte[] prefix = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - byte[] prefix_key = StorageKey.CreateSearchPrefix(context.ScriptHash, prefix); - StorageIterator iterator = engine.AddDisposable(new StorageIterator(engine.Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.AsSpan().StartsWith(prefix)).GetEnumerator())); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator)); - return true; - } - return false; - } - - private static bool Enumerator_Create(ApplicationEngine engine) - { - IEnumerator enumerator; - switch (engine.CurrentContext.EvaluationStack.Pop()) - { - case VMArray array: - enumerator = new ArrayWrapper(array); - break; - case PrimitiveType primitive: - enumerator = new ByteArrayWrapper(primitive); - break; - default: - return false; - } - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(enumerator)); - return true; - } - - private static bool Enumerator_Next(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - IEnumerator enumerator = _interface.GetInterface(); - engine.CurrentContext.EvaluationStack.Push(enumerator.Next()); - return true; - } - return false; - } - - private static bool Enumerator_Value(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - IEnumerator enumerator = _interface.GetInterface(); - engine.CurrentContext.EvaluationStack.Push(enumerator.Value()); - return true; - } - return false; - } - - private static bool Enumerator_Concat(ApplicationEngine engine) - { - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface1)) return false; - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface2)) return false; - IEnumerator first = _interface1.GetInterface(); - IEnumerator second = _interface2.GetInterface(); - IEnumerator result = new ConcatenatedEnumerator(first, second); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(result)); - return true; - } - - private static bool Iterator_Create(ApplicationEngine engine) - { - IIterator iterator; - switch (engine.CurrentContext.EvaluationStack.Pop()) - { - case VMArray array: - iterator = new ArrayWrapper(array); - break; - case Map map: - iterator = new MapWrapper(map); - break; - case PrimitiveType primitive: - iterator = new ByteArrayWrapper(primitive); - break; - default: - return false; - } - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator)); - return true; - } - - private static bool Iterator_Key(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - IIterator iterator = _interface.GetInterface(); - engine.CurrentContext.EvaluationStack.Push(iterator.Key()); - return true; - } - return false; - } - - private static bool Iterator_Keys(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - IIterator iterator = _interface.GetInterface(); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new IteratorKeysWrapper(iterator))); - return true; - } - return false; - } - - private static bool Iterator_Values(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - IIterator iterator = _interface.GetInterface(); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new IteratorValuesWrapper(iterator))); - return true; - } - return false; - } - - private static bool Iterator_Concat(ApplicationEngine engine) - { - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface1)) return false; - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface2)) return false; - IIterator first = _interface1.GetInterface(); - IIterator second = _interface2.GetInterface(); - IIterator result = new ConcatenatedIterator(first, second); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(result)); - return true; - } - - private static bool Json_Deserialize(ApplicationEngine engine) - { - var json = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - try - { - var obj = JObject.Parse(json, 10); - var item = JsonSerializer.Deserialize(obj, engine.ReferenceCounter); - engine.CurrentContext.EvaluationStack.Push(item); - return true; - } - catch - { - return false; - } - } - - private static bool Json_Serialize(ApplicationEngine engine) - { - var item = engine.CurrentContext.EvaluationStack.Pop(); - try - { - var json = JsonSerializer.SerializeToByteArray(item, engine.MaxItemSize); - engine.CurrentContext.EvaluationStack.Push(json); - return true; - } - catch - { - return false; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.Native.cs b/src/neo/SmartContract/InteropService.Native.cs new file mode 100644 index 0000000000..855a355ed5 --- /dev/null +++ b/src/neo/SmartContract/InteropService.Native.cs @@ -0,0 +1,34 @@ +using Neo.Ledger; +using Neo.SmartContract.Native; + +namespace Neo.SmartContract +{ + partial class InteropService + { + internal static class Native + { + public static readonly InteropDescriptor Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application); + + static Native() + { + foreach (NativeContract contract in NativeContract.Contracts) + Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application); + } + + private static bool Native_Deploy(ApplicationEngine engine) + { + if (engine.Snapshot.PersistingBlock.Index != 0) return false; + foreach (NativeContract contract in NativeContract.Contracts) + { + engine.Snapshot.Contracts.Add(contract.Hash, new ContractState + { + Script = contract.Script, + Manifest = contract.Manifest + }); + contract.Initialize(engine); + } + return true; + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.Runtime.cs b/src/neo/SmartContract/InteropService.Runtime.cs new file mode 100644 index 0000000000..6a70ae844c --- /dev/null +++ b/src/neo/SmartContract/InteropService.Runtime.cs @@ -0,0 +1,215 @@ +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Array = Neo.VM.Types.Array; + +namespace Neo.SmartContract +{ + partial class InteropService + { + public static class Runtime + { + public const int MaxNotificationSize = 1024; + + public static readonly InteropDescriptor Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250, TriggerType.All); + public static readonly InteropDescriptor GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250, TriggerType.All); + public static readonly InteropDescriptor GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application); + public static readonly InteropDescriptor GetScriptContainer = Register("System.Runtime.GetScriptContainer", Runtime_GetScriptContainer, 0_00000250, TriggerType.All); + public static readonly InteropDescriptor GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", Runtime_GetExecutingScriptHash, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", Runtime_GetCallingScriptHash, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", Runtime_GetEntryScriptHash, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All); + public static readonly InteropDescriptor GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor Log = Register("System.Runtime.Log", Runtime_Log, 0_01000000, TriggerType.All); + public static readonly InteropDescriptor Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_01000000, TriggerType.All); + public static readonly InteropDescriptor GetNotifications = Register("System.Runtime.GetNotifications", Runtime_GetNotifications, 0_00010000, TriggerType.All); + + private static bool CheckItemForNotification(StackItem state) + { + int size = 0; + List items_checked = new List(); + Queue items_unchecked = new Queue(); + while (true) + { + switch (state) + { + case Struct array: + foreach (StackItem item in array) + items_unchecked.Enqueue(item); + break; + case Array array: + if (items_checked.All(p => !ReferenceEquals(p, array))) + { + items_checked.Add(array); + foreach (StackItem item in array) + items_unchecked.Enqueue(item); + } + break; + case PrimitiveType primitive: + size += primitive.GetByteLength(); + break; + case Null _: + break; + case InteropInterface _: + return false; + case Map map: + if (items_checked.All(p => !ReferenceEquals(p, map))) + { + items_checked.Add(map); + foreach (var pair in map) + { + size += pair.Key.GetByteLength(); + items_unchecked.Enqueue(pair.Value); + } + } + break; + } + if (size > MaxNotificationSize) return false; + if (items_unchecked.Count == 0) return true; + state = items_unchecked.Dequeue(); + } + } + + internal static bool CheckWitnessInternal(ApplicationEngine engine, UInt160 hash) + { + if (engine.ScriptContainer is Transaction tx) + { + Cosigner usage = tx.Cosigners.FirstOrDefault(p => p.Account.Equals(hash)); + if (usage is null) return false; + if (usage.Scopes == WitnessScope.Global) return true; + if (usage.Scopes.HasFlag(WitnessScope.CalledByEntry)) + { + if (engine.CallingScriptHash == engine.EntryScriptHash) + return true; + } + if (usage.Scopes.HasFlag(WitnessScope.CustomContracts)) + { + if (usage.AllowedContracts.Contains(engine.CurrentScriptHash)) + return true; + } + if (usage.Scopes.HasFlag(WitnessScope.CustomGroups)) + { + var contract = engine.Snapshot.Contracts[engine.CallingScriptHash]; + // check if current group is the required one + if (contract.Manifest.Groups.Select(p => p.PubKey).Intersect(usage.AllowedGroups).Any()) + return true; + } + return false; + } + + // only for non-Transaction types (Block, etc) + + var hashes_for_verifying = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); + return hashes_for_verifying.Contains(hash); + } + + private static bool Runtime_Platform(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push(Encoding.ASCII.GetBytes("NEO")); + return true; + } + + private static bool Runtime_GetTrigger(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push((int)engine.Trigger); + return true; + } + + private static bool Runtime_GetTime(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.PersistingBlock.Timestamp); + return true; + } + + private static bool Runtime_GetScriptContainer(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push( + engine.ScriptContainer is IInteroperable value ? value.ToStackItem(engine.ReferenceCounter) : + StackItem.FromInterface(engine.ScriptContainer)); + return true; + } + + private static bool Runtime_GetExecutingScriptHash(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push(engine.CurrentScriptHash.ToArray()); + return true; + } + + private static bool Runtime_GetCallingScriptHash(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push(engine.CallingScriptHash?.ToArray() ?? StackItem.Null); + return true; + } + + private static bool Runtime_GetEntryScriptHash(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push(engine.EntryScriptHash.ToArray()); + return true; + } + + private static bool Runtime_CheckWitness(ApplicationEngine engine) + { + ReadOnlySpan hashOrPubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); + UInt160 hash = hashOrPubkey.Length switch + { + 20 => new UInt160(hashOrPubkey), + 33 => SmartContract.Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(hashOrPubkey, ECCurve.Secp256r1)).ToScriptHash(), + _ => null + }; + if (hash is null) return false; + engine.CurrentContext.EvaluationStack.Push(CheckWitnessInternal(engine, hash)); + return true; + } + + private static bool Runtime_GetInvocationCounter(ApplicationEngine engine) + { + if (!engine.InvocationCounter.TryGetValue(engine.CurrentScriptHash, out var counter)) + { + return false; + } + + engine.CurrentContext.EvaluationStack.Push(counter); + return true; + } + + private static bool Runtime_Log(ApplicationEngine engine) + { + ReadOnlySpan state = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); + if (state.Length > MaxNotificationSize) return false; + string message = Encoding.UTF8.GetString(state); + engine.SendLog(engine.CurrentScriptHash, message); + return true; + } + + private static bool Runtime_Notify(ApplicationEngine engine) + { + StackItem state = engine.CurrentContext.EvaluationStack.Pop(); + if (!CheckItemForNotification(state)) return false; + engine.SendNotification(engine.CurrentScriptHash, state); + return true; + } + + private static bool Runtime_GetNotifications(ApplicationEngine engine) + { + StackItem item = engine.CurrentContext.EvaluationStack.Pop(); + + IEnumerable notifications = engine.Notifications; + if (!item.IsNull) // must filter by scriptHash + { + var hash = new UInt160(item.GetSpan()); + notifications = notifications.Where(p => p.ScriptHash == hash); + } + + if (notifications.Count() > engine.MaxStackSize) return false; + engine.Push(new Array(engine.ReferenceCounter, notifications.Select(u => new Array(engine.ReferenceCounter, new[] { u.ScriptHash.ToArray(), u.State })))); + return true; + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.Storage.cs b/src/neo/SmartContract/InteropService.Storage.cs new file mode 100644 index 0000000000..ac977c69ca --- /dev/null +++ b/src/neo/SmartContract/InteropService.Storage.cs @@ -0,0 +1,180 @@ +using Neo.Ledger; +using Neo.SmartContract.Iterators; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Linq; + +namespace Neo.SmartContract +{ + partial class InteropService + { + public static class Storage + { + public const long GasPerByte = 100000; + public const int MaxKeySize = 64; + public const int MaxValueSize = ushort.MaxValue; + + public static readonly InteropDescriptor GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application); + public static readonly InteropDescriptor GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400, TriggerType.Application); + public static readonly InteropDescriptor AsReadOnly = Register("System.Storage.AsReadOnly", Storage_AsReadOnly, 0_00000400, TriggerType.Application); + public static readonly InteropDescriptor Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application); + public static readonly InteropDescriptor Find = Register("System.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application); + public static readonly InteropDescriptor Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice, TriggerType.Application); + public static readonly InteropDescriptor PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application); + public static readonly InteropDescriptor Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application); + + private static bool CheckStorageContext(ApplicationEngine engine, StorageContext context) + { + ContractState contract = engine.Snapshot.Contracts.TryGet(context.ScriptHash); + if (contract == null) return false; + if (!contract.HasStorage) return false; + return true; + } + + private static long GetStoragePrice(EvaluationStack stack) + { + return (stack.Peek(1).GetByteLength() + stack.Peek(2).GetByteLength()) * GasPerByte; + } + + private static bool PutExInternal(ApplicationEngine engine, StorageContext context, byte[] key, byte[] value, StorageFlags flags) + { + if (key.Length > MaxKeySize) return false; + if (value.Length > MaxValueSize) return false; + if (context.IsReadOnly) return false; + if (!CheckStorageContext(engine, context)) return false; + + StorageKey skey = new StorageKey + { + ScriptHash = context.ScriptHash, + Key = key + }; + + if (engine.Snapshot.Storages.TryGet(skey)?.IsConstant == true) return false; + + if (value.Length == 0 && !flags.HasFlag(StorageFlags.Constant)) + { + // If put 'value' is empty (and non-const), we remove it (implicit `Storage.Delete`) + engine.Snapshot.Storages.Delete(skey); + } + else + { + StorageItem item = engine.Snapshot.Storages.GetAndChange(skey, () => new StorageItem()); + item.Value = value; + item.IsConstant = flags.HasFlag(StorageFlags.Constant); + } + return true; + } + + private static bool Storage_GetContext(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new StorageContext + { + ScriptHash = engine.CurrentScriptHash, + IsReadOnly = false + })); + return true; + } + + private static bool Storage_GetReadOnlyContext(ApplicationEngine engine) + { + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new StorageContext + { + ScriptHash = engine.CurrentScriptHash, + IsReadOnly = true + })); + return true; + } + + private static bool Storage_AsReadOnly(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + StorageContext context = _interface.GetInterface(); + if (!context.IsReadOnly) + context = new StorageContext + { + ScriptHash = context.ScriptHash, + IsReadOnly = true + }; + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(context)); + return true; + } + return false; + } + + private static bool Storage_Get(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + StorageContext context = _interface.GetInterface(); + if (!CheckStorageContext(engine, context)) return false; + byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + StorageItem item = engine.Snapshot.Storages.TryGet(new StorageKey + { + ScriptHash = context.ScriptHash, + Key = key + }); + engine.CurrentContext.EvaluationStack.Push(item?.Value ?? StackItem.Null); + return true; + } + return false; + } + + private static bool Storage_Find(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + StorageContext context = _interface.GetInterface(); + if (!CheckStorageContext(engine, context)) return false; + byte[] prefix = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + byte[] prefix_key = StorageKey.CreateSearchPrefix(context.ScriptHash, prefix); + StorageIterator iterator = engine.AddDisposable(new StorageIterator(engine.Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.AsSpan().StartsWith(prefix)).GetEnumerator())); + engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator)); + return true; + } + return false; + } + + private static bool Storage_Put(ApplicationEngine engine) + { + if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)) + return false; + StorageContext context = _interface.GetInterface(); + byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + byte[] value = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + return PutExInternal(engine, context, key, value, StorageFlags.None); + } + + private static bool Storage_PutEx(ApplicationEngine engine) + { + if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)) + return false; + StorageContext context = _interface.GetInterface(); + byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + byte[] value = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); + StorageFlags flags = (StorageFlags)(byte)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger(); + return PutExInternal(engine, context, key, value, flags); + } + + private static bool Storage_Delete(ApplicationEngine engine) + { + if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) + { + StorageContext context = _interface.GetInterface(); + if (context.IsReadOnly) return false; + if (!CheckStorageContext(engine, context)) return false; + StorageKey key = new StorageKey + { + ScriptHash = context.ScriptHash, + Key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray() + }; + if (engine.Snapshot.Storages.TryGet(key)?.IsConstant == true) return false; + engine.Snapshot.Storages.Delete(key); + return true; + } + return false; + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs index 40e1d77c5b..16a0525510 100644 --- a/src/neo/SmartContract/InteropService.cs +++ b/src/neo/SmartContract/InteropService.cs @@ -1,112 +1,18 @@ -using Neo.Cryptography; -using Neo.Cryptography.ECC; -using Neo.IO; -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.SmartContract.Manifest; using Neo.VM; -using Neo.VM.Types; using System; using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Numerics; -using System.Text; -using Array = Neo.VM.Types.Array; +using System.Reflection; namespace Neo.SmartContract { public static partial class InteropService { - public const long GasPerByte = 100000; - public const int MaxStorageKeySize = 64; - public const int MaxStorageValueSize = ushort.MaxValue; - public const int MaxNotificationSize = 1024; - private static readonly Dictionary methods = new Dictionary(); - public static readonly uint System_ExecutionEngine_GetScriptContainer = Register("System.ExecutionEngine.GetScriptContainer", ExecutionEngine_GetScriptContainer, 0_00000250, TriggerType.All); - public static readonly uint System_ExecutionEngine_GetExecutingScriptHash = Register("System.ExecutionEngine.GetExecutingScriptHash", ExecutionEngine_GetExecutingScriptHash, 0_00000400, TriggerType.All); - public static readonly uint System_ExecutionEngine_GetCallingScriptHash = Register("System.ExecutionEngine.GetCallingScriptHash", ExecutionEngine_GetCallingScriptHash, 0_00000400, TriggerType.All); - public static readonly uint System_ExecutionEngine_GetEntryScriptHash = Register("System.ExecutionEngine.GetEntryScriptHash", ExecutionEngine_GetEntryScriptHash, 0_00000400, TriggerType.All); - public static readonly uint System_Runtime_Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250, TriggerType.All); - public static readonly uint System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250, TriggerType.All); - public static readonly uint System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All); - public static readonly uint System_Runtime_Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_01000000, TriggerType.All); - public static readonly uint System_Runtime_Log = Register("System.Runtime.Log", Runtime_Log, 0_01000000, TriggerType.All); - public static readonly uint System_Runtime_GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application); - public static readonly uint System_Runtime_Serialize = Register("System.Runtime.Serialize", Runtime_Serialize, 0_00100000, TriggerType.All); - public static readonly uint System_Runtime_Deserialize = Register("System.Runtime.Deserialize", Runtime_Deserialize, 0_00500000, TriggerType.All); - public static readonly uint System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400, TriggerType.All); - public static readonly uint System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", Runtime_GetNotifications, 0_00010000, TriggerType.All); - public static readonly uint System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); - public static readonly uint System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application); - public static readonly uint System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application); - public static readonly uint System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application); - public static readonly uint System_Blockchain_GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application); - public static readonly uint System_Blockchain_GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application); - public static readonly uint System_Contract_Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application); - public static readonly uint System_Contract_Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application); - public static readonly uint System_Storage_GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application); - public static readonly uint System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400, TriggerType.Application); - public static readonly uint System_Storage_Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application); - public static readonly uint System_Storage_Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice, TriggerType.Application); - public static readonly uint System_Storage_PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application); - public static readonly uint System_Storage_Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application); - public static readonly uint System_StorageContext_AsReadOnly = Register("System.StorageContext.AsReadOnly", StorageContext_AsReadOnly, 0_00000400, TriggerType.Application); - - private static bool CheckItemForNotification(StackItem state) - { - int size = 0; - List items_checked = new List(); - Queue items_unchecked = new Queue(); - while (true) - { - switch (state) - { - case Struct array: - foreach (StackItem item in array) - items_unchecked.Enqueue(item); - break; - case Array array: - if (items_checked.All(p => !ReferenceEquals(p, array))) - { - items_checked.Add(array); - foreach (StackItem item in array) - items_unchecked.Enqueue(item); - } - break; - case PrimitiveType primitive: - size += primitive.GetByteLength(); - break; - case Null _: - break; - case InteropInterface _: - return false; - case Map map: - if (items_checked.All(p => !ReferenceEquals(p, map))) - { - items_checked.Add(map); - foreach (var pair in map) - { - size += pair.Key.GetByteLength(); - items_unchecked.Enqueue(pair.Value); - } - } - break; - } - if (size > MaxNotificationSize) return false; - if (items_unchecked.Count == 0) return true; - state = items_unchecked.Dequeue(); - } - } - - private static bool CheckStorageContext(ApplicationEngine engine, StorageContext context) + static InteropService() { - ContractState contract = engine.Snapshot.Contracts.TryGet(context.ScriptHash); - if (contract == null) return false; - if (!contract.HasStorage) return false; - return true; + foreach (Type t in typeof(InteropService).GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)) + t.GetFields()[0].GetValue(null); } public static long GetPrice(uint hash, EvaluationStack stack) @@ -114,14 +20,9 @@ public static long GetPrice(uint hash, EvaluationStack stack) return methods[hash].GetPrice(stack); } - public static Dictionary SupportedMethods() + public static IEnumerable SupportedMethods() { - return methods.ToDictionary(p => p.Key, p => p.Value.Method); - } - - private static long GetStoragePrice(EvaluationStack stack) - { - return (stack.Peek(1).GetByteLength() + stack.Peek(2).GetByteLength()) * GasPerByte; + return methods.Values; } internal static bool Invoke(ApplicationEngine engine, uint method) @@ -133,444 +34,18 @@ internal static bool Invoke(ApplicationEngine engine, uint method) return descriptor.Handler(engine); } - private static uint Register(string method, Func handler, long price, TriggerType allowedTriggers) + private static InteropDescriptor Register(string method, Func handler, long price, TriggerType allowedTriggers) { InteropDescriptor descriptor = new InteropDescriptor(method, handler, price, allowedTriggers); methods.Add(descriptor.Hash, descriptor); - return descriptor.Hash; + return descriptor; } - private static uint Register(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers) + private static InteropDescriptor Register(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers) { InteropDescriptor descriptor = new InteropDescriptor(method, handler, priceCalculator, allowedTriggers); methods.Add(descriptor.Hash, descriptor); - return descriptor.Hash; - } - - private static bool ExecutionEngine_GetScriptContainer(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push( - engine.ScriptContainer is IInteroperable value ? value.ToStackItem(engine.ReferenceCounter) : - StackItem.FromInterface(engine.ScriptContainer)); - return true; - } - - private static bool ExecutionEngine_GetExecutingScriptHash(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.CurrentScriptHash.ToArray()); - return true; - } - - private static bool ExecutionEngine_GetCallingScriptHash(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.CallingScriptHash?.ToArray() ?? StackItem.Null); - return true; - } - - private static bool ExecutionEngine_GetEntryScriptHash(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.EntryScriptHash.ToArray()); - return true; - } - - private static bool Runtime_Platform(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(Encoding.ASCII.GetBytes("NEO")); - return true; - } - - private static bool Runtime_GetTrigger(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push((int)engine.Trigger); - return true; - } - - internal static bool CheckWitness(ApplicationEngine engine, UInt160 hash) - { - if (engine.ScriptContainer is Transaction tx) - { - Cosigner usage = tx.Cosigners.FirstOrDefault(p => p.Account.Equals(hash)); - if (usage is null) return false; - if (usage.Scopes == WitnessScope.Global) return true; - if (usage.Scopes.HasFlag(WitnessScope.CalledByEntry)) - { - if (engine.CallingScriptHash == engine.EntryScriptHash) - return true; - } - if (usage.Scopes.HasFlag(WitnessScope.CustomContracts)) - { - if (usage.AllowedContracts.Contains(engine.CurrentScriptHash)) - return true; - } - if (usage.Scopes.HasFlag(WitnessScope.CustomGroups)) - { - var contract = engine.Snapshot.Contracts[engine.CallingScriptHash]; - // check if current group is the required one - if (contract.Manifest.Groups.Select(p => p.PubKey).Intersect(usage.AllowedGroups).Any()) - return true; - } - return false; - } - - // only for non-Transaction types (Block, etc) - - var hashes_for_verifying = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); - return hashes_for_verifying.Contains(hash); - } - - private static bool CheckWitness(ApplicationEngine engine, ECPoint pubkey) - { - return CheckWitness(engine, Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash()); - } - - private static bool Runtime_CheckWitness(ApplicationEngine engine) - { - ReadOnlySpan hashOrPubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - bool result; - if (hashOrPubkey.Length == 20) - result = CheckWitness(engine, new UInt160(hashOrPubkey)); - else if (hashOrPubkey.Length == 33) - result = CheckWitness(engine, ECPoint.DecodePoint(hashOrPubkey, ECCurve.Secp256r1)); - else - return false; - engine.CurrentContext.EvaluationStack.Push(result); - return true; - } - - private static bool Runtime_Notify(ApplicationEngine engine) - { - StackItem state = engine.CurrentContext.EvaluationStack.Pop(); - if (!CheckItemForNotification(state)) return false; - engine.SendNotification(engine.CurrentScriptHash, state); - return true; - } - - private static bool Runtime_Log(ApplicationEngine engine) - { - ReadOnlySpan state = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - if (state.Length > MaxNotificationSize) return false; - string message = Encoding.UTF8.GetString(state); - engine.SendLog(engine.CurrentScriptHash, message); - return true; - } - - private static bool Runtime_GetTime(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.PersistingBlock.Timestamp); - return true; - } - - private static bool Runtime_Serialize(ApplicationEngine engine) - { - byte[] serialized; - try - { - serialized = StackItemSerializer.Serialize(engine.CurrentContext.EvaluationStack.Pop(), engine.MaxItemSize); - } - catch - { - return false; - } - engine.CurrentContext.EvaluationStack.Push(serialized); - return true; - } - - private static bool Runtime_GetNotifications(ApplicationEngine engine) - { - StackItem item = engine.CurrentContext.EvaluationStack.Pop(); - - IEnumerable notifications = engine.Notifications; - if (!item.IsNull) // must filter by scriptHash - { - var hash = new UInt160(item.GetSpan()); - notifications = notifications.Where(p => p.ScriptHash == hash); - } - - if (notifications.Count() > engine.MaxStackSize) return false; - engine.Push(new Array(engine.ReferenceCounter, notifications.Select(u => new Array(engine.ReferenceCounter, new[] { u.ScriptHash.ToArray(), u.State })))); - return true; - } - - private static bool Runtime_GetInvocationCounter(ApplicationEngine engine) - { - if (!engine.InvocationCounter.TryGetValue(engine.CurrentScriptHash, out var counter)) - { - return false; - } - - engine.CurrentContext.EvaluationStack.Push(counter); - return true; - } - - private static bool Runtime_Deserialize(ApplicationEngine engine) - { - StackItem item; - try - { - item = StackItemSerializer.Deserialize(engine.CurrentContext.EvaluationStack.Pop().GetSpan(), engine.MaxItemSize, engine.ReferenceCounter); - } - catch (FormatException) - { - return false; - } - catch (IOException) - { - return false; - } - engine.CurrentContext.EvaluationStack.Push(item); - return true; - } - - private static bool Blockchain_GetHeight(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.Height); - return true; - } - - private static bool Blockchain_GetBlock(ApplicationEngine engine) - { - ReadOnlySpan data = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - UInt256 hash; - if (data.Length <= 5) - hash = Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); - else if (data.Length == 32) - hash = new UInt256(data); - else - return false; - - Block block = hash != null ? engine.Snapshot.GetBlock(hash) : null; - if (block == null) - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - else - engine.CurrentContext.EvaluationStack.Push(block.ToStackItem(engine.ReferenceCounter)); - return true; - } - - private static bool Blockchain_GetTransaction(ApplicationEngine engine) - { - ReadOnlySpan hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - Transaction tx = engine.Snapshot.GetTransaction(new UInt256(hash)); - if (tx == null) - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - else - engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem(engine.ReferenceCounter)); - return true; - } - - private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine) - { - ReadOnlySpan hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - var tx = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); - engine.CurrentContext.EvaluationStack.Push(tx != null ? new BigInteger(tx.BlockIndex) : BigInteger.MinusOne); - return true; - } - - private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) - { - ReadOnlySpan data = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - UInt256 hash; - if (data.Length <= 5) - hash = Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); - else if (data.Length == 32) - hash = new UInt256(data); - else - return false; - - TrimmedBlock block = hash != null ? engine.Snapshot.Blocks.TryGet(hash) : null; - if (block == null) - { - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - } - else - { - int index = (int)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger(); - if (index < 0 || index >= block.Hashes.Length - 1) return false; - - Transaction tx = engine.Snapshot.GetTransaction(block.Hashes[index + 1]); - if (tx == null) - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - else - engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem(engine.ReferenceCounter)); - } - return true; - } - - private static bool Blockchain_GetContract(ApplicationEngine engine) - { - UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan()); - ContractState contract = engine.Snapshot.Contracts.TryGet(hash); - if (contract == null) - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - else - engine.CurrentContext.EvaluationStack.Push(contract.ToStackItem(engine.ReferenceCounter)); - return true; - } - - private static bool Storage_GetContext(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new StorageContext - { - ScriptHash = engine.CurrentScriptHash, - IsReadOnly = false - })); - return true; - } - - private static bool Storage_GetReadOnlyContext(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new StorageContext - { - ScriptHash = engine.CurrentScriptHash, - IsReadOnly = true - })); - return true; - } - - private static bool Storage_Get(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - StorageContext context = _interface.GetInterface(); - if (!CheckStorageContext(engine, context)) return false; - byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - StorageItem item = engine.Snapshot.Storages.TryGet(new StorageKey - { - ScriptHash = context.ScriptHash, - Key = key - }); - engine.CurrentContext.EvaluationStack.Push(item?.Value ?? StackItem.Null); - return true; - } - return false; - } - - private static bool StorageContext_AsReadOnly(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - StorageContext context = _interface.GetInterface(); - if (!context.IsReadOnly) - context = new StorageContext - { - ScriptHash = context.ScriptHash, - IsReadOnly = true - }; - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(context)); - return true; - } - return false; - } - - private static bool Contract_Call(ApplicationEngine engine) - { - StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop(); - - ContractState contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractHash.GetSpan())); - if (contract is null) return false; - - StackItem method = engine.CurrentContext.EvaluationStack.Pop(); - StackItem args = engine.CurrentContext.EvaluationStack.Pop(); - ContractManifest currentManifest = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash)?.Manifest; - - if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method.GetString())) - return false; - - if (engine.InvocationCounter.TryGetValue(contract.ScriptHash, out var counter)) - { - engine.InvocationCounter[contract.ScriptHash] = counter + 1; - } - else - { - engine.InvocationCounter[contract.ScriptHash] = 1; - } - - UInt160 callingScriptHash = engine.CurrentScriptHash; - ExecutionContext context_new = engine.LoadScript(contract.Script, 1); - context_new.GetState().CallingScriptHash = callingScriptHash; - context_new.EvaluationStack.Push(args); - context_new.EvaluationStack.Push(method); - return true; - } - - private static bool Contract_Destroy(ApplicationEngine engine) - { - UInt160 hash = engine.CurrentScriptHash; - ContractState contract = engine.Snapshot.Contracts.TryGet(hash); - if (contract == null) return true; - engine.Snapshot.Contracts.Delete(hash); - if (contract.HasStorage) - foreach (var pair in engine.Snapshot.Storages.Find(hash.ToArray())) - engine.Snapshot.Storages.Delete(pair.Key); - return true; - } - - private static bool PutEx(ApplicationEngine engine, StorageContext context, byte[] key, byte[] value, StorageFlags flags) - { - if (key.Length > MaxStorageKeySize) return false; - if (value.Length > MaxStorageValueSize) return false; - if (context.IsReadOnly) return false; - if (!CheckStorageContext(engine, context)) return false; - - StorageKey skey = new StorageKey - { - ScriptHash = context.ScriptHash, - Key = key - }; - - if (engine.Snapshot.Storages.TryGet(skey)?.IsConstant == true) return false; - - if (value.Length == 0 && !flags.HasFlag(StorageFlags.Constant)) - { - // If put 'value' is empty (and non-const), we remove it (implicit `Storage.Delete`) - engine.Snapshot.Storages.Delete(skey); - } - else - { - StorageItem item = engine.Snapshot.Storages.GetAndChange(skey, () => new StorageItem()); - item.Value = value; - item.IsConstant = flags.HasFlag(StorageFlags.Constant); - } - return true; - } - - private static bool Storage_Put(ApplicationEngine engine) - { - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)) - return false; - StorageContext context = _interface.GetInterface(); - byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - byte[] value = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - return PutEx(engine, context, key, value, StorageFlags.None); - } - - private static bool Storage_PutEx(ApplicationEngine engine) - { - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)) - return false; - StorageContext context = _interface.GetInterface(); - byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - byte[] value = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - StorageFlags flags = (StorageFlags)(byte)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger(); - return PutEx(engine, context, key, value, flags); - } - - private static bool Storage_Delete(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - StorageContext context = _interface.GetInterface(); - if (context.IsReadOnly) return false; - if (!CheckStorageContext(engine, context)) return false; - StorageKey key = new StorageKey - { - ScriptHash = context.ScriptHash, - Key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray() - }; - if (engine.Snapshot.Storages.TryGet(key)?.IsConstant == true) return false; - engine.Snapshot.Storages.Delete(key); - return true; - } - return false; + return descriptor; } } } diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index e9b7984bce..2e48520380 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -41,7 +41,7 @@ private bool CheckValidators(ApplicationEngine engine) { UInt256 prev_hash = engine.Snapshot.PersistingBlock.PrevHash; TrimmedBlock prev_block = engine.Snapshot.Blocks[prev_hash]; - return InteropService.CheckWitness(engine, prev_block.NextConsensus); + return InteropService.Runtime.CheckWitnessInternal(engine, prev_block.NextConsensus); } internal override bool Initialize(ApplicationEngine engine) diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 546cb17032..699e09467b 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -155,7 +155,7 @@ private StackItem Vote(ApplicationEngine engine, Array args) { UInt160 account = new UInt160(args[0].GetSpan()); ECPoint[] pubkeys = ((Array)args[1]).Select(p => p.GetSpan().AsSerializable()).ToArray(); - if (!InteropService.CheckWitness(engine, account)) return false; + if (!InteropService.Runtime.CheckWitnessInternal(engine, account)) return false; StorageKey key_account = CreateAccountKey(account); if (engine.Snapshot.Storages.TryGet(key_account) is null) return false; StorageItem storage_account = engine.Snapshot.Storages.GetAndChange(key_account); diff --git a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs index b42e93814b..76671d92c1 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs @@ -19,7 +19,7 @@ public Nep5AccountState(byte[] data) public void FromByteArray(byte[] data) { - FromStruct((Struct)StackItemSerializer.Deserialize(data, 34)); + FromStruct((Struct)BinarySerializer.Deserialize(data, 34)); } protected virtual void FromStruct(Struct @struct) @@ -29,7 +29,7 @@ protected virtual void FromStruct(Struct @struct) public byte[] ToByteArray() { - return StackItemSerializer.Serialize(ToStruct(), 4096); + return BinarySerializer.Serialize(ToStruct(), 4096); } protected virtual Struct ToStruct() diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index fa9d779ace..bb92fbbd17 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -172,7 +172,7 @@ protected StackItem Transfer(ApplicationEngine engine, Array args) protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount) { if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); - if (!from.Equals(engine.CallingScriptHash) && !InteropService.CheckWitness(engine, from)) + if (!from.Equals(engine.CallingScriptHash) && !InteropService.Runtime.CheckWitnessInternal(engine, from)) return false; ContractState contract_to = engine.Snapshot.Contracts.TryGet(to); if (contract_to?.Payable == false) return false; diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index a921f4411a..28dcb11904 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -27,7 +27,7 @@ public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHas sb.Emit(OpCode.NEWARRAY); sb.EmitPush(operation); sb.EmitPush(scriptHash); - sb.EmitSysCall(InteropService.System_Contract_Call); + sb.EmitSysCall(InteropService.Contract.Call); return sb; } @@ -39,7 +39,7 @@ public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHas sb.Emit(OpCode.PACK); sb.EmitPush(operation); sb.EmitPush(scriptHash); - sb.EmitSysCall(InteropService.System_Contract_Call); + sb.EmitSysCall(InteropService.Contract.Call); return sb; } @@ -51,7 +51,7 @@ public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHas sb.Emit(OpCode.PACK); sb.EmitPush(operation); sb.EmitPush(scriptHash); - sb.EmitSysCall(InteropService.System_Contract_Call); + sb.EmitSysCall(InteropService.Contract.Call); return sb; } diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index a616c971db..1752372bec 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -354,7 +354,7 @@ public static long CalculateNetWorkFee(byte[] witness_script, ref int size) if (witness_script.IsSignatureContract()) { size += 67 + witness_script.GetVarSize(); - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null); + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null); } else if (witness_script.IsMultiSigContract(out int m, out int n)) { @@ -366,7 +366,7 @@ public static long CalculateNetWorkFee(byte[] witness_script, ref int size) networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * n; using (ScriptBuilder sb = new ScriptBuilder()) networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Neo_Crypto_ECDsaVerify, null) * n; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null) * n; } else { diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 772d11edbf..411a2139ca 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -687,7 +687,7 @@ internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint[] votes) { - var st = (VM.Types.Struct)StackItemSerializer.Deserialize(trackable.Item.Value, 32); + var st = (VM.Types.Struct)BinarySerializer.Deserialize(trackable.Item.Value, 32); st.Count.Should().Be(3); st.Select(u => u.GetType()).ToArray().Should().BeEquivalentTo(new Type[] { typeof(VM.Types.Integer), typeof(VM.Types.Integer), typeof(VM.Types.ByteArray) }); // Balance diff --git a/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs b/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs similarity index 67% rename from tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs rename to tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs index ef92f57016..888af9a23a 100644 --- a/tests/neo.UnitTests/SmartContract/UT_StackItemSerializer.cs +++ b/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs @@ -10,36 +10,36 @@ namespace Neo.UnitTests.SmartContract { [TestClass] - public class UT_StackItemSerializer + public class UT_BinarySerializer { private const int MaxItemSize = 1024 * 1024; [TestMethod] public void TestSerialize() { - byte[] result1 = StackItemSerializer.Serialize(new byte[5], MaxItemSize); + byte[] result1 = BinarySerializer.Serialize(new byte[5], MaxItemSize); byte[] expectedArray1 = new byte[] { 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray1), Encoding.Default.GetString(result1)); - byte[] result2 = StackItemSerializer.Serialize(true, MaxItemSize); + byte[] result2 = BinarySerializer.Serialize(true, MaxItemSize); byte[] expectedArray2 = new byte[] { 0x01, 0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray2), Encoding.Default.GetString(result2)); - byte[] result3 = StackItemSerializer.Serialize(1, MaxItemSize); + byte[] result3 = BinarySerializer.Serialize(1, MaxItemSize); byte[] expectedArray3 = new byte[] { 0x02, 0x01, 0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray3), Encoding.Default.GetString(result3)); StackItem stackItem4 = new InteropInterface(new object()); - Action action4 = () => StackItemSerializer.Serialize(stackItem4, MaxItemSize); + Action action4 = () => BinarySerializer.Serialize(stackItem4, MaxItemSize); action4.Should().Throw(); - byte[] result5 = StackItemSerializer.Serialize(1, MaxItemSize); + byte[] result5 = BinarySerializer.Serialize(1, MaxItemSize); byte[] expectedArray5 = new byte[] { 0x02, 0x01, 0x01 }; @@ -48,7 +48,7 @@ public void TestSerialize() List list6 = new List { 1 }; StackItem stackItem62 = new VM.Types.Array(list6); - byte[] result6 = StackItemSerializer.Serialize(stackItem62, MaxItemSize); + byte[] result6 = BinarySerializer.Serialize(stackItem62, MaxItemSize); byte[] expectedArray6 = new byte[] { 0x80,0x01,0x02,0x01,0x01 }; @@ -56,14 +56,14 @@ public void TestSerialize() List list7 = new List { 1 }; StackItem stackItem72 = new Struct(list7); - byte[] result7 = StackItemSerializer.Serialize(stackItem72, MaxItemSize); + byte[] result7 = BinarySerializer.Serialize(stackItem72, MaxItemSize); byte[] expectedArray7 = new byte[] { 0x81,0x01,0x02,0x01,0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray7), Encoding.Default.GetString(result7)); StackItem stackItem82 = new Map { [2] = 1 }; - byte[] result8 = StackItemSerializer.Serialize(stackItem82, MaxItemSize); + byte[] result8 = BinarySerializer.Serialize(stackItem82, MaxItemSize); byte[] expectedArray8 = new byte[] { 0x82,0x01,0x02,0x01,0x02,0x02,0x01,0x01 }; @@ -71,12 +71,12 @@ public void TestSerialize() Map stackItem91 = new Map(); stackItem91[1] = stackItem91; - Action action9 = () => StackItemSerializer.Serialize(stackItem91, MaxItemSize); + Action action9 = () => BinarySerializer.Serialize(stackItem91, MaxItemSize); action9.Should().Throw(); VM.Types.Array stackItem10 = new VM.Types.Array(); stackItem10.Add(stackItem10); - Action action10 = () => StackItemSerializer.Serialize(stackItem10, MaxItemSize); + Action action10 = () => BinarySerializer.Serialize(stackItem10, MaxItemSize); action10.Should().Throw(); } @@ -84,42 +84,42 @@ public void TestSerialize() public void TestDeserializeStackItem() { StackItem stackItem1 = new ByteArray(new byte[5]); - byte[] byteArray1 = StackItemSerializer.Serialize(stackItem1, MaxItemSize); - StackItem result1 = StackItemSerializer.Deserialize(byteArray1, (uint)byteArray1.Length); + byte[] byteArray1 = BinarySerializer.Serialize(stackItem1, MaxItemSize); + StackItem result1 = BinarySerializer.Deserialize(byteArray1, (uint)byteArray1.Length); Assert.AreEqual(stackItem1, result1); StackItem stackItem2 = new VM.Types.Boolean(true); - byte[] byteArray2 = StackItemSerializer.Serialize(stackItem2, MaxItemSize); - StackItem result2 = StackItemSerializer.Deserialize(byteArray2, (uint)byteArray2.Length); + byte[] byteArray2 = BinarySerializer.Serialize(stackItem2, MaxItemSize); + StackItem result2 = BinarySerializer.Deserialize(byteArray2, (uint)byteArray2.Length); Assert.AreEqual(stackItem2, result2); StackItem stackItem3 = new Integer(1); - byte[] byteArray3 = StackItemSerializer.Serialize(stackItem3, MaxItemSize); - StackItem result3 = StackItemSerializer.Deserialize(byteArray3, (uint)byteArray3.Length); + byte[] byteArray3 = BinarySerializer.Serialize(stackItem3, MaxItemSize); + StackItem result3 = BinarySerializer.Deserialize(byteArray3, (uint)byteArray3.Length); Assert.AreEqual(stackItem3, result3); - byte[] byteArray4 = StackItemSerializer.Serialize(1, MaxItemSize); + byte[] byteArray4 = BinarySerializer.Serialize(1, MaxItemSize); byteArray4[0] = 0x40; - Action action4 = () => StackItemSerializer.Deserialize(byteArray4, (uint)byteArray4.Length); + Action action4 = () => BinarySerializer.Deserialize(byteArray4, (uint)byteArray4.Length); action4.Should().Throw(); List list5 = new List { 1 }; StackItem stackItem52 = new VM.Types.Array(list5); - byte[] byteArray5 = StackItemSerializer.Serialize(stackItem52, MaxItemSize); - StackItem result5 = StackItemSerializer.Deserialize(byteArray5, (uint)byteArray5.Length); + byte[] byteArray5 = BinarySerializer.Serialize(stackItem52, MaxItemSize); + StackItem result5 = BinarySerializer.Deserialize(byteArray5, (uint)byteArray5.Length); Assert.AreEqual(((VM.Types.Array)stackItem52).Count, ((VM.Types.Array)result5).Count); Assert.AreEqual(((VM.Types.Array)stackItem52).GetEnumerator().Current, ((VM.Types.Array)result5).GetEnumerator().Current); List list6 = new List { 1 }; StackItem stackItem62 = new Struct(list6); - byte[] byteArray6 = StackItemSerializer.Serialize(stackItem62, MaxItemSize); - StackItem result6 = StackItemSerializer.Deserialize(byteArray6, (uint)byteArray6.Length); + byte[] byteArray6 = BinarySerializer.Serialize(stackItem62, MaxItemSize); + StackItem result6 = BinarySerializer.Deserialize(byteArray6, (uint)byteArray6.Length); Assert.AreEqual(((Struct)stackItem62).Count, ((Struct)result6).Count); Assert.AreEqual(((Struct)stackItem62).GetEnumerator().Current, ((Struct)result6).GetEnumerator().Current); StackItem stackItem72 = new Map { [2] = 1 }; - byte[] byteArray7 = StackItemSerializer.Serialize(stackItem72, MaxItemSize); - StackItem result7 = StackItemSerializer.Deserialize(byteArray7, (uint)byteArray7.Length); + byte[] byteArray7 = BinarySerializer.Serialize(stackItem72, MaxItemSize); + StackItem result7 = BinarySerializer.Deserialize(byteArray7, (uint)byteArray7.Length); Assert.AreEqual(((Map)stackItem72).Count, ((Map)result7).Count); CollectionAssert.AreEqual(((Map)stackItem72).Keys.ToArray(), ((Map)result7).Keys.ToArray()); CollectionAssert.AreEqual(((Map)stackItem72).Values.ToArray(), ((Map)result7).Values.ToArray()); diff --git a/tests/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/neo.UnitTests/SmartContract/UT_Contract.cs index 36efd787cb..294094ccae 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Contract.cs @@ -26,7 +26,7 @@ public void TestGetAddress() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4); Assert.AreEqual(expectedArray.ToScriptHash().ToAddress(), contract.Address); } @@ -44,7 +44,7 @@ public void TestGetScriptHash() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4); Assert.AreEqual(expectedArray.ToScriptHash(), contract.ScriptHash); } @@ -86,7 +86,7 @@ public void TestCreateMultiSigContract() expectedArray[71] = (byte)OpCode.PUSH2; expectedArray[72] = (byte)OpCode.PUSHNULL; expectedArray[73] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaCheckMultiSig), 0, expectedArray, 74, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaCheckMultiSig), 0, expectedArray, 74, 4); CollectionAssert.AreEqual(expectedArray, contract.Script); Assert.AreEqual(2, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); @@ -122,7 +122,7 @@ public void TestCreateMultiSigRedeemScript() expectedArray[71] = (byte)OpCode.PUSH2; expectedArray[72] = (byte)OpCode.PUSHNULL; expectedArray[73] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaCheckMultiSig), 0, expectedArray, 74, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaCheckMultiSig), 0, expectedArray, 74, 4); CollectionAssert.AreEqual(expectedArray, script); } @@ -140,7 +140,7 @@ public void TestCreateSignatureContract() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4); CollectionAssert.AreEqual(expectedArray, contract.Script); Assert.AreEqual(1, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); @@ -160,7 +160,7 @@ public void TestCreateSignatureRedeemScript() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Neo_Crypto_ECDsaVerify), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4); CollectionAssert.AreEqual(expectedArray, script); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index ac8a017d1f..19eb7a5d38 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -16,7 +16,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemRuntimeCheckWitnessHash); - InteropService.GetPrice(InteropService.System_Runtime_CheckWitness, ae.CurrentContext.EvaluationStack).Should().Be(0_00030000L); + InteropService.GetPrice(InteropService.Runtime.CheckWitness, ae.CurrentContext.EvaluationStack).Should().Be(0_00030000L); } // System.Storage.GetContext: 9bf667ce (price is 1) @@ -24,7 +24,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemStorageGetContextHash); - InteropService.GetPrice(InteropService.System_Storage_GetContext, ae.CurrentContext.EvaluationStack).Should().Be(0_00000400L); + InteropService.GetPrice(InteropService.Storage.GetContext, ae.CurrentContext.EvaluationStack).Should().Be(0_00000400L); } // System.Storage.Get: 925de831 (price is 100) @@ -32,7 +32,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemStorageGetHash); - InteropService.GetPrice(InteropService.System_Storage_Get, ae.CurrentContext.EvaluationStack).Should().Be(0_01000000L); + InteropService.GetPrice(InteropService.Storage.Get, ae.CurrentContext.EvaluationStack).Should().Be(0_01000000L); } } @@ -47,7 +47,7 @@ public void ApplicationEngineVariablePrices() ae.LoadScript(SyscallContractCreateHash00); debugger.StepInto(); // PUSHDATA1 debugger.StepInto(); // PUSHDATA1 - InteropService.GetPrice(InteropService.Neo_Contract_Create, ae.CurrentContext.EvaluationStack).Should().Be(0_00300000L); + InteropService.GetPrice(InteropService.Contract.Create, ae.CurrentContext.EvaluationStack).Should().Be(0_00300000L); } // System.Storage.Put: e63f1884 (requires push key and value) @@ -59,7 +59,7 @@ public void ApplicationEngineVariablePrices() debugger.StepInto(); // push 03 (length 1) debugger.StepInto(); // push 03 (length 1) debugger.StepInto(); // push 00 - InteropService.GetPrice(InteropService.System_Storage_Put, ae.CurrentContext.EvaluationStack).Should().Be(200000L); + InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack).Should().Be(200000L); } // System.Storage.PutEx: 73e19b3a (requires push key and value) @@ -71,7 +71,7 @@ public void ApplicationEngineVariablePrices() debugger.StepInto(); // push 03 (length 1) debugger.StepInto(); // push 03 (length 1) debugger.StepInto(); // push 00 - InteropService.GetPrice(InteropService.System_Storage_PutEx, ae.CurrentContext.EvaluationStack).Should().Be(200000L); + InteropService.GetPrice(InteropService.Storage.PutEx, ae.CurrentContext.EvaluationStack).Should().Be(200000L); } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index de16dfd3a1..83700f33a0 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -34,13 +34,13 @@ public void TestCheckSig() engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.ECDsaVerify).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(new byte[70]); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.ECDsaVerify).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); } @@ -76,14 +76,14 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.ECDsaCheckMultiSig).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); pubkeys = new VMArray(); engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Crypto.ECDsaCheckMultiSig).Should().BeFalse(); pubkeys = new VMArray { @@ -94,7 +94,7 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Crypto.ECDsaCheckMultiSig).Should().BeFalse(); pubkeys = new VMArray { @@ -109,7 +109,7 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.ECDsaCheckMultiSig).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); pubkeys = new VMArray @@ -125,7 +125,7 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.ECDsaCheckMultiSig).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); } @@ -138,7 +138,7 @@ public void TestAccount_IsStandard() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; engine.CurrentContext.EvaluationStack.Push(hash); - InteropService.Invoke(engine, InteropService.Neo_Account_IsStandard).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Contract.IsStandard).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); var snapshot = Blockchain.Singleton.GetSnapshot(); @@ -147,7 +147,7 @@ public void TestAccount_IsStandard() engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.Neo_Account_IsStandard).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Contract.IsStandard).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); } @@ -157,23 +157,23 @@ public void TestContract_Create() var engine = GetEngine(false, true); var script = new byte[1024 * 1024 + 1]; engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeFalse(); string manifestStr = new string(new char[ContractManifest.MaxLength + 1]); script = new byte[] { 0x01 }; engine.CurrentContext.EvaluationStack.Push(manifestStr); engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeFalse(); var manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")); engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeFalse(); manifest.Abi.Hash = script.ToScriptHash(); engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeTrue(); var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); @@ -182,7 +182,7 @@ public void TestContract_Create() engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(state.Script); - InteropService.Invoke(engine, InteropService.Neo_Contract_Create).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeFalse(); } [TestMethod] @@ -191,18 +191,18 @@ public void TestContract_Update() var engine = GetEngine(false, true); var script = new byte[1024 * 1024 + 1]; engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeFalse(); string manifestStr = new string(new char[ContractManifest.MaxLength + 1]); script = new byte[] { 0x01 }; engine.CurrentContext.EvaluationStack.Push(manifestStr); engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeFalse(); manifestStr = ""; engine.CurrentContext.EvaluationStack.Push(manifestStr); engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeFalse(); var manifest = ContractManifest.CreateDefault(script.ToScriptHash()); byte[] privkey = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, @@ -238,7 +238,7 @@ public void TestContract_Update() engine.LoadScript(state.Script); engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeTrue(); // Remove Storage flag with something stored @@ -250,7 +250,7 @@ public void TestContract_Update() engine.LoadScript(state.Script); engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Neo_Contract_Update).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeFalse(); } [TestMethod] @@ -281,14 +281,14 @@ public void TestStorage_Find() ScriptHash = state.ScriptHash, IsReadOnly = false })); - InteropService.Invoke(engine, InteropService.Neo_Storage_Find).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Storage.Find).Should().BeTrue(); var iterator = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); iterator.Next(); var ele = iterator.Value(); ele.GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Storage_Find).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Find).Should().BeFalse(); } [TestMethod] @@ -300,14 +300,14 @@ public void TestEnumerator_Create() new byte[]{ 0x02 } }; engine.CurrentContext.EvaluationStack.Push(arr); - InteropService.Invoke(engine, InteropService.Neo_Enumerator_Create).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Enumerator.Create).Should().BeTrue(); var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().Next(); ret.GetInterface().Value().GetSpan().ToHexString() .Should().Be(new byte[] { 0x01 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Enumerator_Create).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Enumerator.Create).Should().BeTrue(); } [TestMethod] @@ -319,11 +319,11 @@ public void TestEnumerator_Next() new byte[]{ 0x02 } }; engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new ArrayWrapper(arr))); - InteropService.Invoke(engine, InteropService.Neo_Enumerator_Next).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Enumerator.Next).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Enumerator_Next).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Enumerator.Next).Should().BeFalse(); } [TestMethod] @@ -337,11 +337,11 @@ public void TestEnumerator_Value() var wrapper = new ArrayWrapper(arr); wrapper.Next(); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); - InteropService.Invoke(engine, InteropService.Neo_Enumerator_Value).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Enumerator.Value).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Enumerator_Value).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Enumerator.Value).Should().BeFalse(); } [TestMethod] @@ -360,7 +360,7 @@ public void TestEnumerator_Concat() var wrapper2 = new ArrayWrapper(arr2); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper2)); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper1)); - InteropService.Invoke(engine, InteropService.Neo_Enumerator_Concat).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Enumerator.Concat).Should().BeTrue(); var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next().Should().BeTrue(); ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); @@ -375,7 +375,7 @@ public void TestIterator_Create() new byte[]{ 0x02 } }; engine.CurrentContext.EvaluationStack.Push(arr); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeTrue(); var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().Next(); ret.GetInterface().Value().GetSpan().ToHexString() @@ -383,7 +383,7 @@ public void TestIterator_Create() var interop = new InteropInterface(1); engine.CurrentContext.EvaluationStack.Push(interop); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeFalse(); var map = new Map { @@ -391,14 +391,14 @@ public void TestIterator_Create() [3] = 4 }; engine.CurrentContext.EvaluationStack.Push(map); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeTrue(); ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().Next(); ret.GetInterface().Key().GetBigInteger().Should().Be(1); ret.GetInterface().Value().GetBigInteger().Should().Be(2); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Create).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeTrue(); } [TestMethod] @@ -412,11 +412,11 @@ public void TestIterator_Key() var wrapper = new ArrayWrapper(arr); wrapper.Next(); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Key).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Iterator.Key).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(0); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Key).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Iterator.Key).Should().BeFalse(); } [TestMethod] @@ -429,13 +429,13 @@ public void TestIterator_Keys() }; var wrapper = new ArrayWrapper(arr); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Keys).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Iterator.Keys).Should().BeTrue(); var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next(); ret.Value().GetBigInteger().Should().Be(0); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Keys).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Iterator.Keys).Should().BeFalse(); } [TestMethod] @@ -448,13 +448,13 @@ public void TestIterator_Values() }; var wrapper = new ArrayWrapper(arr); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Values).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Iterator.Values).Should().BeTrue(); var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next(); ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Values).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Iterator.Values).Should().BeFalse(); } [TestMethod] @@ -473,7 +473,7 @@ public void TestIterator_Concat() var wrapper2 = new ArrayWrapper(arr2); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper2)); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper1)); - InteropService.Invoke(engine, InteropService.Neo_Iterator_Concat).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Iterator.Concat).Should().BeTrue(); var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next().Should().BeTrue(); ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); @@ -484,7 +484,7 @@ public void TestJson_Deserialize() { var engine = GetEngine(); engine.CurrentContext.EvaluationStack.Push("1"); - InteropService.Invoke(engine, InteropService.Neo_Json_Deserialize).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Json.Deserialize).Should().BeTrue(); var ret = engine.CurrentContext.EvaluationStack.Pop(); ret.GetBigInteger().Should().Be(1); } @@ -494,7 +494,7 @@ public void TestJson_Serialize() { var engine = GetEngine(); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Neo_Json_Serialize).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Json.Serialize).Should().BeTrue(); var ret = engine.CurrentContext.EvaluationStack.Pop(); ret.GetString().Should().Be("1"); } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 4e4b05b0c9..e3427aea5b 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -42,7 +42,7 @@ public void Runtime_GetNotifications_Test() // Notify method - script.EmitSysCall(InteropService.System_Runtime_Notify); + script.EmitSysCall(InteropService.Runtime.Notify); // Add return @@ -68,7 +68,7 @@ public void Runtime_GetNotifications_Test() // Retrive script.EmitPush(1); - script.EmitSysCall(InteropService.System_Runtime_GetNotifications); + script.EmitSysCall(InteropService.Runtime.GetNotifications); // Execute @@ -85,7 +85,7 @@ public void Runtime_GetNotifications_Test() // Notification 1 -> 13 script.EmitPush(13); - script.EmitSysCall(InteropService.System_Runtime_Notify); + script.EmitSysCall(InteropService.Runtime.Notify); // Call script @@ -98,7 +98,7 @@ public void Runtime_GetNotifications_Test() // Receive all notifications script.Emit(OpCode.PUSHNULL); - script.EmitSysCall(InteropService.System_Runtime_GetNotifications); + script.EmitSysCall(InteropService.Runtime.GetNotifications); // Execute @@ -135,7 +135,7 @@ public void Runtime_GetNotifications_Test() // Notification 1 -> 13 script.EmitPush(13); - script.EmitSysCall(InteropService.System_Runtime_Notify); + script.EmitSysCall(InteropService.Runtime.Notify); // Call script @@ -148,7 +148,7 @@ public void Runtime_GetNotifications_Test() // Receive all notifications script.EmitPush(scriptHash2.ToArray()); - script.EmitSysCall(InteropService.System_Runtime_GetNotifications); + script.EmitSysCall(InteropService.Runtime.GetNotifications); // Execute @@ -205,7 +205,7 @@ private void AssertNotification(StackItem stackItem, UInt160 scriptHash, int not public void TestExecutionEngine_GetScriptContainer() { var engine = GetEngine(true); - InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetScriptContainer).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.GetScriptContainer).Should().BeTrue(); var stackItem = ((VM.Types.Array)engine.CurrentContext.EvaluationStack.Pop()).ToArray(); stackItem.Length.Should().Be(8); stackItem[0].GetSpan().ToHexString().Should().Be(TestUtils.GetTransaction().Hash.ToArray().ToHexString()); @@ -215,7 +215,7 @@ public void TestExecutionEngine_GetScriptContainer() public void TestExecutionEngine_GetExecutingScriptHash() { var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetExecutingScriptHash).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.GetExecutingScriptHash).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() .Should().Be(engine.CurrentScriptHash.ToArray().ToHexString()); } @@ -226,7 +226,7 @@ public void TestExecutionEngine_GetCallingScriptHash() // Test without var engine = GetEngine(true); - InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetCallingScriptHash).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.GetCallingScriptHash).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); // Test real @@ -234,7 +234,7 @@ public void TestExecutionEngine_GetCallingScriptHash() using ScriptBuilder scriptA = new ScriptBuilder(); scriptA.Emit(OpCode.DROP); // Drop arguments scriptA.Emit(OpCode.DROP); // Drop method - scriptA.EmitSysCall(InteropService.System_ExecutionEngine_GetCallingScriptHash); + scriptA.EmitSysCall(InteropService.Runtime.GetCallingScriptHash); var contract = new ContractState() { @@ -258,7 +258,7 @@ public void TestExecutionEngine_GetCallingScriptHash() public void TestExecutionEngine_GetEntryScriptHash() { var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.System_ExecutionEngine_GetEntryScriptHash).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.GetEntryScriptHash).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() .Should().Be(engine.EntryScriptHash.ToArray().ToHexString()); } @@ -267,7 +267,7 @@ public void TestExecutionEngine_GetEntryScriptHash() public void TestRuntime_Platform() { var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.System_Runtime_Platform).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.Platform).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() .Should().Be(Encoding.ASCII.GetBytes("NEO").ToHexString()); } @@ -276,7 +276,7 @@ public void TestRuntime_Platform() public void TestRuntime_GetTrigger() { var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.System_Runtime_GetTrigger).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.GetTrigger).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBigInteger() .Should().Be((int)engine.Trigger); } @@ -293,17 +293,17 @@ public void TestRuntime_CheckWitness() ((Transaction)engine.ScriptContainer).Sender = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(true)); - InteropService.Invoke(engine, InteropService.System_Runtime_CheckWitness).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.CheckWitness).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Peek().GetType().Should().Be(typeof(Neo.VM.Types.Boolean)); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().Be(false); engine.CurrentContext.EvaluationStack.Push(((Transaction)engine.ScriptContainer).Sender.ToArray()); - InteropService.Invoke(engine, InteropService.System_Runtime_CheckWitness).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.CheckWitness).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Peek().GetType().Should().Be(typeof(Neo.VM.Types.Boolean)); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().Be(false); engine.CurrentContext.EvaluationStack.Push(new byte[0]); - InteropService.Invoke(engine, InteropService.System_Runtime_CheckWitness).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Runtime.CheckWitness).Should().BeFalse(); } [TestMethod] @@ -313,7 +313,7 @@ public void TestRuntime_Log() string message = "hello"; engine.CurrentContext.EvaluationStack.Push(Encoding.UTF8.GetBytes(message)); ApplicationEngine.Log += LogEvent; - InteropService.Invoke(engine, InteropService.System_Runtime_Log).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.Log).Should().BeTrue(); ((Transaction)engine.ScriptContainer).Script.ToHexString().Should().Be(new byte[] { 0x01, 0x02, 0x03 }.ToHexString()); ApplicationEngine.Log -= LogEvent; } @@ -326,7 +326,7 @@ public void TestRuntime_GetTime() var engine = GetEngine(true, true); engine.Snapshot.PersistingBlock = block; - InteropService.Invoke(engine, InteropService.System_Runtime_GetTime).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.GetTime).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(block.Timestamp); } @@ -335,15 +335,15 @@ public void TestRuntime_Serialize() { var engine = GetEngine(); engine.CurrentContext.EvaluationStack.Push(100); - InteropService.Invoke(engine, InteropService.System_Runtime_Serialize).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() .Should().Be(new byte[] { 0x02, 0x01, 0x64 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(new byte[1024 * 1024 * 2]); //Larger than MaxItemSize - InteropService.Invoke(engine, InteropService.System_Runtime_Serialize).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeFalse(); engine.CurrentContext.EvaluationStack.Push(new TestInteropInterface()); //NotSupportedException - InteropService.Invoke(engine, InteropService.System_Runtime_Serialize).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeFalse(); } [TestMethod] @@ -351,21 +351,21 @@ public void TestRuntime_Deserialize() { var engine = GetEngine(); engine.CurrentContext.EvaluationStack.Push(100); - InteropService.Invoke(engine, InteropService.System_Runtime_Serialize).Should().BeTrue(); - InteropService.Invoke(engine, InteropService.System_Runtime_Deserialize).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Binary.Deserialize).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(100); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0xfa, 0x01 }); //FormatException - InteropService.Invoke(engine, InteropService.System_Runtime_Deserialize).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Binary.Deserialize).Should().BeFalse(); } [TestMethod] public void TestRuntime_GetInvocationCounter() { var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.System_Runtime_GetInvocationCounter).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Runtime.GetInvocationCounter).Should().BeFalse(); engine.InvocationCounter.TryAdd(engine.CurrentScriptHash, 10); - InteropService.Invoke(engine, InteropService.System_Runtime_GetInvocationCounter).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Runtime.GetInvocationCounter).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(10); } @@ -384,7 +384,7 @@ public void TestCrypto_Verify() engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); engine.CurrentContext.EvaluationStack.Push(message); - InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.ECDsaVerify).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); byte[] wrongkey = pubkey.EncodePoint(false); @@ -392,7 +392,7 @@ public void TestCrypto_Verify() engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(wrongkey); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(engine.ScriptContainer)); - InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.ECDsaVerify).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Peek().ToBoolean().Should().BeFalse(); } @@ -400,7 +400,7 @@ public void TestCrypto_Verify() public void TestBlockchain_GetHeight() { var engine = GetEngine(true, true); - InteropService.Invoke(engine, InteropService.System_Blockchain_GetHeight).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Blockchain.GetHeight).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(0); } @@ -410,7 +410,7 @@ public void TestBlockchain_GetBlock() var engine = GetEngine(true, true); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); - InteropService.Invoke(engine, InteropService.System_Blockchain_GetBlock).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Blockchain.GetBlock).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); byte[] data1 = new byte[] { 0x01, 0x01, 0x01 ,0x01, 0x01, 0x01, 0x01, 0x01, @@ -418,12 +418,12 @@ public void TestBlockchain_GetBlock() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; engine.CurrentContext.EvaluationStack.Push(data1); - InteropService.Invoke(engine, InteropService.System_Blockchain_GetBlock).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Blockchain.GetBlock).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); byte[] data2 = new byte[] { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; engine.CurrentContext.EvaluationStack.Push(data2); - InteropService.Invoke(engine, InteropService.System_Blockchain_GetBlock).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Blockchain.GetBlock).Should().BeFalse(); } [TestMethod] @@ -435,7 +435,7 @@ public void TestBlockchain_GetTransaction() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; engine.CurrentContext.EvaluationStack.Push(data1); - InteropService.Invoke(engine, InteropService.System_Blockchain_GetTransaction).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Blockchain.GetTransaction).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); } @@ -448,7 +448,7 @@ public void TestBlockchain_GetTransactionHeight() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; engine.CurrentContext.EvaluationStack.Push(data1); - InteropService.Invoke(engine, InteropService.System_Blockchain_GetTransactionHeight).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Blockchain.GetTransactionHeight).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(-1); } @@ -461,7 +461,7 @@ public void TestBlockchain_GetContract() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; engine.CurrentContext.EvaluationStack.Push(data1); - InteropService.Invoke(engine, InteropService.System_Blockchain_GetContract).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Blockchain.GetContract).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); var snapshot = Blockchain.Singleton.GetSnapshot(); @@ -470,7 +470,7 @@ public void TestBlockchain_GetContract() engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.System_Blockchain_GetContract).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Blockchain.GetContract).Should().BeTrue(); var stackItems = ((VM.Types.Array)engine.CurrentContext.EvaluationStack.Pop()).ToArray(); stackItems.Length.Should().Be(3); stackItems[0].GetType().Should().Be(typeof(ByteArray)); @@ -483,7 +483,7 @@ public void TestBlockchain_GetContract() public void TestStorage_GetContext() { var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.System_Storage_GetContext).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Storage.GetContext).Should().BeTrue(); var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().ScriptHash.Should().Be(engine.CurrentScriptHash); ret.GetInterface().IsReadOnly.Should().BeFalse(); @@ -493,7 +493,7 @@ public void TestStorage_GetContext() public void TestStorage_GetReadOnlyContext() { var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.System_Storage_GetReadOnlyContext).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Storage.GetReadOnlyContext).Should().BeTrue(); var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().ScriptHash.Should().Be(engine.CurrentScriptHash); ret.GetInterface().IsReadOnly.Should().BeTrue(); @@ -528,7 +528,7 @@ public void TestStorage_Get() ScriptHash = state.ScriptHash, IsReadOnly = false })); - InteropService.Invoke(engine, InteropService.System_Storage_Get).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Storage.Get).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); snapshot.Contracts.Delete(state.ScriptHash); @@ -540,10 +540,10 @@ public void TestStorage_Get() ScriptHash = state.ScriptHash, IsReadOnly = false })); - InteropService.Invoke(engine, InteropService.System_Storage_Get).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Get).Should().BeFalse(); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.System_Storage_Get).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Get).Should().BeFalse(); } [TestMethod] @@ -551,7 +551,7 @@ public void TestStorage_Put() { var engine = GetEngine(false, true); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //CheckStorageContext fail var key = new byte[] { 0x01 }; @@ -565,15 +565,15 @@ public void TestStorage_Put() IsReadOnly = false }; engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //key.Length > MaxStorageKeySize - key = new byte[InteropService.MaxStorageKeySize + 1]; + key = new byte[InteropService.Storage.MaxKeySize + 1]; value = new byte[] { 0x02 }; engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //value.Length > MaxStorageValueSize key = new byte[] { 0x01 }; @@ -581,7 +581,7 @@ public void TestStorage_Put() engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //context.IsReadOnly key = new byte[] { 0x01 }; @@ -590,7 +590,7 @@ public void TestStorage_Put() engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //storage value is constant var snapshot = Blockchain.Singleton.GetSnapshot(); @@ -616,14 +616,14 @@ public void TestStorage_Put() engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //success storageItem.IsConstant = false; engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeTrue(); //value length == 0 key = new byte[] { 0x01 }; @@ -631,7 +631,7 @@ public void TestStorage_Put() engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_Put).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeTrue(); } [TestMethod] @@ -639,7 +639,7 @@ public void TestStorage_PutEx() { var engine = GetEngine(false, true); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.System_Storage_PutEx).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.PutEx).Should().BeFalse(); var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); @@ -669,7 +669,7 @@ public void TestStorage_PutEx() engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_PutEx).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Storage.PutEx).Should().BeTrue(); } [TestMethod] @@ -677,7 +677,7 @@ public void TestStorage_Delete() { var engine = GetEngine(false, true); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.System_Storage_Delete).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeFalse(); var snapshot = Blockchain.Singleton.GetSnapshot(); @@ -706,20 +706,20 @@ public void TestStorage_Delete() }; engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_Delete).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeTrue(); //context is readonly storageContext.IsReadOnly = true; engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_Delete).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeFalse(); //CheckStorageContext fail storageContext.IsReadOnly = false; state.Manifest.Features = ContractFeatures.NoProperty; engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_Storage_Delete).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeFalse(); } [TestMethod] @@ -727,7 +727,7 @@ public void TestStorageContext_AsReadOnly() { var engine = GetEngine(); engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.System_StorageContext_AsReadOnly).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.AsReadOnly).Should().BeFalse(); var state = TestUtils.GetContract(); var storageContext = new StorageContext @@ -736,7 +736,7 @@ public void TestStorageContext_AsReadOnly() IsReadOnly = false }; engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.System_StorageContext_AsReadOnly).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Storage.AsReadOnly).Should().BeTrue(); var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().IsReadOnly.Should().Be(true); } @@ -746,7 +746,7 @@ public void TestInvoke() { var engine = new ApplicationEngine(TriggerType.Verification, null, null, 0); InteropService.Invoke(engine, 10000).Should().BeFalse(); - InteropService.Invoke(engine, InteropService.System_StorageContext_AsReadOnly).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.AsReadOnly).Should().BeFalse(); } [TestMethod] @@ -764,7 +764,7 @@ public void TestContract_Call() engine.CurrentContext.EvaluationStack.Push(args); engine.CurrentContext.EvaluationStack.Push(method); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.System_Contract_Call).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Contract.Call).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(method.ToHexString()); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(args.ToHexString()); @@ -772,25 +772,25 @@ public void TestContract_Call() engine.CurrentContext.EvaluationStack.Push(args); engine.CurrentContext.EvaluationStack.Push(method); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.System_Contract_Call).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Contract.Call).Should().BeFalse(); state.Manifest.Permissions[0].Methods = WildcardContainer.CreateWildcard(); engine.CurrentContext.EvaluationStack.Push(args); engine.CurrentContext.EvaluationStack.Push(method); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.System_Contract_Call).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Contract.Call).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Push(args); engine.CurrentContext.EvaluationStack.Push(method); engine.CurrentContext.EvaluationStack.Push(UInt160.Zero.ToArray()); - InteropService.Invoke(engine, InteropService.System_Contract_Call).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Contract.Call).Should().BeFalse(); } [TestMethod] public void TestContract_Destroy() { var engine = GetEngine(false, true); - InteropService.Invoke(engine, InteropService.System_Contract_Destroy).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Contract.Destroy).Should().BeTrue(); var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); @@ -811,7 +811,7 @@ public void TestContract_Destroy() snapshot.Storages.Add(storageKey, storageItem); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[0]); - InteropService.Invoke(engine, InteropService.System_Contract_Destroy).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Contract.Destroy).Should().BeTrue(); //storages are removed snapshot = Blockchain.Singleton.GetSnapshot(); @@ -819,7 +819,7 @@ public void TestContract_Destroy() snapshot.Contracts.Add(scriptHash, state); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[0]); - InteropService.Invoke(engine, InteropService.System_Contract_Destroy).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Contract.Destroy).Should().BeTrue(); } public static void LogEvent(object sender, LogEventArgs args) diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index e6c0d006c2..04f60db7ff 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -57,7 +57,7 @@ public void System_Blockchain_GetBlock() using (var script = new ScriptBuilder()) { script.EmitPush(block.Hash.ToArray()); - script.EmitSysCall(InteropService.System_Blockchain_GetBlock); + script.EmitSysCall(InteropService.Blockchain.GetBlock); // Without block @@ -75,7 +75,7 @@ public void System_Blockchain_GetBlock() blocks.Add(block.Hash, block.Trim()); txs.Add(tx.Hash, new TransactionState() { Transaction = tx, BlockIndex = block.Index, VMState = VMState.HALT }); - script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.EmitSysCall(InteropService.Json.Serialize); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(script.ToArray()); @@ -100,9 +100,9 @@ public void Json_Deserialize() using (var script = new ScriptBuilder()) { script.EmitPush("123"); - script.EmitSysCall(InteropService.Neo_Json_Deserialize); + script.EmitSysCall(InteropService.Json.Deserialize); script.EmitPush("null"); - script.EmitSysCall(InteropService.Neo_Json_Deserialize); + script.EmitSysCall(InteropService.Json.Deserialize); using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) { @@ -121,7 +121,7 @@ public void Json_Deserialize() using (var script = new ScriptBuilder()) { script.EmitPush("***"); - script.EmitSysCall(InteropService.Neo_Json_Deserialize); + script.EmitSysCall(InteropService.Json.Deserialize); using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) { @@ -137,7 +137,7 @@ public void Json_Deserialize() using (var script = new ScriptBuilder()) { script.EmitPush("123.45"); - script.EmitSysCall(InteropService.Neo_Json_Deserialize); + script.EmitSysCall(InteropService.Json.Deserialize); using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) { @@ -157,20 +157,20 @@ public void Json_Serialize() using (var script = new ScriptBuilder()) { script.EmitPush(5); - script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.EmitSysCall(InteropService.Json.Serialize); script.Emit(OpCode.PUSH0); script.Emit(OpCode.NOT); - script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.EmitSysCall(InteropService.Json.Serialize); script.EmitPush("test"); - script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.EmitSysCall(InteropService.Json.Serialize); script.Emit(OpCode.PUSHNULL); - script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.EmitSysCall(InteropService.Json.Serialize); script.Emit(OpCode.NEWMAP); script.Emit(OpCode.DUP); script.EmitPush("key"); script.EmitPush("value"); script.Emit(OpCode.SETITEM); - script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.EmitSysCall(InteropService.Json.Serialize); using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) { @@ -191,8 +191,8 @@ public void Json_Serialize() using (var script = new ScriptBuilder()) { - script.EmitSysCall(InteropService.System_Storage_GetContext); - script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.EmitSysCall(InteropService.Storage.GetContext); + script.EmitSysCall(InteropService.Json.Serialize); using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) { @@ -210,7 +210,7 @@ public void System_ExecutionEngine_GetScriptContainer() var snapshot = Blockchain.Singleton.GetSnapshot(); using (var script = new ScriptBuilder()) { - script.EmitSysCall(InteropService.System_ExecutionEngine_GetScriptContainer); + script.EmitSysCall(InteropService.Runtime.GetScriptContainer); // Without tx @@ -223,7 +223,7 @@ public void System_ExecutionEngine_GetScriptContainer() // With tx - script.EmitSysCall(InteropService.Neo_Json_Serialize); + script.EmitSysCall(InteropService.Json.Serialize); var tx = new Transaction() { @@ -262,7 +262,7 @@ public void System_Runtime_GetInvocationCounter() using (var script = new ScriptBuilder()) { - script.EmitSysCall(InteropService.System_Runtime_GetInvocationCounter); + script.EmitSysCall(InteropService.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() }; @@ -283,10 +283,10 @@ public void System_Runtime_GetInvocationCounter() using (var script = new ScriptBuilder()) { - script.EmitSysCall(InteropService.System_Contract_Call, contractA.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.System_Contract_Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.System_Contract_Call, contractC.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.Contract.Call, contractA.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.Contract.Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.Contract.Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitSysCall(InteropService.Contract.Call, contractC.ScriptHash.ToArray(), "dummyMain", 0); // Execute diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 1923f48a66..559f9013ae 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -41,7 +41,7 @@ public void TestEmitAppCall1() tempArray[9] = (byte)OpCode.PUSHDATA1; tempArray[10] = 0x14;//scriptHash.Length Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 11, 20);//operation.data - uint api = InteropService.System_Contract_Call; + uint api = InteropService.Contract.Call; tempArray[31] = (byte)OpCode.SYSCALL; Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 32, 4);//api.data CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -63,7 +63,7 @@ public void TestEmitAppCall2() tempArray[10] = (byte)OpCode.PUSHDATA1; tempArray[11] = 0x14;//scriptHash.Length Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 12, 20);//operation.data - uint api = InteropService.System_Contract_Call; + uint api = InteropService.Contract.Call; tempArray[32] = (byte)OpCode.SYSCALL; Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -85,7 +85,7 @@ public void TestEmitAppCall3() tempArray[10] = (byte)OpCode.PUSHDATA1; tempArray[11] = 0x14;//scriptHash.Length Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 12, 20);//operation.data - uint api = InteropService.System_Contract_Call; + uint api = InteropService.Contract.Call; tempArray[32] = (byte)OpCode.SYSCALL; Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data CollectionAssert.AreEqual(tempArray, sb.ToArray()); From 8904114814a8e006e516678c538a03eb136f3f4c Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Wed, 18 Dec 2019 18:27:29 +0800 Subject: [PATCH 186/305] Change the way to get RemoteNode from ActorSelection to RemoteNodes Dictionary (#1354) * update connections.tell * optimise * update * add comment --- src/neo/Network/P2P/LocalNode.cs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index bda237c219..ffd0ac9930 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -84,9 +84,17 @@ private void BroadcastMessage(MessageCommand command, ISerializable payload = nu /// Broadcast a message to all connected nodes, namely . /// /// The message to be broadcasted. - private void BroadcastMessage(Message message) + private void BroadcastMessage(Message message) => SendToRemoteNodes(message); + + /// + /// Send message to all the RemoteNodes connected to other nodes, faster than ActorSelection. + /// + private void SendToRemoteNodes(object message) { - Connections.Tell(message); + foreach (var connection in RemoteNodes.Keys) + { + connection.Tell(message); + } } private static IPEndPoint GetIPEndpointFromHostPort(string hostNameOrAddress, int port) @@ -189,15 +197,9 @@ private void OnRelay(IInventory inventory) system.Blockchain.Tell(inventory); } - private void OnRelayDirectly(IInventory inventory) - { - Connections.Tell(new RemoteNode.Relay { Inventory = inventory }); - } + private void OnRelayDirectly(IInventory inventory) => SendToRemoteNodes(new RemoteNode.Relay { Inventory = inventory }); - private void OnSendDirectly(IInventory inventory) - { - Connections.Tell(inventory); - } + private void OnSendDirectly(IInventory inventory) => SendToRemoteNodes(inventory); public static Props Props(NeoSystem system) { From 8c2e743f6f2a1cbba125e2691681a26628b120ff Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 19 Dec 2019 06:51:51 +0100 Subject: [PATCH 187/305] CallEx implementation (#1364) --- src/neo/SmartContract/ApplicationEngine.cs | 7 +++ src/neo/SmartContract/CallFlags.cs | 17 ++++++ .../SmartContract/ExecutionContextState.cs | 5 ++ src/neo/SmartContract/Helper.cs | 4 +- src/neo/SmartContract/InteropDescriptor.cs | 12 +++-- .../SmartContract/InteropService.Binary.cs | 4 +- .../InteropService.Blockchain.cs | 12 ++--- .../SmartContract/InteropService.Contract.cs | 48 +++++++++++++---- .../SmartContract/InteropService.Crypto.cs | 4 +- .../InteropService.Enumerator.cs | 8 +-- .../SmartContract/InteropService.Iterator.cs | 10 ++-- src/neo/SmartContract/InteropService.Json.cs | 4 +- .../SmartContract/InteropService.Native.cs | 4 +- .../SmartContract/InteropService.Runtime.cs | 24 ++++----- .../SmartContract/InteropService.Storage.cs | 16 +++--- src/neo/SmartContract/InteropService.cs | 11 ++-- .../Native/ContractMethodMetadata.cs | 1 + .../SmartContract/Native/NativeContract.cs | 6 ++- .../SmartContract/UT_ApplicationEngine.cs | 2 +- .../SmartContract/UT_InteropDescriptor.cs | 3 +- .../SmartContract/UT_InteropService.cs | 53 +++++++++++++++++++ 21 files changed, 188 insertions(+), 67 deletions(-) create mode 100644 src/neo/SmartContract/CallFlags.cs diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 4439012f9c..29a8d9a414 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -60,6 +60,13 @@ protected override void LoadContext(ExecutionContext context) base.LoadContext(context); } + public ExecutionContext LoadScript(Script script, CallFlags callFlags, int rvcount = -1) + { + ExecutionContext context = LoadScript(script, rvcount); + context.GetState().CallFlags = callFlags; + return context; + } + public override void Dispose() { foreach (IDisposable disposable in disposables) diff --git a/src/neo/SmartContract/CallFlags.cs b/src/neo/SmartContract/CallFlags.cs new file mode 100644 index 0000000000..99af3e0599 --- /dev/null +++ b/src/neo/SmartContract/CallFlags.cs @@ -0,0 +1,17 @@ +using System; + +namespace Neo.SmartContract +{ + [Flags] + public enum CallFlags : byte + { + None = 0, + + AllowModifyStates = 0b00000001, + AllowCall = 0b00000010, + AllowNotify = 0b00000100, + + ReadOnly = AllowCall | AllowNotify, + All = AllowModifyStates | AllowCall | AllowNotify + } +} diff --git a/src/neo/SmartContract/ExecutionContextState.cs b/src/neo/SmartContract/ExecutionContextState.cs index 929048fee1..7c5aca4231 100644 --- a/src/neo/SmartContract/ExecutionContextState.cs +++ b/src/neo/SmartContract/ExecutionContextState.cs @@ -11,5 +11,10 @@ internal class ExecutionContextState /// Calling script hash /// public UInt160 CallingScriptHash { get; set; } + + /// + /// Execution context rights + /// + public CallFlags CallFlags { get; set; } = CallFlags.All; } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 1e6b29e7bb..faac766baf 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -122,8 +122,8 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snap } using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, gas)) { - engine.LoadScript(verification); - engine.LoadScript(verifiable.Witnesses[i].InvocationScript); + engine.LoadScript(verification, CallFlags.ReadOnly); + engine.LoadScript(verifiable.Witnesses[i].InvocationScript, CallFlags.None); if (engine.Execute() == VMState.FAULT) return false; if (!engine.ResultStack.TryPop(out StackItem result) || !result.ToBoolean()) return false; } diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index 5cdcf6024a..6984eba13c 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -11,25 +11,27 @@ public class InteropDescriptor public long Price { get; } public Func PriceCalculator { get; } public TriggerType AllowedTriggers { get; } + public CallFlags RequiredCallFlags { get; } - internal InteropDescriptor(string method, Func handler, long price, TriggerType allowedTriggers) - : this(method, handler, allowedTriggers) + internal InteropDescriptor(string method, Func handler, long price, TriggerType allowedTriggers, CallFlags requiredCallFlags) + : this(method, handler, allowedTriggers, requiredCallFlags) { this.Price = price; } - internal InteropDescriptor(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers) - : this(method, handler, allowedTriggers) + internal InteropDescriptor(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers, CallFlags requiredCallFlags) + : this(method, handler, allowedTriggers, requiredCallFlags) { this.PriceCalculator = priceCalculator; } - private InteropDescriptor(string method, Func handler, TriggerType allowedTriggers) + private InteropDescriptor(string method, Func handler, TriggerType allowedTriggers, CallFlags requiredCallFlags) { this.Method = method; this.Hash = method.ToInteropMethodHash(); this.Handler = handler; this.AllowedTriggers = allowedTriggers; + this.RequiredCallFlags = requiredCallFlags; } public long GetPrice(EvaluationStack stack) diff --git a/src/neo/SmartContract/InteropService.Binary.cs b/src/neo/SmartContract/InteropService.Binary.cs index 450e4744c5..f2e2d22a7b 100644 --- a/src/neo/SmartContract/InteropService.Binary.cs +++ b/src/neo/SmartContract/InteropService.Binary.cs @@ -9,8 +9,8 @@ partial class InteropService { public static class Binary { - public static readonly InteropDescriptor Serialize = Register("System.Binary.Serialize", Binary_Serialize, 0_00100000, TriggerType.All); - public static readonly InteropDescriptor Deserialize = Register("System.Binary.Deserialize", Binary_Deserialize, 0_00500000, TriggerType.All); + public static readonly InteropDescriptor Serialize = Register("System.Binary.Serialize", Binary_Serialize, 0_00100000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Deserialize = Register("System.Binary.Deserialize", Binary_Deserialize, 0_00500000, TriggerType.All, CallFlags.None); private static bool Binary_Serialize(ApplicationEngine engine) { diff --git a/src/neo/SmartContract/InteropService.Blockchain.cs b/src/neo/SmartContract/InteropService.Blockchain.cs index d5cdad0769..b31aba4a32 100644 --- a/src/neo/SmartContract/InteropService.Blockchain.cs +++ b/src/neo/SmartContract/InteropService.Blockchain.cs @@ -11,12 +11,12 @@ partial class InteropService { public static class Blockchain { - public static readonly InteropDescriptor GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); - public static readonly InteropDescriptor GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application); - public static readonly InteropDescriptor GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application); - public static readonly InteropDescriptor GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application); - public static readonly InteropDescriptor GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application); - public static readonly InteropDescriptor GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application); + public static readonly InteropDescriptor GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application, CallFlags.None); private static bool Blockchain_GetHeight(ApplicationEngine engine) { diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index 0e50ea8f6d..e6df9d8aa8 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -3,6 +3,7 @@ using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; +using System; using System.Linq; namespace Neo.SmartContract @@ -11,11 +12,12 @@ partial class InteropService { public static class Contract { - public static readonly InteropDescriptor Create = Register("System.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application); - public static readonly InteropDescriptor Update = Register("System.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application); - public static readonly InteropDescriptor Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application); - public static readonly InteropDescriptor Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application); - public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All); + public static readonly InteropDescriptor Create = Register("System.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor Update = Register("System.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); + public static readonly InteropDescriptor CallEx = Register("System.Contract.CallEx", Contract_CallEx, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); + public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All, CallFlags.None); private static long GetDeploymentPrice(EvaluationStack stack) { @@ -112,12 +114,34 @@ private static bool Contract_Destroy(ApplicationEngine engine) private static bool Contract_Call(ApplicationEngine engine) { StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop(); + StackItem method = engine.CurrentContext.EvaluationStack.Pop(); + StackItem args = engine.CurrentContext.EvaluationStack.Pop(); - ContractState contract = engine.Snapshot.Contracts.TryGet(new UInt160(contractHash.GetSpan())); - if (contract is null) return false; + return Contract_CallEx(engine, new UInt160(contractHash.GetSpan()), method, args, CallFlags.All); + } + private static bool Contract_CallEx(ApplicationEngine engine) + { + StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop(); StackItem method = engine.CurrentContext.EvaluationStack.Pop(); StackItem args = engine.CurrentContext.EvaluationStack.Pop(); + + if (!engine.CurrentContext.EvaluationStack.TryPop(out var flagItem)) + { + return false; + } + + CallFlags flags = (CallFlags)(int)flagItem.ToBigInteger(); + if (!Enum.IsDefined(typeof(CallFlags), flags)) return false; + + return Contract_CallEx(engine, new UInt160(contractHash.GetSpan()), method, args, flags); + } + + private static bool Contract_CallEx(ApplicationEngine engine, UInt160 contractHash, StackItem method, StackItem args, CallFlags flags) + { + ContractState contract = engine.Snapshot.Contracts.TryGet(contractHash); + if (contract is null) return false; + ContractManifest currentManifest = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash)?.Manifest; if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method.GetString())) @@ -132,9 +156,15 @@ private static bool Contract_Call(ApplicationEngine engine) engine.InvocationCounter[contract.ScriptHash] = 1; } - UInt160 callingScriptHash = engine.CurrentScriptHash; + ExecutionContextState state = engine.CurrentContext.GetState(); + UInt160 callingScriptHash = state.ScriptHash; + CallFlags callingFlags = state.CallFlags; + ExecutionContext context_new = engine.LoadScript(contract.Script, 1); - context_new.GetState().CallingScriptHash = callingScriptHash; + state = context_new.GetState(); + state.CallingScriptHash = callingScriptHash; + state.CallFlags = flags & callingFlags; + context_new.EvaluationStack.Push(args); context_new.EvaluationStack.Push(method); return true; diff --git a/src/neo/SmartContract/InteropService.Crypto.cs b/src/neo/SmartContract/InteropService.Crypto.cs index f1c2f491e0..448d312ea4 100644 --- a/src/neo/SmartContract/InteropService.Crypto.cs +++ b/src/neo/SmartContract/InteropService.Crypto.cs @@ -13,8 +13,8 @@ partial class InteropService { public static class Crypto { - public static readonly InteropDescriptor ECDsaVerify = Register("Neo.Crypto.ECDsaVerify", Crypto_ECDsaVerify, 0_01000000, TriggerType.All); - public static readonly InteropDescriptor ECDsaCheckMultiSig = Register("Neo.Crypto.ECDsaCheckMultiSig", Crypto_ECDsaCheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All); + public static readonly InteropDescriptor ECDsaVerify = Register("Neo.Crypto.ECDsaVerify", Crypto_ECDsaVerify, 0_01000000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor ECDsaCheckMultiSig = Register("Neo.Crypto.ECDsaCheckMultiSig", Crypto_ECDsaCheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None); private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack) { diff --git a/src/neo/SmartContract/InteropService.Enumerator.cs b/src/neo/SmartContract/InteropService.Enumerator.cs index 143e040ba4..444dda80a2 100644 --- a/src/neo/SmartContract/InteropService.Enumerator.cs +++ b/src/neo/SmartContract/InteropService.Enumerator.cs @@ -9,10 +9,10 @@ partial class InteropService { public static class Enumerator { - public static readonly InteropDescriptor Create = Register("System.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All); - public static readonly InteropDescriptor Next = Register("System.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All); - public static readonly InteropDescriptor Value = Register("System.Enumerator.Value", Enumerator_Value, 0_00000400, TriggerType.All); - public static readonly InteropDescriptor Concat = Register("System.Enumerator.Concat", Enumerator_Concat, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor Create = Register("System.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Next = Register("System.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Value = Register("System.Enumerator.Value", Enumerator_Value, 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Concat = Register("System.Enumerator.Concat", Enumerator_Concat, 0_00000400, TriggerType.All, CallFlags.None); private static bool Enumerator_Create(ApplicationEngine engine) { diff --git a/src/neo/SmartContract/InteropService.Iterator.cs b/src/neo/SmartContract/InteropService.Iterator.cs index 0a99e2848b..590b298eb0 100644 --- a/src/neo/SmartContract/InteropService.Iterator.cs +++ b/src/neo/SmartContract/InteropService.Iterator.cs @@ -9,11 +9,11 @@ partial class InteropService { public static class Iterator { - public static readonly InteropDescriptor Create = Register("System.Iterator.Create", Iterator_Create, 0_00000400, TriggerType.All); - public static readonly InteropDescriptor Key = Register("System.Iterator.Key", Iterator_Key, 0_00000400, TriggerType.All); - public static readonly InteropDescriptor Keys = Register("System.Iterator.Keys", Iterator_Keys, 0_00000400, TriggerType.All); - public static readonly InteropDescriptor Values = Register("System.Iterator.Values", Iterator_Values, 0_00000400, TriggerType.All); - public static readonly InteropDescriptor Concat = Register("System.Iterator.Concat", Iterator_Concat, 0_00000400, TriggerType.All); + public static readonly InteropDescriptor Create = Register("System.Iterator.Create", Iterator_Create, 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Key = Register("System.Iterator.Key", Iterator_Key, 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Keys = Register("System.Iterator.Keys", Iterator_Keys, 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Values = Register("System.Iterator.Values", Iterator_Values, 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Concat = Register("System.Iterator.Concat", Iterator_Concat, 0_00000400, TriggerType.All, CallFlags.None); private static bool Iterator_Create(ApplicationEngine engine) { diff --git a/src/neo/SmartContract/InteropService.Json.cs b/src/neo/SmartContract/InteropService.Json.cs index 45e12db545..5ab72b5e1d 100644 --- a/src/neo/SmartContract/InteropService.Json.cs +++ b/src/neo/SmartContract/InteropService.Json.cs @@ -7,8 +7,8 @@ partial class InteropService { public static class Json { - public static readonly InteropDescriptor Serialize = Register("System.Json.Serialize", Json_Serialize, 0_00100000, TriggerType.All); - public static readonly InteropDescriptor Deserialize = Register("System.Json.Deserialize", Json_Deserialize, 0_00500000, TriggerType.All); + public static readonly InteropDescriptor Serialize = Register("System.Json.Serialize", Json_Serialize, 0_00100000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Deserialize = Register("System.Json.Deserialize", Json_Deserialize, 0_00500000, TriggerType.All, CallFlags.None); private static bool Json_Serialize(ApplicationEngine engine) { diff --git a/src/neo/SmartContract/InteropService.Native.cs b/src/neo/SmartContract/InteropService.Native.cs index 855a355ed5..f664c4020a 100644 --- a/src/neo/SmartContract/InteropService.Native.cs +++ b/src/neo/SmartContract/InteropService.Native.cs @@ -7,12 +7,12 @@ partial class InteropService { internal static class Native { - public static readonly InteropDescriptor Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application); + public static readonly InteropDescriptor Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application, CallFlags.AllowModifyStates); static Native() { foreach (NativeContract contract in NativeContract.Contracts) - Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application); + Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application, CallFlags.None); } private static bool Native_Deploy(ApplicationEngine engine) diff --git a/src/neo/SmartContract/InteropService.Runtime.cs b/src/neo/SmartContract/InteropService.Runtime.cs index 6a70ae844c..b1c0dec22e 100644 --- a/src/neo/SmartContract/InteropService.Runtime.cs +++ b/src/neo/SmartContract/InteropService.Runtime.cs @@ -17,18 +17,18 @@ public static class Runtime { public const int MaxNotificationSize = 1024; - public static readonly InteropDescriptor Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250, TriggerType.All); - public static readonly InteropDescriptor GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250, TriggerType.All); - public static readonly InteropDescriptor GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application); - public static readonly InteropDescriptor GetScriptContainer = Register("System.Runtime.GetScriptContainer", Runtime_GetScriptContainer, 0_00000250, TriggerType.All); - public static readonly InteropDescriptor GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", Runtime_GetExecutingScriptHash, 0_00000400, TriggerType.All); - public static readonly InteropDescriptor GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", Runtime_GetCallingScriptHash, 0_00000400, TriggerType.All); - public static readonly InteropDescriptor GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", Runtime_GetEntryScriptHash, 0_00000400, TriggerType.All); - public static readonly InteropDescriptor CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All); - public static readonly InteropDescriptor GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400, TriggerType.All); - public static readonly InteropDescriptor Log = Register("System.Runtime.Log", Runtime_Log, 0_01000000, TriggerType.All); - public static readonly InteropDescriptor Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_01000000, TriggerType.All); - public static readonly InteropDescriptor GetNotifications = Register("System.Runtime.GetNotifications", Runtime_GetNotifications, 0_00010000, TriggerType.All); + public static readonly InteropDescriptor Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor GetScriptContainer = Register("System.Runtime.GetScriptContainer", Runtime_GetScriptContainer, 0_00000250, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", Runtime_GetExecutingScriptHash, 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", Runtime_GetCallingScriptHash, 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", Runtime_GetEntryScriptHash, 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Log = Register("System.Runtime.Log", Runtime_Log, 0_01000000, TriggerType.All, CallFlags.AllowNotify); + public static readonly InteropDescriptor Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_01000000, TriggerType.All, CallFlags.AllowNotify); + public static readonly InteropDescriptor GetNotifications = Register("System.Runtime.GetNotifications", Runtime_GetNotifications, 0_00010000, TriggerType.All, CallFlags.None); private static bool CheckItemForNotification(StackItem state) { diff --git a/src/neo/SmartContract/InteropService.Storage.cs b/src/neo/SmartContract/InteropService.Storage.cs index ac977c69ca..ebf826b7f5 100644 --- a/src/neo/SmartContract/InteropService.Storage.cs +++ b/src/neo/SmartContract/InteropService.Storage.cs @@ -15,14 +15,14 @@ public static class Storage public const int MaxKeySize = 64; public const int MaxValueSize = ushort.MaxValue; - public static readonly InteropDescriptor GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application); - public static readonly InteropDescriptor GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400, TriggerType.Application); - public static readonly InteropDescriptor AsReadOnly = Register("System.Storage.AsReadOnly", Storage_AsReadOnly, 0_00000400, TriggerType.Application); - public static readonly InteropDescriptor Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application); - public static readonly InteropDescriptor Find = Register("System.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application); - public static readonly InteropDescriptor Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice, TriggerType.Application); - public static readonly InteropDescriptor PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application); - public static readonly InteropDescriptor Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application); + public static readonly InteropDescriptor GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor AsReadOnly = Register("System.Storage.AsReadOnly", Storage_AsReadOnly, 0_00000400, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor Find = Register("System.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates); private static bool CheckStorageContext(ApplicationEngine engine, StorageContext context) { diff --git a/src/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs index 16a0525510..c87ad277ea 100644 --- a/src/neo/SmartContract/InteropService.cs +++ b/src/neo/SmartContract/InteropService.cs @@ -31,19 +31,22 @@ internal static bool Invoke(ApplicationEngine engine, uint method) return false; if (!descriptor.AllowedTriggers.HasFlag(engine.Trigger)) return false; + ExecutionContextState state = engine.CurrentContext.GetState(); + if (!state.CallFlags.HasFlag(descriptor.RequiredCallFlags)) + return false; return descriptor.Handler(engine); } - private static InteropDescriptor Register(string method, Func handler, long price, TriggerType allowedTriggers) + private static InteropDescriptor Register(string method, Func handler, long price, TriggerType allowedTriggers, CallFlags requiredCallFlags) { - InteropDescriptor descriptor = new InteropDescriptor(method, handler, price, allowedTriggers); + InteropDescriptor descriptor = new InteropDescriptor(method, handler, price, allowedTriggers, requiredCallFlags); methods.Add(descriptor.Hash, descriptor); return descriptor; } - private static InteropDescriptor Register(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers) + private static InteropDescriptor Register(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers, CallFlags requiredCallFlags) { - InteropDescriptor descriptor = new InteropDescriptor(method, handler, priceCalculator, allowedTriggers); + InteropDescriptor descriptor = new InteropDescriptor(method, handler, priceCalculator, allowedTriggers, requiredCallFlags); methods.Add(descriptor.Hash, descriptor); return descriptor; } diff --git a/src/neo/SmartContract/Native/ContractMethodMetadata.cs b/src/neo/SmartContract/Native/ContractMethodMetadata.cs index 87dfba4a40..228252f093 100644 --- a/src/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -8,5 +8,6 @@ internal class ContractMethodMetadata { public Func Delegate; public long Price; + public CallFlags RequiredCallFlags; } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index cb2863fda2..17f053ddf8 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -58,7 +58,8 @@ protected NativeContract() methods.Add(name, new ContractMethodMetadata { Delegate = (Func)method.CreateDelegate(typeof(Func), this), - Price = attribute.Price + Price = attribute.Price, + RequiredCallFlags = attribute.SafeMethod ? CallFlags.None : CallFlags.AllowModifyStates }); } this.Manifest.Abi.Methods = descriptors.ToArray(); @@ -92,6 +93,9 @@ internal bool Invoke(ApplicationEngine engine) Array args = (Array)engine.CurrentContext.EvaluationStack.Pop(); if (!methods.TryGetValue(operation, out ContractMethodMetadata method)) return false; + ExecutionContextState state = engine.CurrentContext.GetState(); + if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) + return false; StackItem result = method.Delegate(engine, args); engine.CurrentContext.EvaluationStack.Push(result); return true; diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index 3790aaf094..71e8dfe759 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -119,7 +119,7 @@ public void TestCreateDummyBlock() [TestMethod] public void TestOnSysCall() { - InteropDescriptor descriptor = new InteropDescriptor("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application); + InteropDescriptor descriptor = new InteropDescriptor("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application, CallFlags.None); TestApplicationEngine engine = new TestApplicationEngine(TriggerType.Application, null, null, 0); byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; engine.LoadScript(SyscallSystemRuntimeCheckWitnessHash); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs b/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs index fcccab5dc7..aec2ac3dd7 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; -using System; namespace Neo.UnitTests.SmartContract { @@ -14,7 +13,7 @@ public void TestGetMethod() string method = @"System.ExecutionEngine.GetScriptContainer"; long price = 0_00000250; TriggerType allowedTriggers = TriggerType.All; - InteropDescriptor descriptor = new InteropDescriptor(method, TestHandler, price, allowedTriggers); + InteropDescriptor descriptor = new InteropDescriptor(method, TestHandler, price, allowedTriggers, CallFlags.None); descriptor.Method.Should().Be(method); descriptor.Price.Should().Be(price); } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index e3427aea5b..597da60adc 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -786,6 +786,59 @@ public void TestContract_Call() InteropService.Invoke(engine, InteropService.Contract.Call).Should().BeFalse(); } + [TestMethod] + public void TestContract_CallEx() + { + var snapshot = Blockchain.Singleton.GetSnapshot(); + + var state = TestUtils.GetContract(); + state.Manifest.Features = ContractFeatures.HasStorage; + snapshot.Contracts.Add(state.ScriptHash, state); + + byte[] method = Encoding.UTF8.GetBytes("method"); + byte[] args = new byte[0]; + + foreach (var flags in new CallFlags[] { CallFlags.None, CallFlags.AllowCall, CallFlags.AllowModifyStates, CallFlags.All }) + { + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine.LoadScript(new byte[] { 0x01 }); + + engine.CurrentContext.EvaluationStack.Push((int)CallFlags.All); + engine.CurrentContext.EvaluationStack.Push(args); + engine.CurrentContext.EvaluationStack.Push(method); + engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); + InteropService.Invoke(engine, InteropService.Contract.CallEx).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(method.ToHexString()); + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(args.ToHexString()); + + // Contract doesn't exists + + engine.CurrentContext.EvaluationStack.Push((int)CallFlags.All); + engine.CurrentContext.EvaluationStack.Push(args); + engine.CurrentContext.EvaluationStack.Push(method); + engine.CurrentContext.EvaluationStack.Push(UInt160.Zero.ToArray()); + InteropService.Invoke(engine, InteropService.Contract.CallEx).Should().BeFalse(); + + // Call with rights + + engine.CurrentContext.EvaluationStack.Push((int)flags); + engine.CurrentContext.EvaluationStack.Push(args); + engine.CurrentContext.EvaluationStack.Push(method); + engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); + InteropService.Invoke(engine, InteropService.Contract.CallEx).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(method.ToHexString()); + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(args.ToHexString()); + + // Check rights + + engine.CurrentContext.EvaluationStack.Push((int)CallFlags.All); + engine.CurrentContext.EvaluationStack.Push(args); + engine.CurrentContext.EvaluationStack.Push(method); + engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); + InteropService.Invoke(engine, InteropService.Contract.CallEx).Should().Be(flags.HasFlag(CallFlags.AllowCall)); + } + } + [TestMethod] public void TestContract_Destroy() { From 76d1c56237f94fc044fb62e4483821061fe94f13 Mon Sep 17 00:00:00 2001 From: Ricardo Prado <38396062+lock9@users.noreply.github.com> Date: Thu, 19 Dec 2019 02:52:55 -0300 Subject: [PATCH 188/305] Link fixes (#1351) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 82664ab4f0..14557da887 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ width="25px">   - + @@ -112,7 +112,7 @@ This repository contain main classes of the Visit the [documentation](https://docs.neo.org/docs/en-us/index.html) to get started. -*Note: This is Neo 3 branch, currently under development. For the current stable version, please [click here](https://github.com/neo-project/neo/tree/master-2.x)* +*Note: This is Neo 3 branch, currently under development. For the current stable version, please [click here.](https://github.com/neo-project/neo/tree/master-2.x)* @@ -136,7 +136,7 @@ An overview of the project folders can be seen below. ## Related projects Code references are provided for all platform building blocks. That includes the base library, the VM, a command line application and the compiler. -* [**neo:**](https://github.com/neo-project/neo/tree/) Neo core library, contains base classes, including ledger, p2p and IO modules. +* [neo:](https://github.com/neo-project/neo/) Neo core library, contains base classes, including ledger, p2p and IO modules. * [neo-vm:](https://github.com/neo-project/neo-vm/) Neo Virtual Machine is a decoupled VM that Neo uses to execute its scripts. It also uses the `InteropService` layer to extend its functionalities. * [neo-node:](https://github.com/neo-project/neo-node/) Executable version of the Neo library, exposing features using a command line application or GUI. * [neo-modules:](https://github.com/neo-project/neo-modules/) Neo modules include additional tools and plugins to be used with Neo. From b167c2a6b13d84ccbb97a278589783191475d446 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 22 Dec 2019 14:01:49 +0800 Subject: [PATCH 189/305] Set the price of RET to 0 (#1379) --- src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index a1a2ffd400..f9b04527f6 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -61,7 +61,7 @@ partial class ApplicationEngine [OpCode.THROW] = 30, [OpCode.THROWIF] = 30, [OpCode.THROWIFNOT] = 30, - [OpCode.RET] = 40, + [OpCode.RET] = 0, [OpCode.SYSCALL] = 0, [OpCode.DEPTH] = 60, [OpCode.DROP] = 60, From 9372f8fb339838677e5af5ad3a65d686dff82986 Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Thu, 26 Dec 2019 16:46:02 +0800 Subject: [PATCH 190/305] add data size check (#1381) --- src/neo/IO/Helper.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/neo/IO/Helper.cs b/src/neo/IO/Helper.cs index df74602cdd..531ce4827d 100644 --- a/src/neo/IO/Helper.cs +++ b/src/neo/IO/Helper.cs @@ -79,7 +79,8 @@ public static byte[] CompressLz4(this byte[] data) public static byte[] DecompressLz4(this byte[] data, int maxOutput) { - maxOutput = Math.Min(maxOutput, data.Length * 255); + var maxDecompressDataLength = data.Length * 255; + if (maxDecompressDataLength > 0) maxOutput = Math.Min(maxOutput, maxDecompressDataLength); using var buffer = MemoryPool.Shared.Rent(maxOutput); int length = LZ4Codec.Decode(data, buffer.Memory.Span); if (length < 0 || length > maxOutput) throw new FormatException(); From aa73ecb11bafdf4d6a78495520ad9055a22b7370 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 26 Dec 2019 12:59:45 +0100 Subject: [PATCH 191/305] Set limits in MerkleBlockPayload (3x) (#1377) * Set limits in MerkleBlockPayload (3x) * Update MerkleBlockPayload.cs * Update src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs Co-Authored-By: Erik Zhang * Update MerkleBlockPayload.cs Co-authored-by: Erik Zhang --- src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs index cf2dcba2d9..8a735587cd 100644 --- a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs +++ b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs @@ -37,9 +37,9 @@ public static MerkleBlockPayload Create(Block block, BitArray flags) public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); - ContentCount = (int)reader.ReadVarInt(int.MaxValue); - Hashes = reader.ReadSerializableArray(); - Flags = reader.ReadVarBytes(); + ContentCount = (int)reader.ReadVarInt(Block.MaxTransactionsPerBlock + 1); + Hashes = reader.ReadSerializableArray(ContentCount); + Flags = reader.ReadVarBytes((ContentCount + 7) / 8); } public override void Serialize(BinaryWriter writer) From 830c4a5d57a02b4495f4db60303dc972b5ff095f Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 30 Dec 2019 12:20:20 +0800 Subject: [PATCH 192/305] Neo.VM.3.0.0-CI00201 (#1374) --- src/neo/IO/Helper.cs | 10 ++ .../ApplicationEngine.OpCodePrices.cs | 92 +++++++++++++++---- src/neo/SmartContract/ApplicationEngine.cs | 15 +++ src/neo/SmartContract/BinarySerializer.cs | 75 +++++++-------- src/neo/SmartContract/ContainerPlaceholder.cs | 12 ++- .../SmartContract/InteropService.Binary.cs | 17 +--- .../Iterators/ByteArrayWrapper.cs | 2 +- .../SmartContract/Native/NativeContract.cs | 3 +- .../Native/Tokens/Nep5AccountState.cs | 2 +- src/neo/SmartContract/StackItemType.cs | 14 --- src/neo/VM/Helper.cs | 29 +++--- src/neo/neo.csproj | 2 +- .../Native/Tokens/UT_GasToken.cs | 4 +- .../Native/Tokens/UT_NeoToken.cs | 5 +- .../Native/Tokens/UT_Nep5Token.cs | 3 +- .../SmartContract/UT_BinarySerializer.cs | 35 +++---- .../SmartContract/UT_ContainerPlaceholder.cs | 6 +- .../SmartContract/UT_InteropService.NEO.cs | 38 ++++---- .../SmartContract/UT_InteropService.cs | 53 +++++------ .../SmartContract/UT_NotifyEventArgs.cs | 4 +- tests/neo.UnitTests/VM/UT_Helper.cs | 6 +- 21 files changed, 232 insertions(+), 195 deletions(-) delete mode 100644 src/neo/SmartContract/StackItemType.cs diff --git a/src/neo/IO/Helper.cs b/src/neo/IO/Helper.cs index 531ce4827d..3acc4b2c2f 100644 --- a/src/neo/IO/Helper.cs +++ b/src/neo/IO/Helper.cs @@ -89,6 +89,16 @@ public static byte[] DecompressLz4(this byte[] data, int maxOutput) return result; } + public static void FillBuffer(this BinaryReader reader, Span buffer) + { + while (!buffer.IsEmpty) + { + int count = reader.Read(buffer); + if (count == 0) throw new EndOfStreamException(); + buffer = buffer[count..]; + } + } + public static int GetVarSize(int value) { if (value < 0xFD) diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index f9b04527f6..29552e4397 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -78,28 +78,73 @@ partial class ApplicationEngine [OpCode.REVERSE3] = 60, [OpCode.REVERSE4] = 60, [OpCode.REVERSEN] = 400, - [OpCode.TOALTSTACK] = 60, - [OpCode.FROMALTSTACK] = 60, - [OpCode.DUPFROMALTSTACK] = 60, - [OpCode.DUPFROMALTSTACKBOTTOM] = 60, - [OpCode.ISNULL] = 60, + [OpCode.INITSSLOT] = 400, + [OpCode.INITSLOT] = 800, + [OpCode.LDSFLD0] = 60, + [OpCode.LDSFLD1] = 60, + [OpCode.LDSFLD2] = 60, + [OpCode.LDSFLD3] = 60, + [OpCode.LDSFLD4] = 60, + [OpCode.LDSFLD5] = 60, + [OpCode.LDSFLD6] = 60, + [OpCode.LDSFLD] = 60, + [OpCode.STSFLD0] = 60, + [OpCode.STSFLD1] = 60, + [OpCode.STSFLD2] = 60, + [OpCode.STSFLD3] = 60, + [OpCode.STSFLD4] = 60, + [OpCode.STSFLD5] = 60, + [OpCode.STSFLD6] = 60, + [OpCode.STSFLD] = 60, + [OpCode.LDLOC0] = 60, + [OpCode.LDLOC1] = 60, + [OpCode.LDLOC2] = 60, + [OpCode.LDLOC3] = 60, + [OpCode.LDLOC4] = 60, + [OpCode.LDLOC5] = 60, + [OpCode.LDLOC6] = 60, + [OpCode.LDLOC] = 60, + [OpCode.STLOC0] = 60, + [OpCode.STLOC1] = 60, + [OpCode.STLOC2] = 60, + [OpCode.STLOC3] = 60, + [OpCode.STLOC4] = 60, + [OpCode.STLOC5] = 60, + [OpCode.STLOC6] = 60, + [OpCode.STLOC] = 60, + [OpCode.LDARG0] = 60, + [OpCode.LDARG1] = 60, + [OpCode.LDARG2] = 60, + [OpCode.LDARG3] = 60, + [OpCode.LDARG4] = 60, + [OpCode.LDARG5] = 60, + [OpCode.LDARG6] = 60, + [OpCode.LDARG] = 60, + [OpCode.STARG0] = 60, + [OpCode.STARG1] = 60, + [OpCode.STARG2] = 60, + [OpCode.STARG3] = 60, + [OpCode.STARG4] = 60, + [OpCode.STARG5] = 60, + [OpCode.STARG6] = 60, + [OpCode.STARG] = 60, + [OpCode.NEWBUFFER] = 80000, + [OpCode.MEMCPY] = 80000, [OpCode.CAT] = 80000, [OpCode.SUBSTR] = 80000, [OpCode.LEFT] = 80000, [OpCode.RIGHT] = 80000, - [OpCode.SIZE] = 60, [OpCode.INVERT] = 100, [OpCode.AND] = 200, [OpCode.OR] = 200, [OpCode.XOR] = 200, [OpCode.EQUAL] = 200, - [OpCode.INC] = 100, - [OpCode.DEC] = 100, + [OpCode.NOTEQUAL] = 200, [OpCode.SIGN] = 100, - [OpCode.NEGATE] = 100, [OpCode.ABS] = 100, - [OpCode.NOT] = 100, - [OpCode.NZ] = 100, + [OpCode.NEGATE] = 100, + [OpCode.INC] = 100, + [OpCode.DEC] = 100, [OpCode.ADD] = 200, [OpCode.SUB] = 200, [OpCode.MUL] = 300, @@ -107,31 +152,40 @@ partial class ApplicationEngine [OpCode.MOD] = 300, [OpCode.SHL] = 300, [OpCode.SHR] = 300, + [OpCode.NOT] = 100, [OpCode.BOOLAND] = 200, [OpCode.BOOLOR] = 200, + [OpCode.NZ] = 100, [OpCode.NUMEQUAL] = 200, [OpCode.NUMNOTEQUAL] = 200, [OpCode.LT] = 200, + [OpCode.LE] = 200, [OpCode.GT] = 200, - [OpCode.LTE] = 200, - [OpCode.GTE] = 200, + [OpCode.GE] = 200, [OpCode.MIN] = 200, [OpCode.MAX] = 200, [OpCode.WITHIN] = 200, - [OpCode.ARRAYSIZE] = 150, [OpCode.PACK] = 7000, [OpCode.UNPACK] = 7000, - [OpCode.PICKITEM] = 270000, - [OpCode.SETITEM] = 270000, + [OpCode.NEWARRAY0] = 400, [OpCode.NEWARRAY] = 15000, + [OpCode.NEWARRAY_T] = 15000, + [OpCode.NEWSTRUCT0] = 400, [OpCode.NEWSTRUCT] = 15000, [OpCode.NEWMAP] = 200, - [OpCode.APPEND] = 15000, - [OpCode.REVERSE] = 500, - [OpCode.REMOVE] = 500, + [OpCode.SIZE] = 150, [OpCode.HASKEY] = 270000, [OpCode.KEYS] = 500, [OpCode.VALUES] = 7000, + [OpCode.PICKITEM] = 270000, + [OpCode.APPEND] = 15000, + [OpCode.SETITEM] = 270000, + [OpCode.REVERSEITEMS] = 500, + [OpCode.REMOVE] = 500, + [OpCode.CLEARITEMS] = 400, + [OpCode.ISNULL] = 60, + [OpCode.ISTYPE] = 60, + [OpCode.CONVERT] = 80000, }; } } diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 29a8d9a414..53bc8c68a8 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -5,6 +5,7 @@ using Neo.VM.Types; using System; using System.Collections.Generic; +using System.Text; using Array = System.Array; namespace Neo.SmartContract @@ -140,5 +141,19 @@ internal void SendNotification(UInt160 script_hash, StackItem state) Notify?.Invoke(this, notification); notifications.Add(notification); } + + public bool TryPop(out string s) + { + if (TryPop(out ReadOnlySpan b)) + { + s = Encoding.UTF8.GetString(b); + return true; + } + else + { + s = default; + return false; + } + } } } diff --git a/src/neo/SmartContract/BinarySerializer.cs b/src/neo/SmartContract/BinarySerializer.cs index 1c8f2f9088..3f4d9b44bd 100644 --- a/src/neo/SmartContract/BinarySerializer.cs +++ b/src/neo/SmartContract/BinarySerializer.cs @@ -8,30 +8,31 @@ using System.Numerics; using Array = Neo.VM.Types.Array; using Boolean = Neo.VM.Types.Boolean; +using Buffer = Neo.VM.Types.Buffer; namespace Neo.SmartContract { internal static class BinarySerializer { - public static StackItem Deserialize(byte[] data, uint maxItemSize, ReferenceCounter referenceCounter = null) + public static StackItem Deserialize(byte[] data, uint maxArraySize, uint maxItemSize, ReferenceCounter referenceCounter = null) { using MemoryStream ms = new MemoryStream(data, false); using BinaryReader reader = new BinaryReader(ms); - return Deserialize(reader, maxItemSize, referenceCounter); + return Deserialize(reader, maxArraySize, maxItemSize, referenceCounter); } - public static unsafe StackItem Deserialize(ReadOnlySpan data, uint maxItemSize, ReferenceCounter referenceCounter = null) + public static unsafe StackItem Deserialize(ReadOnlySpan data, uint maxArraySize, uint maxItemSize, ReferenceCounter referenceCounter = null) { if (data.IsEmpty) throw new FormatException(); fixed (byte* pointer = data) { using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, data.Length); using BinaryReader reader = new BinaryReader(ms); - return Deserialize(reader, maxItemSize, referenceCounter); + return Deserialize(reader, maxArraySize, maxItemSize, referenceCounter); } } - private static StackItem Deserialize(BinaryReader reader, uint maxItemSize, ReferenceCounter referenceCounter) + private static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uint maxItemSize, ReferenceCounter referenceCounter) { Stack deserialized = new Stack(); int undeserialized = 1; @@ -40,41 +41,38 @@ private static StackItem Deserialize(BinaryReader reader, uint maxItemSize, Refe StackItemType type = (StackItemType)reader.ReadByte(); switch (type) { - case StackItemType.ByteArray: - deserialized.Push(new ByteArray(reader.ReadVarBytes((int)maxItemSize))); + case StackItemType.Any: + deserialized.Push(StackItem.Null); break; case StackItemType.Boolean: - deserialized.Push(new Boolean(reader.ReadBoolean())); + deserialized.Push(reader.ReadBoolean()); break; case StackItemType.Integer: - deserialized.Push(new Integer(new BigInteger(reader.ReadVarBytes(Integer.MaxSize)))); + deserialized.Push(new BigInteger(reader.ReadVarBytes(Integer.MaxSize))); + break; + case StackItemType.ByteArray: + deserialized.Push(reader.ReadVarBytes((int)maxItemSize)); + break; + case StackItemType.Buffer: + Buffer buffer = new Buffer((int)reader.ReadVarInt(maxItemSize)); + reader.FillBuffer(buffer.InnerBuffer); + deserialized.Push(buffer); break; case StackItemType.Array: case StackItemType.Struct: { - int count = (int)reader.ReadVarInt(maxItemSize); - deserialized.Push(new ContainerPlaceholder - { - Type = type, - ElementCount = count - }); + int count = (int)reader.ReadVarInt(maxArraySize); + deserialized.Push(new ContainerPlaceholder(type, count)); undeserialized += count; } break; case StackItemType.Map: { - int count = (int)reader.ReadVarInt(maxItemSize); - deserialized.Push(new ContainerPlaceholder - { - Type = type, - ElementCount = count - }); + int count = (int)reader.ReadVarInt(maxArraySize); + deserialized.Push(new ContainerPlaceholder(type, count)); undeserialized += count * 2; } break; - case StackItemType.Null: - deserialized.Push(StackItem.Null); - break; default: throw new FormatException(); } @@ -127,36 +125,33 @@ public static byte[] Serialize(StackItem item, uint maxSize) private static void Serialize(StackItem item, BinaryWriter writer, uint maxSize) { - List serialized = new List(); + List serialized = new List(); Stack unserialized = new Stack(); unserialized.Push(item); while (unserialized.Count > 0) { item = unserialized.Pop(); + writer.Write((byte)item.Type); switch (item) { - case ByteArray bytes: - writer.Write((byte)StackItemType.ByteArray); - writer.WriteVarBytes(bytes.ToByteArray()); + case Null _: break; case Boolean _: - writer.Write((byte)StackItemType.Boolean); writer.Write(item.ToBoolean()); break; case Integer integer: - writer.Write((byte)StackItemType.Integer); - writer.WriteVarBytes(integer.ToByteArray()); + writer.WriteVarBytes(integer.Span); + break; + case ByteArray bytes: + writer.WriteVarBytes(bytes.Span); + break; + case Buffer buffer: + writer.WriteVarBytes(buffer.InnerBuffer); break; - case InteropInterface _: - throw new NotSupportedException(); case Array array: if (serialized.Any(p => ReferenceEquals(p, array))) throw new NotSupportedException(); serialized.Add(array); - if (array is Struct) - writer.Write((byte)StackItemType.Struct); - else - writer.Write((byte)StackItemType.Array); writer.WriteVarInt(array.Count); for (int i = array.Count - 1; i >= 0; i--) unserialized.Push(array[i]); @@ -165,7 +160,6 @@ private static void Serialize(StackItem item, BinaryWriter writer, uint maxSize) if (serialized.Any(p => ReferenceEquals(p, map))) throw new NotSupportedException(); serialized.Add(map); - writer.Write((byte)StackItemType.Map); writer.WriteVarInt(map.Count); foreach (var pair in map.Reverse()) { @@ -173,9 +167,8 @@ private static void Serialize(StackItem item, BinaryWriter writer, uint maxSize) unserialized.Push(pair.Key); } break; - case Null _: - writer.Write((byte)StackItemType.Null); - break; + default: + throw new NotSupportedException(); } if (writer.BaseStream.Position > maxSize) throw new InvalidOperationException(); diff --git a/src/neo/SmartContract/ContainerPlaceholder.cs b/src/neo/SmartContract/ContainerPlaceholder.cs index 29fa0f6e6e..72eb68e48d 100644 --- a/src/neo/SmartContract/ContainerPlaceholder.cs +++ b/src/neo/SmartContract/ContainerPlaceholder.cs @@ -5,10 +5,16 @@ namespace Neo.SmartContract { internal class ContainerPlaceholder : StackItem { - public StackItemType Type; - public int ElementCount; + public override StackItemType Type { get; } + public int ElementCount { get; } - public override bool Equals(StackItem other) => throw new NotSupportedException(); + public ContainerPlaceholder(StackItemType type, int count) + { + Type = type; + ElementCount = count; + } + + public override bool Equals(object obj) => throw new NotSupportedException(); public override int GetHashCode() => throw new NotSupportedException(); diff --git a/src/neo/SmartContract/InteropService.Binary.cs b/src/neo/SmartContract/InteropService.Binary.cs index f2e2d22a7b..fa33dbe4ba 100644 --- a/src/neo/SmartContract/InteropService.Binary.cs +++ b/src/neo/SmartContract/InteropService.Binary.cs @@ -1,7 +1,5 @@ -using Neo.VM; using Neo.VM.Types; using System; -using System.IO; namespace Neo.SmartContract { @@ -29,19 +27,8 @@ private static bool Binary_Serialize(ApplicationEngine engine) private static bool Binary_Deserialize(ApplicationEngine engine) { - StackItem item; - try - { - item = BinarySerializer.Deserialize(engine.CurrentContext.EvaluationStack.Pop().GetSpan(), engine.MaxItemSize, engine.ReferenceCounter); - } - catch (FormatException) - { - return false; - } - catch (IOException) - { - return false; - } + if (!engine.TryPop(out ReadOnlySpan data)) return false; + StackItem item = BinarySerializer.Deserialize(data, engine.MaxStackSize, engine.MaxItemSize, engine.ReferenceCounter); engine.CurrentContext.EvaluationStack.Push(item); return true; } diff --git a/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs b/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs index d5baff7da0..c60c839543 100644 --- a/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs +++ b/src/neo/SmartContract/Iterators/ByteArrayWrapper.cs @@ -10,7 +10,7 @@ internal class ByteArrayWrapper : IIterator public ByteArrayWrapper(PrimitiveType value) { - this.array = value.ToByteArray().ToArray(); + this.array = value.Span.ToArray(); } public void Dispose() { } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 17f053ddf8..60c1d6c8db 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -75,8 +75,7 @@ protected StorageKey CreateStorageKey(byte prefix, byte[] key = null) Key = new byte[sizeof(byte) + (key?.Length ?? 0)] }; storageKey.Key[0] = prefix; - if (key != null) - Buffer.BlockCopy(key, 0, storageKey.Key, 1, key.Length); + key?.CopyTo(storageKey.Key.AsSpan(1)); return storageKey; } diff --git a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs index 76671d92c1..b2d62be6d1 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs @@ -19,7 +19,7 @@ public Nep5AccountState(byte[] data) public void FromByteArray(byte[] data) { - FromStruct((Struct)BinarySerializer.Deserialize(data, 34)); + FromStruct((Struct)BinarySerializer.Deserialize(data, 16, 34)); } protected virtual void FromStruct(Struct @struct) diff --git a/src/neo/SmartContract/StackItemType.cs b/src/neo/SmartContract/StackItemType.cs deleted file mode 100644 index 7e19c2982b..0000000000 --- a/src/neo/SmartContract/StackItemType.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Neo.SmartContract -{ - internal enum StackItemType : byte - { - ByteArray = 0x00, - Boolean = 0x01, - Integer = 0x02, - InteropInterface = 0x40, - Array = 0x80, - Struct = 0x81, - Map = 0x82, - Null = 0xff - } -} diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 28dcb11904..83b4aaf3e2 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -9,6 +9,7 @@ using System.Text; using Array = Neo.VM.Types.Array; using Boolean = Neo.VM.Types.Boolean; +using Buffer = Neo.VM.Types.Buffer; namespace Neo.VM { @@ -175,16 +176,22 @@ public static BigInteger GetBigInteger(this StackItem item) public static int GetByteLength(this StackItem item) { - if (!(item is PrimitiveType primitive)) - throw new ArgumentException(); - return primitive.GetByteLength(); + return item switch + { + PrimitiveType p => p.Size, + Buffer b => b.Size, + _ => throw new ArgumentException(), + }; } public static ReadOnlySpan GetSpan(this StackItem item) { - if (!(item is PrimitiveType primitive)) - throw new ArgumentException(); - return primitive.ToByteArray(); + return item switch + { + PrimitiveType p => p.Span, + Buffer b => b.InnerBuffer, + _ => throw new ArgumentException(), + }; } public static string GetString(this StackItem item) @@ -252,18 +259,18 @@ private static ContractParameter ToParameter(StackItem item, List<(StackItem, Co Value = item.ToBoolean() }; break; - case ByteArray _: + case ByteArray array: parameter = new ContractParameter { Type = ContractParameterType.ByteArray, - Value = item.GetSpan().ToArray() + Value = array.Span.ToArray() }; break; - case Integer _: + case Integer i: parameter = new ContractParameter { Type = ContractParameterType.Integer, - Value = item.GetBigInteger() + Value = i.ToBigInteger() }; break; case InteropInterface _: @@ -278,7 +285,7 @@ private static ContractParameter ToParameter(StackItem item, List<(StackItem, Co Type = ContractParameterType.Any }; break; - default: // Null included + default: throw new ArgumentException(); } return parameter; diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index ebac81c4bc..075d17fb7f 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index 2693a40690..51caa8db9c 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -165,10 +165,10 @@ public void TestGetSysFeeAmount2() StorageKey storageKey = new StorageKey { ScriptHash = NativeContract.GAS.Hash, - Key = new byte[sizeof(byte) + (key?.Length ?? 0)] + Key = new byte[sizeof(byte) + key.Length] }; storageKey.Key[0] = 15; - Buffer.BlockCopy(key, 0, storageKey.Key, 1, key.Length); + key.CopyTo(storageKey.Key.AsSpan(1)); BigInteger sys_fee = new BigInteger(10); snapshot.Storages.Add(storageKey, new StorageItem diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 411a2139ca..6111c9b052 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -687,7 +687,7 @@ internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint[] votes) { - var st = (VM.Types.Struct)BinarySerializer.Deserialize(trackable.Item.Value, 32); + var st = (VM.Types.Struct)BinarySerializer.Deserialize(trackable.Item.Value, 16, 32); st.Count.Should().Be(3); st.Select(u => u.GetType()).ToArray().Should().BeEquivalentTo(new Type[] { typeof(VM.Types.Integer), typeof(VM.Types.Integer), typeof(VM.Types.ByteArray) }); // Balance @@ -708,8 +708,7 @@ internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) Key = new byte[sizeof(byte) + (key?.Length ?? 0)] }; storageKey.Key[0] = prefix; - if (key != null) - Buffer.BlockCopy(key, 0, storageKey.Key, 1, key.Length); + key?.CopyTo(storageKey.Key.AsSpan(1)); return storageKey; } } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs index 5e9dbe9617..3cb10defe3 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs @@ -82,8 +82,7 @@ public StorageKey CreateStorageKey(byte prefix, byte[] key = null) Key = new byte[sizeof(byte) + (key?.Length ?? 0)] }; storageKey.Key[0] = prefix; - if (key != null) - Buffer.BlockCopy(key, 0, storageKey.Key, 1, key.Length); + key?.CopyTo(storageKey.Key.AsSpan(1)); return storageKey; } } diff --git a/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs b/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs index 888af9a23a..39330334d9 100644 --- a/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs +++ b/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs @@ -19,38 +19,31 @@ public void TestSerialize() { byte[] result1 = BinarySerializer.Serialize(new byte[5], MaxItemSize); byte[] expectedArray1 = new byte[] { - 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x28, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray1), Encoding.Default.GetString(result1)); byte[] result2 = BinarySerializer.Serialize(true, MaxItemSize); byte[] expectedArray2 = new byte[] { - 0x01, 0x01 + 0x20, 0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray2), Encoding.Default.GetString(result2)); byte[] result3 = BinarySerializer.Serialize(1, MaxItemSize); byte[] expectedArray3 = new byte[] { - 0x02, 0x01, 0x01 + 0x21, 0x01, 0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray3), Encoding.Default.GetString(result3)); - StackItem stackItem4 = new InteropInterface(new object()); + StackItem stackItem4 = new InteropInterface(new object()); Action action4 = () => BinarySerializer.Serialize(stackItem4, MaxItemSize); action4.Should().Throw(); - byte[] result5 = BinarySerializer.Serialize(1, MaxItemSize); - byte[] expectedArray5 = new byte[] { - 0x02, 0x01, 0x01 - }; - Assert.AreEqual(Encoding.Default.GetString(expectedArray5), Encoding.Default.GetString(result5)); - - List list6 = new List { 1 }; StackItem stackItem62 = new VM.Types.Array(list6); byte[] result6 = BinarySerializer.Serialize(stackItem62, MaxItemSize); byte[] expectedArray6 = new byte[] { - 0x80,0x01,0x02,0x01,0x01 + 0x40,0x01,0x21,0x01,0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray6), Encoding.Default.GetString(result6)); @@ -58,14 +51,14 @@ public void TestSerialize() StackItem stackItem72 = new Struct(list7); byte[] result7 = BinarySerializer.Serialize(stackItem72, MaxItemSize); byte[] expectedArray7 = new byte[] { - 0x81,0x01,0x02,0x01,0x01 + 0x41,0x01,0x21,0x01,0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray7), Encoding.Default.GetString(result7)); StackItem stackItem82 = new Map { [2] = 1 }; byte[] result8 = BinarySerializer.Serialize(stackItem82, MaxItemSize); byte[] expectedArray8 = new byte[] { - 0x82,0x01,0x02,0x01,0x02,0x02,0x01,0x01 + 0x48,0x01,0x21,0x01,0x02,0x21,0x01,0x01 }; Assert.AreEqual(Encoding.Default.GetString(expectedArray8), Encoding.Default.GetString(result8)); @@ -85,41 +78,41 @@ public void TestDeserializeStackItem() { StackItem stackItem1 = new ByteArray(new byte[5]); byte[] byteArray1 = BinarySerializer.Serialize(stackItem1, MaxItemSize); - StackItem result1 = BinarySerializer.Deserialize(byteArray1, (uint)byteArray1.Length); + StackItem result1 = BinarySerializer.Deserialize(byteArray1, 2048, (uint)byteArray1.Length); Assert.AreEqual(stackItem1, result1); StackItem stackItem2 = new VM.Types.Boolean(true); byte[] byteArray2 = BinarySerializer.Serialize(stackItem2, MaxItemSize); - StackItem result2 = BinarySerializer.Deserialize(byteArray2, (uint)byteArray2.Length); + StackItem result2 = BinarySerializer.Deserialize(byteArray2, 2048, (uint)byteArray2.Length); Assert.AreEqual(stackItem2, result2); StackItem stackItem3 = new Integer(1); byte[] byteArray3 = BinarySerializer.Serialize(stackItem3, MaxItemSize); - StackItem result3 = BinarySerializer.Deserialize(byteArray3, (uint)byteArray3.Length); + StackItem result3 = BinarySerializer.Deserialize(byteArray3, 2048, (uint)byteArray3.Length); Assert.AreEqual(stackItem3, result3); byte[] byteArray4 = BinarySerializer.Serialize(1, MaxItemSize); byteArray4[0] = 0x40; - Action action4 = () => BinarySerializer.Deserialize(byteArray4, (uint)byteArray4.Length); + Action action4 = () => BinarySerializer.Deserialize(byteArray4, 2048, (uint)byteArray4.Length); action4.Should().Throw(); List list5 = new List { 1 }; StackItem stackItem52 = new VM.Types.Array(list5); byte[] byteArray5 = BinarySerializer.Serialize(stackItem52, MaxItemSize); - StackItem result5 = BinarySerializer.Deserialize(byteArray5, (uint)byteArray5.Length); + StackItem result5 = BinarySerializer.Deserialize(byteArray5, 2048, (uint)byteArray5.Length); Assert.AreEqual(((VM.Types.Array)stackItem52).Count, ((VM.Types.Array)result5).Count); Assert.AreEqual(((VM.Types.Array)stackItem52).GetEnumerator().Current, ((VM.Types.Array)result5).GetEnumerator().Current); List list6 = new List { 1 }; StackItem stackItem62 = new Struct(list6); byte[] byteArray6 = BinarySerializer.Serialize(stackItem62, MaxItemSize); - StackItem result6 = BinarySerializer.Deserialize(byteArray6, (uint)byteArray6.Length); + StackItem result6 = BinarySerializer.Deserialize(byteArray6, 2048, (uint)byteArray6.Length); Assert.AreEqual(((Struct)stackItem62).Count, ((Struct)result6).Count); Assert.AreEqual(((Struct)stackItem62).GetEnumerator().Current, ((Struct)result6).GetEnumerator().Current); StackItem stackItem72 = new Map { [2] = 1 }; byte[] byteArray7 = BinarySerializer.Serialize(stackItem72, MaxItemSize); - StackItem result7 = BinarySerializer.Deserialize(byteArray7, (uint)byteArray7.Length); + StackItem result7 = BinarySerializer.Deserialize(byteArray7, 2048, (uint)byteArray7.Length); Assert.AreEqual(((Map)stackItem72).Count, ((Map)result7).Count); CollectionAssert.AreEqual(((Map)stackItem72).Keys.ToArray(), ((Map)result7).Keys.ToArray()); CollectionAssert.AreEqual(((Map)stackItem72).Values.ToArray(), ((Map)result7).Values.ToArray()); diff --git a/tests/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs b/tests/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs index 0ba48e78a9..73023d6db8 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContainerPlaceholder.cs @@ -12,14 +12,14 @@ public class UT_ContainerPlaceholder [TestMethod] public void TestGenerator() { - ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(); + ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(StackItemType.Any, 0); Assert.IsNotNull(containerPlaceholder); } [TestMethod] public void TestEquals() { - ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(); + ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(StackItemType.Any, 0); Action action = () => containerPlaceholder.Equals(new Integer(0)); action.Should().Throw(); } @@ -27,7 +27,7 @@ public void TestEquals() [TestMethod] public void TestGetBoolean() { - ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(); + ContainerPlaceholder containerPlaceholder = new ContainerPlaceholder(StackItemType.Any, 0); Action action = () => containerPlaceholder.ToBoolean(); action.Should().Throw(); } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 83700f33a0..959eeecae8 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -276,13 +276,13 @@ public void TestStorage_Find() engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext { ScriptHash = state.ScriptHash, IsReadOnly = false })); InteropService.Invoke(engine, InteropService.Storage.Find).Should().BeTrue(); - var iterator = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + var iterator = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); iterator.Next(); var ele = iterator.Value(); ele.GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); @@ -301,7 +301,7 @@ public void TestEnumerator_Create() }; engine.CurrentContext.EvaluationStack.Push(arr); InteropService.Invoke(engine, InteropService.Enumerator.Create).Should().BeTrue(); - var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().Next(); ret.GetInterface().Value().GetSpan().ToHexString() .Should().Be(new byte[] { 0x01 }.ToHexString()); @@ -318,7 +318,7 @@ public void TestEnumerator_Next() new byte[]{ 0x01 }, new byte[]{ 0x02 } }; - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new ArrayWrapper(arr))); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new ArrayWrapper(arr))); InteropService.Invoke(engine, InteropService.Enumerator.Next).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); @@ -336,7 +336,7 @@ public void TestEnumerator_Value() }; var wrapper = new ArrayWrapper(arr); wrapper.Next(); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); InteropService.Invoke(engine, InteropService.Enumerator.Value).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); @@ -358,10 +358,10 @@ public void TestEnumerator_Concat() }; var wrapper1 = new ArrayWrapper(arr1); var wrapper2 = new ArrayWrapper(arr2); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper2)); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper1)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper2)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper1)); InteropService.Invoke(engine, InteropService.Enumerator.Concat).Should().BeTrue(); - var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next().Should().BeTrue(); ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } @@ -376,12 +376,12 @@ public void TestIterator_Create() }; engine.CurrentContext.EvaluationStack.Push(arr); InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeTrue(); - var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().Next(); ret.GetInterface().Value().GetSpan().ToHexString() .Should().Be(new byte[] { 0x01 }.ToHexString()); - var interop = new InteropInterface(1); + var interop = new InteropInterface(1); engine.CurrentContext.EvaluationStack.Push(interop); InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeFalse(); @@ -392,7 +392,7 @@ public void TestIterator_Create() }; engine.CurrentContext.EvaluationStack.Push(map); InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeTrue(); - ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().Next(); ret.GetInterface().Key().GetBigInteger().Should().Be(1); ret.GetInterface().Value().GetBigInteger().Should().Be(2); @@ -411,7 +411,7 @@ public void TestIterator_Key() }; var wrapper = new ArrayWrapper(arr); wrapper.Next(); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); InteropService.Invoke(engine, InteropService.Iterator.Key).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(0); @@ -428,9 +428,9 @@ public void TestIterator_Keys() new byte[]{ 0x02 } }; var wrapper = new ArrayWrapper(arr); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); InteropService.Invoke(engine, InteropService.Iterator.Keys).Should().BeTrue(); - var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next(); ret.Value().GetBigInteger().Should().Be(0); @@ -447,9 +447,9 @@ public void TestIterator_Values() new byte[]{ 0x02 } }; var wrapper = new ArrayWrapper(arr); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); InteropService.Invoke(engine, InteropService.Iterator.Values).Should().BeTrue(); - var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next(); ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); @@ -471,10 +471,10 @@ public void TestIterator_Concat() }; var wrapper1 = new ArrayWrapper(arr1); var wrapper2 = new ArrayWrapper(arr2); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper2)); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper1)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper2)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper1)); InteropService.Invoke(engine, InteropService.Iterator.Concat).Should().BeTrue(); - var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); ret.Next().Should().BeTrue(); ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 597da60adc..1fa676af60 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -36,9 +36,7 @@ public void Runtime_GetNotifications_Test() { // Drop arguments - script.Emit(VM.OpCode.TOALTSTACK); - script.Emit(VM.OpCode.DROP); - script.Emit(VM.OpCode.FROMALTSTACK); + script.Emit(OpCode.NIP); // Notify method @@ -337,12 +335,12 @@ public void TestRuntime_Serialize() engine.CurrentContext.EvaluationStack.Push(100); InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() - .Should().Be(new byte[] { 0x02, 0x01, 0x64 }.ToHexString()); + .Should().Be(new byte[] { 0x21, 0x01, 0x64 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(new byte[1024 * 1024 * 2]); //Larger than MaxItemSize InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeFalse(); - engine.CurrentContext.EvaluationStack.Push(new TestInteropInterface()); //NotSupportedException + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new object())); //NotSupportedException InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeFalse(); } @@ -356,7 +354,7 @@ public void TestRuntime_Deserialize() engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(100); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0xfa, 0x01 }); //FormatException - InteropService.Invoke(engine, InteropService.Binary.Deserialize).Should().BeFalse(); + Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Binary.Deserialize)); } [TestMethod] @@ -391,7 +389,7 @@ public void TestCrypto_Verify() wrongkey[0] = 5; engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(wrongkey); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(engine.ScriptContainer)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(engine.ScriptContainer)); InteropService.Invoke(engine, InteropService.Crypto.ECDsaVerify).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Peek().ToBoolean().Should().BeFalse(); } @@ -484,7 +482,7 @@ public void TestStorage_GetContext() { var engine = GetEngine(); InteropService.Invoke(engine, InteropService.Storage.GetContext).Should().BeTrue(); - var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().ScriptHash.Should().Be(engine.CurrentScriptHash); ret.GetInterface().IsReadOnly.Should().BeFalse(); } @@ -494,7 +492,7 @@ public void TestStorage_GetReadOnlyContext() { var engine = GetEngine(); InteropService.Invoke(engine, InteropService.Storage.GetReadOnlyContext).Should().BeTrue(); - var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().ScriptHash.Should().Be(engine.CurrentScriptHash); ret.GetInterface().IsReadOnly.Should().BeTrue(); } @@ -523,7 +521,7 @@ public void TestStorage_Get() engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext { ScriptHash = state.ScriptHash, IsReadOnly = false @@ -535,7 +533,7 @@ public void TestStorage_Get() engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext { ScriptHash = state.ScriptHash, IsReadOnly = false @@ -564,7 +562,7 @@ public void TestStorage_Put() ScriptHash = state.ScriptHash, IsReadOnly = false }; - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //key.Length > MaxStorageKeySize @@ -572,7 +570,7 @@ public void TestStorage_Put() value = new byte[] { 0x02 }; engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //value.Length > MaxStorageValueSize @@ -580,7 +578,7 @@ public void TestStorage_Put() value = new byte[ushort.MaxValue + 1]; engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //context.IsReadOnly @@ -589,7 +587,7 @@ public void TestStorage_Put() storageContext.IsReadOnly = true; engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //storage value is constant @@ -615,14 +613,14 @@ public void TestStorage_Put() storageContext.IsReadOnly = false; engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //success storageItem.IsConstant = false; engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeTrue(); //value length == 0 @@ -630,7 +628,7 @@ public void TestStorage_Put() value = new byte[0]; engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeTrue(); } @@ -668,7 +666,7 @@ public void TestStorage_PutEx() engine.CurrentContext.EvaluationStack.Push((int)StorageFlags.None); engine.CurrentContext.EvaluationStack.Push(value); engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.PutEx).Should().BeTrue(); } @@ -705,20 +703,20 @@ public void TestStorage_Delete() IsReadOnly = false }; engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeTrue(); //context is readonly storageContext.IsReadOnly = true; engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeFalse(); //CheckStorageContext fail storageContext.IsReadOnly = false; state.Manifest.Features = ContractFeatures.NoProperty; engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeFalse(); } @@ -735,9 +733,9 @@ public void TestStorageContext_AsReadOnly() ScriptHash = state.ScriptHash, IsReadOnly = false }; - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); + engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.AsReadOnly).Should().BeTrue(); - var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); + var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); ret.GetInterface().IsReadOnly.Should().Be(true); } @@ -909,11 +907,4 @@ private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSn return engine; } } - - internal class TestInteropInterface : InteropInterface - { - public override bool Equals(StackItem other) => true; - public override bool ToBoolean() => true; - public override T GetInterface() => throw new NotImplementedException(); - } } diff --git a/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs b/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs index 1dfd6a6ec5..d86a9d208e 100644 --- a/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs +++ b/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Network.P2P.Payloads; using Neo.SmartContract; -using Neo.VM.Types; namespace Neo.UnitTests.SmartContract { @@ -14,8 +13,7 @@ public void TestGetScriptContainer() { IVerifiable container = new TestVerifiable(); UInt160 script_hash = new byte[] { 0x00 }.ToScriptHash(); - StackItem state = new ContainerPlaceholder(); - NotifyEventArgs args = new NotifyEventArgs(container, script_hash, state); + NotifyEventArgs args = new NotifyEventArgs(container, script_hash, 0); args.ScriptContainer.Should().Be(container); } } diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 559f9013ae..809e00c94d 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -96,7 +96,7 @@ public void TestMakeScript() { byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - Assert.AreEqual("0c14000000000000000000000000000000000000000011c10c0962616c616e63654f660c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b52", + Assert.AreEqual("0c14000000000000000000000000000000000000000011c00c0962616c616e63654f660c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b52", testScript.ToHexString()); } @@ -112,7 +112,7 @@ public void TestToParameter() StackItem intItem = new BigInteger(1000); Assert.AreEqual(1000, (BigInteger)intItem.ToParameter().Value); - StackItem interopItem = new VM.Types.InteropInterface("test"); + StackItem interopItem = new VM.Types.InteropInterface("test"); Assert.AreEqual(null, interopItem.ToParameter().Value); StackItem arrayItem = new VM.Types.Array(new[] { byteItem, boolItem, intItem, interopItem }); @@ -493,7 +493,7 @@ public void TestToParameter2() private void TestToParameter2InteropInterface() { - StackItem item = new VM.Types.InteropInterface(new VM.Types.Boolean(true)); + StackItem item = new InteropInterface(new object()); ContractParameter parameter = VM.Helper.ToParameter(item); Assert.AreEqual(ContractParameterType.InteropInterface, parameter.Type); } From dba911bdd15aa1958a614d0f6d9b25063963de0c Mon Sep 17 00:00:00 2001 From: ShawnYun <42930111+ShawnYun@users.noreply.github.com> Date: Mon, 6 Jan 2020 20:27:30 +0800 Subject: [PATCH 193/305] Add relay-block-filter (#1380) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add relay-block-filter * modify * modify * modify * modify Co-authored-by: Vitor Nazário Coelho --- src/neo/Network/P2P/LocalNode.cs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index ffd0ac9930..bdc4713bfe 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -155,7 +155,7 @@ protected override void NeedMorePeers(int count) else { // Will call AddPeers with default SeedList set cached on . - // It will try to add those, sequentially, to the list of currently uncconected ones. + // It will try to add those, sequentially, to the list of currently unconnected ones. Random rand = new Random(); AddPeers(SeedList.Where(u => u != null).OrderBy(p => rand.Next()).Take(count)); @@ -197,7 +197,22 @@ private void OnRelay(IInventory inventory) system.Blockchain.Tell(inventory); } - private void OnRelayDirectly(IInventory inventory) => SendToRemoteNodes(new RemoteNode.Relay { Inventory = inventory }); + private void OnRelayDirectly(IInventory inventory) + { + var message = new RemoteNode.Relay { Inventory = inventory }; + // When relaying a block, if the block's index is greater than 'LastBlockIndex' of the RemoteNode, relay the block; + // otherwise, don't relay. + if (inventory is Block block) + { + foreach (KeyValuePair kvp in RemoteNodes) + { + if (block.Index > kvp.Value.LastBlockIndex) + kvp.Key.Tell(message); + } + } + else + SendToRemoteNodes(message); + } private void OnSendDirectly(IInventory inventory) => SendToRemoteNodes(inventory); From 73753fc9c8ecbb54c5537a8d87869f6127fa6af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Mon, 6 Jan 2020 15:18:27 -0300 Subject: [PATCH 194/305] Adding some comments to datacache (#1347) * Adding some comments to datacache * Adding Exception comment tip of @Tommo-L Co-authored-by: Shargon --- src/neo/IO/Caching/DataCache.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/neo/IO/Caching/DataCache.cs b/src/neo/IO/Caching/DataCache.cs index 1665c8f238..4529978a19 100644 --- a/src/neo/IO/Caching/DataCache.cs +++ b/src/neo/IO/Caching/DataCache.cs @@ -43,6 +43,16 @@ public class Trackable } } + /// + /// Try to Add a specific key, with associated value, to the current cached dictionary. + /// It will not read from internal state. + /// However, if previously cached into Dictionary, request may fail. + /// + /// Key to be possible added. + /// Key will not be added if value exists cached and the modification was not a Deleted one. + /// + /// 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) { lock (dictionary) @@ -60,6 +70,9 @@ public void Add(TKey key, TValue value) protected abstract void AddInternal(TKey key, TValue value); + /// + /// Update internals with all changes cached on Dictionary which are not None. + /// public void Commit() { foreach (Trackable trackable in GetChangeSet()) @@ -82,6 +95,10 @@ public void Commit() return new CloneCache(this); } + /// + /// Delete key from cached Dictionary or search in Internal. + /// + /// Key to be deleted. public void Delete(TKey key) { lock (dictionary) @@ -186,6 +203,14 @@ public IEnumerable GetChangeSet() protected abstract TValue GetInternal(TKey key); + /// + /// Try to Get a specific key from current cached dictionary. + /// Otherwise, tries to get from internal data with TryGetInternal. + /// + /// Key to be searched. + /// 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) { lock (dictionary) From 62fc2326024ff4e45232bb75387e33f1f79a1124 Mon Sep 17 00:00:00 2001 From: joeqian Date: Tue, 7 Jan 2020 21:06:05 +0800 Subject: [PATCH 195/305] Rename CalculateNetWorkFee() (#1401) --- src/neo/Wallets/Wallet.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 1752372bec..892cc40c1c 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -339,7 +339,7 @@ private Transaction MakeTransaction(StoreView snapshot, byte[] script, Transacti { byte[] witness_script = GetAccount(hash)?.Contract?.Script ?? snapshot.Contracts.TryGet(hash)?.Script; if (witness_script is null) continue; - tx.NetworkFee += CalculateNetWorkFee(witness_script, ref size); + tx.NetworkFee += CalculateNetworkFee(witness_script, ref size); } tx.NetworkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); if (value >= tx.SystemFee + tx.NetworkFee) return tx; @@ -347,7 +347,7 @@ private Transaction MakeTransaction(StoreView snapshot, byte[] script, Transacti throw new InvalidOperationException("Insufficient GAS"); } - public static long CalculateNetWorkFee(byte[] witness_script, ref int size) + public static long CalculateNetworkFee(byte[] witness_script, ref int size) { long networkFee = 0; From 61208fd04be0dec70c06840b23f41946bebe5a99 Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Thu, 9 Jan 2020 18:44:52 +0800 Subject: [PATCH 196/305] Use HashsetCache instead of FIFOSet for KnownHashes and SentHashes (#1389) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update * change fifoset to hashsetcache for knownhashes and senthashes * format * optimise HashSetCache * remove FIFOSet * remove FIFOSet and update ut * change List to LinkedList * remove bucket count upper limit * update exception message * Adding some comments * dotnet format * rename maxBucketCount * format * Cache count * Vitor suggestions * Update HashSetCache.cs * format * Fix * fix counting count * optimise * optimse * update List Co-authored-by: Shargon Co-authored-by: Vitor Nazário Coelho --- src/neo/Helper.cs | 2 +- src/neo/IO/Caching/FIFOSet.cs | 67 ----------- src/neo/IO/Caching/HashSetCache.cs | 106 ++++++++++++++++++ src/neo/Network/P2P/ProtocolHandler.cs | 8 +- src/neo/Network/P2P/TaskManager.cs | 4 +- .../{UT_FIFOSet.cs => UT_HashSetCache.cs} | 83 ++++++-------- 6 files changed, 148 insertions(+), 122 deletions(-) delete mode 100644 src/neo/IO/Caching/FIFOSet.cs create mode 100644 src/neo/IO/Caching/HashSetCache.cs rename tests/neo.UnitTests/IO/Caching/{UT_FIFOSet.cs => UT_HashSetCache.cs} (64%) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index b8a0a7f79e..be66ce2013 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -83,7 +83,7 @@ internal static void Remove(this HashSet set, ISet other) } } - internal static void Remove(this HashSet set, FIFOSet other) + internal static void Remove(this HashSet set, HashSetCache other) where T : IEquatable { if (set.Count > other.Count) diff --git a/src/neo/IO/Caching/FIFOSet.cs b/src/neo/IO/Caching/FIFOSet.cs deleted file mode 100644 index af65db1b8b..0000000000 --- a/src/neo/IO/Caching/FIFOSet.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; - -namespace Neo.IO.Caching -{ - internal class FIFOSet : IReadOnlyCollection where T : IEquatable - { - private readonly int maxCapacity; - private readonly int removeCount; - private readonly OrderedDictionary dictionary; - - public int Count => dictionary.Count; - - public FIFOSet(int maxCapacity, decimal batchSize = 0.1m) - { - if (maxCapacity <= 0) throw new ArgumentOutOfRangeException(nameof(maxCapacity)); - if (batchSize <= 0 || batchSize > 1) throw new ArgumentOutOfRangeException(nameof(batchSize)); - - this.maxCapacity = maxCapacity; - this.removeCount = Math.Max((int)(maxCapacity * batchSize), 1); - this.dictionary = new OrderedDictionary(maxCapacity); - } - - public bool Add(T item) - { - if (dictionary.Contains(item)) return false; - if (dictionary.Count >= maxCapacity) - { - if (removeCount == maxCapacity) - { - dictionary.Clear(); - } - else - { - for (int i = 0; i < removeCount; i++) - dictionary.RemoveAt(0); - } - } - dictionary.Add(item, null); - return true; - } - - public bool Contains(T item) - { - return dictionary.Contains(item); - } - - public void ExceptWith(IEnumerable entries) - { - foreach (var entry in entries) - { - dictionary.Remove(entry); - } - } - - public IEnumerator GetEnumerator() - { - var entries = dictionary.Keys.Cast().ToArray(); - foreach (var entry in entries) yield return entry; - } - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} diff --git a/src/neo/IO/Caching/HashSetCache.cs b/src/neo/IO/Caching/HashSetCache.cs new file mode 100644 index 0000000000..af34cd76fa --- /dev/null +++ b/src/neo/IO/Caching/HashSetCache.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Neo.IO.Caching +{ + public class HashSetCache : IReadOnlyCollection where T : IEquatable + { + /// + /// Sets where the Hashes are stored + /// + private readonly LinkedList> sets = new LinkedList>(); + + /// + /// Maximum capacity of each bucket inside each HashSet of . + /// + private readonly int bucketCapacity; + + /// + /// Maximum number of buckets for the LinkedList, meaning its maximum cardinality. + /// + private readonly int maxBucketCount; + + /// + /// Entry count + /// + public int Count { get; private set; } + + public HashSetCache(int bucketCapacity, int maxBucketCount = 10) + { + if (bucketCapacity <= 0) throw new ArgumentOutOfRangeException($"{nameof(bucketCapacity)} should be greater than 0"); + if (maxBucketCount <= 0) throw new ArgumentOutOfRangeException($"{nameof(maxBucketCount)} should be greater than 0"); + + this.Count = 0; + this.bucketCapacity = bucketCapacity; + this.maxBucketCount = maxBucketCount; + sets.AddFirst(new HashSet()); + } + + public bool Add(T item) + { + if (Contains(item)) return false; + Count++; + if (sets.First.Value.Count < bucketCapacity) return sets.First.Value.Add(item); + var newSet = new HashSet + { + item + }; + sets.AddFirst(newSet); + if (sets.Count > maxBucketCount) + { + Count -= sets.Last.Value.Count; + sets.RemoveLast(); + } + return true; + } + + public bool Contains(T item) + { + foreach (var set in sets) + { + if (set.Contains(item)) return true; + } + return false; + } + + public void ExceptWith(IEnumerable items) + { + List> removeList = null; + foreach (var item in items) + { + foreach (var set in sets) + { + if (set.Remove(item)) + { + Count--; + if (set.Count == 0) + { + removeList ??= new List>(); + removeList.Add(set); + } + break; + } + } + } + if (removeList == null) return; + foreach (var set in removeList) + { + sets.Remove(set); + } + } + + public IEnumerator GetEnumerator() + { + foreach (var set in sets) + { + foreach (var item in set) + { + yield return item; + } + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/neo/Network/P2P/ProtocolHandler.cs b/src/neo/Network/P2P/ProtocolHandler.cs index cecbb02796..a97cb46922 100644 --- a/src/neo/Network/P2P/ProtocolHandler.cs +++ b/src/neo/Network/P2P/ProtocolHandler.cs @@ -32,8 +32,8 @@ protected override UInt256 GetKeyForItem((UInt256, DateTime) item) private readonly NeoSystem system; private readonly PendingKnownHashesCollection pendingKnownHashes; - private readonly FIFOSet knownHashes; - private readonly FIFOSet sentHashes; + private readonly HashSetCache knownHashes; + private readonly HashSetCache sentHashes; private VersionPayload version; private bool verack = false; private BloomFilter bloom_filter; @@ -47,8 +47,8 @@ public ProtocolHandler(NeoSystem system) { this.system = system; this.pendingKnownHashes = new PendingKnownHashesCollection(); - this.knownHashes = new FIFOSet(Blockchain.Singleton.MemPool.Capacity * 2); - this.sentHashes = new FIFOSet(Blockchain.Singleton.MemPool.Capacity * 2); + this.knownHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); + this.sentHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); } protected override void OnReceive(object message) diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 818973ea4b..73fa49441a 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -32,7 +32,7 @@ private class Timer { } /// /// A set of known hashes, of inventories or payloads, already received. /// - private readonly FIFOSet knownHashes; + private readonly HashSetCache knownHashes; private readonly Dictionary globalTasks = new Dictionary(); private readonly Dictionary sessions = new Dictionary(); private readonly ICancelable timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimerInterval, TimerInterval, Context.Self, new Timer(), ActorRefs.NoSender); @@ -43,7 +43,7 @@ private class Timer { } public TaskManager(NeoSystem system) { this.system = system; - this.knownHashes = new FIFOSet(Blockchain.Singleton.MemPool.Capacity * 2); + this.knownHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); } private void OnHeaderTaskCompleted() diff --git a/tests/neo.UnitTests/IO/Caching/UT_FIFOSet.cs b/tests/neo.UnitTests/IO/Caching/UT_HashSetCache.cs similarity index 64% rename from tests/neo.UnitTests/IO/Caching/UT_FIFOSet.cs rename to tests/neo.UnitTests/IO/Caching/UT_HashSetCache.cs index d785127778..5f227c7e35 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_FIFOSet.cs +++ b/tests/neo.UnitTests/IO/Caching/UT_HashSetCache.cs @@ -8,63 +8,50 @@ namespace Neo.UnitTests.IO.Caching { [TestClass] - public class UT_FIFOSet + public class UT_HashSetCache { [TestMethod] - public void FIFOSetTest() + public void TestHashSetCache() { - var a = UInt256.Zero; - var b = new UInt256(); - var c = new UInt256(new byte[32] { - 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 - }); - - var set = new FIFOSet(3); - - Assert.IsTrue(set.Add(a)); - Assert.IsFalse(set.Add(a)); - Assert.IsFalse(set.Add(b)); - Assert.IsTrue(set.Add(c)); - - CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a, c }); - - var d = new UInt256(new byte[32] { - 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, 0x02 - }); + var bucket = new HashSetCache(10); + for (int i = 1; i <= 100; i++) + { + bucket.Add(i); + } + bucket.Count.Should().Be(100); - // Testing Fifo max size - Assert.IsTrue(set.Add(d)); - CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a, c, d }); + int sum = 0; + foreach (var ele in bucket) + { + sum += ele; + } + sum.Should().Be(5050); - var e = new UInt256(new byte[32] { - 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, 0x03 - }); + bucket.Add(101); + bucket.Count.Should().Be(91); - Assert.IsTrue(set.Add(e)); - Assert.IsFalse(set.Add(e)); - CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { c, d, e }); + var items = new int[10]; + var value = 11; + for (int i = 0; i < 10; i++) + { + items[i] = value; + value += 2; + } + bucket.ExceptWith(items); + bucket.Count.Should().Be(81); + + bucket.Contains(13).Should().BeFalse(); + bucket.Contains(50).Should().BeTrue(); } [TestMethod] public void TestConstructor() { - Action action1 = () => new FIFOSet(-1); + Action action1 = () => new HashSetCache(-1); action1.Should().Throw(); - Action action2 = () => new FIFOSet(1, -1); + Action action2 = () => new HashSetCache(1, -1); action2.Should().Throw(); - - Action action3 = () => new FIFOSet(1, 2); - action3.Should().Throw(); } [TestMethod] @@ -82,7 +69,7 @@ public void TestAdd() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 }); - var set = new FIFOSet(1, 1) + var set = new HashSetCache(1, 1) { a, b @@ -105,7 +92,7 @@ public void TestGetEnumerator() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02 }); - var set = new FIFOSet(1, 1) + var set = new HashSetCache(1, 1) { a, b @@ -136,7 +123,7 @@ public void TestExceptWith() 0x01, 0x03 }); - var set = new FIFOSet(10) + var set = new HashSetCache(10) { a, b, @@ -145,7 +132,7 @@ public void TestExceptWith() set.ExceptWith(new UInt256[] { b, c }); CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { a }); - set = new FIFOSet(10) + set = new HashSetCache(10) { a, b, @@ -154,7 +141,7 @@ public void TestExceptWith() set.ExceptWith(new UInt256[] { a }); CollectionAssert.AreEqual(set.ToArray(), new UInt256[] { b, c }); - set = new FIFOSet(10) + set = new HashSetCache(10) { a, b, From 633384f83b3a3f42537f61e5a28f39c340fe6151 Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Mon, 13 Jan 2020 23:54:53 +0800 Subject: [PATCH 197/305] Add ContainsTransaction Recheck when ParallelVerifiedTransaction Received (#1408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add containsTx recheck * add UT * Update to just View check * update * revert * Update UT_Blockchain.cs Co-authored-by: Vitor Nazário Coelho --- src/neo/Ledger/Blockchain.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 74d06806e4..48c7da914a 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -429,7 +429,9 @@ private void OnParallelVerified(ParallelVerified parallelVerified) RelayResultReason reason = parallelVerified.VerifyResult; if (reason == RelayResultReason.Succeed) { - if (!MemPool.CanTransactionFitInPool(parallelVerified.Transaction)) + if (View.ContainsTransaction(parallelVerified.Transaction.Hash)) + reason = RelayResultReason.AlreadyExists; + else if (!MemPool.CanTransactionFitInPool(parallelVerified.Transaction)) reason = RelayResultReason.OutOfMemory; else if (!MemPool.TryAdd(parallelVerified.Transaction.Hash, parallelVerified.Transaction)) reason = RelayResultReason.OutOfMemory; From 82f060bbde5c8bfabda6e26811c5c2198123f8ec Mon Sep 17 00:00:00 2001 From: cn1010 <1062108372@qq.com> Date: Tue, 14 Jan 2020 16:07:16 +0800 Subject: [PATCH 198/305] Enhance security (1) (#1403) * inputs validation * more validation * recover constructor in ECPoint * format * minor changes * format * changes based on review * recover validation in AesEncrypt --- src/neo/Cryptography/Base58.cs | 1 + src/neo/Cryptography/BloomFilter.cs | 2 + src/neo/Cryptography/ECC/ECDsa.cs | 107 ------------------ src/neo/Cryptography/ECC/ECFieldElement.cs | 5 +- src/neo/Cryptography/ECC/ECPoint.cs | 3 +- src/neo/Cryptography/MerkleTree.cs | 2 +- .../Cryptography/ECC/UT_ECDsa.cs | 55 --------- .../Cryptography/ECC/UT_ECFieldElement.cs | 24 ++++ .../Cryptography/ECC/UT_ECPoint.cs | 18 ++- 9 files changed, 41 insertions(+), 176 deletions(-) delete mode 100644 src/neo/Cryptography/ECC/ECDsa.cs delete mode 100644 tests/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs diff --git a/src/neo/Cryptography/Base58.cs b/src/neo/Cryptography/Base58.cs index 316f1de72b..f571f9540d 100644 --- a/src/neo/Cryptography/Base58.cs +++ b/src/neo/Cryptography/Base58.cs @@ -12,6 +12,7 @@ public static class Base58 public static byte[] Base58CheckDecode(this string input) { + if (input is null) throw new ArgumentNullException(nameof(input)); byte[] buffer = Decode(input); if (buffer.Length < 4) throw new FormatException(); byte[] checksum = buffer.Sha256(0, buffer.Length - 4).Sha256(); diff --git a/src/neo/Cryptography/BloomFilter.cs b/src/neo/Cryptography/BloomFilter.cs index c4b79d01f2..32cfa50dd4 100644 --- a/src/neo/Cryptography/BloomFilter.cs +++ b/src/neo/Cryptography/BloomFilter.cs @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.Linq; @@ -16,6 +17,7 @@ public class BloomFilter public BloomFilter(int m, int k, uint nTweak, byte[] elements = null) { + if (k < 0 || m < 0) throw new ArgumentOutOfRangeException(); this.seeds = Enumerable.Range(0, k).Select(p => (uint)p * 0xFBA4C795 + nTweak).ToArray(); this.bits = elements == null ? new BitArray(m) : new BitArray(elements); this.bits.Length = m; diff --git a/src/neo/Cryptography/ECC/ECDsa.cs b/src/neo/Cryptography/ECC/ECDsa.cs deleted file mode 100644 index 08f48b1dc3..0000000000 --- a/src/neo/Cryptography/ECC/ECDsa.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Numerics; -using System.Security.Cryptography; - -namespace Neo.Cryptography.ECC -{ - public class ECDsa - { - private readonly byte[] privateKey; - private readonly ECPoint publicKey; - private readonly ECCurve curve; - - public ECDsa(byte[] privateKey, ECCurve curve) - : this(curve.G * privateKey) - { - this.privateKey = privateKey; - } - - public ECDsa(ECPoint publicKey) - { - this.publicKey = publicKey; - this.curve = publicKey.Curve; - } - - private BigInteger CalculateE(BigInteger n, ReadOnlySpan message) - { - int messageBitLength = message.Length * 8; - BigInteger trunc = new BigInteger(message, isUnsigned: true, isBigEndian: true); - if (n.GetBitLength() < messageBitLength) - { - trunc >>= messageBitLength - n.GetBitLength(); - } - return trunc; - } - - public BigInteger[] GenerateSignature(ReadOnlySpan message) - { - if (privateKey == null) throw new InvalidOperationException(); - BigInteger e = CalculateE(curve.N, message); - BigInteger d = new BigInteger(privateKey, isUnsigned: true, isBigEndian: true); - BigInteger r, s; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - do - { - BigInteger k; - do - { - do - { - k = rng.NextBigInteger(curve.N.GetBitLength()); - } - while (k.Sign == 0 || k.CompareTo(curve.N) >= 0); - ECPoint p = ECPoint.Multiply(curve.G, k); - BigInteger x = p.X.Value; - r = x.Mod(curve.N); - } - while (r.Sign == 0); - s = (k.ModInverse(curve.N) * (e + d * r)).Mod(curve.N); - if (s > curve.N / 2) - { - s = curve.N - s; - } - } - while (s.Sign == 0); - } - return new BigInteger[] { r, s }; - } - - private static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) - { - int m = Math.Max(k.GetBitLength(), l.GetBitLength()); - ECPoint Z = P + Q; - ECPoint R = P.Curve.Infinity; - for (int i = m - 1; i >= 0; --i) - { - R = R.Twice(); - if (k.TestBit(i)) - { - if (l.TestBit(i)) - R = R + Z; - else - R = R + P; - } - else - { - if (l.TestBit(i)) - R = R + Q; - } - } - return R; - } - - public bool VerifySignature(ReadOnlySpan message, BigInteger r, BigInteger s) - { - if (r.Sign < 1 || s.Sign < 1 || r.CompareTo(curve.N) >= 0 || s.CompareTo(curve.N) >= 0) - return false; - BigInteger e = CalculateE(curve.N, message); - BigInteger c = s.ModInverse(curve.N); - BigInteger u1 = (e * c).Mod(curve.N); - BigInteger u2 = (r * c).Mod(curve.N); - ECPoint point = SumOfTwoMultiplies(curve.G, u1, publicKey, u2); - BigInteger v = point.X.Value.Mod(curve.N); - return v.Equals(r); - } - } -} diff --git a/src/neo/Cryptography/ECC/ECFieldElement.cs b/src/neo/Cryptography/ECC/ECFieldElement.cs index 948afc263d..9ebd48082d 100644 --- a/src/neo/Cryptography/ECC/ECFieldElement.cs +++ b/src/neo/Cryptography/ECC/ECFieldElement.cs @@ -10,6 +10,8 @@ internal class ECFieldElement : IComparable, IEquatable= curve.Q) throw new ArgumentException("x value too large in field element"); this.Value = value; @@ -19,6 +21,7 @@ public ECFieldElement(BigInteger value, ECCurve curve) public int CompareTo(ECFieldElement other) { if (ReferenceEquals(this, other)) return 0; + if (!curve.Equals(other.curve)) throw new InvalidOperationException("Invalid comparision for points with different curves"); return Value.CompareTo(other.Value); } @@ -35,7 +38,7 @@ public override bool Equals(object obj) public bool Equals(ECFieldElement other) { - return Value.Equals(other.Value); + return Value.Equals(other.Value) && curve.Equals(other.curve); } private static BigInteger[] FastLucasSequence(BigInteger p, BigInteger P, BigInteger Q, BigInteger k) diff --git a/src/neo/Cryptography/ECC/ECPoint.cs b/src/neo/Cryptography/ECC/ECPoint.cs index 4189dcbb9b..f0b0b19a36 100644 --- a/src/neo/Cryptography/ECC/ECPoint.cs +++ b/src/neo/Cryptography/ECC/ECPoint.cs @@ -22,7 +22,7 @@ public bool IsInfinity internal ECPoint(ECFieldElement x, ECFieldElement y, ECCurve curve) { - if ((x != null && y == null) || (x == null && y != null)) + if ((x is null ^ y is null) || (curve is null)) throw new ArgumentException("Exactly one of the field elements is null"); this.X = x; this.Y = y; @@ -31,6 +31,7 @@ internal ECPoint(ECFieldElement x, ECFieldElement y, ECCurve curve) public int CompareTo(ECPoint other) { + if (!Curve.Equals(other.Curve)) throw new InvalidOperationException("Invalid comparision for points with different curves"); if (ReferenceEquals(this, other)) return 0; int result = X.CompareTo(other.X); if (result != 0) return result; diff --git a/src/neo/Cryptography/MerkleTree.cs b/src/neo/Cryptography/MerkleTree.cs index 89d0a275b5..870eaabbb9 100644 --- a/src/neo/Cryptography/MerkleTree.cs +++ b/src/neo/Cryptography/MerkleTree.cs @@ -15,7 +15,7 @@ public class MerkleTree internal MerkleTree(UInt256[] hashes) { - if (hashes.Length == 0) throw new ArgumentException(); + if (hashes is null || hashes.Length == 0) throw new ArgumentException(); this.root = Build(hashes.Select(p => new MerkleTreeNode { Hash = p }).ToArray()); int depth = 1; for (MerkleTreeNode i = root; i.LeftChild != null; i = i.LeftChild) diff --git a/tests/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs b/tests/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs deleted file mode 100644 index 62206741cb..0000000000 --- a/tests/neo.UnitTests/Cryptography/ECC/UT_ECDsa.cs +++ /dev/null @@ -1,55 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Wallets; -using System; -using System.Numerics; -using ECDsa = Neo.Cryptography.ECC.ECDsa; - -namespace Neo.UnitTests.Cryptography -{ - [TestClass] - public class UT_ECDsa - { - private KeyPair key = null; - - [TestInitialize] - public void TestSetup() - { - key = UT_Crypto.generateCertainKey(32); - } - - [TestMethod] - public void TestECDsaConstructor() - { - Action action = () => new ECDsa(key.PublicKey); - action.Should().NotThrow(); - action = () => new ECDsa(key.PrivateKey, key.PublicKey.Curve); - action.Should().NotThrow(); - } - - [TestMethod] - public void TestGenerateSignature() - { - ECDsa sa = new ECDsa(key.PrivateKey, key.PublicKey.Curve); - byte[] message = System.Text.Encoding.Default.GetBytes("HelloWorld"); - for (int i = 0; i < 10; i++) - { - BigInteger[] result = sa.GenerateSignature(message); - result.Length.Should().Be(2); - } - sa = new ECDsa(key.PublicKey); - Action action = () => sa.GenerateSignature(message); - action.Should().Throw(); - } - - [TestMethod] - public void TestVerifySignature() - { - ECDsa sa = new ECDsa(key.PrivateKey, key.PublicKey.Curve); - byte[] message = System.Text.Encoding.Default.GetBytes("HelloWorld"); - BigInteger[] result = sa.GenerateSignature(message); - sa.VerifySignature(message, result[0], result[1]).Should().BeTrue(); - sa.VerifySignature(message, new BigInteger(-100), result[1]).Should().BeFalse(); - } - } -} diff --git a/tests/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs b/tests/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs index 6b2965b41f..725fc46962 100644 --- a/tests/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs +++ b/tests/neo.UnitTests/Cryptography/ECC/UT_ECFieldElement.cs @@ -23,6 +23,29 @@ public void TestECFieldElementConstructor() action.Should().Throw(); } + [TestMethod] + public void TestCompareTo() + { + ECFieldElement X1 = new ECFieldElement(new BigInteger(100), ECCurve.Secp256k1); + ECFieldElement Y1 = new ECFieldElement(new BigInteger(200), ECCurve.Secp256k1); + ECFieldElement X2 = new ECFieldElement(new BigInteger(300), ECCurve.Secp256k1); + ECFieldElement Y2 = new ECFieldElement(new BigInteger(400), ECCurve.Secp256k1); + ECFieldElement X3 = new ECFieldElement(new BigInteger(100), ECCurve.Secp256r1); + ECFieldElement Y3 = new ECFieldElement(new BigInteger(400), ECCurve.Secp256r1); + ECPoint point1 = new ECPoint(X1, Y1, ECCurve.Secp256k1); + ECPoint point2 = new ECPoint(X2, Y1, ECCurve.Secp256k1); + ECPoint point3 = new ECPoint(X1, Y2, ECCurve.Secp256k1); + ECPoint point4 = new ECPoint(X3, Y3, ECCurve.Secp256r1); + + point1.CompareTo(point1).Should().Be(0); + point1.CompareTo(point2).Should().Be(-1); + point2.CompareTo(point1).Should().Be(1); + point1.CompareTo(point3).Should().Be(-1); + point3.CompareTo(point1).Should().Be(1); + Action action = () => point3.CompareTo(point4); + action.Should().Throw(); + } + [TestMethod] public void TestEquals() { @@ -30,6 +53,7 @@ public void TestEquals() object element = new ECFieldElement(input, ECCurve.Secp256k1); element.Equals(element).Should().BeTrue(); element.Equals(1).Should().BeFalse(); + element.Equals(new ECFieldElement(input, ECCurve.Secp256r1)).Should().BeFalse(); input = new BigInteger(200); element.Equals(new ECFieldElement(input, ECCurve.Secp256k1)).Should().BeFalse(); diff --git a/tests/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs b/tests/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs index b38df0fa44..9a8232d9fb 100644 --- a/tests/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs +++ b/tests/neo.UnitTests/Cryptography/ECC/UT_ECPoint.cs @@ -28,18 +28,12 @@ public static byte[] generatePrivateKey(int privateKeyLength) public void TestCompareTo() { ECFieldElement X1 = new ECFieldElement(new BigInteger(100), ECCurve.Secp256k1); - ECFieldElement Y1 = new ECFieldElement(new BigInteger(200), ECCurve.Secp256k1); - ECFieldElement X2 = new ECFieldElement(new BigInteger(300), ECCurve.Secp256k1); - ECFieldElement Y2 = new ECFieldElement(new BigInteger(400), ECCurve.Secp256k1); - ECPoint point1 = new ECPoint(X1, Y1, ECCurve.Secp256k1); - ECPoint point2 = new ECPoint(X2, Y1, ECCurve.Secp256k1); - ECPoint point3 = new ECPoint(X1, Y2, ECCurve.Secp256k1); + ECFieldElement X2 = new ECFieldElement(new BigInteger(200), ECCurve.Secp256k1); + ECFieldElement X3 = new ECFieldElement(new BigInteger(100), ECCurve.Secp256r1); - point1.CompareTo(point1).Should().Be(0); - point1.CompareTo(point2).Should().Be(-1); - point2.CompareTo(point1).Should().Be(1); - point1.CompareTo(point3).Should().Be(-1); - point3.CompareTo(point1).Should().Be(1); + X1.CompareTo(X2).Should().Be(-1); + Action action = () => X1.CompareTo(X3); + action.Should().Throw(); } [TestMethod] @@ -60,6 +54,8 @@ public void TestECPointConstructor() action.Should().Throw(); action = () => new ECPoint(null, Y, ECCurve.Secp256k1); action.Should().Throw(); + action = () => new ECPoint(null, Y, null); + action.Should().Throw(); } [TestMethod] From e0d11d39c75055b73f8fe4e55abf666eb05476c3 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 15 Jan 2020 14:44:59 +0000 Subject: [PATCH 199/305] Fix undefined layout behaviour, and remove pinning: Uint256 and Uint160 (#1387) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix undefined layout behaviour, and remove pinning * Update UInt256.cs * Update UInt256.cs * add StructLayout * set pack=1 * Explicit * Remove UIntBase * Revert constructor * add unsafe * Comment * Comment Uint256 * Removing comments from value 1 * Removing comments from value1 Co-authored-by: Luchuan Co-authored-by: Vitor Nazário Coelho Co-authored-by: Erik Zhang Co-authored-by: Shargon --- src/neo/UInt160.cs | 25 ++++++--- src/neo/UInt256.cs | 27 ++++++--- src/neo/UIntBase.cs | 90 ------------------------------ tests/neo.UnitTests/UT_UIntBase.cs | 49 ---------------- 4 files changed, 35 insertions(+), 156 deletions(-) delete mode 100644 src/neo/UIntBase.cs delete mode 100644 tests/neo.UnitTests/UT_UIntBase.cs diff --git a/src/neo/UInt160.cs b/src/neo/UInt160.cs index 69c2f85ed1..a7cb19fc2a 100644 --- a/src/neo/UInt160.cs +++ b/src/neo/UInt160.cs @@ -1,22 +1,26 @@ +using Neo.IO; using System; using System.Globalization; using System.IO; +using System.Runtime.InteropServices; namespace Neo { /// /// This class stores a 160 bit unsigned int, represented as a 20-byte little-endian byte array + /// It is composed by ulong(64) + ulong(64) + uint(32) = UInt160(160) /// - public class UInt160 : UIntBase, IComparable, IEquatable + [StructLayout(LayoutKind.Explicit, Size = 20)] + public class UInt160 : IComparable, IEquatable, ISerializable { public const int Length = 20; public static readonly UInt160 Zero = new UInt160(); - private ulong value1; - private ulong value2; - private uint value3; + [FieldOffset(0)] private ulong value1; + [FieldOffset(8)] private ulong value2; + [FieldOffset(16)] private uint value3; - public override int Size => Length; + public int Size => Length; public UInt160() { @@ -44,7 +48,7 @@ public int CompareTo(UInt160 other) return value1.CompareTo(other.value1); } - public override void Deserialize(BinaryReader reader) + public void Deserialize(BinaryReader reader) { value1 = reader.ReadUInt64(); value2 = reader.ReadUInt64(); @@ -80,7 +84,7 @@ public override int GetHashCode() /// Method Parse receives a big-endian hex string and stores as a UInt160 little-endian 20-bytes array /// Example: Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01") should create UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 /// - public static new UInt160 Parse(string value) + public static UInt160 Parse(string value) { if (value == null) throw new ArgumentNullException(); @@ -93,13 +97,18 @@ public static new UInt160 Parse(string value) return new UInt160(data); } - public override void Serialize(BinaryWriter writer) + public void Serialize(BinaryWriter writer) { writer.Write(value1); writer.Write(value2); writer.Write(value3); } + public override string ToString() + { + return "0x" + this.ToArray().ToHexString(reverse: true); + } + /// /// Method TryParse tries to parse a big-endian hex string and store it as a UInt160 little-endian 20-bytes array /// Example: TryParse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01", result) should create result UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 diff --git a/src/neo/UInt256.cs b/src/neo/UInt256.cs index bee5743c3c..d0c01f4f51 100644 --- a/src/neo/UInt256.cs +++ b/src/neo/UInt256.cs @@ -1,23 +1,27 @@ +using Neo.IO; using System; using System.Globalization; using System.IO; +using System.Runtime.InteropServices; namespace Neo { /// /// This class stores a 256 bit unsigned int, represented as a 32-byte little-endian byte array + /// Composed by ulong(64) + ulong(64) + ulong(64) + ulong(64) = UInt256(256) /// - public class UInt256 : UIntBase, IComparable, IEquatable + [StructLayout(LayoutKind.Explicit, Size = 32)] + public class UInt256 : IComparable, IEquatable, ISerializable { public const int Length = 32; public static readonly UInt256 Zero = new UInt256(); - private ulong value1; - private ulong value2; - private ulong value3; - private ulong value4; + [FieldOffset(0)] private ulong value1; + [FieldOffset(8)] private ulong value2; + [FieldOffset(16)] private ulong value3; + [FieldOffset(24)] private ulong value4; - public override int Size => Length; + public int Size => Length; public UInt256() { @@ -47,7 +51,7 @@ public int CompareTo(UInt256 other) return value1.CompareTo(other.value1); } - public override void Deserialize(BinaryReader reader) + public void Deserialize(BinaryReader reader) { value1 = reader.ReadUInt64(); value2 = reader.ReadUInt64(); @@ -85,7 +89,7 @@ public override int GetHashCode() /// Method Parse receives a big-endian hex string and stores as a UInt256 little-endian 32-bytes array /// Example: Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01") should create UInt256 01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 /// - public static new UInt256 Parse(string s) + public static UInt256 Parse(string s) { if (s == null) throw new ArgumentNullException(); @@ -98,7 +102,7 @@ public static new UInt256 Parse(string s) return new UInt256(data); } - public override void Serialize(BinaryWriter writer) + public void Serialize(BinaryWriter writer) { writer.Write(value1); writer.Write(value2); @@ -106,6 +110,11 @@ public override void Serialize(BinaryWriter writer) writer.Write(value4); } + public override string ToString() + { + return "0x" + this.ToArray().ToHexString(reverse: true); + } + /// /// Method TryParse tries to parse a big-endian hex string and store it as a UInt256 little-endian 32-bytes array /// Example: TryParse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01", result) should create result UInt256 01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 diff --git a/src/neo/UIntBase.cs b/src/neo/UIntBase.cs deleted file mode 100644 index b78ccd3023..0000000000 --- a/src/neo/UIntBase.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Neo.IO; -using System; -using System.IO; - -namespace Neo -{ - /// - /// Base class for little-endian unsigned integers. Two classes inherit from this: UInt160 and UInt256. - /// Only basic comparison/serialization are proposed for these classes. For arithmetic purposes, use BigInteger class. - /// - public abstract class UIntBase : ISerializable - { - /// - /// Number of bytes of the unsigned int. - /// Currently, inherited classes use 20-bytes (UInt160) or 32-bytes (UInt256) - /// - public abstract int Size { get; } - - public abstract void Deserialize(BinaryReader reader); - - public abstract override bool Equals(object obj); - - /// - /// Method GetHashCode returns a 32-bit int representing a hash code, composed of the first 4 bytes. - /// - public abstract override int GetHashCode(); - - /// - /// Method Parse receives a big-endian hex string and stores as a UInt160 or UInt256 little-endian byte array - /// Example: Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01") should create UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 - /// - public static UIntBase Parse(string s) - { - if (s.Length == 40 || s.Length == 42) - return UInt160.Parse(s); - else if (s.Length == 64 || s.Length == 66) - return UInt256.Parse(s); - else - throw new FormatException(); - } - - public abstract void Serialize(BinaryWriter writer); - - /// - /// Method ToString returns a big-endian string starting by "0x" representing the little-endian unsigned int - /// Example: if this is storing 20-bytes 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4, ToString() should return "0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01" - /// - public override string ToString() - { - return "0x" + this.ToArray().ToHexString(reverse: true); - } - - /// - /// Method TryParse tries to parse a big-endian hex string and stores it as a UInt160 or UInt256 little-endian bytes array - /// Example: TryParse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01", result) should create result UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 - /// - public static bool TryParse(string s, out T result) where T : UIntBase - { - int size; - if (typeof(T) == typeof(UInt160)) - size = 20; - else if (typeof(T) == typeof(UInt256)) - size = 32; - else if (s.Length == 40 || s.Length == 42) - size = 20; - else if (s.Length == 64 || s.Length == 66) - size = 32; - else - size = 0; - if (size == 20) - { - if (UInt160.TryParse(s, out UInt160 r)) - { - result = (T)(UIntBase)r; - return true; - } - } - else if (size == 32) - { - if (UInt256.TryParse(s, out UInt256 r)) - { - result = (T)(UIntBase)r; - return true; - } - } - result = null; - return false; - } - } -} diff --git a/tests/neo.UnitTests/UT_UIntBase.cs b/tests/neo.UnitTests/UT_UIntBase.cs deleted file mode 100644 index 5f02b92c25..0000000000 --- a/tests/neo.UnitTests/UT_UIntBase.cs +++ /dev/null @@ -1,49 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; - -namespace Neo.UnitTests.IO -{ - [TestClass] - public class UT_UIntBase - { - [TestMethod] - public void TestParse() - { - UInt160 uInt1601 = (UInt160)UIntBase.Parse("0x0000000000000000000000000000000000000000"); - UInt256 uInt2561 = (UInt256)UIntBase.Parse("0x0000000000000000000000000000000000000000000000000000000000000000"); - UInt160 uInt1602 = (UInt160)UIntBase.Parse("0000000000000000000000000000000000000000"); - UInt256 uInt2562 = (UInt256)UIntBase.Parse("0000000000000000000000000000000000000000000000000000000000000000"); - Assert.AreEqual(UInt160.Zero, uInt1601); - Assert.AreEqual(UInt256.Zero, uInt2561); - Assert.AreEqual(UInt160.Zero, uInt1602); - Assert.AreEqual(UInt256.Zero, uInt2562); - Action action = () => UIntBase.Parse("0000000"); - action.Should().Throw(); - } - - [TestMethod] - public void TestTryParse() - { - UInt160 uInt160 = new UInt160(); - Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000", out uInt160)); - Assert.AreEqual(UInt160.Zero, uInt160); - Assert.AreEqual(false, UIntBase.TryParse("0x00000000000000000000000000000000000000", out uInt160)); - UInt256 uInt256 = new UInt256(); - Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000000000000000000000000000", out uInt256)); - Assert.AreEqual(UInt256.Zero, uInt256); - Assert.AreEqual(false, UIntBase.TryParse("0x00000000000000000000000000000000000000000000000000000000000000", out uInt256)); - UIntBase uIntBase = new UInt160(); - Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000", out uIntBase)); - Assert.AreEqual(UInt160.Zero, uIntBase); - Assert.AreEqual(true, UIntBase.TryParse("0000000000000000000000000000000000000000", out uIntBase)); - Assert.AreEqual(UInt160.Zero, uIntBase); - uIntBase = new UInt256(); - Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000000000000000000000000000", out uIntBase)); - Assert.AreEqual(UInt256.Zero, uIntBase); - Assert.AreEqual(true, UIntBase.TryParse("0000000000000000000000000000000000000000000000000000000000000000", out uIntBase)); - Assert.AreEqual(UInt256.Zero, uIntBase); - Assert.AreEqual(false, UIntBase.TryParse("00000000000000000000000000000000000000000000000000000000000000", out uIntBase)); - } - } -} From abffe5c3b3df977674d04970165288c6e211dbdd Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 17 Jan 2020 17:36:06 +0100 Subject: [PATCH 200/305] Allow to verify import blocks (#1415) * Verify Import * Default true --- src/neo/Ledger/Blockchain.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 48c7da914a..9090a5b339 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -23,7 +23,7 @@ public sealed partial class Blockchain : UntypedActor { public partial class ApplicationExecuted { } public class PersistCompleted { public Block Block; } - public class Import { public IEnumerable Blocks; } + public class Import { public IEnumerable Blocks; public bool Verify = true; } public class ImportCompleted { } public class FillMemoryPool { public IEnumerable Transactions; } public class FillCompleted { } @@ -239,13 +239,15 @@ public Transaction GetTransaction(UInt256 hash) return View.GetTransaction(hash); } - private void OnImport(IEnumerable blocks) + private void OnImport(IEnumerable blocks, bool verify) { foreach (Block block in blocks) { if (block.Index <= Height) continue; if (block.Index != Height + 1) throw new InvalidOperationException(); + if (verify && !block.Verify(currentSnapshot)) + throw new InvalidOperationException(); Persist(block); SaveHeaderHashList(); } @@ -453,7 +455,7 @@ protected override void OnReceive(object message) switch (message) { case Import import: - OnImport(import.Blocks); + OnImport(import.Blocks, import.Verify); break; case FillMemoryPool fill: OnFillMemoryPool(fill.Transactions); From 6b60462b85305b9b31d5c31631e930cc75efa7d1 Mon Sep 17 00:00:00 2001 From: Shargon Date: Sat, 18 Jan 2020 08:54:59 +0100 Subject: [PATCH 201/305] Fix notifications (#1422) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vitor Nazário Coelho --- src/neo/SmartContract/Native/Tokens/Nep5Token.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index bb92fbbd17..3e331de487 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -86,7 +86,7 @@ internal protected virtual void Mint(ApplicationEngine engine, UInt160 account, BigInteger totalSupply = new BigInteger(storage.Value); totalSupply += amount; storage.Value = totalSupply.ToByteArrayStandard(); - engine.SendNotification(Hash, new Array(engine.ReferenceCounter, new StackItem[] { "Transfer", StackItem.Null, account.ToArray(), amount })); + engine.SendNotification(Hash, new Array(new StackItem[] { "Transfer", StackItem.Null, account.ToArray(), amount })); } internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, BigInteger amount) @@ -112,7 +112,7 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, BigInteger totalSupply = new BigInteger(storage.Value); totalSupply -= amount; storage.Value = totalSupply.ToByteArrayStandard(); - engine.SendNotification(Hash, new Array(engine.ReferenceCounter, new StackItem[] { "Transfer", account.ToArray(), StackItem.Null, amount })); + engine.SendNotification(Hash, new Array(new StackItem[] { "Transfer", account.ToArray(), StackItem.Null, amount })); } [ContractMethod(0, ContractParameterType.String, Name = "name", SafeMethod = true)] @@ -222,7 +222,7 @@ protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 storage_to.Value = state_to.ToByteArray(); } } - engine.SendNotification(Hash, new Array(engine.ReferenceCounter, new StackItem[] { "Transfer", from.ToArray(), to.ToArray(), amount })); + engine.SendNotification(Hash, new Array(new StackItem[] { "Transfer", from.ToArray(), to.ToArray(), amount })); return true; } From e0f7b4d1c7376dab019fd858d241d21abe5ca41e Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 27 Jan 2020 13:56:33 +0800 Subject: [PATCH 202/305] Add ID to ContractState (#1400) --- src/neo/Ledger/ContractIdState.cs | 37 ++++++++++++ src/neo/Ledger/ContractState.cs | 16 +++--- src/neo/Ledger/StorageKey.cs | 16 +++--- src/neo/Persistence/ClonedView.cs | 2 + src/neo/Persistence/Prefixes.cs | 1 + src/neo/Persistence/ReadOnlyView.cs | 1 + src/neo/Persistence/SnapshotView.cs | 2 + src/neo/Persistence/StoreView.cs | 2 + .../SmartContract/InteropService.Contract.cs | 19 +------ .../SmartContract/InteropService.Native.cs | 1 + .../SmartContract/InteropService.Storage.cs | 32 +++++------ .../SmartContract/Native/NativeContract.cs | 3 +- .../SmartContract/Native/PolicyContract.cs | 1 + .../SmartContract/Native/Tokens/GasToken.cs | 1 + .../SmartContract/Native/Tokens/NeoToken.cs | 3 +- src/neo/SmartContract/StorageContext.cs | 2 +- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 2 +- .../neo.UnitTests/Ledger/UT_ContractState.cs | 2 +- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 6 +- tests/neo.UnitTests/Ledger/UT_StorageKey.cs | 44 +++++++------- .../Native/Tokens/UT_GasToken.cs | 2 +- .../Native/Tokens/UT_NeoToken.cs | 2 +- .../Native/Tokens/UT_Nep5Token.cs | 11 ++-- .../SmartContract/Native/UT_NativeContract.cs | 3 + .../SmartContract/Native/UT_PolicyContract.cs | 2 +- .../SmartContract/UT_InteropService.NEO.cs | 18 +----- .../SmartContract/UT_InteropService.cs | 57 +++++++------------ .../SmartContract/UT_StorageContext.cs | 23 -------- tests/neo.UnitTests/UT_DataCache.cs | 42 +++++++------- 29 files changed, 168 insertions(+), 185 deletions(-) create mode 100644 src/neo/Ledger/ContractIdState.cs delete mode 100644 tests/neo.UnitTests/SmartContract/UT_StorageContext.cs diff --git a/src/neo/Ledger/ContractIdState.cs b/src/neo/Ledger/ContractIdState.cs new file mode 100644 index 0000000000..b29f1d21ac --- /dev/null +++ b/src/neo/Ledger/ContractIdState.cs @@ -0,0 +1,37 @@ +using Neo.IO; +using System; +using System.IO; + +namespace Neo.Ledger +{ + public class ContractIdState : ICloneable, ISerializable + { + public int NextId; + + int ISerializable.Size => sizeof(int); + + ContractIdState ICloneable.Clone() + { + return new ContractIdState + { + NextId = NextId + }; + } + + void ISerializable.Deserialize(BinaryReader reader) + { + NextId = reader.ReadInt32(); + if (NextId < 0) throw new FormatException(); + } + + void ICloneable.FromReplica(ContractIdState replica) + { + NextId = replica.NextId; + } + + void ISerializable.Serialize(BinaryWriter writer) + { + writer.Write(NextId); + } + } +} diff --git a/src/neo/Ledger/ContractState.cs b/src/neo/Ledger/ContractState.cs index f6a50b67d0..a47a6366f7 100644 --- a/src/neo/Ledger/ContractState.cs +++ b/src/neo/Ledger/ContractState.cs @@ -12,6 +12,7 @@ namespace Neo.Ledger { public class ContractState : ICloneable, ISerializable, IInteroperable { + public int Id; public byte[] Script; public ContractManifest Manifest; @@ -31,12 +32,13 @@ public UInt160 ScriptHash } } - int ISerializable.Size => Script.GetVarSize() + Manifest.ToJson().ToString().GetVarSize(); + int ISerializable.Size => sizeof(int) + Script.GetVarSize() + Manifest.ToJson().ToString().GetVarSize(); ContractState ICloneable.Clone() { return new ContractState { + Id = Id, Script = Script, Manifest = Manifest.Clone() }; @@ -44,18 +46,21 @@ ContractState ICloneable.Clone() void ISerializable.Deserialize(BinaryReader reader) { + Id = reader.ReadInt32(); Script = reader.ReadVarBytes(); Manifest = reader.ReadSerializable(); } void ICloneable.FromReplica(ContractState replica) { + Id = replica.Id; Script = replica.Script; Manifest = replica.Manifest.Clone(); } void ISerializable.Serialize(BinaryWriter writer) { + writer.Write(Id); writer.WriteVarBytes(Script); writer.Write(Manifest); } @@ -63,20 +68,13 @@ void ISerializable.Serialize(BinaryWriter writer) public JObject ToJson() { JObject json = new JObject(); + json["id"] = Id; json["hash"] = ScriptHash.ToString(); json["script"] = Convert.ToBase64String(Script); json["manifest"] = Manifest.ToJson(); return json; } - public static ContractState FromJson(JObject json) - { - ContractState contractState = new ContractState(); - contractState.Script = Convert.FromBase64String(json["script"].AsString()); - contractState.Manifest = ContractManifest.FromJson(json["manifest"]); - return contractState; - } - public StackItem ToStackItem(ReferenceCounter referenceCounter) { return new Array(referenceCounter, new StackItem[] { Script, HasStorage, Payable }); diff --git a/src/neo/Ledger/StorageKey.cs b/src/neo/Ledger/StorageKey.cs index 70e045560e..af7258f2ce 100644 --- a/src/neo/Ledger/StorageKey.cs +++ b/src/neo/Ledger/StorageKey.cs @@ -7,12 +7,12 @@ namespace Neo.Ledger { public class StorageKey : IEquatable, ISerializable { - public UInt160 ScriptHash; + public int Id; public byte[] Key; - int ISerializable.Size => ScriptHash.Size + (Key.Length / 16 + 1) * 17; + int ISerializable.Size => sizeof(int) + (Key.Length / 16 + 1) * 17; - internal static byte[] CreateSearchPrefix(UInt160 hash, byte[] prefix) + internal static byte[] CreateSearchPrefix(int id, byte[] prefix) { using (MemoryStream ms = new MemoryStream()) { @@ -27,13 +27,13 @@ internal static byte[] CreateSearchPrefix(UInt160 hash, byte[] prefix) } if (remain > 0) ms.Write(prefix, index, remain); - return Helper.Concat(hash.ToArray(), ms.ToArray()); + return Helper.Concat(BitConverter.GetBytes(id), ms.ToArray()); } } void ISerializable.Deserialize(BinaryReader reader) { - ScriptHash = reader.ReadSerializable(); + Id = reader.ReadInt32(); Key = reader.ReadBytesWithGrouping(); } @@ -43,7 +43,7 @@ public bool Equals(StorageKey other) return false; if (ReferenceEquals(this, other)) return true; - return ScriptHash.Equals(other.ScriptHash) && MemoryExtensions.SequenceEqual(Key, other.Key); + return Id == other.Id && MemoryExtensions.SequenceEqual(Key, other.Key); } public override bool Equals(object obj) @@ -54,12 +54,12 @@ public override bool Equals(object obj) public override int GetHashCode() { - return ScriptHash.GetHashCode() + (int)Key.Murmur32(0); + return Id.GetHashCode() + (int)Key.Murmur32(0); } void ISerializable.Serialize(BinaryWriter writer) { - writer.Write(ScriptHash); + writer.Write(Id); writer.WriteBytesWithGrouping(Key); } } diff --git a/src/neo/Persistence/ClonedView.cs b/src/neo/Persistence/ClonedView.cs index f820b8f31b..307438b2db 100644 --- a/src/neo/Persistence/ClonedView.cs +++ b/src/neo/Persistence/ClonedView.cs @@ -13,6 +13,7 @@ internal class ClonedView : StoreView public override DataCache, HeaderHashList> HeaderHashList { get; } public override MetaDataCache BlockHashIndex { get; } public override MetaDataCache HeaderHashIndex { get; } + public override MetaDataCache ContractId { get; } public ClonedView(StoreView view) { @@ -24,6 +25,7 @@ public ClonedView(StoreView view) this.HeaderHashList = view.HeaderHashList.CreateSnapshot(); this.BlockHashIndex = view.BlockHashIndex.CreateSnapshot(); this.HeaderHashIndex = view.HeaderHashIndex.CreateSnapshot(); + this.ContractId = view.ContractId.CreateSnapshot(); } } } diff --git a/src/neo/Persistence/Prefixes.cs b/src/neo/Persistence/Prefixes.cs index dad8540787..421fe086c3 100644 --- a/src/neo/Persistence/Prefixes.cs +++ b/src/neo/Persistence/Prefixes.cs @@ -11,6 +11,7 @@ internal static class Prefixes public const byte IX_HeaderHashList = 0x80; public const byte IX_CurrentBlock = 0xc0; public const byte IX_CurrentHeader = 0xc1; + public const byte IX_ContractId = 0xc2; /* Prefixes 0xf0 to 0xff are reserved for external use. * diff --git a/src/neo/Persistence/ReadOnlyView.cs b/src/neo/Persistence/ReadOnlyView.cs index 18bf05fae5..5f87f236ab 100644 --- a/src/neo/Persistence/ReadOnlyView.cs +++ b/src/neo/Persistence/ReadOnlyView.cs @@ -19,6 +19,7 @@ public class ReadOnlyView : StoreView 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 override MetaDataCache ContractId => new StoreMetaDataCache(store, Prefixes.IX_ContractId); public ReadOnlyView(IReadOnlyStore store) { diff --git a/src/neo/Persistence/SnapshotView.cs b/src/neo/Persistence/SnapshotView.cs index 3b20025cd0..d634200134 100644 --- a/src/neo/Persistence/SnapshotView.cs +++ b/src/neo/Persistence/SnapshotView.cs @@ -19,6 +19,7 @@ public class SnapshotView : StoreView, IDisposable public override DataCache, HeaderHashList> HeaderHashList { get; } public override MetaDataCache BlockHashIndex { get; } public override MetaDataCache HeaderHashIndex { get; } + public override MetaDataCache ContractId { get; } public SnapshotView(IStore store) { @@ -30,6 +31,7 @@ public SnapshotView(IStore store) HeaderHashList = new StoreDataCache, HeaderHashList>(snapshot, Prefixes.IX_HeaderHashList); BlockHashIndex = new StoreMetaDataCache(snapshot, Prefixes.IX_CurrentBlock); HeaderHashIndex = new StoreMetaDataCache(snapshot, Prefixes.IX_CurrentHeader); + ContractId = new StoreMetaDataCache(snapshot, Prefixes.IX_ContractId); } public override void Commit() diff --git a/src/neo/Persistence/StoreView.cs b/src/neo/Persistence/StoreView.cs index 4c2a1eed78..c0126b0c95 100644 --- a/src/neo/Persistence/StoreView.cs +++ b/src/neo/Persistence/StoreView.cs @@ -18,6 +18,7 @@ public abstract class StoreView public abstract DataCache, HeaderHashList> HeaderHashList { get; } public abstract MetaDataCache BlockHashIndex { get; } public abstract MetaDataCache HeaderHashIndex { get; } + public abstract MetaDataCache ContractId { get; } public uint Height => BlockHashIndex.Get().Index; public uint HeaderHeight => HeaderHashIndex.Get().Index; @@ -38,6 +39,7 @@ public virtual void Commit() HeaderHashList.Commit(); BlockHashIndex.Commit(); HeaderHashIndex.Commit(); + ContractId.Commit(); } public bool ContainsBlock(UInt256 hash) diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index e6df9d8aa8..14823db8c8 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -38,6 +38,7 @@ private static bool Contract_Create(ApplicationEngine engine) if (contract != null) return false; contract = new ContractState { + Id = engine.Snapshot.ContractId.GetAndChange().NextId++, Script = script, Manifest = ContractManifest.Parse(manifest) }; @@ -66,27 +67,13 @@ private static bool Contract_Update(ApplicationEngine engine) if (engine.Snapshot.Contracts.TryGet(hash_new) != null) return false; contract = new ContractState { + Id = contract.Id, Script = script, Manifest = contract.Manifest }; contract.Manifest.Abi.Hash = hash_new; engine.Snapshot.Contracts.Add(hash_new, contract); - if (contract.HasStorage) - { - foreach (var (key, value) in engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).ToArray()) - { - engine.Snapshot.Storages.Add(new StorageKey - { - ScriptHash = hash_new, - Key = key.Key - }, new StorageItem - { - Value = value.Value, - IsConstant = false - }); - } - } - Contract_Destroy(engine); + engine.Snapshot.Contracts.Delete(engine.CurrentScriptHash); } if (manifest.Length > 0) { diff --git a/src/neo/SmartContract/InteropService.Native.cs b/src/neo/SmartContract/InteropService.Native.cs index f664c4020a..da3263b4a2 100644 --- a/src/neo/SmartContract/InteropService.Native.cs +++ b/src/neo/SmartContract/InteropService.Native.cs @@ -22,6 +22,7 @@ private static bool Native_Deploy(ApplicationEngine engine) { engine.Snapshot.Contracts.Add(contract.Hash, new ContractState { + Id = contract.Id, Script = contract.Script, Manifest = contract.Manifest }); diff --git a/src/neo/SmartContract/InteropService.Storage.cs b/src/neo/SmartContract/InteropService.Storage.cs index ebf826b7f5..3355a5f83c 100644 --- a/src/neo/SmartContract/InteropService.Storage.cs +++ b/src/neo/SmartContract/InteropService.Storage.cs @@ -24,14 +24,6 @@ public static class Storage public static readonly InteropDescriptor PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); public static readonly InteropDescriptor Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates); - private static bool CheckStorageContext(ApplicationEngine engine, StorageContext context) - { - ContractState contract = engine.Snapshot.Contracts.TryGet(context.ScriptHash); - if (contract == null) return false; - if (!contract.HasStorage) return false; - return true; - } - private static long GetStoragePrice(EvaluationStack stack) { return (stack.Peek(1).GetByteLength() + stack.Peek(2).GetByteLength()) * GasPerByte; @@ -42,11 +34,10 @@ private static bool PutExInternal(ApplicationEngine engine, StorageContext conte if (key.Length > MaxKeySize) return false; if (value.Length > MaxValueSize) return false; if (context.IsReadOnly) return false; - if (!CheckStorageContext(engine, context)) return false; StorageKey skey = new StorageKey { - ScriptHash = context.ScriptHash, + Id = context.Id, Key = key }; @@ -68,9 +59,12 @@ private static bool PutExInternal(ApplicationEngine engine, StorageContext conte private static bool Storage_GetContext(ApplicationEngine engine) { + ContractState contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash); + if (contract == null) return false; + if (!contract.HasStorage) return false; engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new StorageContext { - ScriptHash = engine.CurrentScriptHash, + Id = contract.Id, IsReadOnly = false })); return true; @@ -78,9 +72,12 @@ private static bool Storage_GetContext(ApplicationEngine engine) private static bool Storage_GetReadOnlyContext(ApplicationEngine engine) { + ContractState contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash); + if (contract == null) return false; + if (!contract.HasStorage) return false; engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new StorageContext { - ScriptHash = engine.CurrentScriptHash, + Id = contract.Id, IsReadOnly = true })); return true; @@ -94,7 +91,7 @@ private static bool Storage_AsReadOnly(ApplicationEngine engine) if (!context.IsReadOnly) context = new StorageContext { - ScriptHash = context.ScriptHash, + Id = context.Id, IsReadOnly = true }; engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(context)); @@ -108,11 +105,10 @@ private static bool Storage_Get(ApplicationEngine engine) if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) { StorageContext context = _interface.GetInterface(); - if (!CheckStorageContext(engine, context)) return false; byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); StorageItem item = engine.Snapshot.Storages.TryGet(new StorageKey { - ScriptHash = context.ScriptHash, + Id = context.Id, Key = key }); engine.CurrentContext.EvaluationStack.Push(item?.Value ?? StackItem.Null); @@ -126,9 +122,8 @@ private static bool Storage_Find(ApplicationEngine engine) if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) { StorageContext context = _interface.GetInterface(); - if (!CheckStorageContext(engine, context)) return false; byte[] prefix = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - byte[] prefix_key = StorageKey.CreateSearchPrefix(context.ScriptHash, prefix); + byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); StorageIterator iterator = engine.AddDisposable(new StorageIterator(engine.Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.AsSpan().StartsWith(prefix)).GetEnumerator())); engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator)); return true; @@ -163,10 +158,9 @@ private static bool Storage_Delete(ApplicationEngine engine) { StorageContext context = _interface.GetInterface(); if (context.IsReadOnly) return false; - if (!CheckStorageContext(engine, context)) return false; StorageKey key = new StorageKey { - ScriptHash = context.ScriptHash, + Id = context.Id, Key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray() }; if (engine.Snapshot.Storages.TryGet(key)?.IsConstant == true) return false; diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 60c1d6c8db..b7c7f85251 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -28,6 +28,7 @@ public abstract class NativeContract public uint ServiceHash { get; } public byte[] Script { get; } public UInt160 Hash { get; } + public abstract int Id { get; } public ContractManifest Manifest { get; } public virtual string[] SupportedStandards { get; } = { "NEP-10" }; @@ -71,7 +72,7 @@ protected StorageKey CreateStorageKey(byte prefix, byte[] key = null) { StorageKey storageKey = new StorageKey { - ScriptHash = Hash, + Id = Id, Key = new byte[sizeof(byte) + (key?.Length ?? 0)] }; storageKey.Key[0] = prefix; diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 2e48520380..ee5f45376e 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -18,6 +18,7 @@ namespace Neo.SmartContract.Native public sealed class PolicyContract : NativeContract { public override string ServiceName => "Neo.Native.Policy"; + public override int Id => -3; private const byte Prefix_MaxTransactionsPerBlock = 23; private const byte Prefix_FeePerByte = 10; diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index daa57b6747..7e89bd02c2 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -16,6 +16,7 @@ namespace Neo.SmartContract.Native.Tokens public sealed class GasToken : Nep5Token { public override string ServiceName => "Neo.Native.Tokens.GAS"; + public override int Id => -2; public override string Name => "GAS"; public override string Symbol => "gas"; public override byte Decimals => 8; diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 699e09467b..aaea004edf 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -19,6 +19,7 @@ namespace Neo.SmartContract.Native.Tokens public sealed class NeoToken : Nep5Token { public override string ServiceName => "Neo.Native.Tokens.NEO"; + public override int Id => -1; public override string Name => "NEO"; public override string Symbol => "neo"; public override byte Decimals => 0; @@ -201,7 +202,7 @@ private StackItem GetRegisteredValidators(ApplicationEngine engine, Array args) public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetRegisteredValidators(StoreView snapshot) { - byte[] prefix_key = StorageKey.CreateSearchPrefix(Hash, new[] { Prefix_Validator }); + byte[] prefix_key = StorageKey.CreateSearchPrefix(Id, new[] { Prefix_Validator }); return snapshot.Storages.Find(prefix_key).Select(p => ( p.Key.Key.AsSerializable(1), diff --git a/src/neo/SmartContract/StorageContext.cs b/src/neo/SmartContract/StorageContext.cs index 9937caa4f8..2944583f38 100644 --- a/src/neo/SmartContract/StorageContext.cs +++ b/src/neo/SmartContract/StorageContext.cs @@ -2,7 +2,7 @@ namespace Neo.SmartContract { internal class StorageContext { - public UInt160 ScriptHash; + public int Id; public bool IsReadOnly; } } diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 97ebacfdbe..c7a0687729 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -915,7 +915,7 @@ private StorageKey CreateStorageKeyForNativeNeo(byte prefix) { StorageKey storageKey = new StorageKey { - ScriptHash = NativeContract.NEO.Hash, + Id = NativeContract.NEO.Id, Key = new byte[sizeof(byte)] }; storageKey.Key[0] = prefix; diff --git a/tests/neo.UnitTests/Ledger/UT_ContractState.cs b/tests/neo.UnitTests/Ledger/UT_ContractState.cs index 670f31d757..8f730a20c0 100644 --- a/tests/neo.UnitTests/Ledger/UT_ContractState.cs +++ b/tests/neo.UnitTests/Ledger/UT_ContractState.cs @@ -84,7 +84,7 @@ public void TestDeserialize() public void TestGetSize() { ISerializable newContract = contract; - newContract.Size.Should().Be(368); + newContract.Size.Should().Be(372); } [TestMethod] diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 38a456bcf3..86ad694641 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -485,8 +485,8 @@ public void TestUpdatePoolForBlockPersisted() }; var key1 = CreateStorageKey(Prefix_MaxTransactionsPerBlock); var key2 = CreateStorageKey(Prefix_FeePerByte); - key1.ScriptHash = NativeContract.Policy.Hash; - key2.ScriptHash = NativeContract.Policy.Hash; + key1.Id = NativeContract.Policy.Id; + key2.Id = NativeContract.Policy.Id; snapshot.Storages.Add(key1, item1); snapshot.Storages.Add(key2, item2); @@ -510,7 +510,7 @@ public StorageKey CreateStorageKey(byte prefix, byte[] key = null) { StorageKey storageKey = new StorageKey { - ScriptHash = null, + Id = 0, Key = new byte[sizeof(byte) + (key?.Length ?? 0)] }; storageKey.Key[0] = prefix; diff --git a/tests/neo.UnitTests/Ledger/UT_StorageKey.cs b/tests/neo.UnitTests/Ledger/UT_StorageKey.cs index 1941cbc0cc..e71cdba716 100644 --- a/tests/neo.UnitTests/Ledger/UT_StorageKey.cs +++ b/tests/neo.UnitTests/Ledger/UT_StorageKey.cs @@ -18,30 +18,30 @@ public void TestSetup() } [TestMethod] - public void ScriptHash_Get() + public void Id_Get() { - uut.ScriptHash.Should().BeNull(); + uut.Id.Should().Be(0); } [TestMethod] public void Size() { - var ut = new StorageKey() { Key = new byte[17], ScriptHash = UInt160.Zero }; + var ut = new StorageKey() { Key = new byte[17], Id = 0 }; ut.ToArray().Length.Should().Be(((ISerializable)ut).Size); - ut = new StorageKey() { Key = new byte[0], ScriptHash = UInt160.Zero }; + ut = new StorageKey() { Key = new byte[0], Id = 0 }; ut.ToArray().Length.Should().Be(((ISerializable)ut).Size); - ut = new StorageKey() { Key = new byte[16], ScriptHash = UInt160.Zero }; + ut = new StorageKey() { Key = new byte[16], Id = 0 }; ut.ToArray().Length.Should().Be(((ISerializable)ut).Size); } [TestMethod] - public void ScriptHash_Set() + public void Id_Set() { - UInt160 val = new UInt160(TestUtils.GetByteArray(20, 0x42)); - uut.ScriptHash = val; - uut.ScriptHash.Should().Be(val); + int val = 1; + uut.Id = val; + uut.Id.Should().Be(val); } [TestMethod] @@ -75,14 +75,14 @@ public void Equals_Null() [TestMethod] public void Equals_SameHash_SameKey() { - UInt160 val = new UInt160(TestUtils.GetByteArray(20, 0x42)); + int val = 0x42000000; byte[] keyVal = TestUtils.GetByteArray(10, 0x42); StorageKey newSk = new StorageKey { - ScriptHash = val, + Id = val, Key = keyVal }; - uut.ScriptHash = val; + uut.Id = val; uut.Key = keyVal; uut.Equals(newSk).Should().BeTrue(); @@ -91,14 +91,14 @@ public void Equals_SameHash_SameKey() [TestMethod] public void Equals_DiffHash_SameKey() { - UInt160 val = new UInt160(TestUtils.GetByteArray(20, 0x42)); + int val = 0x42000000; byte[] keyVal = TestUtils.GetByteArray(10, 0x42); StorageKey newSk = new StorageKey { - ScriptHash = val, + Id = val, Key = keyVal }; - uut.ScriptHash = new UInt160(TestUtils.GetByteArray(20, 0x88)); + uut.Id = 0x78000000; uut.Key = keyVal; uut.Equals(newSk).Should().BeFalse(); @@ -108,14 +108,14 @@ public void Equals_DiffHash_SameKey() [TestMethod] public void Equals_SameHash_DiffKey() { - UInt160 val = new UInt160(TestUtils.GetByteArray(20, 0x42)); + int val = 0x42000000; byte[] keyVal = TestUtils.GetByteArray(10, 0x42); StorageKey newSk = new StorageKey { - ScriptHash = val, + Id = val, Key = keyVal }; - uut.ScriptHash = val; + uut.Id = val; uut.Key = TestUtils.GetByteArray(10, 0x88); uut.Equals(newSk).Should().BeFalse(); @@ -124,9 +124,9 @@ public void Equals_SameHash_DiffKey() [TestMethod] public void GetHashCode_Get() { - uut.ScriptHash = new UInt160(TestUtils.GetByteArray(20, 0x42)); + uut.Id = 0x42000000; uut.Key = TestUtils.GetByteArray(10, 0x42); - uut.GetHashCode().Should().Be(806209853); + uut.GetHashCode().Should().Be(1374529787); } [TestMethod] @@ -143,13 +143,13 @@ public void TestDeserialize() using (BinaryWriter writer = new BinaryWriter(ms)) using (BinaryReader reader = new BinaryReader(ms)) { - uut.ScriptHash = new UInt160(TestUtils.GetByteArray(20, 0x42)); + uut.Id = 0x42000000; uut.Key = TestUtils.GetByteArray(10, 0x42); ((ISerializable)uut).Serialize(writer); ms.Seek(0, SeekOrigin.Begin); StorageKey dest = new StorageKey(); ((ISerializable)dest).Deserialize(reader); - dest.ScriptHash.Should().Be(uut.ScriptHash); + dest.Id.Should().Be(uut.Id); dest.Key.Should().BeEquivalentTo(uut.Key); } } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index 51caa8db9c..614322aa20 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -164,7 +164,7 @@ public void TestGetSysFeeAmount2() byte[] key = BitConverter.GetBytes(1); StorageKey storageKey = new StorageKey { - ScriptHash = NativeContract.GAS.Hash, + Id = NativeContract.GAS.Id, Key = new byte[sizeof(byte) + key.Length] }; storageKey.Key[0] = 15; diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 6111c9b052..6533b07541 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -704,7 +704,7 @@ internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) { StorageKey storageKey = new StorageKey { - ScriptHash = NativeContract.NEO.Hash, + Id = NativeContract.NEO.Id, Key = new byte[sizeof(byte) + (key?.Length ?? 0)] }; storageKey.Key[0] = prefix; diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs index 3cb10defe3..1e31914441 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs @@ -19,6 +19,8 @@ public class UT_Nep5Token public void TestTotalSupply() { var snapshot = Blockchain.Singleton.GetSnapshot(); + + TestNep5Token test = new TestNep5Token(); StorageItem item = new StorageItem { Value = new byte[] { 0x01 } @@ -33,10 +35,9 @@ public void TestTotalSupply() script = sb.ToArray(); } var Hash = script.ToScriptHash(); - key.ScriptHash = Hash; + key.Id = test.Id; snapshot.Storages.Add(key, item); - TestNep5Token test = new TestNep5Token(); ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); StackItem stackItem = test.TotalSupply(ae, null); stackItem.GetBigInteger().Should().Be(1); @@ -65,7 +66,7 @@ public void TestTotalSupplyDecimal() script = sb.ToArray(); } var Hash = script.ToScriptHash(); - key.ScriptHash = Hash; + key.Id = test.Id; snapshot.Storages.Add(key, item); @@ -78,7 +79,7 @@ public StorageKey CreateStorageKey(byte prefix, byte[] key = null) { StorageKey storageKey = new StorageKey { - ScriptHash = null, + Id = 0, Key = new byte[sizeof(byte) + (key?.Length ?? 0)] }; storageKey.Key[0] = prefix; @@ -89,6 +90,8 @@ public StorageKey CreateStorageKey(byte prefix, byte[] key = null) public class TestNep5Token : Nep5Token { + public override int Id => 0x10000005; + public override string Name => throw new NotImplementedException(); public override string Symbol => throw new NotImplementedException(); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 71cfd3c093..2252e60d8a 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -89,6 +89,9 @@ public void TestTestCall() public class TestNativeContract : NativeContract { public override string ServiceName => "test"; + + public override int Id => 0x10000006; + public StackItem TestOnPersist(ApplicationEngine engine, VMArray args) { return OnPersist(engine, args); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 0b15d0732d..a173585182 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -234,7 +234,7 @@ public void TestCheckPolicy() StorageKey storageKey = new StorageKey { - ScriptHash = NativeContract.Policy.Hash, + Id = NativeContract.Policy.Id, Key = new byte[sizeof(byte)] }; storageKey.Key[0] = 15; diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 959eeecae8..814f55cb67 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -229,7 +229,7 @@ public void TestContract_Update() var storageKey = new StorageKey { - ScriptHash = state.ScriptHash, + Id = state.Id, Key = new byte[] { 0x01 } }; snapshot.Contracts.Add(state.ScriptHash, state); @@ -239,18 +239,6 @@ public void TestContract_Update() engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeTrue(); - - // Remove Storage flag with something stored - - state.Manifest.Features = ContractFeatures.NoProperty; - snapshot.Contracts.Add(state.ScriptHash, state); - snapshot.Storages.Add(storageKey, storageItem); - - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); - engine.LoadScript(state.Script); - engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); - engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeFalse(); } [TestMethod] @@ -267,7 +255,7 @@ public void TestStorage_Find() }; var storageKey = new StorageKey { - ScriptHash = state.ScriptHash, + Id = state.Id, Key = new byte[] { 0x01 } }; snapshot.Contracts.Add(state.ScriptHash, state); @@ -278,7 +266,7 @@ public void TestStorage_Find() engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext { - ScriptHash = state.ScriptHash, + Id = state.Id, IsReadOnly = false })); InteropService.Invoke(engine, InteropService.Storage.Find).Should().BeTrue(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 1fa676af60..7ad570ef0f 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -480,20 +480,26 @@ public void TestBlockchain_GetContract() [TestMethod] public void TestStorage_GetContext() { - var engine = GetEngine(); + var engine = GetEngine(false, true); + var state = TestUtils.GetContract(); + state.Manifest.Features = ContractFeatures.HasStorage; + engine.Snapshot.Contracts.Add(state.ScriptHash, state); + engine.LoadScript(state.Script); InteropService.Invoke(engine, InteropService.Storage.GetContext).Should().BeTrue(); var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); - ret.GetInterface().ScriptHash.Should().Be(engine.CurrentScriptHash); ret.GetInterface().IsReadOnly.Should().BeFalse(); } [TestMethod] public void TestStorage_GetReadOnlyContext() { - var engine = GetEngine(); + var engine = GetEngine(false, true); + var state = TestUtils.GetContract(); + state.Manifest.Features = ContractFeatures.HasStorage; + engine.Snapshot.Contracts.Add(state.ScriptHash, state); + engine.LoadScript(state.Script); InteropService.Invoke(engine, InteropService.Storage.GetReadOnlyContext).Should().BeTrue(); var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); - ret.GetInterface().ScriptHash.Should().Be(engine.CurrentScriptHash); ret.GetInterface().IsReadOnly.Should().BeTrue(); } @@ -506,7 +512,7 @@ public void TestStorage_Get() var storageKey = new StorageKey { - ScriptHash = state.ScriptHash, + Id = state.Id, Key = new byte[] { 0x01 } }; @@ -523,25 +529,11 @@ public void TestStorage_Get() engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext { - ScriptHash = state.ScriptHash, + Id = state.Id, IsReadOnly = false })); InteropService.Invoke(engine, InteropService.Storage.Get).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); - - snapshot.Contracts.Delete(state.ScriptHash); - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); - engine.LoadScript(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext - { - ScriptHash = state.ScriptHash, - IsReadOnly = false - })); - InteropService.Invoke(engine, InteropService.Storage.Get).Should().BeFalse(); - - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Storage.Get).Should().BeFalse(); } [TestMethod] @@ -559,11 +551,11 @@ public void TestStorage_Put() var state = TestUtils.GetContract(); var storageContext = new StorageContext { - ScriptHash = state.ScriptHash, + Id = state.Id, IsReadOnly = false }; engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeTrue(); //key.Length > MaxStorageKeySize key = new byte[InteropService.Storage.MaxKeySize + 1]; @@ -596,7 +588,7 @@ public void TestStorage_Put() var storageKey = new StorageKey { - ScriptHash = state.ScriptHash, + Id = state.Id, Key = new byte[] { 0x01 } }; var storageItem = new StorageItem @@ -644,7 +636,7 @@ public void TestStorage_PutEx() state.Manifest.Features = ContractFeatures.HasStorage; var storageKey = new StorageKey { - ScriptHash = new UInt160(TestUtils.GetByteArray(20, 0x42)), + Id = 0x42000000, Key = new byte[] { 0x01 } }; var storageItem = new StorageItem @@ -660,7 +652,7 @@ public void TestStorage_PutEx() var value = new byte[] { 0x02 }; var storageContext = new StorageContext { - ScriptHash = state.ScriptHash, + Id = state.Id, IsReadOnly = false }; engine.CurrentContext.EvaluationStack.Push((int)StorageFlags.None); @@ -683,7 +675,7 @@ public void TestStorage_Delete() state.Manifest.Features = ContractFeatures.HasStorage; var storageKey = new StorageKey { - ScriptHash = new UInt160(TestUtils.GetByteArray(20, 0x42)), + Id = 0x42000000, Key = new byte[] { 0x01 } }; var storageItem = new StorageItem @@ -699,7 +691,7 @@ public void TestStorage_Delete() var key = new byte[] { 0x01 }; var storageContext = new StorageContext { - ScriptHash = state.ScriptHash, + Id = state.Id, IsReadOnly = false }; engine.CurrentContext.EvaluationStack.Push(key); @@ -711,13 +703,6 @@ public void TestStorage_Delete() engine.CurrentContext.EvaluationStack.Push(key); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeFalse(); - - //CheckStorageContext fail - storageContext.IsReadOnly = false; - state.Manifest.Features = ContractFeatures.NoProperty; - engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeFalse(); } [TestMethod] @@ -730,7 +715,7 @@ public void TestStorageContext_AsReadOnly() var state = TestUtils.GetContract(); var storageContext = new StorageContext { - ScriptHash = state.ScriptHash, + Id = state.Id, IsReadOnly = false }; engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); @@ -855,7 +840,7 @@ public void TestContract_Destroy() var storageKey = new StorageKey { - ScriptHash = scriptHash, + Id = 0x43000000, Key = new byte[] { 0x01 } }; snapshot.Contracts.Add(scriptHash, state); diff --git a/tests/neo.UnitTests/SmartContract/UT_StorageContext.cs b/tests/neo.UnitTests/SmartContract/UT_StorageContext.cs deleted file mode 100644 index d1210169dd..0000000000 --- a/tests/neo.UnitTests/SmartContract/UT_StorageContext.cs +++ /dev/null @@ -1,23 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.SmartContract; - -namespace Neo.UnitTests.SmartContract -{ - [TestClass] - public class UT_StorageContext - { - [TestMethod] - public void TestToArray() - { - UInt160 script_hash = new byte[] { 0x00 }.ToScriptHash(); - StorageContext context = new StorageContext() - { - ScriptHash = script_hash, - IsReadOnly = true - }; - context.ScriptHash.ToArray().Should().BeEquivalentTo(new byte[] { 0x00 }.ToScriptHash().ToArray()); - } - } -} diff --git a/tests/neo.UnitTests/UT_DataCache.cs b/tests/neo.UnitTests/UT_DataCache.cs index 0cee679316..4b8ad1de74 100644 --- a/tests/neo.UnitTests/UT_DataCache.cs +++ b/tests/neo.UnitTests/UT_DataCache.cs @@ -21,40 +21,40 @@ public void TestCachedFind_Between() var storages = snapshot.Storages; var cache = new CloneCache(storages); - storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + storages.DeleteWhere((k, v) => k.Id == 0); storages.Add ( - new StorageKey() { Key = new byte[] { 0x01, 0x01 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); storages.Add ( - new StorageKey() { Key = new byte[] { 0x00, 0x01 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x00, 0x01 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); storages.Add ( - new StorageKey() { Key = new byte[] { 0x00, 0x03 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x00, 0x03 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); cache.Add ( - new StorageKey() { Key = new byte[] { 0x01, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x01, 0x02 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); cache.Add ( - new StorageKey() { Key = new byte[] { 0x00, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x00, 0x02 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); CollectionAssert.AreEqual( - cache.Find(new byte[21]).Select(u => u.Key.Key[1]).ToArray(), + cache.Find(new byte[5]).Select(u => u.Key.Key[1]).ToArray(), new byte[] { 0x01, 0x02, 0x03 } ); - storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + storages.DeleteWhere((k, v) => k.Id == 0); } [TestMethod] @@ -64,35 +64,33 @@ public void TestCachedFind_Last() var storages = snapshot.Storages; var cache = new CloneCache(storages); - storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + storages.DeleteWhere((k, v) => k.Id == 0); storages.Add ( - new StorageKey() { Key = new byte[] { 0x00, 0x01 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x00, 0x01 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); storages.Add ( - new StorageKey() { Key = new byte[] { 0x01, 0x01 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); cache.Add ( - new StorageKey() { Key = new byte[] { 0x00, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x00, 0x02 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); cache.Add ( - new StorageKey() { Key = new byte[] { 0x01, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x01, 0x02 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); - - CollectionAssert.AreEqual( - cache.Find(new byte[21]).Select(u => u.Key.Key[1]).ToArray(), + CollectionAssert.AreEqual(cache.Find(new byte[5]).Select(u => u.Key.Key[1]).ToArray(), new byte[] { 0x01, 0x02 } ); - storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + storages.DeleteWhere((k, v) => k.Id == 0); } [TestMethod] @@ -102,25 +100,25 @@ public void TestCachedFind_Empty() var storages = snapshot.Storages; var cache = new CloneCache(storages); - storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + storages.DeleteWhere((k, v) => k.Id == 0); cache.Add ( - new StorageKey() { Key = new byte[] { 0x00, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x00, 0x02 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); cache.Add ( - new StorageKey() { Key = new byte[] { 0x01, 0x02 }, ScriptHash = UInt160.Zero }, + new StorageKey() { Key = new byte[] { 0x01, 0x02 }, Id = 0 }, new StorageItem() { IsConstant = false, Value = new byte[] { } } ); CollectionAssert.AreEqual( - cache.Find(new byte[21]).Select(u => u.Key.Key[1]).ToArray(), + cache.Find(new byte[5]).Select(u => u.Key.Key[1]).ToArray(), new byte[] { 0x02 } ); - storages.DeleteWhere((k, v) => k.ScriptHash == UInt160.Zero); + storages.DeleteWhere((k, v) => k.Id == 0); } } } From 6a5569ab69ce7c21abf5c5eac5540fb3ff8c646d Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 11 Feb 2020 10:22:23 +0100 Subject: [PATCH 203/305] Fix dotnet-format (#1443) --- .github/workflows/main.yml | 2 +- tests/neo.UnitTests/IO/UT_ByteArrayComparer.cs | 2 +- tests/neo.UnitTests/Ledger/UT_HashIndexState.cs | 2 +- tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs | 2 +- tests/neo.UnitTests/Ledger/UT_TransactionState.cs | 2 +- .../SmartContract/Manifest/UT_ContractEventDescriptor.cs | 2 +- .../SmartContract/Manifest/UT_ContractPermissionDescriptor.cs | 2 +- .../SmartContract/Manifest/UT_WildCardContainer.cs | 2 +- tests/neo.UnitTests/SmartContract/UT_NefFile.cs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f53a6b53f2..a4b5d0a845 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,7 @@ jobs: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Check format run: | - dotnet tool install --tool-path ./ dotnet-format + dotnet tool install --version 3.2.111002 --tool-path ./ dotnet-format --add-source https://dotnet.myget.org/F/format/api/v3/index.json ./dotnet-format --check --dry-run -v diagnostic - name: Test run: | diff --git a/tests/neo.UnitTests/IO/UT_ByteArrayComparer.cs b/tests/neo.UnitTests/IO/UT_ByteArrayComparer.cs index dab1adcd8b..562e2b6f6e 100644 --- a/tests/neo.UnitTests/IO/UT_ByteArrayComparer.cs +++ b/tests/neo.UnitTests/IO/UT_ByteArrayComparer.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; diff --git a/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs b/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs index bfc85479dd..b7a0de93c8 100644 --- a/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs +++ b/tests/neo.UnitTests/Ledger/UT_HashIndexState.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Ledger; diff --git a/tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs b/tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs index 2f198f4c6c..482c620a00 100644 --- a/tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs +++ b/tests/neo.UnitTests/Ledger/UT_HeaderHashList.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Ledger; diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionState.cs b/tests/neo.UnitTests/Ledger/UT_TransactionState.cs index 7abf431849..11ef772556 100644 --- a/tests/neo.UnitTests/Ledger/UT_TransactionState.cs +++ b/tests/neo.UnitTests/Ledger/UT_TransactionState.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.Ledger; diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs index abbc4af5a0..a829354e34 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractEventDescriptor.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Manifest; namespace Neo.UnitTests.SmartContract.Manifest diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs index 299ff2e49d..972911b296 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermissionDescriptor.cs @@ -1,4 +1,4 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract.Manifest; using Neo.Wallets; using System.Security.Cryptography; diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs index 9a6743e4a5..f983d65f09 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_WildCardContainer.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; using Neo.SmartContract.Manifest; diff --git a/tests/neo.UnitTests/SmartContract/UT_NefFile.cs b/tests/neo.UnitTests/SmartContract/UT_NefFile.cs index 23d8040c79..36a988d8b8 100644 --- a/tests/neo.UnitTests/SmartContract/UT_NefFile.cs +++ b/tests/neo.UnitTests/SmartContract/UT_NefFile.cs @@ -1,4 +1,4 @@ -using FluentAssertions; +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; using Neo.SmartContract; From 862aa7ac890f37a7537f2cadb5e1d91fce541885 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 21 Feb 2020 10:39:35 +0100 Subject: [PATCH 204/305] Add Salt and NEP2 to db3 wallet (#1444) * NEP2 * Clean code * Use MasterKey as passphrase * Add Salt * Unique Scrypt for all wallets * Clean using * Clean parameters * Fix UT * Random Salt --- src/neo/Wallets/SQLite/Account.cs | 2 +- src/neo/Wallets/SQLite/UserWallet.cs | 67 +++++++++++-------- .../Wallets/SQLite/VerificationContract.cs | 1 - src/neo/Wallets/SQLite/WalletDataContext.cs | 2 +- .../Wallets/SQLite/UT_Account.cs | 6 +- 5 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/neo/Wallets/SQLite/Account.cs b/src/neo/Wallets/SQLite/Account.cs index ce8ed1e01e..77aef83d93 100644 --- a/src/neo/Wallets/SQLite/Account.cs +++ b/src/neo/Wallets/SQLite/Account.cs @@ -2,7 +2,7 @@ namespace Neo.Wallets.SQLite { internal class Account { - public byte[] PrivateKeyEncrypted { get; set; } public byte[] PublicKeyHash { get; set; } + public string Nep2key { get; set; } } } diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs index 53d0795708..36b865a2e3 100644 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ b/src/neo/Wallets/SQLite/UserWallet.cs @@ -2,6 +2,7 @@ using Neo.Cryptography; using Neo.IO; using Neo.SmartContract; +using Neo.Wallets.NEP6; using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -10,6 +11,7 @@ using System.Reflection; using System.Security; using System.Security.Cryptography; +using System.Text; namespace Neo.Wallets.SQLite { @@ -18,7 +20,9 @@ public class UserWallet : Wallet private readonly object db_lock = new object(); private readonly string path; private readonly byte[] iv; + private readonly byte[] salt; private readonly byte[] masterKey; + private readonly ScryptParameters scrypt; private readonly Dictionary accounts; public override string Name => Path.GetFileNameWithoutExtension(path); @@ -37,38 +41,60 @@ public override Version Version } } - private UserWallet(string path, byte[] passwordKey, bool create) + /// + /// Constructor + /// + /// Path + /// Password Key + /// True for create the wallet + /// Scrypt initialization value (only if create=True) + private UserWallet(string path, byte[] passwordKey, bool create, ScryptParameters scrypt = null) { this.path = path; + if (create) { this.iv = new byte[16]; + this.salt = new byte[20]; this.masterKey = new byte[32]; + this.scrypt = scrypt ?? ScryptParameters.Default; this.accounts = new Dictionary(); using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) { rng.GetBytes(iv); + rng.GetBytes(salt); rng.GetBytes(masterKey); } Version version = Assembly.GetExecutingAssembly().GetName().Version; BuildDatabase(); - SaveStoredData("PasswordHash", passwordKey.Sha256()); SaveStoredData("IV", iv); + SaveStoredData("Salt", salt); + SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); SaveStoredData("MasterKey", masterKey.AesEncrypt(passwordKey, iv)); SaveStoredData("Version", new[] { version.Major, version.Minor, version.Build, version.Revision }.Select(p => BitConverter.GetBytes(p)).SelectMany(p => p).ToArray()); + SaveStoredData("ScryptN", BitConverter.GetBytes(this.scrypt.N)); + SaveStoredData("ScryptR", BitConverter.GetBytes(this.scrypt.R)); + SaveStoredData("ScryptP", BitConverter.GetBytes(this.scrypt.P)); } else { + this.salt = LoadStoredData("Salt"); byte[] passwordHash = LoadStoredData("PasswordHash"); - if (passwordHash != null && !passwordHash.SequenceEqual(passwordKey.Sha256())) + if (passwordHash != null && !passwordHash.SequenceEqual(passwordKey.Concat(salt).ToArray().Sha256())) throw new CryptographicException(); this.iv = LoadStoredData("IV"); this.masterKey = LoadStoredData("MasterKey").AesDecrypt(passwordKey, iv); + this.scrypt = new ScryptParameters + ( + BitConverter.ToInt32(LoadStoredData("ScryptN")), + BitConverter.ToInt32(LoadStoredData("ScryptR")), + BitConverter.ToInt32(LoadStoredData("ScryptP")) + ); this.accounts = LoadAccounts(); } } - private void AddAccount(UserWalletAccount account, bool is_import) + private void AddAccount(UserWalletAccount account) { lock (accounts) { @@ -86,23 +112,19 @@ private void AddAccount(UserWalletAccount account, bool is_import) { if (account.HasKey) { - byte[] decryptedPrivateKey = new byte[96]; - Buffer.BlockCopy(account.Key.PublicKey.EncodePoint(false), 1, decryptedPrivateKey, 0, 64); - Buffer.BlockCopy(account.Key.PrivateKey, 0, decryptedPrivateKey, 64, 32); - byte[] encryptedPrivateKey = EncryptPrivateKey(decryptedPrivateKey); - Array.Clear(decryptedPrivateKey, 0, decryptedPrivateKey.Length); + string passphrase = Encoding.UTF8.GetString(masterKey); Account db_account = ctx.Accounts.FirstOrDefault(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); if (db_account == null) { db_account = ctx.Accounts.Add(new Account { - PrivateKeyEncrypted = encryptedPrivateKey, + Nep2key = account.Key.Export(passphrase, scrypt.N, scrypt.R, scrypt.P), PublicKeyHash = account.Key.PublicKeyHash.ToArray() }).Entity; } else { - db_account.PrivateKeyEncrypted = encryptedPrivateKey; + db_account.Nep2key = account.Key.Export(passphrase, scrypt.N, scrypt.R, scrypt.P); } } if (account.Contract != null) @@ -193,7 +215,7 @@ public override WalletAccount CreateAccount(byte[] privateKey) Key = key, Contract = contract }; - AddAccount(account, false); + AddAccount(account); return account; } @@ -213,24 +235,17 @@ public override WalletAccount CreateAccount(SmartContract.Contract contract, Key Key = key, Contract = verification_contract }; - AddAccount(account, false); + AddAccount(account); return account; } public override WalletAccount CreateAccount(UInt160 scriptHash) { UserWalletAccount account = new UserWalletAccount(scriptHash); - AddAccount(account, true); + AddAccount(account); return account; } - private byte[] DecryptPrivateKey(byte[] encryptedPrivateKey) - { - if (encryptedPrivateKey == null) throw new ArgumentNullException(nameof(encryptedPrivateKey)); - if (encryptedPrivateKey.Length != 96) throw new ArgumentException(); - return encryptedPrivateKey.AesDecrypt(masterKey, iv); - } - public override bool DeleteAccount(UInt160 scriptHash) { UserWalletAccount account; @@ -266,11 +281,6 @@ public override bool DeleteAccount(UInt160 scriptHash) return false; } - private byte[] EncryptPrivateKey(byte[] decryptedPrivateKey) - { - return decryptedPrivateKey.AesEncrypt(masterKey, iv); - } - public override WalletAccount GetAccount(UInt160 scriptHash) { lock (accounts) @@ -293,13 +303,14 @@ public override IEnumerable GetAccounts() { using (WalletDataContext ctx = new WalletDataContext(path)) { + string passphrase = Encoding.UTF8.GetString(masterKey); Dictionary accounts = ctx.Addresses.Select(p => p.ScriptHash).AsEnumerable().Select(p => new UserWalletAccount(new UInt160(p))).ToDictionary(p => p.ScriptHash); foreach (Contract db_contract in ctx.Contracts.Include(p => p.Account)) { VerificationContract contract = db_contract.RawData.AsSerializable(); UserWalletAccount account = accounts[contract.ScriptHash]; account.Contract = contract; - account.Key = new KeyPair(DecryptPrivateKey(db_contract.Account.PrivateKeyEncrypted)); + account.Key = new KeyPair(GetPrivateKeyFromNEP2(db_contract.Account.Nep2key, passphrase, scrypt.N, scrypt.R, scrypt.P)); } return accounts; } @@ -352,7 +363,7 @@ private static void SaveStoredData(WalletDataContext ctx, string name, byte[] va public override bool VerifyPassword(string password) { - return password.ToAesKey().Sha256().SequenceEqual(LoadStoredData("PasswordHash")); + return password.ToAesKey().Concat(salt).ToArray().Sha256().SequenceEqual(LoadStoredData("PasswordHash")); } } } diff --git a/src/neo/Wallets/SQLite/VerificationContract.cs b/src/neo/Wallets/SQLite/VerificationContract.cs index b195f50ca5..9e9016b96b 100644 --- a/src/neo/Wallets/SQLite/VerificationContract.cs +++ b/src/neo/Wallets/SQLite/VerificationContract.cs @@ -1,6 +1,5 @@ using Neo.IO; using Neo.SmartContract; -using Neo.VM; using System; using System.IO; using System.Linq; diff --git a/src/neo/Wallets/SQLite/WalletDataContext.cs b/src/neo/Wallets/SQLite/WalletDataContext.cs index 6d1ec957a8..efc22b58d9 100644 --- a/src/neo/Wallets/SQLite/WalletDataContext.cs +++ b/src/neo/Wallets/SQLite/WalletDataContext.cs @@ -32,7 +32,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) base.OnModelCreating(modelBuilder); modelBuilder.Entity().ToTable(nameof(Account)); modelBuilder.Entity().HasKey(p => p.PublicKeyHash); - modelBuilder.Entity().Property(p => p.PrivateKeyEncrypted).HasColumnType("VarBinary").HasMaxLength(96).IsRequired(); + modelBuilder.Entity().Property(p => p.Nep2key).HasColumnType("VarChar").HasMaxLength(byte.MaxValue).IsRequired(); modelBuilder.Entity().Property(p => p.PublicKeyHash).HasColumnType("Binary").HasMaxLength(20).IsRequired(); modelBuilder.Entity
().ToTable(nameof(Address)); modelBuilder.Entity
().HasKey(p => p.ScriptHash); diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_Account.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_Account.cs index 0fabb80b1e..d04a31e7e1 100644 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_Account.cs +++ b/tests/neo.UnitTests/Wallets/SQLite/UT_Account.cs @@ -15,13 +15,13 @@ public void TestGenerator() } [TestMethod] - public void TestSetAndGetPrivateKeyEncrypted() + public void TestSetAndGetNep2key() { Account account = new Account { - PrivateKeyEncrypted = new byte[] { 0x01 } + Nep2key = "123" }; - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x01 }), Encoding.Default.GetString(account.PrivateKeyEncrypted)); + Assert.AreEqual("123", account.Nep2key); } [TestMethod] From 8fd6ade0ffb136a825ac5078ed801e5c09a885e0 Mon Sep 17 00:00:00 2001 From: Krain Chen Date: Wed, 26 Feb 2020 22:26:36 +0800 Subject: [PATCH 205/305] fix ToStackItem issue (#1427) --- src/neo/VM/Helper.cs | 7 ++++--- tests/neo.UnitTests/VM/UT_Helper.cs | 24 +++++++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 83b4aaf3e2..675d4be0d1 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -225,6 +225,7 @@ public static ContractParameter ToParameter(this StackItem item) private static ContractParameter ToParameter(StackItem item, List<(StackItem, ContractParameter)> context) { + if (item is null) throw new ArgumentNullException(); ContractParameter parameter = null; switch (item) { @@ -286,7 +287,7 @@ private static ContractParameter ToParameter(StackItem item, List<(StackItem, Co }; break; default: - throw new ArgumentException(); + throw new ArgumentException($"StackItemType({item.Type}) is not supported to ContractParameter."); } return parameter; } @@ -298,6 +299,8 @@ public static StackItem ToStackItem(this ContractParameter parameter) private static StackItem ToStackItem(ContractParameter parameter, List<(StackItem, ContractParameter)> context) { + if (parameter is null) throw new ArgumentNullException(); + if (parameter.Value is null) return StackItem.Null; StackItem stackItem = null; switch (parameter.Type) { @@ -348,8 +351,6 @@ private static StackItem ToStackItem(ContractParameter parameter, List<(StackIte case ContractParameterType.String: stackItem = (string)parameter.Value; break; - case ContractParameterType.InteropInterface: - break; default: throw new ArgumentException($"ContractParameterType({parameter.Type}) is not supported to StackItem."); } diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 809e00c94d..1a749bc567 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -112,8 +112,8 @@ public void TestToParameter() StackItem intItem = new BigInteger(1000); Assert.AreEqual(1000, (BigInteger)intItem.ToParameter().Value); - StackItem interopItem = new VM.Types.InteropInterface("test"); - Assert.AreEqual(null, interopItem.ToParameter().Value); + StackItem interopItem = new InteropInterface("test"); + Assert.AreEqual(ContractParameterType.InteropInterface, interopItem.ToParameter().Type); StackItem arrayItem = new VM.Types.Array(new[] { byteItem, boolItem, intItem, interopItem }); Assert.AreEqual(1000, (BigInteger)(arrayItem.ToParameter().Value as List)[2].Value); @@ -125,6 +125,9 @@ public void TestToParameter() [TestMethod] public void TestToStackItem() { + ContractParameter parameter = null; + Assert.ThrowsException(() => parameter.ToStackItem()); + ContractParameter byteParameter = new ContractParameter { Type = ContractParameterType.ByteArray, Value = "00e057eb481b".HexToBytes() }; Assert.AreEqual(30000000000000L, (long)byteParameter.ToStackItem().GetBigInteger()); @@ -146,10 +149,13 @@ public void TestToStackItem() ContractParameter strParameter = new ContractParameter { Type = ContractParameterType.String, Value = "test😂👍" }; Assert.AreEqual("test😂👍", strParameter.ToStackItem().GetString()); - ContractParameter interopParameter = new ContractParameter { Type = ContractParameterType.InteropInterface }; - Assert.AreEqual(null, interopParameter.ToStackItem()); + ContractParameter interopParameter = new ContractParameter { Type = ContractParameterType.InteropInterface, Value = new object() }; + Assert.ThrowsException(() => interopParameter.ToStackItem()); + + ContractParameter interopParameter2 = new ContractParameter { Type = ContractParameterType.InteropInterface }; + Assert.AreEqual(StackItem.Null, interopParameter2.ToStackItem()); - ContractParameter arrayParameter = new ContractParameter { Type = ContractParameterType.Array, Value = new[] { byteParameter, boolParameter, intParameter, h160Parameter, h256Parameter, pkParameter, strParameter, interopParameter }.ToList() }; + ContractParameter arrayParameter = new ContractParameter { Type = ContractParameterType.Array, Value = new[] { byteParameter, boolParameter, intParameter, h160Parameter, h256Parameter, pkParameter, strParameter }.ToList() }; Assert.AreEqual(1000, ((VM.Types.Array)arrayParameter.ToStackItem())[2].GetBigInteger()); ContractParameter mapParameter = new ContractParameter { Type = ContractParameterType.Map, Value = new[] { new KeyValuePair(byteParameter, pkParameter) } }; @@ -486,9 +492,13 @@ public void TestToParameter2() TestToParameter2ByteArray(); TestToParameter2Integer(); TestToParameter2InteropInterface(); + TestToParameterNull(); + } - Action action = () => VM.Helper.ToParameter(null); - action.Should().Throw(); + private void TestToParameterNull() + { + StackItem item = null; + Assert.ThrowsException(() => item.ToParameter()); } private void TestToParameter2InteropInterface() From 01cfd33d4571af94fb702bbf73336c34a57783ea Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 27 Feb 2020 20:20:46 +0100 Subject: [PATCH 206/305] Allow MultiSig contracts in Wallet.Sign method (#1451) * Allow to sign multisignature account if you have some of them * Update IsMultiSigContract() * Fixes UT * Check null * Update Wallet.cs * Change if Co-authored-by: Erik Zhang --- .../ContractParametersContext.cs | 20 +---------- src/neo/SmartContract/Helper.cs | 30 +++++++++++++++- src/neo/Wallets/Wallet.cs | 35 ++++++++++++++++--- .../SmartContract/UT_SmartContractHelper.cs | 15 +++++--- 4 files changed, 71 insertions(+), 29 deletions(-) diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index f57f1f9679..d87ec74967 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -130,7 +130,7 @@ public bool Add(Contract contract, params object[] parameters) public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) { - if (contract.Script.IsMultiSigContract(out _, out _)) + if (contract.Script.IsMultiSigContract(out _, out ECPoint[] points)) { ContextItem item = CreateItem(contract); if (item == null) return false; @@ -139,24 +139,6 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) item.Signatures = new Dictionary(); else if (item.Signatures.ContainsKey(pubkey)) return false; - List points = new List(); - { - int i = 0; - switch (contract.Script[i++]) - { - case (byte)OpCode.PUSHINT8: - ++i; - break; - case (byte)OpCode.PUSHINT16: - i += 2; - break; - } - while (contract.Script[i++] == (byte)OpCode.PUSHDATA1) - { - points.Add(ECPoint.DecodePoint(contract.Script.AsSpan(++i, 33), ECCurve.Secp256r1)); - i += 33; - } - } if (!points.Contains(pubkey)) return false; item.Signatures.Add(pubkey, signature); if (item.Signatures.Count == contract.ParameterList.Length) diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index faac766baf..4823832fab 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -1,17 +1,44 @@ using Neo.Cryptography; +using Neo.Cryptography.ECC; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.VM; using Neo.VM.Types; using System; using System.Buffers.Binary; +using System.Collections.Generic; using System.Text; namespace Neo.SmartContract { public static class Helper { + public static bool IsMultiSigContract(this byte[] script) + { + return IsMultiSigContract(script, out _, out _, null); + } + public static bool IsMultiSigContract(this byte[] script, out int m, out int n) + { + return IsMultiSigContract(script, out m, out n, null); + } + + public static bool IsMultiSigContract(this byte[] script, out int m, out ECPoint[] points) + { + List list = new List(); + if (IsMultiSigContract(script, out m, out _, list)) + { + points = list.ToArray(); + return true; + } + else + { + points = null; + return false; + } + } + + private static bool IsMultiSigContract(byte[] script, out int m, out int n, List points) { m = 0; n = 0; int i = 0; @@ -38,6 +65,7 @@ public static bool IsMultiSigContract(this byte[] script, out int m, out int n) { if (script.Length <= i + 35) return false; if (script[++i] != 33) return false; + points?.Add(ECPoint.DecodePoint(script.AsSpan(i + 1, 33), ECCurve.Secp256r1)); i += 34; ++n; } @@ -81,7 +109,7 @@ public static bool IsSignatureContract(this byte[] script) public static bool IsStandardContract(this byte[] script) { - return script.IsSignatureContract() || script.IsMultiSigContract(out _, out _); + return script.IsSignatureContract() || script.IsMultiSigContract(); } public static uint ToInteropMethodHash(this string method) diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 892cc40c1c..3fe4c61652 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -382,10 +382,37 @@ public bool Sign(ContractParametersContext context) foreach (UInt160 scriptHash in context.ScriptHashes) { WalletAccount account = GetAccount(scriptHash); - if (account?.HasKey != true) continue; - KeyPair key = account.GetKey(); - byte[] signature = context.Verifiable.Sign(key); - fSuccess |= context.AddSignature(account.Contract, key.PublicKey, signature); + if (account is null) continue; + + // Try to sign self-contained multiSig + + Contract multiSigContract = account.Contract; + + if (multiSigContract != null && + multiSigContract.Script.IsMultiSigContract(out int m, out ECPoint[] points)) + { + foreach (var point in points) + { + account = GetAccount(point); + if (account?.HasKey != true) continue; + KeyPair key = account.GetKey(); + byte[] signature = context.Verifiable.Sign(key); + fSuccess |= context.AddSignature(multiSigContract, key.PublicKey, signature); + if (fSuccess) m--; + if (context.Completed || m <= 0) break; + } + } + else + { + // Try to sign with regular accounts + + if (account.HasKey) + { + KeyPair key = account.GetKey(); + byte[] signature = context.Verifiable.Sign(key); + fSuccess |= context.AddSignature(account.Contract, key.PublicKey, signature); + } + } } return fSuccess; } diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index dc7729f626..8be6866885 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -4,8 +4,10 @@ using Neo.SmartContract; using Neo.Wallets; using System; +using System.Linq; using System.Security.Cryptography; using System.Text; +using ECPoint = Neo.Cryptography.ECC.ECPoint; namespace Neo.UnitTests.SmartContract { @@ -25,7 +27,8 @@ public void TestIsMultiSigContract() publicKeys1[i] = key1.PublicKey; } byte[] script1 = Contract.CreateMultiSigRedeemScript(20, publicKeys1); - Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script1, out int m1, out int n1)); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script1, out _, out ECPoint[] p1)); + CollectionAssert.AreEqual(publicKeys1.OrderBy(p => p).ToArray(), p1); Neo.Cryptography.ECC.ECPoint[] publicKeys2 = new Neo.Cryptography.ECC.ECPoint[256]; for (int i = 0; i < 256; i++) @@ -37,7 +40,8 @@ public void TestIsMultiSigContract() publicKeys2[i] = key2.PublicKey; } byte[] script2 = Contract.CreateMultiSigRedeemScript(256, publicKeys2); - Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script2, out int m2, out int n2)); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script2, out _, out ECPoint[] p2)); + CollectionAssert.AreEqual(publicKeys2.OrderBy(p => p).ToArray(), p2); Neo.Cryptography.ECC.ECPoint[] publicKeys3 = new Neo.Cryptography.ECC.ECPoint[3]; for (int i = 0; i < 3; i++) @@ -49,7 +53,8 @@ public void TestIsMultiSigContract() publicKeys3[i] = key3.PublicKey; } byte[] script3 = Contract.CreateMultiSigRedeemScript(3, publicKeys3); - Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script3, out int m3, out int n3)); + Assert.AreEqual(true, Neo.SmartContract.Helper.IsMultiSigContract(script3, out _, out ECPoint[] p3)); + CollectionAssert.AreEqual(publicKeys3.OrderBy(p => p).ToArray(), p3); Neo.Cryptography.ECC.ECPoint[] publicKeys4 = new Neo.Cryptography.ECC.ECPoint[3]; for (int i = 0; i < 3; i++) @@ -62,8 +67,8 @@ public void TestIsMultiSigContract() } byte[] script4 = Contract.CreateMultiSigRedeemScript(3, publicKeys4); script4[script4.Length - 1] = 0x00; - Assert.AreEqual(false, Neo.SmartContract.Helper.IsMultiSigContract(script4, out int m4, out int n4)); - + Assert.AreEqual(false, Neo.SmartContract.Helper.IsMultiSigContract(script4, out _, out ECPoint[] p4)); + Assert.IsNull(p4); } [TestMethod] From e5d366314e357baa5b10fa34e4f8c6d7dfe5354f Mon Sep 17 00:00:00 2001 From: Krain Chen Date: Fri, 28 Feb 2020 22:10:05 +0800 Subject: [PATCH 207/305] update neo-vm version (#1456) --- src/neo/neo.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 075d17fb7f..85efaaf594 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -27,7 +27,7 @@ - + From a2594db7e9b8dd2453a0385dca50c2ca9a6b3845 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 3 Mar 2020 07:40:06 +0100 Subject: [PATCH 208/305] Remove parallel verification (#1429) --- src/neo/Ledger/Blockchain.cs | 37 +++------------------ src/neo/Network/P2P/Payloads/Transaction.cs | 11 ++---- 2 files changed, 8 insertions(+), 40 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 9090a5b339..65bba7bee0 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -27,7 +27,6 @@ public class Import { public IEnumerable Blocks; public bool Verify = tru public class ImportCompleted { } public class FillMemoryPool { public IEnumerable Transactions; } public class FillCompleted { } - private class ParallelVerified { public Transaction Transaction; public bool ShouldRelay; public RelayResultReason VerifyResult; } public static readonly uint MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock; public const uint DecrementInterval = 2000000; @@ -407,39 +406,16 @@ private void OnNewTransaction(Transaction transaction, bool relay) else if (!MemPool.CanTransactionFitInPool(transaction)) reason = RelayResultReason.OutOfMemory; else - reason = transaction.VerifyForEachBlock(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(transaction.Sender)); - if (reason == RelayResultReason.Succeed) - { - Task.Run(() => - { - return new ParallelVerified - { - Transaction = transaction, - ShouldRelay = relay, - VerifyResult = transaction.VerifyParallelParts(currentSnapshot) - }; - }).PipeTo(Self, Sender); - } - else - { - Sender.Tell(reason); - } - } + reason = transaction.Verify(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(transaction.Sender)); - private void OnParallelVerified(ParallelVerified parallelVerified) - { - RelayResultReason reason = parallelVerified.VerifyResult; if (reason == RelayResultReason.Succeed) { - if (View.ContainsTransaction(parallelVerified.Transaction.Hash)) - reason = RelayResultReason.AlreadyExists; - else if (!MemPool.CanTransactionFitInPool(parallelVerified.Transaction)) + if (!MemPool.TryAdd(transaction.Hash, transaction)) reason = RelayResultReason.OutOfMemory; - else if (!MemPool.TryAdd(parallelVerified.Transaction.Hash, parallelVerified.Transaction)) - reason = RelayResultReason.OutOfMemory; - else if (parallelVerified.ShouldRelay) - system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = parallelVerified.Transaction }); + else if (relay) + system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = transaction }); } + Sender.Tell(reason); } @@ -475,9 +451,6 @@ protected override void OnReceive(object message) case Transaction transaction: OnNewTransaction(transaction, true); break; - case ParallelVerified parallelVerified: - OnParallelVerified(parallelVerified); - break; case ConsensusPayload payload: Sender.Tell(OnNewConsensus(payload)); break; diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 2b6523a6aa..e511e17c94 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -266,13 +266,6 @@ bool IInventory.Verify(StoreView snapshot) return Verify(snapshot, BigInteger.Zero) == RelayResultReason.Succeed; } - public virtual RelayResultReason Verify(StoreView snapshot, BigInteger totalSenderFeeFromPool) - { - RelayResultReason result = VerifyForEachBlock(snapshot, totalSenderFeeFromPool); - if (result != RelayResultReason.Succeed) return result; - return VerifyParallelParts(snapshot); - } - public virtual RelayResultReason VerifyForEachBlock(StoreView snapshot, BigInteger totalSenderFeeFromPool) { if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) @@ -292,8 +285,10 @@ public virtual RelayResultReason VerifyForEachBlock(StoreView snapshot, BigInteg return RelayResultReason.Succeed; } - public RelayResultReason VerifyParallelParts(StoreView snapshot) + public virtual RelayResultReason Verify(StoreView snapshot, BigInteger totalSenderFeeFromPool) { + RelayResultReason result = VerifyForEachBlock(snapshot, totalSenderFeeFromPool); + if (result != RelayResultReason.Succeed) return result; int size = Size; if (size > MaxTransactionSize) return RelayResultReason.Invalid; long net_fee = NetworkFee - size * NativeContract.Policy.GetFeePerByte(snapshot); From d442b94e3f93ddebeaeddccecfcdbd2d48add4a9 Mon Sep 17 00:00:00 2001 From: belane Date: Mon, 9 Mar 2020 09:57:24 +0100 Subject: [PATCH 209/305] cast M to double (#1465) --- src/neo/Consensus/ConsensusService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 5ead54d233..7276b9ea1b 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -247,7 +247,7 @@ private void OnCommitReceived(ConsensusPayload payload, Commit commit) // 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 / context.M); + 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); } From acef71985098558651b375006d8791bca9a04bc6 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 11 Mar 2020 10:30:08 +0100 Subject: [PATCH 210/305] Read Fixed (#1454) * Read Fixed * format * Rename * Fix Consensus UT * Add ut, and allow buffered streams * Add ut, and allow buffered streams * Change name * Update Helper.cs Co-authored-by: erikzhang --- src/neo/Consensus/Commit.cs | 3 +- .../RecoveryMessage.CommitPayloadCompact.cs | 2 +- src/neo/Consensus/RecoveryMessage.cs | 2 +- src/neo/IO/Helper.cs | 27 +++++++++++++-- .../P2P/Payloads/NetworkAddressWithTime.cs | 3 +- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 4 +-- tests/neo.UnitTests/IO/UT_IOHelper.cs | 34 +++++++++++++++++++ 7 files changed, 65 insertions(+), 10 deletions(-) diff --git a/src/neo/Consensus/Commit.cs b/src/neo/Consensus/Commit.cs index 84585e1ee4..fce9b8caaf 100644 --- a/src/neo/Consensus/Commit.cs +++ b/src/neo/Consensus/Commit.cs @@ -1,3 +1,4 @@ +using Neo.IO; using System.IO; namespace Neo.Consensus @@ -13,7 +14,7 @@ public class Commit : ConsensusMessage public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); - Signature = reader.ReadBytes(64); + Signature = reader.ReadFixedBytes(64); } public override void Serialize(BinaryWriter writer) diff --git a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs index 72962f92b3..5d783c6796 100644 --- a/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs +++ b/src/neo/Consensus/RecoveryMessage.CommitPayloadCompact.cs @@ -23,7 +23,7 @@ void ISerializable.Deserialize(BinaryReader reader) { ViewNumber = reader.ReadByte(); ValidatorIndex = reader.ReadUInt16(); - Signature = reader.ReadBytes(64); + Signature = reader.ReadFixedBytes(64); InvocationScript = reader.ReadVarBytes(1024); } diff --git a/src/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs index a5e1c09ac6..c25816ee7f 100644 --- a/src/neo/Consensus/RecoveryMessage.cs +++ b/src/neo/Consensus/RecoveryMessage.cs @@ -39,7 +39,7 @@ public override void Deserialize(BinaryReader reader) { int preparationHashSize = UInt256.Zero.Size; if (preparationHashSize == (int)reader.ReadVarInt((ulong)preparationHashSize)) - PreparationHash = new UInt256(reader.ReadBytes(preparationHashSize)); + PreparationHash = new UInt256(reader.ReadFixedBytes(preparationHashSize)); } PreparationMessages = reader.ReadSerializableArray(Blockchain.MaxValidators).ToDictionary(p => (int)p.ValidatorIndex); diff --git a/src/neo/IO/Helper.cs b/src/neo/IO/Helper.cs index 3acc4b2c2f..e44e5bbe0a 100644 --- a/src/neo/IO/Helper.cs +++ b/src/neo/IO/Helper.cs @@ -151,7 +151,7 @@ public static byte[] ReadBytesWithGrouping(this BinaryReader reader) int count; do { - byte[] group = reader.ReadBytes(GroupingSizeInBytes); + byte[] group = reader.ReadFixedBytes(GroupingSizeInBytes); count = reader.ReadByte(); if (count > GroupingSizeInBytes) throw new FormatException(); @@ -162,9 +162,30 @@ public static byte[] ReadBytesWithGrouping(this BinaryReader reader) } } + public static byte[] ReadFixedBytes(this BinaryReader reader, int size) + { + var index = 0; + var data = new byte[size]; + + while (size > 0) + { + var bytesRead = reader.Read(data, index, size); + + if (bytesRead <= 0) + { + throw new FormatException(); + } + + size -= bytesRead; + index += bytesRead; + } + + return data; + } + public static string ReadFixedString(this BinaryReader reader, int length) { - byte[] data = reader.ReadBytes(length); + byte[] data = reader.ReadFixedBytes(length); return Encoding.UTF8.GetString(data.TakeWhile(p => p != 0).ToArray()); } @@ -196,7 +217,7 @@ public static T[] ReadSerializableArray(this BinaryReader reader, int max = 0 public static byte[] ReadVarBytes(this BinaryReader reader, int max = 0x1000000) { - return reader.ReadBytes((int)reader.ReadVarInt((ulong)max)); + return reader.ReadFixedBytes((int)reader.ReadVarInt((ulong)max)); } public static ulong ReadVarInt(this BinaryReader reader, ulong max = ulong.MaxValue) diff --git a/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs b/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs index 6fcda2f809..a65e28c846 100644 --- a/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs +++ b/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs @@ -31,8 +31,7 @@ void ISerializable.Deserialize(BinaryReader reader) Timestamp = reader.ReadUInt32(); // Address - byte[] data = reader.ReadBytes(16); - if (data.Length != 16) throw new FormatException(); + byte[] data = reader.ReadFixedBytes(16); Address = new IPAddress(data).Unmap(); // Capabilities diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index c7a0687729..4bdb0da2c5 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -531,8 +531,8 @@ public void TestSerializeAndDeserializeConsensusContext() consensusContext.CommitPayloads = new ConsensusPayload[consensusContext.Validators.Length]; using (SHA256 sha256 = SHA256.Create()) { - consensusContext.CommitPayloads[3] = MakeSignedPayload(consensusContext, new Commit { Signature = sha256.ComputeHash(testTx1.Hash.ToArray()) }, 3, new[] { (byte)'3', (byte)'4' }); - consensusContext.CommitPayloads[6] = MakeSignedPayload(consensusContext, new Commit { Signature = sha256.ComputeHash(testTx2.Hash.ToArray()) }, 3, new[] { (byte)'6', (byte)'7' }); + 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(); diff --git a/tests/neo.UnitTests/IO/UT_IOHelper.cs b/tests/neo.UnitTests/IO/UT_IOHelper.cs index d0f63e5c1c..3b36695b2d 100644 --- a/tests/neo.UnitTests/IO/UT_IOHelper.cs +++ b/tests/neo.UnitTests/IO/UT_IOHelper.cs @@ -23,6 +23,40 @@ public void TestAsSerializableGeneric() Assert.AreEqual(UInt160.Zero, result); } + [TestMethod] + public void TestReadFixedBytes() + { + byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + + // Less data + + using (var reader = new BinaryReader(new MemoryStream(data), Encoding.UTF8, false)) + { + byte[] result = Neo.IO.Helper.ReadFixedBytes(reader, 3); + + Assert.AreEqual("010203", result.ToHexString()); + Assert.AreEqual(3, reader.BaseStream.Position); + } + + // Same data + + using (var reader = new BinaryReader(new MemoryStream(data), Encoding.UTF8, false)) + { + byte[] result = Neo.IO.Helper.ReadFixedBytes(reader, 4); + + Assert.AreEqual("01020304", result.ToHexString()); + Assert.AreEqual(4, reader.BaseStream.Position); + } + + // More data + + using (var reader = new BinaryReader(new MemoryStream(data), Encoding.UTF8, false)) + { + Assert.ThrowsException(() => Neo.IO.Helper.ReadFixedBytes(reader, 5)); + Assert.AreEqual(4, reader.BaseStream.Position); + } + } + [TestMethod] public void TestNullableArray() { From 3b1dd1fce614482ecf83c8a2bbe067a5429f8348 Mon Sep 17 00:00:00 2001 From: Charis Zhao Date: Fri, 13 Mar 2020 18:28:11 +0800 Subject: [PATCH 211/305] Adjust the Charge for Storage Writting (#1441) --- src/neo/SmartContract/ApplicationEngine.cs | 3 +- src/neo/SmartContract/InteropDescriptor.cs | 9 +- .../SmartContract/InteropService.Contract.cs | 3 +- .../SmartContract/InteropService.Crypto.cs | 3 +- .../SmartContract/InteropService.Storage.cs | 26 ++- src/neo/SmartContract/InteropService.cs | 7 +- .../SmartContract/Native/NativeContract.cs | 3 +- src/neo/Wallets/Wallet.cs | 4 +- .../SmartContract/UT_InteropPrices.cs | 206 +++++++++++++++++- tests/neo.UnitTests/TestUtils.cs | 28 +++ 10 files changed, 270 insertions(+), 22 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 53bc8c68a8..500e75a6b5 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -25,6 +25,7 @@ public partial class ApplicationEngine : ExecutionEngine public IVerifiable ScriptContainer { get; } public StoreView Snapshot { get; } public long GasConsumed { get; private set; } = 0; + public UInt160 CurrentScriptHash => CurrentContext?.GetState().ScriptHash; public UInt160 CallingScriptHash => CurrentContext?.GetState().CallingScriptHash; public UInt160 EntryScriptHash => EntryContext?.GetState().ScriptHash; @@ -78,7 +79,7 @@ public override void Dispose() protected override bool OnSysCall(uint method) { - if (!AddGas(InteropService.GetPrice(method, CurrentContext.EvaluationStack))) + if (!AddGas(InteropService.GetPrice(method, CurrentContext.EvaluationStack, Snapshot))) return false; return InteropService.Invoke(this, method); } diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index 6984eba13c..ebc94c2f3e 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -1,3 +1,4 @@ +using Neo.Persistence; using Neo.VM; using System; @@ -9,7 +10,7 @@ public class InteropDescriptor public uint Hash { get; } internal Func Handler { get; } public long Price { get; } - public Func PriceCalculator { get; } + public Func PriceCalculator { get; } public TriggerType AllowedTriggers { get; } public CallFlags RequiredCallFlags { get; } @@ -19,7 +20,7 @@ internal InteropDescriptor(string method, Func handler, this.Price = price; } - internal InteropDescriptor(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers, CallFlags requiredCallFlags) + internal InteropDescriptor(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers, CallFlags requiredCallFlags) : this(method, handler, allowedTriggers, requiredCallFlags) { this.PriceCalculator = priceCalculator; @@ -34,9 +35,9 @@ private InteropDescriptor(string method, Func handler, this.RequiredCallFlags = requiredCallFlags; } - public long GetPrice(EvaluationStack stack) + public long GetPrice(EvaluationStack stack, StoreView snapshot) { - return PriceCalculator is null ? Price : PriceCalculator(stack); + return PriceCalculator is null ? Price : PriceCalculator(stack, snapshot); } public static implicit operator uint(InteropDescriptor descriptor) diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index 14823db8c8..b6cd2862b7 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -1,5 +1,6 @@ using Neo.IO; using Neo.Ledger; +using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; @@ -19,7 +20,7 @@ public static class Contract public static readonly InteropDescriptor CallEx = Register("System.Contract.CallEx", Contract_CallEx, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All, CallFlags.None); - private static long GetDeploymentPrice(EvaluationStack stack) + private static long GetDeploymentPrice(EvaluationStack stack, StoreView snapshot) { int size = stack.Peek(0).GetByteLength() + stack.Peek(1).GetByteLength(); return Storage.GasPerByte * size; diff --git a/src/neo/SmartContract/InteropService.Crypto.cs b/src/neo/SmartContract/InteropService.Crypto.cs index 448d312ea4..9678f321ce 100644 --- a/src/neo/SmartContract/InteropService.Crypto.cs +++ b/src/neo/SmartContract/InteropService.Crypto.cs @@ -1,6 +1,7 @@ using Neo.Cryptography; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.VM; using Neo.VM.Types; using System; @@ -16,7 +17,7 @@ public static class Crypto public static readonly InteropDescriptor ECDsaVerify = Register("Neo.Crypto.ECDsaVerify", Crypto_ECDsaVerify, 0_01000000, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor ECDsaCheckMultiSig = Register("Neo.Crypto.ECDsaCheckMultiSig", Crypto_ECDsaCheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None); - private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack) + private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack, StoreView snapshot) { if (stack.Count < 2) return 0; var item = stack.Peek(1); diff --git a/src/neo/SmartContract/InteropService.Storage.cs b/src/neo/SmartContract/InteropService.Storage.cs index 3355a5f83c..a9fc9e9303 100644 --- a/src/neo/SmartContract/InteropService.Storage.cs +++ b/src/neo/SmartContract/InteropService.Storage.cs @@ -1,4 +1,5 @@ using Neo.Ledger; +using Neo.Persistence; using Neo.SmartContract.Iterators; using Neo.VM; using Neo.VM.Types; @@ -22,11 +23,30 @@ public static class Storage public static readonly InteropDescriptor Find = Register("System.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application, CallFlags.None); public static readonly InteropDescriptor Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); public static readonly InteropDescriptor PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor Delete = Register("System.Storage.Delete", Storage_Delete, 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor Delete = Register("System.Storage.Delete", Storage_Delete, 1 * GasPerByte, TriggerType.Application, CallFlags.AllowModifyStates); - private static long GetStoragePrice(EvaluationStack stack) + private static long GetStoragePrice(EvaluationStack stack, StoreView snapshot) { - return (stack.Peek(1).GetByteLength() + stack.Peek(2).GetByteLength()) * GasPerByte; + var key = stack.Peek(1); + var value = stack.Peek(2); + var newDataSize = value.IsNull ? 0 : value.GetByteLength(); + if (!(stack.Peek() is InteropInterface _interface)) + throw new InvalidOperationException(); + + StorageContext context = _interface.GetInterface(); + StorageKey skey = new StorageKey + { + Id = context.Id, + Key = key.GetSpan().ToArray() + }; + var skeyValue = snapshot.Storages.TryGet(skey); + if (skeyValue is null) + newDataSize += key.GetByteLength(); + else if (newDataSize <= skeyValue.Value.Length) + newDataSize = 1; + else + newDataSize -= skeyValue.Value.Length; + return newDataSize * GasPerByte; } private static bool PutExInternal(ApplicationEngine engine, StorageContext context, byte[] key, byte[] value, StorageFlags flags) diff --git a/src/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs index c87ad277ea..f2a673c82f 100644 --- a/src/neo/SmartContract/InteropService.cs +++ b/src/neo/SmartContract/InteropService.cs @@ -1,3 +1,4 @@ +using Neo.Persistence; using Neo.VM; using System; using System.Collections.Generic; @@ -15,9 +16,9 @@ static InteropService() t.GetFields()[0].GetValue(null); } - public static long GetPrice(uint hash, EvaluationStack stack) + public static long GetPrice(uint hash, EvaluationStack stack, StoreView snapshot) { - return methods[hash].GetPrice(stack); + return methods[hash].GetPrice(stack, snapshot); } public static IEnumerable SupportedMethods() @@ -44,7 +45,7 @@ private static InteropDescriptor Register(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers, CallFlags requiredCallFlags) + private static InteropDescriptor Register(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers, CallFlags requiredCallFlags) { InteropDescriptor descriptor = new InteropDescriptor(method, handler, priceCalculator, allowedTriggers, requiredCallFlags); methods.Add(descriptor.Hash, descriptor); diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index b7c7f85251..b28f55be14 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -2,6 +2,7 @@ using Neo.IO; using Neo.Ledger; +using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.SmartContract.Native.Tokens; using Neo.VM; @@ -101,7 +102,7 @@ internal bool Invoke(ApplicationEngine engine) return true; } - internal long GetPrice(EvaluationStack stack) + internal long GetPrice(EvaluationStack stack, StoreView snapshot) { return methods.TryGetValue(stack.Peek().GetString(), out ContractMethodMetadata method) ? method.Price : 0; } diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 3fe4c61652..c0bf012c2a 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -354,7 +354,7 @@ public static long CalculateNetworkFee(byte[] witness_script, ref int size) if (witness_script.IsSignatureContract()) { size += 67 + witness_script.GetVarSize(); - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null); + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null, null); } else if (witness_script.IsMultiSigContract(out int m, out int n)) { @@ -366,7 +366,7 @@ public static long CalculateNetworkFee(byte[] witness_script, ref int size) networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * n; using (ScriptBuilder sb = new ScriptBuilder()) networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null) * n; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null, null) * n; } else { diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index 19eb7a5d38..e34c80eb96 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -1,13 +1,22 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Ledger; using Neo.SmartContract; +using Neo.SmartContract.Manifest; using Neo.VM; +using System; namespace Neo.UnitTests.SmartContract { [TestClass] public class UT_InteropPrices { + [TestInitialize] + public void Initialize() + { + TestBlockchain.InitializeMockNeoSystem(); + } + [TestMethod] public void ApplicationEngineFixedPrices() { @@ -16,7 +25,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemRuntimeCheckWitnessHash); - InteropService.GetPrice(InteropService.Runtime.CheckWitness, ae.CurrentContext.EvaluationStack).Should().Be(0_00030000L); + InteropService.GetPrice(InteropService.Runtime.CheckWitness, ae.CurrentContext.EvaluationStack, ae.Snapshot).Should().Be(0_00030000L); } // System.Storage.GetContext: 9bf667ce (price is 1) @@ -24,7 +33,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemStorageGetContextHash); - InteropService.GetPrice(InteropService.Storage.GetContext, ae.CurrentContext.EvaluationStack).Should().Be(0_00000400L); + InteropService.GetPrice(InteropService.Storage.GetContext, ae.CurrentContext.EvaluationStack, ae.Snapshot).Should().Be(0_00000400L); } // System.Storage.Get: 925de831 (price is 100) @@ -32,7 +41,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemStorageGetHash); - InteropService.GetPrice(InteropService.Storage.Get, ae.CurrentContext.EvaluationStack).Should().Be(0_01000000L); + InteropService.GetPrice(InteropService.Storage.Get, ae.CurrentContext.EvaluationStack, ae.Snapshot).Should().Be(0_01000000L); } } @@ -47,7 +56,7 @@ public void ApplicationEngineVariablePrices() ae.LoadScript(SyscallContractCreateHash00); debugger.StepInto(); // PUSHDATA1 debugger.StepInto(); // PUSHDATA1 - InteropService.GetPrice(InteropService.Contract.Create, ae.CurrentContext.EvaluationStack).Should().Be(0_00300000L); + InteropService.GetPrice(InteropService.Contract.Create, ae.CurrentContext.EvaluationStack, ae.Snapshot).Should().Be(0_00300000L); } // System.Storage.Put: e63f1884 (requires push key and value) @@ -59,7 +68,8 @@ public void ApplicationEngineVariablePrices() debugger.StepInto(); // push 03 (length 1) debugger.StepInto(); // push 03 (length 1) debugger.StepInto(); // push 00 - InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack).Should().Be(200000L); + Action act = () => InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); + act.Should().Throw(); } // System.Storage.PutEx: 73e19b3a (requires push key and value) @@ -71,8 +81,192 @@ public void ApplicationEngineVariablePrices() debugger.StepInto(); // push 03 (length 1) debugger.StepInto(); // push 03 (length 1) debugger.StepInto(); // push 00 - InteropService.GetPrice(InteropService.Storage.PutEx, ae.CurrentContext.EvaluationStack).Should().Be(200000L); + Action act = () => InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); + act.Should().Throw(); + } + } + + /// + /// Put without previous content (should charge per byte used) + /// + [TestMethod] + public void ApplicationEngineRegularPut() + { + var key = new byte[] { (byte)OpCode.PUSH1 }; + var value = new byte[] { (byte)OpCode.PUSH1 }; + + byte[] script = CreatePutScript(key, value); + + ContractState contractState = TestUtils.GetContract(script); + contractState.Manifest.Features = ContractFeatures.HasStorage; + + StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); + StorageItem sItem = TestUtils.GetStorageItem(new byte[0] { }); + + var snapshot = Blockchain.Singleton.GetSnapshot(); + snapshot.Storages.Add(skey, sItem); + snapshot.Contracts.Add(script.ToScriptHash(), contractState); + + using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, testMode: true)) + { + Debugger debugger = new Debugger(ae); + ae.LoadScript(script); + debugger.StepInto(); + debugger.StepInto(); + debugger.StepInto(); + var setupPrice = ae.GasConsumed; + var defaultDataPrice = InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); + defaultDataPrice.Should().Be(InteropService.Storage.GasPerByte * value.Length); + var expectedCost = defaultDataPrice + setupPrice; + debugger.Execute(); + ae.GasConsumed.Should().Be(expectedCost); } } + + /// + /// Reuses the same amount of storage. Should cost 0. + /// + [TestMethod] + public void ApplicationEngineReusedStorage_FullReuse() + { + var key = new byte[] { (byte)OpCode.PUSH1 }; + var value = new byte[] { (byte)OpCode.PUSH1 }; + + byte[] script = CreatePutScript(key, value); + + ContractState contractState = TestUtils.GetContract(script); + contractState.Manifest.Features = ContractFeatures.HasStorage; + + StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); + StorageItem sItem = TestUtils.GetStorageItem(value); + + var snapshot = Blockchain.Singleton.GetSnapshot(); + snapshot.Storages.Add(skey, sItem); + snapshot.Contracts.Add(script.ToScriptHash(), contractState); + + using (ApplicationEngine applicationEngine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, testMode: true)) + { + Debugger debugger = new Debugger(applicationEngine); + applicationEngine.LoadScript(script); + debugger.StepInto(); + debugger.StepInto(); + debugger.StepInto(); + var setupPrice = applicationEngine.GasConsumed; + var reusedDataPrice = InteropService.GetPrice(InteropService.Storage.Put, applicationEngine.CurrentContext.EvaluationStack, applicationEngine.Snapshot); + reusedDataPrice.Should().Be(1 * InteropService.Storage.GasPerByte); + debugger.Execute(); + var expectedCost = reusedDataPrice + setupPrice; + applicationEngine.GasConsumed.Should().Be(expectedCost); + } + } + + /// + /// Reuses one byte and allocates a new one + /// It should only pay for the second byte. + /// + [TestMethod] + public void ApplicationEngineReusedStorage_PartialReuse() + { + var key = new byte[] { (byte)OpCode.PUSH1 }; + var oldValue = new byte[] { (byte)OpCode.PUSH1 }; + var value = new byte[] { (byte)OpCode.PUSH1, (byte)OpCode.PUSH1 }; + + byte[] script = CreatePutScript(key, value); + + ContractState contractState = TestUtils.GetContract(script); + contractState.Manifest.Features = ContractFeatures.HasStorage; + + StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); + StorageItem sItem = TestUtils.GetStorageItem(oldValue); + + var snapshot = Blockchain.Singleton.GetSnapshot(); + snapshot.Storages.Add(skey, sItem); + snapshot.Contracts.Add(script.ToScriptHash(), contractState); + + using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, testMode: true)) + { + Debugger debugger = new Debugger(ae); + ae.LoadScript(script); + debugger.StepInto(); + debugger.StepInto(); + debugger.StepInto(); + var setupPrice = ae.GasConsumed; + var reusedDataPrice = InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); + reusedDataPrice.Should().Be(1 * InteropService.Storage.GasPerByte); + debugger.StepInto(); + var expectedCost = reusedDataPrice + setupPrice; + debugger.StepInto(); + ae.GasConsumed.Should().Be(expectedCost); + } + } + + /// + /// Use put for the same key twice. + /// Pays for 1 extra byte for the first Put and 1 byte for the second basic fee (as value2.length == value1.length). + /// + [TestMethod] + public void ApplicationEngineReusedStorage_PartialReuseTwice() + { + var key = new byte[] { (byte)OpCode.PUSH1 }; + var oldValue = new byte[] { (byte)OpCode.PUSH1 }; + var value = new byte[] { (byte)OpCode.PUSH1, (byte)OpCode.PUSH1 }; + + byte[] script = CreateMultiplePutScript(key, value); + + ContractState contractState = TestUtils.GetContract(script); + contractState.Manifest.Features = ContractFeatures.HasStorage; + + StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); + StorageItem sItem = TestUtils.GetStorageItem(oldValue); + + var snapshot = Blockchain.Singleton.GetSnapshot(); + snapshot.Storages.Add(skey, sItem); + snapshot.Contracts.Add(script.ToScriptHash(), contractState); + + using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, testMode: true)) + { + Debugger debugger = new Debugger(ae); + ae.LoadScript(script); + debugger.StepInto(); //push key + debugger.StepInto(); //push value + debugger.StepInto(); //syscall Storage.GetContext + var setupPrice = ae.GasConsumed; + var incrementDataPrice = InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); + incrementDataPrice.Should().Be(1 * InteropService.Storage.GasPerByte); + debugger.StepInto(); // syscall Storage.Put + + debugger.StepInto(); //push key + debugger.StepInto(); //push value + debugger.StepInto(); + setupPrice = ae.GasConsumed; + var reusedDataPrice = InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); + reusedDataPrice.Should().Be(1 * InteropService.Storage.GasPerByte); // = PUT basic fee + } + } + + private byte[] CreateMultiplePutScript(byte[] key, byte[] value, int times = 2) + { + var scriptBuilder = new ScriptBuilder(); + + for (int i = 0; i < times; i++) + { + scriptBuilder.EmitPush(value); + scriptBuilder.EmitPush(key); + scriptBuilder.EmitSysCall(InteropService.Storage.GetContext); + scriptBuilder.EmitSysCall(InteropService.Storage.Put); + } + + return scriptBuilder.ToArray(); + } + + private byte[] CreatePutScript(byte[] key, byte[] value) + { + var scriptBuilder = new ScriptBuilder(); + scriptBuilder.EmitPush(value); + scriptBuilder.EmitPush(key); + scriptBuilder.EmitSysCall(InteropService.Storage.GetContext); + scriptBuilder.EmitSysCall(InteropService.Storage.Put); + return scriptBuilder.ToArray(); + } } } diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index a68eba49a0..dfbd973a26 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -4,6 +4,7 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Manifest; +using Neo.SmartContract; using Neo.VM; using Neo.Wallets.NEP6; using System; @@ -64,6 +65,33 @@ internal static ContractState GetContract() }; } + internal static ContractState GetContract(byte[] script) + { + return new ContractState + { + Id = 1, + Script = script, + Manifest = ContractManifest.CreateDefault(script.ToScriptHash()) + }; + } + + internal static StorageItem GetStorageItem(byte[] value) + { + return new StorageItem + { + Value = value + }; + } + + internal static StorageKey GetStorageKey(int id, byte[] keyValue) + { + return new StorageKey + { + Id = id, + Key = keyValue + }; + } + public static void SetupHeaderWithValues(Header header, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal) { setupBlockBaseWithValues(header, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal); From a4c8c96952c6087d92593450e22336dac18c3bd3 Mon Sep 17 00:00:00 2001 From: cn1010 <1062108372@qq.com> Date: Tue, 17 Mar 2020 16:27:29 +0800 Subject: [PATCH 212/305] fix BigInteger0 storage outputs null (#1471) --- src/neo/SmartContract/InteropService.Storage.cs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/neo/SmartContract/InteropService.Storage.cs b/src/neo/SmartContract/InteropService.Storage.cs index a9fc9e9303..e732cf8b60 100644 --- a/src/neo/SmartContract/InteropService.Storage.cs +++ b/src/neo/SmartContract/InteropService.Storage.cs @@ -63,17 +63,10 @@ private static bool PutExInternal(ApplicationEngine engine, StorageContext conte if (engine.Snapshot.Storages.TryGet(skey)?.IsConstant == true) return false; - if (value.Length == 0 && !flags.HasFlag(StorageFlags.Constant)) - { - // If put 'value' is empty (and non-const), we remove it (implicit `Storage.Delete`) - engine.Snapshot.Storages.Delete(skey); - } - else - { - StorageItem item = engine.Snapshot.Storages.GetAndChange(skey, () => new StorageItem()); - item.Value = value; - item.IsConstant = flags.HasFlag(StorageFlags.Constant); - } + StorageItem item = engine.Snapshot.Storages.GetAndChange(skey, () => new StorageItem()); + item.Value = value; + item.IsConstant = flags.HasFlag(StorageFlags.Constant); + return true; } From 3f142cb35cb6c67b9450c6f1b0adbae5672ccb67 Mon Sep 17 00:00:00 2001 From: doubiliu Date: Wed, 18 Mar 2020 13:13:02 -0500 Subject: [PATCH 213/305] Fix bug in contract update/destory (#1483) * first commit * format * commit again * format --- src/neo/SmartContract/InteropService.Contract.cs | 4 ++-- tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs | 3 +++ tests/neo.UnitTests/SmartContract/UT_InteropService.cs | 3 +++ tests/neo.UnitTests/TestUtils.cs | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index b6cd2862b7..f0fb6bfef1 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -81,7 +81,7 @@ private static bool Contract_Update(ApplicationEngine engine) contract = engine.Snapshot.Contracts.GetAndChange(contract.ScriptHash); contract.Manifest = ContractManifest.Parse(manifest); if (!contract.Manifest.IsValid(contract.ScriptHash)) return false; - if (!contract.HasStorage && engine.Snapshot.Storages.Find(engine.CurrentScriptHash.ToArray()).Any()) return false; + if (!contract.HasStorage && engine.Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id)).Any()) return false; } return true; @@ -94,7 +94,7 @@ private static bool Contract_Destroy(ApplicationEngine engine) if (contract == null) return true; engine.Snapshot.Contracts.Delete(hash); if (contract.HasStorage) - foreach (var (key, _) in engine.Snapshot.Storages.Find(hash.ToArray())) + foreach (var (key, _) in engine.Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id))) engine.Snapshot.Storages.Delete(key); return true; } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 814f55cb67..c187a08043 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -13,6 +13,7 @@ using Neo.VM; using Neo.VM.Types; using Neo.Wallets; +using System; using System.Linq; using VMArray = Neo.VM.Types.Array; @@ -218,6 +219,7 @@ public void TestContract_Update() Signature = signature } }; + manifest.Features = ContractFeatures.HasStorage; var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); state.Manifest.Features = ContractFeatures.HasStorage; @@ -239,6 +241,7 @@ public void TestContract_Update() engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeTrue(); + engine.Snapshot.Storages.Find(BitConverter.GetBytes(state.Id)).ToList().Count().Should().Be(1); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 7ad570ef0f..fb8d668c10 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -848,6 +848,7 @@ public void TestContract_Destroy() engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[0]); InteropService.Invoke(engine, InteropService.Contract.Destroy).Should().BeTrue(); + engine.Snapshot.Storages.Find(BitConverter.GetBytes(0x43000000)).Any().Should().BeFalse(); //storages are removed snapshot = Blockchain.Singleton.GetSnapshot(); @@ -856,6 +857,8 @@ public void TestContract_Destroy() engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[0]); InteropService.Invoke(engine, InteropService.Contract.Destroy).Should().BeTrue(); + engine.Snapshot.Storages.Find(BitConverter.GetBytes(0x43000000)).Any().Should().BeFalse(); + } public static void LogEvent(object sender, LogEventArgs args) diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index dfbd973a26..cf3a59c92a 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -60,6 +60,7 @@ internal static ContractState GetContract() { return new ContractState { + Id = 0x43000000, Script = new byte[] { 0x01, 0x01, 0x01, 0x01 }, Manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")) }; From 47303bb02f622b9ca9e6e177df6091ab90019ad0 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 20 Mar 2020 15:04:19 +0800 Subject: [PATCH 214/305] Burn sys_fee (#1430) --- src/neo/Network/P2P/Payloads/Transaction.cs | 2 +- .../SmartContract/Native/Tokens/GasToken.cs | 30 ------------- .../SmartContract/Native/Tokens/NeoToken.cs | 1 - .../Native/Tokens/UT_GasToken.cs | 42 ------------------- 4 files changed, 1 insertion(+), 74 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index e511e17c94..41bc126474 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -127,7 +127,7 @@ public int Size } /// - /// Distributed to NEO holders. + /// Fee to be burned. /// public long SystemFee { diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index 7e89bd02c2..1f05114062 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -3,13 +3,8 @@ using Neo.Cryptography.ECC; using Neo.Ledger; using Neo.Network.P2P.Payloads; -using Neo.Persistence; -using Neo.VM; -using Neo.VM.Types; -using System; using System.Linq; using System.Numerics; -using VMArray = Neo.VM.Types.Array; namespace Neo.SmartContract.Native.Tokens { @@ -21,8 +16,6 @@ public sealed class GasToken : Nep5Token public override string Symbol => "gas"; public override byte Decimals => 8; - private const byte Prefix_SystemFeeAmount = 15; - internal GasToken() { } @@ -44,30 +37,7 @@ protected override bool OnPersist(ApplicationEngine engine) ECPoint[] validators = NEO.GetNextBlockValidators(engine.Snapshot); UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.Snapshot.PersistingBlock.ConsensusData.PrimaryIndex]).ToScriptHash(); Mint(engine, primary, engine.Snapshot.PersistingBlock.Transactions.Sum(p => p.NetworkFee)); - BigInteger sys_fee = GetSysFeeAmount(engine.Snapshot, engine.Snapshot.PersistingBlock.Index - 1) + engine.Snapshot.PersistingBlock.Transactions.Sum(p => p.SystemFee); - StorageKey key = CreateStorageKey(Prefix_SystemFeeAmount, BitConverter.GetBytes(engine.Snapshot.PersistingBlock.Index)); - engine.Snapshot.Storages.Add(key, new StorageItem - { - Value = sys_fee.ToByteArrayStandard(), - IsConstant = true - }); return true; } - - [ContractMethod(0_01000000, ContractParameterType.Integer, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "index" }, SafeMethod = true)] - private StackItem GetSysFeeAmount(ApplicationEngine engine, VMArray args) - { - uint index = (uint)args[0].GetBigInteger(); - return GetSysFeeAmount(engine.Snapshot, index); - } - - public BigInteger GetSysFeeAmount(StoreView snapshot, uint index) - { - if (index == 0) return Blockchain.GenesisBlock.Transactions.Sum(p => p.SystemFee); - StorageKey key = CreateStorageKey(Prefix_SystemFeeAmount, BitConverter.GetBytes(index)); - StorageItem storage = snapshot.Storages.TryGet(key); - if (storage is null) return BigInteger.Zero; - return new BigInteger(storage.Value); - } } } diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index aaea004edf..ec19edb85f 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -94,7 +94,6 @@ private BigInteger CalculateBonus(StoreView snapshot, BigInteger value, uint sta } amount += (iend - istart) * Blockchain.GenerationAmount[ustart]; } - amount += (GAS.GetSysFeeAmount(snapshot, end - 1) - (start == 0 ? 0 : GAS.GetSysFeeAmount(snapshot, start - 1))) / GAS.Factor; return value * amount * GAS.Factor / TotalAmount; } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index 614322aa20..be6e6a1d77 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -7,7 +7,6 @@ using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; using Neo.VM; -using Neo.VM.Types; using System; using System.Linq; using System.Numerics; @@ -138,46 +137,5 @@ public void Check_BadScript() NativeContract.GAS.Invoke(engine).Should().BeFalse(); } - - [TestMethod] - public void TestGetSysFeeAmount1() - { - using (ApplicationEngine engine = NativeContract.GAS.TestCall("getSysFeeAmount", 2u)) - { - engine.ResultStack.Peek().GetBigInteger().Should().Be(new BigInteger(0)); - engine.ResultStack.Peek().GetType().Should().Be(typeof(Integer)); - } - - using (ApplicationEngine engine = NativeContract.GAS.TestCall("getSysFeeAmount", 0u)) - { - engine.ResultStack.Peek().GetBigInteger().Should().Be(new BigInteger(0)); - } - } - - [TestMethod] - public void TestGetSysFeeAmount2() - { - var snapshot = Blockchain.Singleton.GetSnapshot(); - NativeContract.GAS.GetSysFeeAmount(snapshot, 0).Should().Be(new BigInteger(0)); - NativeContract.GAS.GetSysFeeAmount(snapshot, 1).Should().Be(new BigInteger(0)); - - byte[] key = BitConverter.GetBytes(1); - StorageKey storageKey = new StorageKey - { - Id = NativeContract.GAS.Id, - Key = new byte[sizeof(byte) + key.Length] - }; - storageKey.Key[0] = 15; - key.CopyTo(storageKey.Key.AsSpan(1)); - - BigInteger sys_fee = new BigInteger(10); - snapshot.Storages.Add(storageKey, new StorageItem - { - Value = sys_fee.ToByteArrayStandard(), - IsConstant = true - }); - - NativeContract.GAS.GetSysFeeAmount(snapshot, 1).Should().Be(sys_fee); - } } } From 23b1d01fc29ee92368812909802d43b882a7ddc2 Mon Sep 17 00:00:00 2001 From: Luchuan Date: Sun, 22 Mar 2020 19:25:50 +0800 Subject: [PATCH 215/305] Replace THROWIFNOT by ASSERT (#1475) --- .../ApplicationEngine.OpCodePrices.cs | 4 ++-- src/neo/Wallets/Wallet.cs | 2 +- src/neo/neo.csproj | 2 +- .../Network/P2P/Payloads/UT_Transaction.cs | 14 +++++++------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index 29552e4397..4bfa5775bf 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -59,8 +59,8 @@ partial class ApplicationEngine [OpCode.CALL_L] = 22000, [OpCode.CALLA] = 22000, [OpCode.THROW] = 30, - [OpCode.THROWIF] = 30, - [OpCode.THROWIFNOT] = 30, + [OpCode.ABORT] = 30, + [OpCode.ASSERT] = 30, [OpCode.RET] = 0, [OpCode.SYSCALL] = 0, [OpCode.DEPTH] = 60, diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index c0bf012c2a..722c4dadea 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -255,7 +255,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null foreach (var (account, value) in balances_used) { sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); - sb.Emit(OpCode.THROWIFNOT); + sb.Emit(OpCode.ASSERT); } } if (assetId.Equals(NativeContract.GAS.Hash)) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 85efaaf594..6b06ca6960 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 679103a470..339df5605e 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -324,7 +324,7 @@ public void FeeIsSignatureContract_TestScope_Global() // 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.Emit(OpCode.THROWIFNOT); + sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -417,7 +417,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_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); - sb.Emit(OpCode.THROWIFNOT); + sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -511,7 +511,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); - sb.Emit(OpCode.THROWIFNOT); + sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -606,7 +606,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.Emit(OpCode.THROWIFNOT); + sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -666,7 +666,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_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); - sb.Emit(OpCode.THROWIFNOT); + sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -763,7 +763,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() // 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.Emit(OpCode.THROWIFNOT); + sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -1023,7 +1023,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() // 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.Emit(OpCode.THROWIFNOT); + sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } From 90b2564ebba69515d06690de8ec659fba8f7eb2b Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Mon, 23 Mar 2020 14:39:48 +0800 Subject: [PATCH 216/305] Fix change password (#1490) --- src/neo/Wallets/SQLite/UserWallet.cs | 2 +- tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs index 36b865a2e3..1764db5299 100644 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ b/src/neo/Wallets/SQLite/UserWallet.cs @@ -174,7 +174,7 @@ public bool ChangePassword(string password_old, string password_new) byte[] passwordKey = password_new.ToAesKey(); try { - SaveStoredData("PasswordHash", passwordKey.Sha256()); + SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); SaveStoredData("MasterKey", masterKey.AesEncrypt(passwordKey, iv)); return true; } diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs index 87993f12cd..fc7f9702d2 100644 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs +++ b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs @@ -167,6 +167,7 @@ public void TestChangePassword() { wallet.ChangePassword("123455", "654321").Should().BeFalse(); wallet.ChangePassword("123456", "654321").Should().BeTrue(); + wallet.ChangePassword("654321", "123456").Should().BeTrue(); } [TestMethod] From 8fdf7782a4ce18ec1dcaf6cd776ae8d871344b1b Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 23 Mar 2020 19:21:28 +0800 Subject: [PATCH 217/305] Optimize the wallet UT (#1496) --- src/neo/Wallets/SQLite/UserWallet.cs | 98 ++++++++++--------- .../Wallets/SQLite/UT_UserWallet.cs | 3 +- 2 files changed, 52 insertions(+), 49 deletions(-) diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs index 1764db5299..7cfbca8034 100644 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ b/src/neo/Wallets/SQLite/UserWallet.cs @@ -42,56 +42,58 @@ public override Version Version } /// - /// Constructor + /// Open an existing wallet /// /// Path /// Password Key - /// True for create the wallet - /// Scrypt initialization value (only if create=True) - private UserWallet(string path, byte[] passwordKey, bool create, ScryptParameters scrypt = null) + private UserWallet(string path, byte[] passwordKey) { this.path = path; + this.salt = LoadStoredData("Salt"); + byte[] passwordHash = LoadStoredData("PasswordHash"); + if (passwordHash != null && !passwordHash.SequenceEqual(passwordKey.Concat(salt).ToArray().Sha256())) + throw new CryptographicException(); + this.iv = LoadStoredData("IV"); + this.masterKey = LoadStoredData("MasterKey").AesDecrypt(passwordKey, iv); + this.scrypt = new ScryptParameters + ( + BitConverter.ToInt32(LoadStoredData("ScryptN")), + BitConverter.ToInt32(LoadStoredData("ScryptR")), + BitConverter.ToInt32(LoadStoredData("ScryptP")) + ); + this.accounts = LoadAccounts(); + } - if (create) - { - this.iv = new byte[16]; - this.salt = new byte[20]; - this.masterKey = new byte[32]; - this.scrypt = scrypt ?? ScryptParameters.Default; - this.accounts = new Dictionary(); - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(iv); - rng.GetBytes(salt); - rng.GetBytes(masterKey); - } - Version version = Assembly.GetExecutingAssembly().GetName().Version; - BuildDatabase(); - SaveStoredData("IV", iv); - SaveStoredData("Salt", salt); - SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); - SaveStoredData("MasterKey", masterKey.AesEncrypt(passwordKey, iv)); - SaveStoredData("Version", new[] { version.Major, version.Minor, version.Build, version.Revision }.Select(p => BitConverter.GetBytes(p)).SelectMany(p => p).ToArray()); - SaveStoredData("ScryptN", BitConverter.GetBytes(this.scrypt.N)); - SaveStoredData("ScryptR", BitConverter.GetBytes(this.scrypt.R)); - SaveStoredData("ScryptP", BitConverter.GetBytes(this.scrypt.P)); - } - else + /// + /// Create a new wallet + /// + /// Path + /// Password Key + /// Scrypt initialization value + private UserWallet(string path, byte[] passwordKey, ScryptParameters scrypt) + { + this.path = path; + this.iv = new byte[16]; + this.salt = new byte[20]; + this.masterKey = new byte[32]; + this.scrypt = scrypt; + this.accounts = new Dictionary(); + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) { - this.salt = LoadStoredData("Salt"); - byte[] passwordHash = LoadStoredData("PasswordHash"); - if (passwordHash != null && !passwordHash.SequenceEqual(passwordKey.Concat(salt).ToArray().Sha256())) - throw new CryptographicException(); - this.iv = LoadStoredData("IV"); - this.masterKey = LoadStoredData("MasterKey").AesDecrypt(passwordKey, iv); - this.scrypt = new ScryptParameters - ( - BitConverter.ToInt32(LoadStoredData("ScryptN")), - BitConverter.ToInt32(LoadStoredData("ScryptR")), - BitConverter.ToInt32(LoadStoredData("ScryptP")) - ); - this.accounts = LoadAccounts(); + rng.GetBytes(iv); + rng.GetBytes(salt); + rng.GetBytes(masterKey); } + Version version = Assembly.GetExecutingAssembly().GetName().Version; + BuildDatabase(); + SaveStoredData("IV", iv); + SaveStoredData("Salt", salt); + SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); + SaveStoredData("MasterKey", masterKey.AesEncrypt(passwordKey, iv)); + SaveStoredData("Version", new[] { version.Major, version.Minor, version.Build, version.Revision }.Select(p => BitConverter.GetBytes(p)).SelectMany(p => p).ToArray()); + SaveStoredData("ScryptN", BitConverter.GetBytes(this.scrypt.N)); + SaveStoredData("ScryptR", BitConverter.GetBytes(this.scrypt.R)); + SaveStoredData("ScryptP", BitConverter.GetBytes(this.scrypt.P)); } private void AddAccount(UserWalletAccount account) @@ -192,14 +194,14 @@ public override bool Contains(UInt160 scriptHash) } } - public static UserWallet Create(string path, string password) + public static UserWallet Create(string path, string password, ScryptParameters scrypt = null) { - return new UserWallet(path, password.ToAesKey(), true); + return new UserWallet(path, password.ToAesKey(), scrypt ?? ScryptParameters.Default); } - public static UserWallet Create(string path, SecureString password) + public static UserWallet Create(string path, SecureString password, ScryptParameters scrypt = null) { - return new UserWallet(path, password.ToAesKey(), true); + return new UserWallet(path, password.ToAesKey(), scrypt ?? ScryptParameters.Default); } public override WalletAccount CreateAccount(byte[] privateKey) @@ -326,12 +328,12 @@ private byte[] LoadStoredData(string name) public static UserWallet Open(string path, string password) { - return new UserWallet(path, password.ToAesKey(), false); + return new UserWallet(path, password.ToAesKey()); } public static UserWallet Open(string path, SecureString password) { - return new UserWallet(path, password.ToAesKey(), false); + return new UserWallet(path, password.ToAesKey()); } private void SaveStoredData(string name, byte[] value) diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs index fc7f9702d2..ad37d38d77 100644 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs +++ b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; using Neo.Wallets; +using Neo.Wallets.NEP6; using Neo.Wallets.SQLite; using System; using System.IO; @@ -27,7 +28,7 @@ public static string GetRandomPath() public void Setup() { path = GetRandomPath(); - wallet = UserWallet.Create(path, "123456"); + wallet = UserWallet.Create(path, "123456", new ScryptParameters(0, 0, 0)); } [TestCleanup] From ce79a8a2e3aa8af070e268ff9814c4c570fa6b12 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 24 Mar 2020 14:46:04 +0800 Subject: [PATCH 218/305] Limit the max height for some SYSCALLs (#1494) --- .../InteropService.Blockchain.cs | 75 +++++++++++-------- src/neo/neo.csproj | 2 +- .../SmartContract/UT_Syscalls.cs | 21 +++++- 3 files changed, 63 insertions(+), 35 deletions(-) diff --git a/src/neo/SmartContract/InteropService.Blockchain.cs b/src/neo/SmartContract/InteropService.Blockchain.cs index b31aba4a32..1183c5e462 100644 --- a/src/neo/SmartContract/InteropService.Blockchain.cs +++ b/src/neo/SmartContract/InteropService.Blockchain.cs @@ -1,5 +1,6 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.VM; using Neo.VM.Types; using System; @@ -11,6 +12,8 @@ partial class InteropService { public static class Blockchain { + public const uint MaxTraceableBlocks = Transaction.MaxValidUntilBlockIncrement; + public static readonly InteropDescriptor GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application, CallFlags.None); public static readonly InteropDescriptor GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application, CallFlags.None); public static readonly InteropDescriptor GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application, CallFlags.None); @@ -26,68 +29,72 @@ private static bool Blockchain_GetHeight(ApplicationEngine engine) private static bool Blockchain_GetBlock(ApplicationEngine engine) { - ReadOnlySpan data = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); UInt256 hash; - if (data.Length <= 5) - hash = Ledger.Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); - else if (data.Length == 32) + if (engine.TryPop(out uint height)) + { + hash = Ledger.Blockchain.Singleton.GetBlockHash(height); + } + else if (engine.TryPop(out ReadOnlySpan data)) + { + if (data.Length != 32) return false; hash = new UInt256(data); + } else + { return false; - + } Block block = hash != null ? engine.Snapshot.GetBlock(hash) : null; - if (block == null) - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - else - engine.CurrentContext.EvaluationStack.Push(block.ToStackItem(engine.ReferenceCounter)); + if (block != null && !IsTraceableBlock(engine.Snapshot, block.Index)) block = null; + engine.Push(block?.ToStackItem(engine.ReferenceCounter) ?? StackItem.Null); return true; } private static bool Blockchain_GetTransaction(ApplicationEngine engine) { - ReadOnlySpan hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - Transaction tx = engine.Snapshot.GetTransaction(new UInt256(hash)); - if (tx == null) - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - else - engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem(engine.ReferenceCounter)); + if (!engine.TryPop(out ReadOnlySpan hash)) return false; + TransactionState state = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); + if (state != null && !IsTraceableBlock(engine.Snapshot, state.BlockIndex)) state = null; + engine.Push(state?.Transaction.ToStackItem(engine.ReferenceCounter) ?? StackItem.Null); return true; } private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine) { - ReadOnlySpan hash = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - var tx = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); - engine.CurrentContext.EvaluationStack.Push(tx != null ? new BigInteger(tx.BlockIndex) : BigInteger.MinusOne); + if (!engine.TryPop(out ReadOnlySpan hash)) return false; + TransactionState state = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); + if (state != null && !IsTraceableBlock(engine.Snapshot, state.BlockIndex)) state = null; + engine.Push(state?.BlockIndex ?? BigInteger.MinusOne); return true; } private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) { - ReadOnlySpan data = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); UInt256 hash; - if (data.Length <= 5) - hash = Ledger.Blockchain.Singleton.GetBlockHash((uint)new BigInteger(data)); - else if (data.Length == 32) + if (engine.TryPop(out uint height)) + { + hash = Ledger.Blockchain.Singleton.GetBlockHash(height); + } + else if (engine.TryPop(out ReadOnlySpan data)) + { + if (data.Length != 32) return false; hash = new UInt256(data); + } else + { return false; - + } TrimmedBlock block = hash != null ? engine.Snapshot.Blocks.TryGet(hash) : null; - if (block == null) + if (block != null && !IsTraceableBlock(engine.Snapshot, block.Index)) block = null; + if (block is null) { - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + engine.Push(StackItem.Null); } else { - int index = (int)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger(); + if (!engine.TryPop(out int index)) return false; if (index < 0 || index >= block.Hashes.Length - 1) return false; - Transaction tx = engine.Snapshot.GetTransaction(block.Hashes[index + 1]); - if (tx == null) - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - else - engine.CurrentContext.EvaluationStack.Push(tx.ToStackItem(engine.ReferenceCounter)); + engine.Push(tx?.ToStackItem(engine.ReferenceCounter) ?? StackItem.Null); } return true; } @@ -102,6 +109,12 @@ private static bool Blockchain_GetContract(ApplicationEngine engine) engine.CurrentContext.EvaluationStack.Push(contract.ToStackItem(engine.ReferenceCounter)); return true; } + + private static bool IsTraceableBlock(StoreView snapshot, uint index) + { + if (index > snapshot.Height) return false; + return index + MaxTraceableBlocks > snapshot.Height; + } } } } diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 6b06ca6960..2b146e992e 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 04f60db7ff..8044d8ae95 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -37,7 +37,7 @@ public void System_Blockchain_GetBlock() var block = new Block() { - Index = 1, + Index = 0, Timestamp = 2, Version = 3, Witness = new Witness() @@ -68,13 +68,27 @@ public void System_Blockchain_GetBlock() Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsTrue(engine.ResultStack.Peek().IsNull); - // With block + // Not traceable block + + var height = snapshot.BlockHashIndex.GetAndChange(); + height.Index = block.Index + Transaction.MaxValidUntilBlockIncrement; 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 }); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsTrue(engine.ResultStack.Peek().IsNull); + + // With block + + height.Index = block.Index; + script.EmitSysCall(InteropService.Json.Serialize); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(script.ToArray()); @@ -83,10 +97,11 @@ public void System_Blockchain_GetBlock() Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(), - "5b22556168352f4b6f446d39723064555950636353714346745a30594f726b583164646e7334366e676e3962383d222c332c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c322c312c224141414141414141414141414141414141414141414141414141413d222c315d"); + "5b22366b4139757552614430373634585358466c706674686b436b5954702f6e34623878715057476c6a6659303d222c332c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c322c302c224141414141414141414141414141414141414141414141414141413d222c315d"); Assert.AreEqual(0, engine.ResultStack.Count); // Clean + blocks.Delete(block.Hash); txs.Delete(tx.Hash); } From 954451afa78aef196a8a0ae005bc6b578e80df1d Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 24 Mar 2020 22:36:22 +0800 Subject: [PATCH 219/305] Send RelayResult to event stream (#1495) --- src/neo/Consensus/ConsensusService.cs | 2 +- src/neo/Ledger/Blockchain.cs | 76 ++++++++++--------- src/neo/Ledger/MemoryPool.cs | 2 +- .../{RelayResultReason.cs => VerifyResult.cs} | 2 +- src/neo/Network/P2P/LocalNode.cs | 2 - src/neo/Network/P2P/Payloads/Transaction.cs | 30 ++++---- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 6 +- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 8 +- .../Ledger/UT_SendersFeeMonitor.cs | 4 +- .../Network/P2P/Payloads/UT_Transaction.cs | 2 +- 10 files changed, 70 insertions(+), 64 deletions(-) rename src/neo/Ledger/{RelayResultReason.cs => VerifyResult.cs} (84%) diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 7276b9ea1b..9e59ba7283 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -61,7 +61,7 @@ internal ConsensusService(IActorRef localNode, IActorRef taskManager, ConsensusC private bool AddTransaction(Transaction tx, bool verify) { - if (verify && tx.Verify(context.Snapshot, context.SendersFeeMonitor.GetSenderFee(tx.Sender)) != RelayResultReason.Succeed) + if (verify && tx.Verify(context.Snapshot, context.SendersFeeMonitor.GetSenderFee(tx.Sender)) != VerifyResult.Succeed) { Log($"Invalid transaction: {tx.Hash}{Environment.NewLine}{tx.ToArray().ToHexString()}", LogLevel.Warning); RequestChangeView(ChangeViewReason.TxInvalid); diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 65bba7bee0..f36784d89b 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -15,7 +15,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; -using System.Threading.Tasks; namespace Neo.Ledger { @@ -27,6 +26,7 @@ public class Import { public IEnumerable Blocks; public bool Verify = tru public class ImportCompleted { } public class FillMemoryPool { public IEnumerable Transactions; } public class FillCompleted { } + public class RelayResult { public IInventory Inventory; public VerifyResult Result; } public static readonly uint MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock; public const uint DecrementInterval = 2000000; @@ -279,7 +279,7 @@ private void OnFillMemoryPool(IEnumerable transactions) // First remove the tx if it is unverified in the pool. MemPool.TryRemoveUnVerified(tx.Hash, out _); // Verify the the transaction - if (tx.Verify(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(tx.Sender)) != RelayResultReason.Succeed) + if (tx.Verify(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(tx.Sender)) != VerifyResult.Succeed) continue; // Add to the memory pool MemPool.TryAdd(tx.Hash, tx); @@ -289,26 +289,44 @@ private void OnFillMemoryPool(IEnumerable transactions) Sender.Tell(new FillCompleted()); } - private RelayResultReason OnNewBlock(Block block) + private void OnInventory(IInventory inventory, bool relay = true) + { + RelayResult rr = new RelayResult + { + Inventory = inventory, + Result = inventory switch + { + Block block => OnNewBlock(block), + Transaction transaction => OnNewTransaction(transaction), + ConsensusPayload payload => OnNewConsensus(payload), + _ => VerifyResult.Unknown + } + }; + if (relay && rr.Result == VerifyResult.Succeed) + system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = inventory }); + Context.System.EventStream.Publish(rr); + } + + private VerifyResult OnNewBlock(Block block) { if (block.Index <= Height) - return RelayResultReason.AlreadyExists; + return VerifyResult.AlreadyExists; if (block_cache.ContainsKey(block.Hash)) - return RelayResultReason.AlreadyExists; + return VerifyResult.AlreadyExists; if (block.Index - 1 >= header_index.Count) { AddUnverifiedBlockToCache(block); - return RelayResultReason.UnableToVerify; + return VerifyResult.UnableToVerify; } if (block.Index == header_index.Count) { if (!block.Verify(currentSnapshot)) - return RelayResultReason.Invalid; + return VerifyResult.Invalid; } else { if (!block.Hash.Equals(header_index[(int)block.Index])) - return RelayResultReason.Invalid; + return VerifyResult.Invalid; } if (block.Index == Height + 1) { @@ -365,16 +383,15 @@ private RelayResultReason OnNewBlock(Block block) UpdateCurrentSnapshot(); } } - return RelayResultReason.Succeed; + return VerifyResult.Succeed; } - private RelayResultReason OnNewConsensus(ConsensusPayload payload) + private VerifyResult OnNewConsensus(ConsensusPayload payload) { - if (!payload.Verify(currentSnapshot)) return RelayResultReason.Invalid; + if (!payload.Verify(currentSnapshot)) return VerifyResult.Invalid; system.Consensus?.Tell(payload); ConsensusRelayCache.Add(payload); - system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = payload }); - return RelayResultReason.Succeed; + return VerifyResult.Succeed; } private void OnNewHeaders(Header[] headers) @@ -398,25 +415,14 @@ private void OnNewHeaders(Header[] headers) system.TaskManager.Tell(new TaskManager.HeaderTaskCompleted(), Sender); } - private void OnNewTransaction(Transaction transaction, bool relay) + private VerifyResult OnNewTransaction(Transaction transaction) { - RelayResultReason reason; - if (ContainsTransaction(transaction.Hash)) - reason = RelayResultReason.AlreadyExists; - else if (!MemPool.CanTransactionFitInPool(transaction)) - reason = RelayResultReason.OutOfMemory; - else - reason = transaction.Verify(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(transaction.Sender)); - - if (reason == RelayResultReason.Succeed) - { - if (!MemPool.TryAdd(transaction.Hash, transaction)) - reason = RelayResultReason.OutOfMemory; - else if (relay) - system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = transaction }); - } - - Sender.Tell(reason); + if (ContainsTransaction(transaction.Hash)) return VerifyResult.AlreadyExists; + if (!MemPool.CanTransactionFitInPool(transaction)) return VerifyResult.OutOfMemory; + VerifyResult reason = transaction.Verify(currentSnapshot, MemPool.SendersFeeMonitor.GetSenderFee(transaction.Sender)); + if (reason != VerifyResult.Succeed) return reason; + if (!MemPool.TryAdd(transaction.Hash, transaction)) return VerifyResult.OutOfMemory; + return VerifyResult.Succeed; } private void OnPersistCompleted(Block block) @@ -440,19 +446,19 @@ protected override void OnReceive(object message) OnNewHeaders(headers); break; case Block block: - Sender.Tell(OnNewBlock(block)); + OnInventory(block, false); break; case Transaction[] transactions: { // This message comes from a mempool's revalidation, already relayed - foreach (var tx in transactions) OnNewTransaction(tx, false); + foreach (var tx in transactions) OnInventory(tx, false); break; } case Transaction transaction: - OnNewTransaction(transaction, true); + OnInventory(transaction); break; case ConsensusPayload payload: - Sender.Tell(OnNewConsensus(payload)); + OnInventory(payload); break; case Idle _: if (MemPool.ReVerifyTopUnverifiedTransactionsIfNeeded(MaxTxToReverifyPerIdle, currentSnapshot)) diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index f0409b9e54..62466d65e8 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -416,7 +416,7 @@ internal void InvalidateAllTransactions() // Since unverifiedSortedTxPool is ordered in an ascending manner, we take from the end. foreach (PoolItem item in unverifiedSortedTxPool.Reverse().Take(count)) { - if (item.Tx.VerifyForEachBlock(snapshot, SendersFeeMonitor.GetSenderFee(item.Tx.Sender)) == RelayResultReason.Succeed) + if (item.Tx.VerifyForEachBlock(snapshot, SendersFeeMonitor.GetSenderFee(item.Tx.Sender)) == VerifyResult.Succeed) { reverifiedItems.Add(item); SendersFeeMonitor.AddSenderFee(item.Tx); diff --git a/src/neo/Ledger/RelayResultReason.cs b/src/neo/Ledger/VerifyResult.cs similarity index 84% rename from src/neo/Ledger/RelayResultReason.cs rename to src/neo/Ledger/VerifyResult.cs index 38ddfff73e..ab5c1f673e 100644 --- a/src/neo/Ledger/RelayResultReason.cs +++ b/src/neo/Ledger/VerifyResult.cs @@ -1,6 +1,6 @@ namespace Neo.Ledger { - public enum RelayResultReason : byte + public enum VerifyResult : byte { Succeed, AlreadyExists, diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index bdc4713bfe..ebdbca98fd 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -179,8 +179,6 @@ protected override void OnReceive(object message) case SendDirectly send: OnSendDirectly(send.Inventory); break; - case RelayResultReason _: - break; } } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 41bc126474..f5a17fbf11 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -263,38 +263,38 @@ public static Transaction FromJson(JObject json) bool IInventory.Verify(StoreView snapshot) { - return Verify(snapshot, BigInteger.Zero) == RelayResultReason.Succeed; + return Verify(snapshot, BigInteger.Zero) == VerifyResult.Succeed; } - public virtual RelayResultReason VerifyForEachBlock(StoreView snapshot, BigInteger totalSenderFeeFromPool) + public virtual VerifyResult VerifyForEachBlock(StoreView snapshot, BigInteger totalSenderFeeFromPool) { if (ValidUntilBlock <= snapshot.Height || ValidUntilBlock > snapshot.Height + MaxValidUntilBlockIncrement) - return RelayResultReason.Expired; + return VerifyResult.Expired; UInt160[] hashes = GetScriptHashesForVerifying(snapshot); if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(hashes).Any()) - return RelayResultReason.PolicyFail; + return VerifyResult.PolicyFail; BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); BigInteger fee = SystemFee + NetworkFee + totalSenderFeeFromPool; - if (balance < fee) return RelayResultReason.InsufficientFunds; - if (hashes.Length != Witnesses.Length) return RelayResultReason.Invalid; + if (balance < fee) return VerifyResult.InsufficientFunds; + if (hashes.Length != Witnesses.Length) return VerifyResult.Invalid; for (int i = 0; i < hashes.Length; i++) { if (Witnesses[i].VerificationScript.Length > 0) continue; - if (snapshot.Contracts.TryGet(hashes[i]) is null) return RelayResultReason.Invalid; + if (snapshot.Contracts.TryGet(hashes[i]) is null) return VerifyResult.Invalid; } - return RelayResultReason.Succeed; + return VerifyResult.Succeed; } - public virtual RelayResultReason Verify(StoreView snapshot, BigInteger totalSenderFeeFromPool) + public virtual VerifyResult Verify(StoreView snapshot, BigInteger totalSenderFeeFromPool) { - RelayResultReason result = VerifyForEachBlock(snapshot, totalSenderFeeFromPool); - if (result != RelayResultReason.Succeed) return result; + VerifyResult result = VerifyForEachBlock(snapshot, totalSenderFeeFromPool); + if (result != VerifyResult.Succeed) return result; int size = Size; - if (size > MaxTransactionSize) return RelayResultReason.Invalid; + if (size > MaxTransactionSize) return VerifyResult.Invalid; long net_fee = NetworkFee - size * NativeContract.Policy.GetFeePerByte(snapshot); - if (net_fee < 0) return RelayResultReason.InsufficientFunds; - if (!this.VerifyWitnesses(snapshot, net_fee)) return RelayResultReason.Invalid; - return RelayResultReason.Succeed; + if (net_fee < 0) return VerifyResult.InsufficientFunds; + if (!this.VerifyWitnesses(snapshot, net_fee)) return VerifyResult.Invalid; + return VerifyResult.Succeed; } public StackItem ToStackItem(ReferenceCounter referenceCounter) diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index 622f156b16..822a9a70bb 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -134,11 +134,13 @@ public void TestValidTransaction() var tx = CreateValidTx(walletA, acc.ScriptHash, 0); + system.ActorSystem.EventStream.Subscribe(senderProbe, typeof(Blockchain.RelayResult)); + senderProbe.Send(system.Blockchain, tx); - senderProbe.ExpectMsg(RelayResultReason.Succeed); + senderProbe.ExpectMsg(p => p.Result == VerifyResult.Succeed); senderProbe.Send(system.Blockchain, tx); - senderProbe.ExpectMsg(RelayResultReason.AlreadyExists); + senderProbe.ExpectMsg(p => p.Result == VerifyResult.AlreadyExists); } } diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 86ad694641..08d18ce58f 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -74,8 +74,8 @@ private Transaction CreateTransactionWithFee(long fee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); - mock.Setup(p => p.VerifyForEachBlock(It.IsAny(), It.IsAny())).Returns(RelayResultReason.Succeed); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(RelayResultReason.Succeed); + mock.Setup(p => p.VerifyForEachBlock(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = fee; @@ -99,8 +99,8 @@ private Transaction CreateTransactionWithFeeAndBalanceVerify(long fee) random.NextBytes(randomBytes); Mock mock = new Mock(); UInt160 sender = UInt160.Zero; - mock.Setup(p => p.VerifyForEachBlock(It.IsAny(), It.IsAny())).Returns((StoreView snapshot, BigInteger amount) => NativeContract.GAS.BalanceOf(snapshot, sender) >= amount + fee ? RelayResultReason.Succeed : RelayResultReason.InsufficientFunds); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(RelayResultReason.Succeed); + mock.Setup(p => p.VerifyForEachBlock(It.IsAny(), It.IsAny())).Returns((StoreView snapshot, BigInteger amount) => NativeContract.GAS.BalanceOf(snapshot, sender) >= amount + fee ? VerifyResult.Succeed : VerifyResult.InsufficientFunds); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.Sender = sender; mock.Object.NetworkFee = fee; diff --git a/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs b/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs index 6268fa0fde..2b0d7a349b 100644 --- a/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs +++ b/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs @@ -18,8 +18,8 @@ private Transaction CreateTransactionWithFee(long networkFee, long systemFee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); - mock.Setup(p => p.VerifyForEachBlock(It.IsAny(), It.IsAny())).Returns(RelayResultReason.Succeed); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(RelayResultReason.Succeed); + mock.Setup(p => p.VerifyForEachBlock(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = networkFee; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 339df5605e..06ead63238 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -806,7 +806,7 @@ public void Transaction_Reverify_Hashes_Length_Unequal_To_Witnesses_Length() }; UInt160[] hashes = txSimple.GetScriptHashesForVerifying(snapshot); Assert.AreEqual(2, hashes.Length); - Assert.AreNotEqual(RelayResultReason.Succeed, txSimple.VerifyForEachBlock(snapshot, BigInteger.Zero)); + Assert.AreNotEqual(VerifyResult.Succeed, txSimple.VerifyForEachBlock(snapshot, BigInteger.Zero)); } [TestMethod] From b7cf78ed2ec6304d0e21378f852cbbce5f76ede7 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 26 Mar 2020 06:13:29 +0100 Subject: [PATCH 220/305] Add GasLeft syscall (#1509) --- src/neo/SmartContract/ApplicationEngine.cs | 1 + .../SmartContract/InteropService.Runtime.cs | 7 +++ .../SmartContract/UT_Syscalls.cs | 51 +++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 500e75a6b5..fda6a3ba84 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -25,6 +25,7 @@ public partial class ApplicationEngine : ExecutionEngine public IVerifiable ScriptContainer { get; } public StoreView Snapshot { get; } public long GasConsumed { get; private set; } = 0; + public long GasLeft => testMode ? -1 : gas_amount - GasConsumed; public UInt160 CurrentScriptHash => CurrentContext?.GetState().ScriptHash; public UInt160 CallingScriptHash => CurrentContext?.GetState().CallingScriptHash; diff --git a/src/neo/SmartContract/InteropService.Runtime.cs b/src/neo/SmartContract/InteropService.Runtime.cs index b1c0dec22e..2e1eb8083d 100644 --- a/src/neo/SmartContract/InteropService.Runtime.cs +++ b/src/neo/SmartContract/InteropService.Runtime.cs @@ -29,6 +29,7 @@ public static class Runtime public static readonly InteropDescriptor Log = Register("System.Runtime.Log", Runtime_Log, 0_01000000, TriggerType.All, CallFlags.AllowNotify); public static readonly InteropDescriptor Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_01000000, TriggerType.All, CallFlags.AllowNotify); public static readonly InteropDescriptor GetNotifications = Register("System.Runtime.GetNotifications", Runtime_GetNotifications, 0_00010000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor GasLeft = Register("System.Runtime.GasLeft", Runtime_GasLeft, 0_00000400, TriggerType.All, CallFlags.None); private static bool CheckItemForNotification(StackItem state) { @@ -167,6 +168,12 @@ private static bool Runtime_CheckWitness(ApplicationEngine engine) return true; } + private static bool Runtime_GasLeft(ApplicationEngine engine) + { + engine.Push(engine.GasLeft); + return true; + } + private static bool Runtime_GetInvocationCounter(ApplicationEngine engine) { if (!engine.InvocationCounter.TryGetValue(engine.CurrentScriptHash, out var counter)) diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 8044d8ae95..442450f844 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -266,6 +266,57 @@ public void System_ExecutionEngine_GetScriptContainer() } } + [TestMethod] + public void System_Runtime_GasLeft() + { + var snapshot = Blockchain.Singleton.GetSnapshot(); + + using (var script = new ScriptBuilder()) + { + script.Emit(OpCode.NOP); + script.EmitSysCall(InteropService.Runtime.GasLeft); + script.Emit(OpCode.NOP); + script.EmitSysCall(InteropService.Runtime.GasLeft); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + script.Emit(OpCode.NOP); + script.EmitSysCall(InteropService.Runtime.GasLeft); + + // Execute + + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 100_000_000, false); + engine.LoadScript(script.ToArray()); + Assert.AreEqual(engine.Execute(), VMState.HALT); + + // Check the results + + CollectionAssert.AreEqual + ( + engine.ResultStack.Select(u => (int)((VM.Types.Integer)u).GetBigInteger()).ToArray(), + new int[] { 99_999_570, 99_999_140, 99_998_650 } + ); + } + + // Check test mode + + using (var script = new ScriptBuilder()) + { + script.EmitSysCall(InteropService.Runtime.GasLeft); + + // Execute + + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + // Check the results + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(1, engine.ResultStack.Count); + Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(Integer)); + Assert.AreEqual(-1, engine.ResultStack.Pop().GetBigInteger()); + } + } + [TestMethod] public void System_Runtime_GetInvocationCounter() { From b7d150ff8ce87ce8e9916911fa7695056072539c Mon Sep 17 00:00:00 2001 From: ShawnYun <42930111+ShawnYun@users.noreply.github.com> Date: Fri, 27 Mar 2020 15:57:25 +0800 Subject: [PATCH 221/305] Update akka to 1.4.2 (#1466) --- src/neo/neo.csproj | 2 +- tests/neo.UnitTests/neo.UnitTests.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 2b146e992e..465438721a 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -21,7 +21,7 @@ - + diff --git a/tests/neo.UnitTests/neo.UnitTests.csproj b/tests/neo.UnitTests/neo.UnitTests.csproj index 5857ea504f..25aa788129 100644 --- a/tests/neo.UnitTests/neo.UnitTests.csproj +++ b/tests/neo.UnitTests/neo.UnitTests.csproj @@ -20,8 +20,8 @@ - - + + From f4e17251ca57f83957b3aa7f9205fbd24ca26f18 Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Tue, 31 Mar 2020 02:29:02 +0800 Subject: [PATCH 222/305] Optimize wallet tests (#1499) * Optimize the wallet UT * Optimize wallet UT test Co-authored-by: erikzhang Co-authored-by: Jin Qiao --- .../Wallets/SQLite/UT_UserWallet.cs | 155 ++++++++---------- 1 file changed, 70 insertions(+), 85 deletions(-) diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs index ad37d38d77..2a1cc699b0 100644 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs +++ b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs @@ -16,76 +16,46 @@ namespace Neo.UnitTests.Wallets.SQLite [TestClass] public class UT_UserWallet { - private string path; - private UserWallet wallet; + private static string path; + private static UserWallet wallet; + private static WalletAccount account; public static string GetRandomPath() { string threadName = Thread.CurrentThread.ManagedThreadId.ToString(); return Path.GetFullPath(string.Format("Wallet_{0}", new Random().Next(1, 1000000).ToString("X8")) + threadName); } - [TestInitialize] - public void Setup() + [ClassInitialize] + public static void Setup(TestContext ctx) { path = GetRandomPath(); wallet = UserWallet.Create(path, "123456", new ScryptParameters(0, 0, 0)); + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + account = wallet.CreateAccount(privateKey); } - [TestCleanup] - public void Cleanup() + [ClassCleanup] + public static void Cleanup() { TestUtils.DeleteFile(path); } [TestMethod] - public void TestGetName() - { - wallet.Name.Should().Be(Path.GetFileNameWithoutExtension(path)); - } - - [TestMethod] - public void TestGetVersion() - { - Action action = () => wallet.Version.ToString(); - action.Should().NotThrow(); - } - - [TestMethod] - public void TestCreateAndOpenSecureString() + public void TestChangePassword() { - string myPath = GetRandomPath(); - var ss = new SecureString(); - ss.AppendChar('a'); - ss.AppendChar('b'); - ss.AppendChar('c'); - - var w1 = UserWallet.Create(myPath, ss); - w1.Should().NotBeNull(); - - var w2 = UserWallet.Open(myPath, ss); - w2.Should().NotBeNull(); - - ss.AppendChar('d'); - Action action = () => UserWallet.Open(myPath, ss); - action.Should().Throw(); - - TestUtils.DeleteFile(myPath); + wallet.ChangePassword("123455", "654321").Should().BeFalse(); + wallet.ChangePassword("123456", "654321").Should().BeTrue(); + wallet.ChangePassword("654321", "123456").Should().BeTrue(); } [TestMethod] - public void TestOpen() + public void TestContains() { - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - var account = wallet.CreateAccount(privateKey); - var w1 = UserWallet.Open(path, "123456"); - w1.Should().NotBeNull(); - - Action action = () => UserWallet.Open(path, "123"); - action.Should().Throw(); + wallet.Contains(account.ScriptHash).Should().BeTrue(); } [TestMethod] @@ -103,6 +73,8 @@ public void TestCreateAccountAndGetByPrivateKey() var account1 = wallet.CreateAccount(privateKey); var dbAccount1 = wallet.GetAccount(account1.ScriptHash); account1.Should().Be(dbAccount1); + wallet.DeleteAccount(account.ScriptHash); + wallet.DeleteAccount(account1.ScriptHash); } [TestMethod] @@ -111,6 +83,7 @@ public void TestCreateAccountByScriptHash() var account = wallet.CreateAccount(UInt160.Parse("0xa6ee944042f3c7ea900481a95d65e4a887320cf0")); var dbAccount = wallet.GetAccount(account.ScriptHash); account.Should().Be(dbAccount); + wallet.DeleteAccount(account.ScriptHash); } [TestMethod] @@ -145,65 +118,77 @@ public void TestCreateAccountBySmartContract() var account2 = wallet.CreateAccount(contract2, key2); var dbAccount2 = wallet.GetAccount(account2.ScriptHash); account2.Should().Be(dbAccount2); + wallet.DeleteAccount(account.ScriptHash); + wallet.DeleteAccount(account2.ScriptHash); } [TestMethod] - public void TestDeleteAccount() + public void TestCreateAndOpenSecureString() { - bool ret = wallet.DeleteAccount(UInt160.Parse("0xa6ee944042f3c7ea900481a95d65e4a887320cf0")); - ret.Should().BeFalse(); + string myPath = GetRandomPath(); + var ss = new SecureString(); + ss.AppendChar('a'); + ss.AppendChar('b'); + ss.AppendChar('c'); - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + var w1 = UserWallet.Create(myPath, ss, new ScryptParameters(0, 0, 0)); + w1.Should().NotBeNull(); + + var w2 = UserWallet.Open(myPath, ss); + w2.Should().NotBeNull(); + + ss.AppendChar('d'); + Action action = () => UserWallet.Open(myPath, ss); + action.Should().Throw(); + + TestUtils.DeleteFile(myPath); + } + + [TestMethod] + public void TestGetAccounts() + { + var ret = wallet.GetAccounts(); + ret.Should().NotBeEmpty(); + foreach (var dbAccount in ret) { - rng.GetBytes(privateKey); + dbAccount.Should().Be(account); } - var account = wallet.CreateAccount(privateKey); - bool ret2 = wallet.DeleteAccount(account.ScriptHash); - ret2.Should().BeTrue(); } [TestMethod] - public void TestChangePassword() + public void TestGetName() { - wallet.ChangePassword("123455", "654321").Should().BeFalse(); - wallet.ChangePassword("123456", "654321").Should().BeTrue(); - wallet.ChangePassword("654321", "123456").Should().BeTrue(); + wallet.Name.Should().Be(Path.GetFileNameWithoutExtension(path)); } [TestMethod] - public void TestContains() + public void TestGetVersion() { - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - var account = wallet.CreateAccount(privateKey); - wallet.Contains(account.ScriptHash).Should().BeTrue(); + Action action = () => wallet.Version.ToString(); + action.Should().NotThrow(); } [TestMethod] - public void TestGetAccounts() + public void TestOpen() { - var ret = wallet.GetAccounts(); - ret.Should().BeNullOrEmpty(); + var w1 = UserWallet.Open(path, "123456"); + w1.Should().NotBeNull(); - byte[] privateKey = new byte[32]; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - rng.GetBytes(privateKey); - } - var account = wallet.CreateAccount(privateKey); - ret = wallet.GetAccounts(); - foreach (var dbAccount in ret) - { - dbAccount.Should().Be(account); - } + Action action = () => UserWallet.Open(path, "123"); + action.Should().Throw(); + } + + [TestMethod] + public void TestToDeleteAccount() + { + bool ret = wallet.DeleteAccount(UInt160.Parse("0xa6ee944042f3c7ea900481a95d65e4a887320cf0")); + ret.Should().BeFalse(); + bool ret2 = wallet.DeleteAccount(account.ScriptHash); + ret2.Should().BeTrue(); } [TestMethod] - public void TestVerifyPassword() + public void TestToVerifyPassword() { wallet.VerifyPassword("123456").Should().BeTrue(); wallet.VerifyPassword("123").Should().BeFalse(); From 53186fdeab5586cc67cf1496b5076700a4c9df1f Mon Sep 17 00:00:00 2001 From: cn1010 Date: Wed, 1 Apr 2020 20:55:00 +0800 Subject: [PATCH 223/305] rename bytearray to bytestring (#1524) * rename bytearray to bytestring * update dependency --- src/neo/SmartContract/BinarySerializer.cs | 4 ++-- src/neo/SmartContract/JsonSerializer.cs | 4 ++-- src/neo/VM/Helper.cs | 2 +- src/neo/neo.csproj | 2 +- .../Extensions/Nep5NativeContractExtensions.cs | 4 ++-- .../SmartContract/Iterators/UT_PrimitiveWrapper.cs | 2 +- .../SmartContract/Iterators/UT_StorageIterator.cs | 4 ++-- .../SmartContract/Native/Tokens/UT_NeoToken.cs | 2 +- .../SmartContract/Native/UT_NativeContract.cs | 4 ++-- .../SmartContract/UT_BinarySerializer.cs | 2 +- .../SmartContract/UT_InteropService.cs | 2 +- tests/neo.UnitTests/SmartContract/UT_Syscalls.cs | 14 +++++++------- tests/neo.UnitTests/VM/UT_Helper.cs | 2 +- 13 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/neo/SmartContract/BinarySerializer.cs b/src/neo/SmartContract/BinarySerializer.cs index 3f4d9b44bd..5083be59a7 100644 --- a/src/neo/SmartContract/BinarySerializer.cs +++ b/src/neo/SmartContract/BinarySerializer.cs @@ -50,7 +50,7 @@ private static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uin case StackItemType.Integer: deserialized.Push(new BigInteger(reader.ReadVarBytes(Integer.MaxSize))); break; - case StackItemType.ByteArray: + case StackItemType.ByteString: deserialized.Push(reader.ReadVarBytes((int)maxItemSize)); break; case StackItemType.Buffer: @@ -142,7 +142,7 @@ private static void Serialize(StackItem item, BinaryWriter writer, uint maxSize) case Integer integer: writer.WriteVarBytes(integer.Span); break; - case ByteArray bytes: + case ByteString bytes: writer.WriteVarBytes(bytes.Span); break; case Buffer buffer: diff --git a/src/neo/SmartContract/JsonSerializer.cs b/src/neo/SmartContract/JsonSerializer.cs index 8bf3e6758f..375709a13d 100644 --- a/src/neo/SmartContract/JsonSerializer.cs +++ b/src/neo/SmartContract/JsonSerializer.cs @@ -27,7 +27,7 @@ public static JObject Serialize(StackItem item) { return array.Select(p => Serialize(p)).ToArray(); } - case ByteArray buffer: + case ByteString buffer: { return Convert.ToBase64String(buffer.GetSpan()); } @@ -87,7 +87,7 @@ public static byte[] SerializeToByteArray(StackItem item, uint maxSize) case JsonTokenType.EndArray: writer.WriteEndArray(); break; - case ByteArray buffer: + case ByteString buffer: writer.WriteStringValue(Convert.ToBase64String(buffer.GetSpan())); break; case Integer num: diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 675d4be0d1..db17011eff 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -260,7 +260,7 @@ private static ContractParameter ToParameter(StackItem item, List<(StackItem, Co Value = item.ToBoolean() }; break; - case ByteArray array: + case ByteString array: parameter = new ContractParameter { Type = ContractParameterType.ByteArray, diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 465438721a..41d80ba88c 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs index 4a04d97c84..70098c9c04 100644 --- a/tests/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/Nep5NativeContractExtensions.cs @@ -166,7 +166,7 @@ public static string Symbol(this NativeContract contract) engine.Execute().Should().Be(VMState.HALT); var result = engine.ResultStack.Pop(); - result.Should().BeOfType(typeof(VM.Types.ByteArray)); + result.Should().BeOfType(typeof(VM.Types.ByteString)); return result.GetString(); } @@ -186,7 +186,7 @@ public static string Name(this NativeContract contract) engine.Execute().Should().Be(VMState.HALT); var result = engine.ResultStack.Pop(); - result.Should().BeOfType(typeof(VM.Types.ByteArray)); + result.Should().BeOfType(typeof(VM.Types.ByteString)); return result.GetString(); } diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs index d8fd144812..72c4750225 100644 --- a/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs +++ b/tests/neo.UnitTests/SmartContract/Iterators/UT_PrimitiveWrapper.cs @@ -13,7 +13,7 @@ public class UT_PrimitiveWrapper [TestMethod] public void TestGeneratorAndDispose() { - ByteArrayWrapper arrayWrapper = new ByteArrayWrapper(new ByteArray(new byte[0])); + ByteArrayWrapper arrayWrapper = new ByteArrayWrapper(new ByteString(new byte[0])); Assert.IsNotNull(arrayWrapper); Action action = () => arrayWrapper.Dispose(); action.Should().NotThrow(); diff --git a/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs b/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs index a0126b3b03..72cb823d70 100644 --- a/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs +++ b/tests/neo.UnitTests/SmartContract/Iterators/UT_StorageIterator.cs @@ -31,8 +31,8 @@ public void TestKeyAndValueAndNext() list.Add((storageKey, storageItem)); StorageIterator storageIterator = new StorageIterator(list.GetEnumerator()); storageIterator.Next(); - Assert.AreEqual(new ByteArray(new byte[1]), storageIterator.Key()); - Assert.AreEqual(new ByteArray(new byte[1]), storageIterator.Value()); + 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/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 6533b07541..ad17dffb28 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -690,7 +690,7 @@ internal static void CheckBalance(byte[] account, DataCache u.GetType()).ToArray().Should().BeEquivalentTo(new Type[] { typeof(VM.Types.Integer), typeof(VM.Types.Integer), typeof(VM.Types.ByteArray) }); // Balance + st.Select(u => u.GetType()).ToArray().Should().BeEquivalentTo(new Type[] { typeof(VM.Types.Integer), typeof(VM.Types.Integer), typeof(VM.Types.ByteString) }); // Balance st[0].GetBigInteger().Should().Be(balance); // Balance st[1].GetBigInteger().Should().Be(height); // BalanceHeight diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 2252e60d8a..bbcbcd9d1d 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -49,13 +49,13 @@ public void TestInvoke() sb2.EmitSysCall("test".ToInteropMethodHash()); engine2.LoadScript(sb2.ToArray()); - ByteArray method1 = new ByteArray(System.Text.Encoding.Default.GetBytes("wrongMethod")); + ByteString method1 = new ByteString(System.Text.Encoding.Default.GetBytes("wrongMethod")); VMArray args1 = new VMArray(); engine2.CurrentContext.EvaluationStack.Push(args1); engine2.CurrentContext.EvaluationStack.Push(method1); testNativeContract.Invoke(engine2).Should().BeFalse(); - ByteArray method2 = new ByteArray(System.Text.Encoding.Default.GetBytes("onPersist")); + ByteString method2 = new ByteString(System.Text.Encoding.Default.GetBytes("onPersist")); VMArray args2 = new VMArray(); engine2.CurrentContext.EvaluationStack.Push(args2); engine2.CurrentContext.EvaluationStack.Push(method2); diff --git a/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs b/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs index 39330334d9..e5c0b0063d 100644 --- a/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs +++ b/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs @@ -76,7 +76,7 @@ public void TestSerialize() [TestMethod] public void TestDeserializeStackItem() { - StackItem stackItem1 = new ByteArray(new byte[5]); + StackItem stackItem1 = new ByteString(new byte[5]); byte[] byteArray1 = BinarySerializer.Serialize(stackItem1, MaxItemSize); StackItem result1 = BinarySerializer.Deserialize(byteArray1, 2048, (uint)byteArray1.Length); Assert.AreEqual(stackItem1, result1); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index fb8d668c10..27fd3abd46 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -471,7 +471,7 @@ public void TestBlockchain_GetContract() InteropService.Invoke(engine, InteropService.Blockchain.GetContract).Should().BeTrue(); var stackItems = ((VM.Types.Array)engine.CurrentContext.EvaluationStack.Pop()).ToArray(); stackItems.Length.Should().Be(3); - stackItems[0].GetType().Should().Be(typeof(ByteArray)); + stackItems[0].GetType().Should().Be(typeof(ByteString)); stackItems[0].GetSpan().ToHexString().Should().Be(state.Script.ToHexString()); stackItems[1].ToBoolean().Should().BeFalse(); stackItems[2].ToBoolean().Should().BeFalse(); diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 442450f844..f7d80a4d84 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -95,7 +95,7 @@ public void System_Blockchain_GetBlock() Assert.AreEqual(engine.Execute(), VMState.HALT); Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); + Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteString)); Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(), "5b22366b4139757552614430373634585358466c706674686b436b5954702f6e34623878715057476c6a6659303d222c332c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c322c302c224141414141414141414141414141414141414141414141414141413d222c315d"); Assert.AreEqual(0, engine.ResultStack.Count); @@ -194,11 +194,11 @@ public void Json_Serialize() Assert.AreEqual(engine.Execute(), VMState.HALT); Assert.AreEqual(5, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.TryPop(out var m) && m.GetString() == "{\"key\":\"dmFsdWU=\"}"); - Assert.IsTrue(engine.ResultStack.TryPop(out var n) && n.GetString() == "null"); - Assert.IsTrue(engine.ResultStack.TryPop(out var s) && s.GetString() == "\"dGVzdA==\""); - Assert.IsTrue(engine.ResultStack.TryPop(out var b) && b.GetString() == "true"); - Assert.IsTrue(engine.ResultStack.TryPop(out var i) && i.GetString() == "5"); + Assert.IsTrue(engine.ResultStack.TryPop(out var m) && m.GetString() == "{\"key\":\"dmFsdWU=\"}"); + Assert.IsTrue(engine.ResultStack.TryPop(out var n) && n.GetString() == "null"); + Assert.IsTrue(engine.ResultStack.TryPop(out var s) && s.GetString() == "\"dGVzdA==\""); + Assert.IsTrue(engine.ResultStack.TryPop(out var b) && b.GetString() == "true"); + Assert.IsTrue(engine.ResultStack.TryPop(out var i) && i.GetString() == "5"); } } @@ -259,7 +259,7 @@ public void System_ExecutionEngine_GetScriptContainer() Assert.AreEqual(engine.Execute(), VMState.HALT); Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteArray)); + Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteString)); Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(), @"5b225c75303032426b53415959527a4c4b69685a676464414b50596f754655737a63544d7867445a6572584a3172784c37303d222c362c342c222f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f383d222c332c322c352c2241513d3d225d"); Assert.AreEqual(0, engine.ResultStack.Count); diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 1a749bc567..64e36f194f 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -518,7 +518,7 @@ private void TestToParameter2Integer() private void TestToParameter2ByteArray() { - StackItem item = new VM.Types.ByteArray(new byte[] { 0x00 }); + StackItem item = new VM.Types.ByteString(new byte[] { 0x00 }); ContractParameter parameter = VM.Helper.ToParameter(item); Assert.AreEqual(ContractParameterType.ByteArray, parameter.Type); Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0x00 }), Encoding.Default.GetString((byte[])parameter.Value)); From ddb818f77cb0e2d9810f76bff470d21eec0f43ad Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Fri, 3 Apr 2020 13:14:48 +0800 Subject: [PATCH 224/305] Support changing password for nep6 wallet (#1504) --- src/neo/Wallets/NEP6/NEP6Account.cs | 46 ++++++++++++++++++- src/neo/Wallets/NEP6/NEP6Wallet.cs | 30 ++++++++++++ .../Wallets/NEP6/UT_NEP6Account.cs | 20 ++++++++ .../Wallets/NEP6/UT_NEP6Wallet.cs | 20 ++++++++ 4 files changed, 115 insertions(+), 1 deletion(-) diff --git a/src/neo/Wallets/NEP6/NEP6Account.cs b/src/neo/Wallets/NEP6/NEP6Account.cs index 990612d542..58ed806fb1 100644 --- a/src/neo/Wallets/NEP6/NEP6Account.cs +++ b/src/neo/Wallets/NEP6/NEP6Account.cs @@ -1,12 +1,14 @@ using Neo.IO.Json; using System; +using System.Threading; namespace Neo.Wallets.NEP6 { internal class NEP6Account : WalletAccount { private readonly NEP6Wallet wallet; - private readonly string nep2key; + private string nep2key; + private string nep2KeyNew = null; private KeyPair key; public JObject Extra; @@ -83,5 +85,47 @@ public bool VerifyPassword(string password) return false; } } + + /// + /// Cache draft nep2key during wallet password changing process. Should not be called alone for a single account + /// + internal bool ChangePasswordPrepare(string password_old, string password_new) + { + if (WatchOnly) return true; + KeyPair keyTemplate = key; + if (nep2key == null) + { + if (keyTemplate == null) + { + return true; + } + } + else + { + try + { + keyTemplate = new KeyPair(Wallet.GetPrivateKeyFromNEP2(nep2key, password_old, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P)); + } + catch + { + return false; + } + } + nep2KeyNew = keyTemplate.Export(password_new, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P); + return true; + } + + internal void ChangePasswordCommit() + { + if (nep2KeyNew != null) + { + nep2key = Interlocked.Exchange(ref nep2KeyNew, null); + } + } + + internal void ChangePasswordRoolback() + { + nep2KeyNew = null; + } } } diff --git a/src/neo/Wallets/NEP6/NEP6Wallet.cs b/src/neo/Wallets/NEP6/NEP6Wallet.cs index daf1b9158d..2db4d87e77 100644 --- a/src/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/neo/Wallets/NEP6/NEP6Wallet.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; using UserWallet = Neo.Wallets.SQLite.UserWallet; namespace Neo.Wallets.NEP6 @@ -300,5 +301,34 @@ public override bool VerifyPassword(string password) } } } + + public bool ChangePassword(string password_old, string password_new) + { + bool succeed = true; + lock (accounts) + { + Parallel.ForEach(accounts.Values, (account, state) => + { + if (!account.ChangePasswordPrepare(password_old, password_new)) + { + state.Stop(); + succeed = false; + } + }); + } + if (succeed) + { + foreach (NEP6Account account in accounts.Values) + account.ChangePasswordCommit(); + if (password != null) + password = password_new; + } + else + { + foreach (NEP6Account account in accounts.Values) + account.ChangePasswordRoolback(); + } + return succeed; + } } } diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index f1c719c853..addfc5b54e 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -35,6 +35,26 @@ public void TestSetup() _account = new NEP6Account(_wallet, _hash); } + [TestMethod] + public void TestChangePassword() + { + _account = new NEP6Account(_wallet, _hash, _nep2); + _account.ChangePasswordPrepare("b", "Satoshi").Should().BeTrue(); + _account.ChangePasswordCommit(); + _account.Contract = new Contract(); + _wallet.Unlock("Satoshi"); + _account.ChangePasswordPrepare("b", "Satoshi").Should().BeFalse(); + _account.ChangePasswordPrepare("Satoshi", "b").Should().BeTrue(); + _account.ChangePasswordCommit(); + _account.VerifyPassword("b").Should().BeTrue(); + _account.ChangePasswordPrepare("b", "Satoshi").Should().BeTrue(); + _account.ChangePasswordCommit(); + _account.ChangePasswordPrepare("Satoshi", "b").Should().BeTrue(); + _account.ChangePasswordRoolback(); + _account.VerifyPassword("Satoshi").Should().BeTrue(); + _wallet.Lock(); + } + [TestMethod] public void TestConstructorWithNep2Key() { diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index dcf436e993..0e8ac02e05 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -70,6 +70,26 @@ public void TestCleanUp() if (Directory.Exists(rootPath)) Directory.Delete(rootPath); } + [TestMethod] + public void TestChangePassword() + { + JObject wallet = new JObject(); + wallet["name"] = "name"; + wallet["version"] = new System.Version("3.0").ToString(); + wallet["scrypt"] = new ScryptParameters(0, 0, 0).ToJson(); + wallet["accounts"] = new JArray(); + wallet["extra"] = new JObject(); + File.WriteAllText(wPath, wallet.ToString()); + uut = new NEP6Wallet(wPath); + uut.Unlock("123"); + uut.CreateAccount(keyPair.PrivateKey); + uut.ChangePassword("456", "123").Should().BeFalse(); + uut.ChangePassword("123", "456").Should().BeTrue(); + uut.VerifyPassword("456").Should().BeTrue(); + uut.ChangePassword("456", "123").Should().BeTrue(); + uut.Lock(); + } + [TestMethod] public void TestConstructorWithPathAndName() { From 8b043e880d00949169bb8d01d31a6eea83c80ed5 Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Fri, 3 Apr 2020 17:28:13 +0800 Subject: [PATCH 225/305] Remove UT_Culture (#1529) * Optimize the wallet UT * Optimize wallet UT test * Test 2 culture * Code optimization * Delete UT_Culture Co-authored-by: erikzhang Co-authored-by: Jin Qiao --- .../Network/P2P/UT_RemoteNode.cs | 1 - .../Network/P2P/UT_RemoteNodeMailbox.cs | 1 - tests/neo.UnitTests/UT_Culture.cs | 108 ------------------ 3 files changed, 110 deletions(-) delete mode 100644 tests/neo.UnitTests/UT_Culture.cs diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs index a700902f6c..7b2e083cbb 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs @@ -9,7 +9,6 @@ namespace Neo.UnitTests.Network.P2P { [TestClass] - [NotReRunnable] public class UT_RemoteNode : TestKit { private static NeoSystem testBlockchain; diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs index b57e9e93a7..cd566f44f2 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs @@ -8,7 +8,6 @@ namespace Neo.UnitTests.Network.P2P { [TestClass] - [NotReRunnable] public class UT_RemoteNodeMailbox : TestKit { private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism diff --git a/tests/neo.UnitTests/UT_Culture.cs b/tests/neo.UnitTests/UT_Culture.cs deleted file mode 100644 index a16de9717a..0000000000 --- a/tests/neo.UnitTests/UT_Culture.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections; -using System.Globalization; -using System.Linq; -using System.Reflection; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_Culture - { - // This test runs all the other unit tests in the project, with a variety of cultures - // This test will fail when any other test in the project fails. Fix the other failing test(s) and this test should pass again. - [TestMethod] - [NotReRunnable] - public void All_Tests_Cultures() - { - // get all tests in the unit test project assembly - var testClasses = (from t in typeof(NotReRunnableAttribute).GetTypeInfo().Assembly.DefinedTypes - where t.GetCustomAttribute() != null && t.GetCustomAttribute() == null - select new - { - Constructor = t.GetConstructor(new Type[] { }), - ClassInit = t.GetMethods().Where( - m => m.GetCustomAttribute() != null).SingleOrDefault(), - TestInit = t.GetMethods().Where( - m => m.GetCustomAttribute() != null).SingleOrDefault(), - TestCleanup = t.GetMethods().Where( - m => m.GetCustomAttribute() != null).SingleOrDefault(), - ClassCleanup = t.GetMethods().Where( - m => m.GetCustomAttribute() != null).SingleOrDefault(), - TestMethods = t.GetMethods().Where( - m => m.GetCustomAttribute() != null - && m.GetCustomAttribute() == null).ToList() - }).ToList(); - - var cultures = new string[] { "en-US", "zh-CN", "de-DE", "ko-KR", "ja-JP" }; - var originalUICulture = CultureInfo.CurrentCulture; - var emptyObjArray = new object[] { }; - var testContext = new object[] { new UnitTestContext() }; - - // run all the tests, varying the culture each time. - try - { - foreach (var culture in cultures) - { - CultureInfo.CurrentCulture = new CultureInfo(culture); - - foreach (var c in testClasses) - { - var instance = c.Constructor.Invoke(emptyObjArray); - if (c.ClassInit != null) - { - c.ClassInit.Invoke(instance, testContext); - } - foreach (var m in c.TestMethods) - { - if (c.TestInit != null) - { - c.TestInit.Invoke(instance, emptyObjArray); - } - m.Invoke(instance, emptyObjArray); - if (c.TestCleanup != null) - { - c.TestCleanup.Invoke(instance, emptyObjArray); - } - } - if (c.ClassCleanup != null) - { - c.ClassCleanup.Invoke(instance, emptyObjArray); - } - } - } - } - finally - { - CultureInfo.CurrentCulture = originalUICulture; - } - - } - } - - public class UnitTestContext : TestContext - { - public override IDictionary Properties => throw new NotImplementedException(); - - public override void AddResultFile(string fileName) - { - Console.WriteLine(fileName); - } - - public override void WriteLine(string message) - { - Console.WriteLine(message); - } - - public override void WriteLine(string format, params object[] args) - { - Console.WriteLine(format, args); - } - } - - public class NotReRunnableAttribute : Attribute - { - - } -} From 9fe188a233f5f1a31384e5111b5b83d82ebef057 Mon Sep 17 00:00:00 2001 From: cn1010 Date: Wed, 8 Apr 2020 12:56:04 +0800 Subject: [PATCH 226/305] remove round up sysfee (#1522) --- src/neo/Network/P2P/Payloads/Transaction.cs | 1 - src/neo/Wallets/Wallet.cs | 9 --------- 2 files changed, 10 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index f5a17fbf11..6035ac2285 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -172,7 +172,6 @@ public void DeserializeUnsigned(BinaryReader reader) Sender = reader.ReadSerializable(); SystemFee = reader.ReadInt64(); if (SystemFee < 0) throw new FormatException(); - if (SystemFee % NativeContract.GAS.Factor != 0) throw new FormatException(); NetworkFee = reader.ReadInt64(); if (NetworkFee < 0) throw new FormatException(); if (SystemFee + NetworkFee < SystemFee) throw new FormatException(); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 722c4dadea..0c8151a257 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -319,15 +319,6 @@ private Transaction MakeTransaction(StoreView snapshot, byte[] script, Transacti if (engine.State.HasFlag(VMState.FAULT)) throw new InvalidOperationException($"Failed execution for '{script.ToHexString()}'"); tx.SystemFee = Math.Max(engine.GasConsumed - ApplicationEngine.GasFree, 0); - if (tx.SystemFee > 0) - { - long d = (long)NativeContract.GAS.Factor; - long remainder = tx.SystemFee % d; - if (remainder > 0) - tx.SystemFee += d - remainder; - else if (remainder < 0) - tx.SystemFee -= remainder; - } } UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); From fbc0eba85c84f3d967a5a27c922ca3b64dc5cf20 Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Wed, 8 Apr 2020 14:39:25 +0800 Subject: [PATCH 227/305] Enable converting between public keys, script hashes and address (#1428) * Enable converting between public keys, script hashes and address * Add test cases * Code optimization * Fee adjusting * Code optimization * Add comment * Code optimization * Code optimization * Merge from master * Update InteropService.Contract.cs * Code optimization * Add wrong public key test cases * Kick off new test * format changing * Code optimization * Add comment Co-authored-by: Jin Qiao Co-authored-by: Shargon Co-authored-by: erikzhang --- .../SmartContract/InteropService.Contract.cs | 15 +++++++ .../SmartContract/UT_InteropService.cs | 39 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index f0fb6bfef1..bb85af6091 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -1,3 +1,4 @@ +using Neo.Cryptography.ECC; using Neo.IO; using Neo.Ledger; using Neo.Persistence; @@ -20,6 +21,12 @@ public static class Contract public static readonly InteropDescriptor CallEx = Register("System.Contract.CallEx", Contract_CallEx, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All, 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 CreateStandardAccount = Register("System.Contract.CreateStandardAccount", Contract_CreateStandardAccount, 0_00010000, TriggerType.All, CallFlags.None); + private static long GetDeploymentPrice(EvaluationStack stack, StoreView snapshot) { int size = stack.Peek(0).GetByteLength() + stack.Peek(1).GetByteLength(); @@ -166,6 +173,14 @@ private static bool Contract_IsStandard(ApplicationEngine engine) engine.CurrentContext.EvaluationStack.Push(isStandard); return true; } + + private static bool Contract_CreateStandardAccount(ApplicationEngine engine) + { + if (!engine.TryPop(out ReadOnlySpan pubKey)) return false; + UInt160 scriptHash = SmartContract.Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubKey, ECCurve.Secp256r1)).ToScriptHash(); + engine.Push(scriptHash.ToArray()); + return true; + } } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 27fd3abd46..bed768d2be 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -861,6 +861,45 @@ public void TestContract_Destroy() } + [TestMethod] + public void TestContract_CreateStandardAccount() + { + var engine = GetEngine(true, true); + byte[] data = "024b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e".HexToBytes(); + + engine.CurrentContext.EvaluationStack.Push(data); + InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray().Should().BeEquivalentTo(UInt160.Parse("0x2c847208959ec1cc94dd13bfe231fa622a404a8a").ToArray()); + + data = "064b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e".HexToBytes(); + engine.CurrentContext.EvaluationStack.Push(data); + Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("Invalid point encoding 6"); + + data = "024b817ef37f2fc3d4a33fe36687e599f30fe24b3e28187dc8f12b3b3b2b839e".HexToBytes(); + engine.CurrentContext.EvaluationStack.Push(data); + Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("Incorrect length for compressed encoding"); + + data = "02ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".HexToBytes(); + engine.CurrentContext.EvaluationStack.Push(data); + Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("x value too large in field element"); + + data = "020fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".HexToBytes(); + engine.CurrentContext.EvaluationStack.Push(data); + Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("Invalid point compression"); + + data = "044b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e".HexToBytes(); + engine.CurrentContext.EvaluationStack.Push(data); + Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("Incorrect length for uncompressed/hybrid encoding"); + + data = "04ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".HexToBytes(); + engine.CurrentContext.EvaluationStack.Push(data); + Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("x value too large in field element"); + + data = "040fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".HexToBytes(); + engine.CurrentContext.EvaluationStack.Push(data); + Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("x value too large in field element"); + } + public static void LogEvent(object sender, LogEventArgs args) { Transaction tx = (Transaction)args.ScriptContainer; From 2c8733b82d27b69cb0564f74bd659ec45555d220 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 9 Apr 2020 17:01:19 +0800 Subject: [PATCH 228/305] Fix SYSCALLs (#1511) --- src/neo/SmartContract/Helper.cs | 5 +++ .../SmartContract/InteropService.Binary.cs | 13 ++----- .../SmartContract/InteropService.Contract.cs | 31 +++++++++-------- src/neo/SmartContract/InteropService.Json.cs | 34 ++++++------------- .../SmartContract/InteropService.Runtime.cs | 4 +-- .../SmartContract/InteropService.Storage.cs | 19 +++++------ .../Manifest/ContractManifest.cs | 6 ++-- src/neo/VM/Helper.cs | 1 + .../SmartContract/UT_InteropPrices.cs | 4 +-- .../SmartContract/UT_InteropService.cs | 4 +-- 10 files changed, 54 insertions(+), 67 deletions(-) diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 4823832fab..fc10de2acf 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -122,6 +122,11 @@ public static UInt160 ToScriptHash(this byte[] script) return new UInt160(Crypto.Hash160(script)); } + public static UInt160 ToScriptHash(this ReadOnlySpan script) + { + return new UInt160(Crypto.Hash160(script)); + } + internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snapshot, long gas) { if (gas < 0) return false; diff --git a/src/neo/SmartContract/InteropService.Binary.cs b/src/neo/SmartContract/InteropService.Binary.cs index fa33dbe4ba..50d5946dbd 100644 --- a/src/neo/SmartContract/InteropService.Binary.cs +++ b/src/neo/SmartContract/InteropService.Binary.cs @@ -12,16 +12,9 @@ public static class Binary private static bool Binary_Serialize(ApplicationEngine engine) { - byte[] serialized; - try - { - serialized = BinarySerializer.Serialize(engine.CurrentContext.EvaluationStack.Pop(), engine.MaxItemSize); - } - catch - { - return false; - } - engine.CurrentContext.EvaluationStack.Push(serialized); + if (!engine.TryPop(out StackItem item)) return false; + byte[] serialized = BinarySerializer.Serialize(item, engine.MaxItemSize); + engine.Push(serialized); return true; } diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index bb85af6091..351345d29b 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -14,13 +14,14 @@ partial class InteropService { public static class Contract { + public const int MaxLength = 1024 * 1024; + public static readonly InteropDescriptor Create = Register("System.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application, CallFlags.AllowModifyStates); public static readonly InteropDescriptor Update = Register("System.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application, CallFlags.AllowModifyStates); public static readonly InteropDescriptor Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates); public static readonly InteropDescriptor Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); public static readonly InteropDescriptor CallEx = Register("System.Contract.CallEx", Contract_CallEx, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All, CallFlags.None); - /// /// Calculate corresponding account scripthash for given public key /// Warning: check first that input public key is valid, before creating the script. @@ -35,11 +36,11 @@ private static long GetDeploymentPrice(EvaluationStack stack, StoreView snapshot private static bool Contract_Create(ApplicationEngine engine) { - byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - if (script.Length > 1024 * 1024) return false; + if (!engine.TryPop(out ReadOnlySpan script)) return false; + if (script.Length == 0 || script.Length > MaxLength) return false; - var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString(); - if (manifest.Length > ContractManifest.MaxLength) return false; + if (!engine.TryPop(out ReadOnlySpan manifest)) return false; + if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) return false; UInt160 hash = script.ToScriptHash(); ContractState contract = engine.Snapshot.Contracts.TryGet(hash); @@ -47,44 +48,46 @@ private static bool Contract_Create(ApplicationEngine engine) contract = new ContractState { Id = engine.Snapshot.ContractId.GetAndChange().NextId++, - Script = script, + Script = script.ToArray(), Manifest = ContractManifest.Parse(manifest) }; if (!contract.Manifest.IsValid(hash)) return false; engine.Snapshot.Contracts.Add(hash, contract); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(contract)); + engine.Push(StackItem.FromInterface(contract)); return true; } private static bool Contract_Update(ApplicationEngine engine) { - byte[] script = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - if (script.Length > 1024 * 1024) return false; - var manifest = engine.CurrentContext.EvaluationStack.Pop().GetString(); - if (manifest.Length > ContractManifest.MaxLength) return false; + if (!engine.TryPop(out StackItem item0)) return false; + if (!engine.TryPop(out StackItem item1)) return false; var contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash); if (contract is null) return false; - if (script.Length > 0) + if (!item0.IsNull) { + ReadOnlySpan script = item0.GetSpan(); + if (script.Length == 0 || script.Length > MaxLength) return false; UInt160 hash_new = script.ToScriptHash(); if (hash_new.Equals(engine.CurrentScriptHash)) return false; if (engine.Snapshot.Contracts.TryGet(hash_new) != null) return false; contract = new ContractState { Id = contract.Id, - Script = script, + Script = script.ToArray(), Manifest = contract.Manifest }; contract.Manifest.Abi.Hash = hash_new; engine.Snapshot.Contracts.Add(hash_new, contract); engine.Snapshot.Contracts.Delete(engine.CurrentScriptHash); } - if (manifest.Length > 0) + if (!item1.IsNull) { + ReadOnlySpan manifest = item1.GetSpan(); + if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) return false; contract = engine.Snapshot.Contracts.GetAndChange(contract.ScriptHash); contract.Manifest = ContractManifest.Parse(manifest); if (!contract.Manifest.IsValid(contract.ScriptHash)) return false; diff --git a/src/neo/SmartContract/InteropService.Json.cs b/src/neo/SmartContract/InteropService.Json.cs index 5ab72b5e1d..262fbe2e9a 100644 --- a/src/neo/SmartContract/InteropService.Json.cs +++ b/src/neo/SmartContract/InteropService.Json.cs @@ -1,5 +1,6 @@ using Neo.IO.Json; -using Neo.VM; +using Neo.VM.Types; +using System; namespace Neo.SmartContract { @@ -12,33 +13,18 @@ public static class Json private static bool Json_Serialize(ApplicationEngine engine) { - var item = engine.CurrentContext.EvaluationStack.Pop(); - try - { - var json = JsonSerializer.SerializeToByteArray(item, engine.MaxItemSize); - engine.CurrentContext.EvaluationStack.Push(json); - return true; - } - catch - { - return false; - } + if (!engine.TryPop(out StackItem item)) return false; + byte[] json = JsonSerializer.SerializeToByteArray(item, engine.MaxItemSize); + engine.Push(json); + return true; } private static bool Json_Deserialize(ApplicationEngine engine) { - var json = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - try - { - var obj = JObject.Parse(json, 10); - var item = JsonSerializer.Deserialize(obj, engine.ReferenceCounter); - engine.CurrentContext.EvaluationStack.Push(item); - return true; - } - catch - { - return false; - } + if (!engine.TryPop(out ReadOnlySpan json)) return false; + StackItem item = JsonSerializer.Deserialize(JObject.Parse(json, 10), engine.ReferenceCounter); + engine.Push(item); + return true; } } } diff --git a/src/neo/SmartContract/InteropService.Runtime.cs b/src/neo/SmartContract/InteropService.Runtime.cs index 2e1eb8083d..aa525c1600 100644 --- a/src/neo/SmartContract/InteropService.Runtime.cs +++ b/src/neo/SmartContract/InteropService.Runtime.cs @@ -53,7 +53,7 @@ private static bool CheckItemForNotification(StackItem state) } break; case PrimitiveType primitive: - size += primitive.GetByteLength(); + size += primitive.Size; break; case Null _: break; @@ -65,7 +65,7 @@ private static bool CheckItemForNotification(StackItem state) items_checked.Add(map); foreach (var pair in map) { - size += pair.Key.GetByteLength(); + size += pair.Key.Size; items_unchecked.Enqueue(pair.Value); } } diff --git a/src/neo/SmartContract/InteropService.Storage.cs b/src/neo/SmartContract/InteropService.Storage.cs index e732cf8b60..b7df35ea26 100644 --- a/src/neo/SmartContract/InteropService.Storage.cs +++ b/src/neo/SmartContract/InteropService.Storage.cs @@ -27,25 +27,22 @@ public static class Storage private static long GetStoragePrice(EvaluationStack stack, StoreView snapshot) { - var key = stack.Peek(1); - var value = stack.Peek(2); - var newDataSize = value.IsNull ? 0 : value.GetByteLength(); - if (!(stack.Peek() is InteropInterface _interface)) - throw new InvalidOperationException(); - - StorageContext context = _interface.GetInterface(); + StorageContext context = ((InteropInterface)stack.Peek(0)).GetInterface(); + ReadOnlySpan key = stack.Peek(1).GetSpan(); + ReadOnlySpan value = stack.Peek(2).GetSpan(); + int newDataSize; StorageKey skey = new StorageKey { Id = context.Id, - Key = key.GetSpan().ToArray() + Key = key.ToArray() }; var skeyValue = snapshot.Storages.TryGet(skey); if (skeyValue is null) - newDataSize += key.GetByteLength(); - else if (newDataSize <= skeyValue.Value.Length) + newDataSize = key.Length + value.Length; + else if (value.Length <= skeyValue.Value.Length) newDataSize = 1; else - newDataSize -= skeyValue.Value.Length; + newDataSize = value.Length - skeyValue.Value.Length; return newDataSize * GasPerByte; } diff --git a/src/neo/SmartContract/Manifest/ContractManifest.cs b/src/neo/SmartContract/Manifest/ContractManifest.cs index 112a608030..0ad289c9da 100644 --- a/src/neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/neo/SmartContract/Manifest/ContractManifest.cs @@ -1,6 +1,6 @@ using Neo.IO; using Neo.IO.Json; -using System.Collections.Generic; +using System; using System.IO; using System.Linq; @@ -117,7 +117,9 @@ public static ContractManifest FromJson(JObject json) /// /// Json /// Return ContractManifest - public static ContractManifest Parse(string json) => FromJson(JObject.Parse(json)); + public static ContractManifest Parse(ReadOnlySpan json) => FromJson(JObject.Parse(json)); + + internal static ContractManifest Parse(string json) => FromJson(JObject.Parse(json)); /// p.Size, Buffer b => b.Size, + Null _ => 0, _ => throw new ArgumentException(), }; } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index e34c80eb96..c4c050fd2c 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -69,7 +69,7 @@ public void ApplicationEngineVariablePrices() debugger.StepInto(); // push 03 (length 1) debugger.StepInto(); // push 00 Action act = () => InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); - act.Should().Throw(); + act.Should().Throw(); } // System.Storage.PutEx: 73e19b3a (requires push key and value) @@ -82,7 +82,7 @@ public void ApplicationEngineVariablePrices() debugger.StepInto(); // push 03 (length 1) debugger.StepInto(); // push 00 Action act = () => InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); - act.Should().Throw(); + act.Should().Throw(); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index bed768d2be..f1420fac23 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -338,10 +338,10 @@ public void TestRuntime_Serialize() .Should().Be(new byte[] { 0x21, 0x01, 0x64 }.ToHexString()); engine.CurrentContext.EvaluationStack.Push(new byte[1024 * 1024 * 2]); //Larger than MaxItemSize - InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeFalse(); + Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Binary.Serialize)); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new object())); //NotSupportedException - InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeFalse(); + Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Binary.Serialize)); } [TestMethod] From 875b5b43b896f71bec562a5a215b290648a49f80 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 9 Apr 2020 17:05:19 +0800 Subject: [PATCH 229/305] Combine RemoteNode and ProtocolHandler (#1520) --- src/neo/NeoSystem.cs | 1 - src/neo/Network/P2P/Connection.cs | 10 +- ...ndler.cs => RemoteNode.ProtocolHandler.cs} | 166 ++++++---------- src/neo/Network/P2P/RemoteNode.cs | 116 ++++------- .../Network/P2P/UT_ProtocolHandler.cs | 42 ---- .../Network/P2P/UT_ProtocolHandlerMailbox.cs | 185 ------------------ .../Network/P2P/UT_RemoteNode.cs | 16 +- .../Network/P2P/UT_RemoteNodeMailbox.cs | 146 +++++++++++++- 8 files changed, 251 insertions(+), 431 deletions(-) rename src/neo/Network/P2P/{ProtocolHandler.cs => RemoteNode.ProtocolHandler.cs} (74%) delete mode 100644 tests/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs delete mode 100644 tests/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index b837426d45..301df2c5d6 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -16,7 +16,6 @@ public class NeoSystem : IDisposable $"blockchain-mailbox {{ mailbox-type: \"{typeof(BlockchainMailbox).AssemblyQualifiedName}\" }}" + $"task-manager-mailbox {{ mailbox-type: \"{typeof(TaskManagerMailbox).AssemblyQualifiedName}\" }}" + $"remote-node-mailbox {{ mailbox-type: \"{typeof(RemoteNodeMailbox).AssemblyQualifiedName}\" }}" + - $"protocol-handler-mailbox {{ mailbox-type: \"{typeof(ProtocolHandlerMailbox).AssemblyQualifiedName}\" }}" + $"consensus-service-mailbox {{ mailbox-type: \"{typeof(ConsensusServiceMailbox).AssemblyQualifiedName}\" }}"); public IActorRef Blockchain { get; } public IActorRef LocalNode { get; } diff --git a/src/neo/Network/P2P/Connection.cs b/src/neo/Network/P2P/Connection.cs index 7dff7e3a02..b12b2723a5 100644 --- a/src/neo/Network/P2P/Connection.cs +++ b/src/neo/Network/P2P/Connection.cs @@ -9,7 +9,7 @@ namespace Neo.Network.P2P { public abstract class Connection : UntypedActor { - internal class Timer { public static Timer Instance = new Timer(); } + internal class Close { public bool Abort; } internal class Ack : Tcp.Event { public static Ack Instance = new Ack(); } /// @@ -32,7 +32,7 @@ protected Connection(object connection, IPEndPoint remote, IPEndPoint local) { this.Remote = remote; this.Local = local; - this.timer = Context.System.Scheduler.ScheduleTellOnceCancelable(TimeSpan.FromSeconds(connectionTimeoutLimitStart), Self, Timer.Instance, ActorRefs.NoSender); + this.timer = Context.System.Scheduler.ScheduleTellOnceCancelable(TimeSpan.FromSeconds(connectionTimeoutLimitStart), Self, new Close { Abort = true }, ActorRefs.NoSender); switch (connection) { case IActorRef tcp: @@ -89,8 +89,8 @@ protected override void OnReceive(object message) { switch (message) { - case Timer _: - Disconnect(true); + case Close close: + Disconnect(close.Abort); break; case Ack _: OnAck(); @@ -107,7 +107,7 @@ protected override void OnReceive(object message) private void OnReceived(ByteString data) { timer.CancelIfNotNull(); - timer = Context.System.Scheduler.ScheduleTellOnceCancelable(TimeSpan.FromSeconds(connectionTimeoutLimit), Self, Timer.Instance, ActorRefs.NoSender); + timer = Context.System.Scheduler.ScheduleTellOnceCancelable(TimeSpan.FromSeconds(connectionTimeoutLimit), Self, new Close { Abort = true }, ActorRefs.NoSender); try { OnData(data); diff --git a/src/neo/Network/P2P/ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs similarity index 74% rename from src/neo/Network/P2P/ProtocolHandler.cs rename to src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index a97cb46922..91b3921326 100644 --- a/src/neo/Network/P2P/ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -1,10 +1,8 @@ using Akka.Actor; -using Akka.Configuration; using Neo.Cryptography; -using Neo.IO; -using Neo.IO.Actors; using Neo.IO.Caching; using Neo.Ledger; +using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.Plugins; @@ -17,11 +15,9 @@ namespace Neo.Network.P2P { - internal class ProtocolHandler : UntypedActor + partial class RemoteNode { - public class SetFilter { public BloomFilter Filter; } - internal class Timer { } - + private class Timer { } private class PendingKnownHashesCollection : KeyedCollection { protected override UInt256 GetKeyForItem((UInt256, DateTime) item) @@ -30,11 +26,9 @@ protected override UInt256 GetKeyForItem((UInt256, DateTime) item) } } - private readonly NeoSystem system; - private readonly PendingKnownHashesCollection pendingKnownHashes; - private readonly HashSetCache knownHashes; - private readonly HashSetCache sentHashes; - private VersionPayload version; + private readonly PendingKnownHashesCollection pendingKnownHashes = new PendingKnownHashesCollection(); + private readonly HashSetCache knownHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); + private readonly HashSetCache sentHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); private bool verack = false; private BloomFilter bloom_filter; @@ -43,33 +37,12 @@ protected override UInt256 GetKeyForItem((UInt256, DateTime) item) private readonly ICancelable timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimerInterval, TimerInterval, Context.Self, new Timer(), ActorRefs.NoSender); - public ProtocolHandler(NeoSystem system) - { - this.system = system; - this.pendingKnownHashes = new PendingKnownHashesCollection(); - this.knownHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); - this.sentHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); - } - - protected override void OnReceive(object message) - { - switch (message) - { - case Message msg: - OnMessage(msg); - break; - case Timer _: - OnTimer(); - break; - } - } - private void OnMessage(Message msg) { foreach (IP2PPlugin plugin in Plugin.P2PPlugins) if (!plugin.OnP2PMessage(msg)) return; - if (version == null) + if (Version == null) { if (msg.Command != MessageCommand.Version) throw new ProtocolViolationException(); @@ -158,20 +131,17 @@ private void OnAddrMessageReceived(AddrPayload payload) private void OnFilterAddMessageReceived(FilterAddPayload payload) { - if (bloom_filter != null) - bloom_filter.Add(payload.Data); + bloom_filter?.Add(payload.Data); } private void OnFilterClearMessageReceived() { bloom_filter = null; - Context.Parent.Tell(new SetFilter { Filter = null }); } private void OnFilterLoadMessageReceived(FilterLoadPayload payload) { bloom_filter = new BloomFilter(payload.Filter.Length * 8, payload.K, payload.Tweak, payload.Filter); - Context.Parent.Tell(new SetFilter { Filter = bloom_filter }); } /// @@ -189,7 +159,7 @@ private void OnGetAddrMessageReceived() .Take(AddrPayload.MaxCountToSend); NetworkAddressWithTime[] networkAddresses = peers.Select(p => NetworkAddressWithTime.Create(p.Listener.Address, p.Version.Timestamp, p.Version.Capabilities)).ToArray(); if (networkAddresses.Length == 0) return; - Context.Parent.Tell(Message.Create(MessageCommand.Addr, AddrPayload.Create(networkAddresses))); + EnqueueMessage(Message.Create(MessageCommand.Addr, AddrPayload.Create(networkAddresses))); } /// @@ -216,7 +186,7 @@ private void OnGetBlocksMessageReceived(GetBlocksPayload payload) hashes.Add(hash); } if (hashes.Count == 0) return; - Context.Parent.Tell(Message.Create(MessageCommand.Inv, InvPayload.Create(InventoryType.Block, hashes.ToArray()))); + EnqueueMessage(Message.Create(MessageCommand.Inv, InvPayload.Create(InventoryType.Block, hashes.ToArray()))); } private void OnGetBlockDataMessageReceived(GetBlockDataPayload payload) @@ -229,12 +199,12 @@ private void OnGetBlockDataMessageReceived(GetBlockDataPayload payload) if (bloom_filter == null) { - Context.Parent.Tell(Message.Create(MessageCommand.Block, block)); + EnqueueMessage(Message.Create(MessageCommand.Block, block)); } else { BitArray flags = new BitArray(block.Transactions.Select(p => bloom_filter.Test(p)).ToArray()); - Context.Parent.Tell(Message.Create(MessageCommand.MerkleBlock, MerkleBlockPayload.Create(block, flags))); + EnqueueMessage(Message.Create(MessageCommand.MerkleBlock, MerkleBlockPayload.Create(block, flags))); } } } @@ -255,7 +225,7 @@ private void OnGetDataMessageReceived(InvPayload payload) case InventoryType.TX: Transaction tx = Blockchain.Singleton.GetTransaction(hash); if (tx != null) - Context.Parent.Tell(Message.Create(MessageCommand.Transaction, tx)); + EnqueueMessage(Message.Create(MessageCommand.Transaction, tx)); break; case InventoryType.Block: Block block = Blockchain.Singleton.GetBlock(hash); @@ -263,18 +233,18 @@ private void OnGetDataMessageReceived(InvPayload payload) { if (bloom_filter == null) { - Context.Parent.Tell(Message.Create(MessageCommand.Block, block)); + EnqueueMessage(Message.Create(MessageCommand.Block, block)); } else { BitArray flags = new BitArray(block.Transactions.Select(p => bloom_filter.Test(p)).ToArray()); - Context.Parent.Tell(Message.Create(MessageCommand.MerkleBlock, MerkleBlockPayload.Create(block, flags))); + EnqueueMessage(Message.Create(MessageCommand.MerkleBlock, MerkleBlockPayload.Create(block, flags))); } } break; case InventoryType.Consensus: if (Blockchain.Singleton.ConsensusRelayCache.TryGet(hash, out IInventory inventoryConsensus)) - Context.Parent.Tell(Message.Create(MessageCommand.Consensus, inventoryConsensus)); + EnqueueMessage(Message.Create(MessageCommand.Consensus, inventoryConsensus)); break; } } @@ -304,18 +274,18 @@ private void OnGetHeadersMessageReceived(GetBlocksPayload payload) headers.Add(header); } if (headers.Count == 0) return; - Context.Parent.Tell(Message.Create(MessageCommand.Headers, HeadersPayload.Create(headers.ToArray()))); + EnqueueMessage(Message.Create(MessageCommand.Headers, HeadersPayload.Create(headers.ToArray()))); } private void OnHeadersMessageReceived(HeadersPayload payload) { if (payload.Headers.Length == 0) return; - system.Blockchain.Tell(payload.Headers, Context.Parent); + system.Blockchain.Tell(payload.Headers); } private void OnInventoryReceived(IInventory inventory) { - system.TaskManager.Tell(new TaskManager.TaskCompleted { Hash = inventory.Hash }, Context.Parent); + system.TaskManager.Tell(new TaskManager.TaskCompleted { Hash = inventory.Hash }); system.LocalNode.Tell(new LocalNode.Relay { Inventory = inventory }); pendingKnownHashes.Remove(inventory.Hash); knownHashes.Add(inventory.Hash); @@ -339,47 +309,61 @@ private void OnInvMessageReceived(InvPayload payload) if (hashes.Length == 0) return; foreach (UInt256 hash in hashes) pendingKnownHashes.Add((hash, DateTime.UtcNow)); - system.TaskManager.Tell(new TaskManager.NewTasks { Payload = InvPayload.Create(payload.Type, hashes) }, Context.Parent); + system.TaskManager.Tell(new TaskManager.NewTasks { Payload = InvPayload.Create(payload.Type, hashes) }); } private void OnMemPoolMessageReceived() { foreach (InvPayload payload in InvPayload.CreateGroup(InventoryType.TX, Blockchain.Singleton.MemPool.GetVerifiedTransactions().Select(p => p.Hash).ToArray())) - Context.Parent.Tell(Message.Create(MessageCommand.Inv, payload)); + EnqueueMessage(Message.Create(MessageCommand.Inv, payload)); } private void OnPingMessageReceived(PingPayload payload) { - Context.Parent.Tell(payload); - Context.Parent.Tell(Message.Create(MessageCommand.Pong, PingPayload.Create(Blockchain.Singleton.Height, payload.Nonce))); + UpdateLastBlockIndex(payload); + EnqueueMessage(Message.Create(MessageCommand.Pong, PingPayload.Create(Blockchain.Singleton.Height, payload.Nonce))); } private void OnPongMessageReceived(PingPayload payload) { - Context.Parent.Tell(payload); + UpdateLastBlockIndex(payload); } private void OnVerackMessageReceived() { verack = true; - Context.Parent.Tell(MessageCommand.Verack); + system.TaskManager.Tell(new TaskManager.Register { Version = Version }); + CheckMessageQueue(); } private void OnVersionMessageReceived(VersionPayload payload) { - version = payload; - Context.Parent.Tell(payload); - } - - private void OnTimer() - { - RefreshPendingKnownHashes(); - } - - protected override void PostStop() - { - timer.CancelIfNotNull(); - base.PostStop(); + Version = payload; + foreach (NodeCapability capability in payload.Capabilities) + { + switch (capability) + { + case FullNodeCapability fullNodeCapability: + IsFullNode = true; + LastBlockIndex = fullNodeCapability.StartHeight; + break; + case ServerCapability serverCapability: + if (serverCapability.Type == NodeCapabilityType.TcpServer) + ListenerTcpPort = serverCapability.Port; + break; + } + } + if (payload.Nonce == LocalNode.Nonce || payload.Magic != ProtocolSettings.Default.Magic) + { + Disconnect(true); + return; + } + if (LocalNode.Singleton.RemoteNodes.Values.Where(p => p != this).Any(p => p.Remote.Address.Equals(Remote.Address) && p.Version?.Nonce == payload.Nonce)) + { + Disconnect(true); + return; + } + SendMessage(Message.Create(MessageCommand.Verack)); } private void RefreshPendingKnownHashes() @@ -393,50 +377,12 @@ private void RefreshPendingKnownHashes() } } - public static Props Props(NeoSystem system) - { - return Akka.Actor.Props.Create(() => new ProtocolHandler(system)).WithMailbox("protocol-handler-mailbox"); - } - } - - internal class ProtocolHandlerMailbox : PriorityMailbox - { - public ProtocolHandlerMailbox(Settings settings, Config config) - : base(settings, config) - { - } - - internal protected override bool IsHighPriority(object message) + private void UpdateLastBlockIndex(PingPayload payload) { - if (!(message is Message msg)) return false; - switch (msg.Command) + if (payload.LastBlockIndex > LastBlockIndex) { - case MessageCommand.Consensus: - case MessageCommand.FilterAdd: - case MessageCommand.FilterClear: - case MessageCommand.FilterLoad: - case MessageCommand.Verack: - case MessageCommand.Version: - case MessageCommand.Alert: - return true; - default: - return false; - } - } - - internal protected override bool ShallDrop(object message, IEnumerable queue) - { - if (message is ProtocolHandler.Timer) return false; - if (!(message is Message msg)) return true; - switch (msg.Command) - { - case MessageCommand.GetAddr: - case MessageCommand.GetBlocks: - case MessageCommand.GetHeaders: - case MessageCommand.Mempool: - return queue.OfType().Any(p => p.Command == msg.Command); - default: - return false; + LastBlockIndex = payload.LastBlockIndex; + 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 44bb2ef107..88b58163bf 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -7,24 +7,22 @@ using Neo.Ledger; using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net; namespace Neo.Network.P2P { - public class RemoteNode : Connection + public partial class RemoteNode : Connection { internal class Relay { public IInventory Inventory; } private readonly NeoSystem system; - private readonly IActorRef protocol; private readonly Queue message_queue_high = new Queue(); private readonly Queue message_queue_low = new Queue(); private ByteString msg_buffer = ByteString.Empty; - private BloomFilter bloom_filter; private bool ack = true; - private bool verack = false; public IPEndPoint Listener => new IPEndPoint(Remote.Address, ListenerTcpPort); public int ListenerTcpPort { get; private set; } = 0; @@ -36,7 +34,6 @@ public RemoteNode(NeoSystem system, object connection, IPEndPoint remote, IPEndP : base(connection, remote, local) { this.system = system; - this.protocol = Context.ActorOf(ProtocolHandler.Props(system)); LocalNode.Singleton.RemoteNodes.TryAdd(Self, this); var capabilities = new List @@ -124,7 +121,7 @@ protected override void OnData(ByteString data) msg_buffer = msg_buffer.Concat(data); for (Message message = TryParseMessage(); message != null; message = TryParseMessage()) - protocol.Tell(message); + OnMessage(message); } protected override void OnReceive(object message) @@ -132,6 +129,9 @@ protected override void OnReceive(object message) base.OnReceive(message); switch (message) { + case Timer _: + RefreshPendingKnownHashes(); + break; case Message msg: EnqueueMessage(msg); break; @@ -141,27 +141,6 @@ protected override void OnReceive(object message) case Relay relay: OnRelay(relay.Inventory); break; - case VersionPayload payload: - OnVersionPayload(payload); - break; - case MessageCommand.Verack: - OnVerack(); - break; - case ProtocolHandler.SetFilter setFilter: - OnSetFilter(setFilter.Filter); - break; - case PingPayload payload: - OnPingPayload(payload); - break; - } - } - - private void OnPingPayload(PingPayload payload) - { - if (payload.LastBlockIndex > LastBlockIndex) - { - LastBlockIndex = payload.LastBlockIndex; - system.TaskManager.Tell(new TaskManager.Update { LastBlockIndex = LastBlockIndex }); } } @@ -187,50 +166,9 @@ private void OnSend(IInventory inventory) EnqueueMessage((MessageCommand)inventory.InventoryType, inventory); } - private void OnSetFilter(BloomFilter filter) - { - bloom_filter = filter; - } - - private void OnVerack() - { - verack = true; - system.TaskManager.Tell(new TaskManager.Register { Version = Version }); - CheckMessageQueue(); - } - - private void OnVersionPayload(VersionPayload version) - { - Version = version; - foreach (NodeCapability capability in version.Capabilities) - { - switch (capability) - { - case FullNodeCapability fullNodeCapability: - IsFullNode = true; - LastBlockIndex = fullNodeCapability.StartHeight; - break; - case ServerCapability serverCapability: - if (serverCapability.Type == NodeCapabilityType.TcpServer) - ListenerTcpPort = serverCapability.Port; - break; - } - } - if (version.Nonce == LocalNode.Nonce || version.Magic != ProtocolSettings.Default.Magic) - { - Disconnect(true); - return; - } - if (LocalNode.Singleton.RemoteNodes.Values.Where(p => p != this).Any(p => p.Remote.Address.Equals(Remote.Address) && p.Version?.Nonce == version.Nonce)) - { - Disconnect(true); - return; - } - SendMessage(Message.Create(MessageCommand.Verack)); - } - protected override void PostStop() { + timer.CancelIfNotNull(); LocalNode.Singleton.RemoteNodes.TryRemove(Self, out _); base.PostStop(); } @@ -246,15 +184,6 @@ private void SendMessage(Message message) SendData(ByteString.FromBytes(message.ToArray())); } - protected override SupervisorStrategy SupervisorStrategy() - { - return new OneForOneStrategy(ex => - { - Disconnect(true); - return Directive.Stop; - }, loggingEnabled: false); - } - private Message TryParseMessage() { var length = Message.TryDeserialize(msg_buffer, out var msg); @@ -273,13 +202,42 @@ internal protected override bool IsHighPriority(object message) { switch (message) { + case Message msg: + switch (msg.Command) + { + case MessageCommand.Consensus: + case MessageCommand.FilterAdd: + case MessageCommand.FilterClear: + case MessageCommand.FilterLoad: + case MessageCommand.Verack: + case MessageCommand.Version: + case MessageCommand.Alert: + return true; + default: + return false; + } case Tcp.ConnectionClosed _: - case Connection.Timer _: + case Connection.Close _: case Connection.Ack _: return true; default: return false; } } + + internal protected override bool ShallDrop(object message, IEnumerable queue) + { + if (!(message is Message msg)) return false; + switch (msg.Command) + { + case MessageCommand.GetAddr: + case MessageCommand.GetBlocks: + case MessageCommand.GetHeaders: + case MessageCommand.Mempool: + return queue.OfType().Any(p => p.Command == msg.Command); + default: + return false; + } + } } } diff --git a/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs b/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs deleted file mode 100644 index 1f62a77580..0000000000 --- a/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandler.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Akka.TestKit.Xunit2; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.P2P; -using Neo.Network.P2P.Capabilities; -using Neo.Network.P2P.Payloads; - -namespace Neo.UnitTests.Network.P2P -{ - [TestClass] - public class UT_ProtocolHandler : TestKit - { - [TestCleanup] - public void Cleanup() - { - Shutdown(); - } - - [TestMethod] - public void ProtocolHandler_Test_SendVersion_TellParent() - { - var senderProbe = CreateTestProbe(); - var parent = CreateTestProbe(); - var protocolActor = ActorOfAsTestActorRef(() => new ProtocolHandler(TestBlockchain.TheNeoSystem), parent); - - var payload = new VersionPayload() - { - UserAgent = "".PadLeft(1024, '0'), - Nonce = 1, - Magic = 2, - Timestamp = 5, - Version = 6, - Capabilities = new NodeCapability[] - { - new ServerCapability(NodeCapabilityType.TcpServer, 25) - } - }; - - senderProbe.Send(protocolActor, Message.Create(MessageCommand.Version, payload)); - parent.ExpectMsg(); - } - } -} diff --git a/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs b/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs deleted file mode 100644 index f41deead2a..0000000000 --- a/tests/neo.UnitTests/Network/P2P/UT_ProtocolHandlerMailbox.cs +++ /dev/null @@ -1,185 +0,0 @@ -using Akka.TestKit.Xunit2; -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.Network.P2P; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Neo.UnitTests.Network.P2P -{ - [TestClass] - public class UT_ProtocolHandlerMailbox : TestKit - { - private static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism - - ProtocolHandlerMailbox 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 ProtocolHandlerMailbox(akkaSettings, config); - } - - [TestMethod] - public void ProtocolHandlerMailbox_Test_IsHighPriority() - { - ISerializable s = null; - - //handshaking - uut.IsHighPriority(Message.Create(MessageCommand.Version, s)).Should().Be(true); - uut.IsHighPriority(Message.Create(MessageCommand.Verack, s)).Should().Be(true); - - //connectivity - uut.IsHighPriority(Message.Create(MessageCommand.GetAddr, s)).Should().Be(false); - uut.IsHighPriority(Message.Create(MessageCommand.Addr, s)).Should().Be(false); - uut.IsHighPriority(Message.Create(MessageCommand.Ping, s)).Should().Be(false); - uut.IsHighPriority(Message.Create(MessageCommand.Pong, s)).Should().Be(false); - - //synchronization - uut.IsHighPriority(Message.Create(MessageCommand.GetHeaders, s)).Should().Be(false); - uut.IsHighPriority(Message.Create(MessageCommand.Headers, s)).Should().Be(false); - uut.IsHighPriority(Message.Create(MessageCommand.GetBlocks, s)).Should().Be(false); - uut.IsHighPriority(Message.Create(MessageCommand.Mempool, s)).Should().Be(false); - uut.IsHighPriority(Message.Create(MessageCommand.Inv, s)).Should().Be(false); - uut.IsHighPriority(Message.Create(MessageCommand.GetData, s)).Should().Be(false); - 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.Reject, s)).Should().Be(false); - - //SPV protocol - uut.IsHighPriority(Message.Create(MessageCommand.FilterLoad, s)).Should().Be(true); - uut.IsHighPriority(Message.Create(MessageCommand.FilterAdd, s)).Should().Be(true); - uut.IsHighPriority(Message.Create(MessageCommand.FilterClear, s)).Should().Be(true); - uut.IsHighPriority(Message.Create(MessageCommand.MerkleBlock, s)).Should().Be(false); - - //others - uut.IsHighPriority(Message.Create(MessageCommand.Alert, s)).Should().Be(true); - - // any random object (non Message) should not have priority - object obj = null; - uut.IsHighPriority(obj).Should().Be(false); - } - - - [TestMethod] - public void ProtocolHandlerMailbox_Test_ShallDrop() - { - // using this for messages - ISerializable s = null; - Message msg = null; // multiple uses - // empty queue - IEnumerable emptyQueue = Enumerable.Empty(); - - // any random object (non Message) should be dropped - object obj = null; - uut.ShallDrop(obj, emptyQueue).Should().Be(true); - - //handshaking - // Version (no drop) - msg = Message.Create(MessageCommand.Version, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // Verack (no drop) - msg = Message.Create(MessageCommand.Verack, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - - //connectivity - // GetAddr (drop) - msg = Message.Create(MessageCommand.GetAddr, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); - // Addr (no drop) - msg = Message.Create(MessageCommand.Addr, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // Ping (no drop) - msg = Message.Create(MessageCommand.Ping, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // Pong (no drop) - msg = Message.Create(MessageCommand.Pong, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - - //synchronization - // GetHeaders (drop) - msg = Message.Create(MessageCommand.GetHeaders, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); - // Headers (no drop) - msg = Message.Create(MessageCommand.Headers, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // GetBlocks (drop) - msg = Message.Create(MessageCommand.GetBlocks, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); - // Mempool (drop) - msg = Message.Create(MessageCommand.Mempool, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); - // Inv (no drop) - msg = Message.Create(MessageCommand.Inv, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // NotFound (no drop) - msg = Message.Create(MessageCommand.NotFound, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // Transaction (no drop) - msg = Message.Create(MessageCommand.Transaction, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // Block (no drop) - msg = Message.Create(MessageCommand.Block, s); - 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); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // Reject (no drop) - msg = Message.Create(MessageCommand.Reject, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - - //SPV protocol - // FilterLoad (no drop) - msg = Message.Create(MessageCommand.FilterLoad, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // FilterAdd (no drop) - msg = Message.Create(MessageCommand.FilterAdd, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // FilterClear (no drop) - msg = Message.Create(MessageCommand.FilterClear, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - // MerkleBlock (no drop) - msg = Message.Create(MessageCommand.MerkleBlock, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - - //others - // Alert (no drop) - msg = Message.Create(MessageCommand.Alert, s); - uut.ShallDrop(msg, emptyQueue).Should().Be(false); - uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); - } - } -} diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs index 7b2e083cbb..297af38e0c 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs @@ -2,6 +2,7 @@ using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.Network.P2P; using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; @@ -14,8 +15,7 @@ public class UT_RemoteNode : TestKit private static NeoSystem testBlockchain; public UT_RemoteNode() - : base($"remote-node-mailbox {{ mailbox-type: \"{typeof(RemoteNodeMailbox).AssemblyQualifiedName}\" }}" + - $"protocol-handler-mailbox {{ mailbox-type: \"{typeof(ProtocolHandlerMailbox).AssemblyQualifiedName}\" }}") + : base($"remote-node-mailbox {{ mailbox-type: \"{typeof(RemoteNodeMailbox).AssemblyQualifiedName}\" }}") { } @@ -33,7 +33,7 @@ public void RemoteNode_Test_Abort_DifferentMagic() connectionTestProbe.ExpectMsg(); - var payload = new VersionPayload() + var msg = Message.Create(MessageCommand.Version, new VersionPayload { UserAgent = "".PadLeft(1024, '0'), Nonce = 1, @@ -44,10 +44,10 @@ public void RemoteNode_Test_Abort_DifferentMagic() { new ServerCapability(NodeCapabilityType.TcpServer, 25) } - }; + }); var testProbe = CreateTestProbe(); - testProbe.Send(remoteNodeActor, payload); + testProbe.Send(remoteNodeActor, new Tcp.Received((ByteString)msg.ToArray())); connectionTestProbe.ExpectMsg(); } @@ -60,7 +60,7 @@ public void RemoteNode_Test_Accept_IfSameMagic() connectionTestProbe.ExpectMsg(); - var payload = new VersionPayload() + var msg = Message.Create(MessageCommand.Version, new VersionPayload() { UserAgent = "Unit Test".PadLeft(1024, '0'), Nonce = 1, @@ -71,10 +71,10 @@ public void RemoteNode_Test_Accept_IfSameMagic() { new ServerCapability(NodeCapabilityType.TcpServer, 25) } - }; + }); var testProbe = CreateTestProbe(); - testProbe.Send(remoteNodeActor, payload); + testProbe.Send(remoteNodeActor, new Tcp.Received((ByteString)msg.ToArray())); var verackMessage = connectionTestProbe.ExpectMsg(); diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs index cd566f44f2..5ae7aec0da 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_RemoteNodeMailbox.cs @@ -2,8 +2,11 @@ using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; using Neo.Network.P2P; using System; +using System.Collections.Generic; +using System.Linq; namespace Neo.UnitTests.Network.P2P { @@ -32,14 +35,155 @@ public void TestSetup() [TestMethod] public void RemoteNode_Test_IsHighPriority() { + ISerializable s = null; + + //handshaking + uut.IsHighPriority(Message.Create(MessageCommand.Version, s)).Should().Be(true); + uut.IsHighPriority(Message.Create(MessageCommand.Verack, s)).Should().Be(true); + + //connectivity + uut.IsHighPriority(Message.Create(MessageCommand.GetAddr, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Addr, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Ping, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Pong, s)).Should().Be(false); + + //synchronization + uut.IsHighPriority(Message.Create(MessageCommand.GetHeaders, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Headers, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.GetBlocks, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Mempool, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.Inv, s)).Should().Be(false); + uut.IsHighPriority(Message.Create(MessageCommand.GetData, s)).Should().Be(false); + 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.Reject, s)).Should().Be(false); + + //SPV protocol + uut.IsHighPriority(Message.Create(MessageCommand.FilterLoad, s)).Should().Be(true); + uut.IsHighPriority(Message.Create(MessageCommand.FilterAdd, s)).Should().Be(true); + uut.IsHighPriority(Message.Create(MessageCommand.FilterClear, s)).Should().Be(true); + uut.IsHighPriority(Message.Create(MessageCommand.MerkleBlock, s)).Should().Be(false); + + //others + uut.IsHighPriority(Message.Create(MessageCommand.Alert, s)).Should().Be(true); + // high priority commands uut.IsHighPriority(new Tcp.ConnectionClosed()).Should().Be(true); - uut.IsHighPriority(new Connection.Timer()).Should().Be(true); + uut.IsHighPriority(new Connection.Close()).Should().Be(true); uut.IsHighPriority(new Connection.Ack()).Should().Be(true); // any random object should not have priority object obj = null; uut.IsHighPriority(obj).Should().Be(false); } + + public void ProtocolHandlerMailbox_Test_ShallDrop() + { + // using this for messages + ISerializable s = null; + Message msg; // multiple uses + // empty queue + IEnumerable emptyQueue = Enumerable.Empty(); + + // any random object (non Message) should be dropped + object obj = null; + uut.ShallDrop(obj, emptyQueue).Should().Be(true); + + //handshaking + // Version (no drop) + msg = Message.Create(MessageCommand.Version, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // Verack (no drop) + msg = Message.Create(MessageCommand.Verack, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + + //connectivity + // GetAddr (drop) + msg = Message.Create(MessageCommand.GetAddr, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); + // Addr (no drop) + msg = Message.Create(MessageCommand.Addr, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // Ping (no drop) + msg = Message.Create(MessageCommand.Ping, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // Pong (no drop) + msg = Message.Create(MessageCommand.Pong, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + + //synchronization + // GetHeaders (drop) + msg = Message.Create(MessageCommand.GetHeaders, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); + // Headers (no drop) + msg = Message.Create(MessageCommand.Headers, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // GetBlocks (drop) + msg = Message.Create(MessageCommand.GetBlocks, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); + // Mempool (drop) + msg = Message.Create(MessageCommand.Mempool, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(true); + // Inv (no drop) + msg = Message.Create(MessageCommand.Inv, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // NotFound (no drop) + msg = Message.Create(MessageCommand.NotFound, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // Transaction (no drop) + msg = Message.Create(MessageCommand.Transaction, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // Block (no drop) + msg = Message.Create(MessageCommand.Block, s); + 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); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // Reject (no drop) + msg = Message.Create(MessageCommand.Reject, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + + //SPV protocol + // FilterLoad (no drop) + msg = Message.Create(MessageCommand.FilterLoad, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // FilterAdd (no drop) + msg = Message.Create(MessageCommand.FilterAdd, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // FilterClear (no drop) + msg = Message.Create(MessageCommand.FilterClear, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + // MerkleBlock (no drop) + msg = Message.Create(MessageCommand.MerkleBlock, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + + //others + // Alert (no drop) + msg = Message.Create(MessageCommand.Alert, s); + uut.ShallDrop(msg, emptyQueue).Should().Be(false); + uut.ShallDrop(msg, new object[] { msg }).Should().Be(false); + } } } From b40806a45d55a19e9d46fe69f1c6173caa579f7b Mon Sep 17 00:00:00 2001 From: ShawnYun <42930111+ShawnYun@users.noreply.github.com> Date: Fri, 10 Apr 2020 14:10:28 +0800 Subject: [PATCH 230/305] Fix akka warning (#1533) --- src/neo/Network/P2P/LocalNode.cs | 6 ++++- src/neo/Network/P2P/Peer.cs | 5 ++++ src/neo/Network/P2P/RemoteNode.cs | 27 ++++++++++++------- .../Network/P2P/UT_RemoteNode.cs | 4 --- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index ebdbca98fd..c7f0e472e7 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -1,6 +1,5 @@ using Akka.Actor; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using System; using System.Collections.Concurrent; @@ -214,6 +213,11 @@ private void OnRelayDirectly(IInventory inventory) private void OnSendDirectly(IInventory inventory) => SendToRemoteNodes(inventory); + protected override void OnTcpConnected(IActorRef connection) + { + connection.Tell(new RemoteNode.StartProtocol()); + } + public static Props Props(NeoSystem system) { return Akka.Actor.Props.Create(() => new LocalNode(system)); diff --git a/src/neo/Network/P2P/Peer.cs b/src/neo/Network/P2P/Peer.cs index 8e7eb21bd2..dc921b9526 100644 --- a/src/neo/Network/P2P/Peer.cs +++ b/src/neo/Network/P2P/Peer.cs @@ -226,9 +226,14 @@ private void OnTcpConnected(IPEndPoint remote, IPEndPoint local) Context.Watch(connection); Sender.Tell(new Tcp.Register(connection)); ConnectedPeers.TryAdd(connection, remote); + OnTcpConnected(connection); } } + protected virtual void OnTcpConnected(IActorRef connection) + { + } + /// /// Will be triggered when a Tcp.CommandFailed message is received. /// If it's a Tcp.Connect command, remove the related endpoint from ConnectingPeers. diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index 88b58163bf..b1457a8522 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -16,6 +16,7 @@ namespace Neo.Network.P2P { public partial class RemoteNode : Connection { + internal class StartProtocol { } internal class Relay { public IInventory Inventory; } private readonly NeoSystem system; @@ -35,16 +36,6 @@ public RemoteNode(NeoSystem system, object connection, IPEndPoint remote, IPEndP { this.system = system; LocalNode.Singleton.RemoteNodes.TryAdd(Self, this); - - var capabilities = new List - { - new FullNodeCapability(Blockchain.Singleton.Height) - }; - - if (LocalNode.Singleton.ListenerTcpPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.TcpServer, (ushort)LocalNode.Singleton.ListenerTcpPort)); - if (LocalNode.Singleton.ListenerWsPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.WsServer, (ushort)LocalNode.Singleton.ListenerWsPort)); - - SendMessage(Message.Create(MessageCommand.Version, VersionPayload.Create(LocalNode.Nonce, LocalNode.UserAgent, capabilities.ToArray()))); } /// @@ -141,6 +132,9 @@ protected override void OnReceive(object message) case Relay relay: OnRelay(relay.Inventory); break; + case StartProtocol _: + OnStartProtocol(); + break; } } @@ -166,6 +160,19 @@ private void OnSend(IInventory inventory) EnqueueMessage((MessageCommand)inventory.InventoryType, inventory); } + private void OnStartProtocol() + { + var capabilities = new List + { + new FullNodeCapability(Blockchain.Singleton.Height) + }; + + if (LocalNode.Singleton.ListenerTcpPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.TcpServer, (ushort)LocalNode.Singleton.ListenerTcpPort)); + if (LocalNode.Singleton.ListenerWsPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.WsServer, (ushort)LocalNode.Singleton.ListenerWsPort)); + + SendMessage(Message.Create(MessageCommand.Version, VersionPayload.Create(LocalNode.Nonce, LocalNode.UserAgent, capabilities.ToArray()))); + } + protected override void PostStop() { timer.CancelIfNotNull(); diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs index 297af38e0c..61702115f3 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs @@ -31,8 +31,6 @@ public void RemoteNode_Test_Abort_DifferentMagic() var connectionTestProbe = CreateTestProbe(); var remoteNodeActor = ActorOfAsTestActorRef(() => new RemoteNode(testBlockchain, connectionTestProbe, null, null)); - connectionTestProbe.ExpectMsg(); - var msg = Message.Create(MessageCommand.Version, new VersionPayload { UserAgent = "".PadLeft(1024, '0'), @@ -58,8 +56,6 @@ public void RemoteNode_Test_Accept_IfSameMagic() var connectionTestProbe = CreateTestProbe(); var remoteNodeActor = ActorOfAsTestActorRef(() => new RemoteNode(testBlockchain, connectionTestProbe, null, null)); - connectionTestProbe.ExpectMsg(); - var msg = Message.Create(MessageCommand.Version, new VersionPayload() { UserAgent = "Unit Test".PadLeft(1024, '0'), From 445918e038478d2f2b36f365131643c4567fa20b Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 10 Apr 2020 15:11:40 +0800 Subject: [PATCH 231/305] Tell RelayResult to the sender (#1548) --- src/neo/Ledger/Blockchain.cs | 1 + tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index f36784d89b..54a4f26327 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -304,6 +304,7 @@ private void OnInventory(IInventory inventory, bool relay = true) }; if (relay && rr.Result == VerifyResult.Succeed) system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = inventory }); + Sender.Tell(rr); Context.System.EventStream.Publish(rr); } diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index 822a9a70bb..11456371ae 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -134,8 +134,6 @@ public void TestValidTransaction() var tx = CreateValidTx(walletA, acc.ScriptHash, 0); - system.ActorSystem.EventStream.Subscribe(senderProbe, typeof(Blockchain.RelayResult)); - senderProbe.Send(system.Blockchain, tx); senderProbe.ExpectMsg(p => p.Result == VerifyResult.Succeed); From ef9c7986706c72fbf17361a6c8311a00b0cc4eec Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 11 Apr 2020 15:51:08 +0800 Subject: [PATCH 232/305] Add abstract method Wallet.ChangePassword() (#1552) --- src/neo/Wallets/NEP6/NEP6Wallet.cs | 6 +++--- src/neo/Wallets/SQLite/UserWallet.cs | 6 +++--- src/neo/Wallets/Wallet.cs | 1 + tests/neo.UnitTests/Wallets/UT_Wallet.cs | 5 +++++ 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/neo/Wallets/NEP6/NEP6Wallet.cs b/src/neo/Wallets/NEP6/NEP6Wallet.cs index 2db4d87e77..423e079dbf 100644 --- a/src/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/neo/Wallets/NEP6/NEP6Wallet.cs @@ -302,14 +302,14 @@ public override bool VerifyPassword(string password) } } - public bool ChangePassword(string password_old, string password_new) + public override bool ChangePassword(string oldPassword, string newPassword) { bool succeed = true; lock (accounts) { Parallel.ForEach(accounts.Values, (account, state) => { - if (!account.ChangePasswordPrepare(password_old, password_new)) + if (!account.ChangePasswordPrepare(oldPassword, newPassword)) { state.Stop(); succeed = false; @@ -321,7 +321,7 @@ public bool ChangePassword(string password_old, string password_new) foreach (NEP6Account account in accounts.Values) account.ChangePasswordCommit(); if (password != null) - password = password_new; + password = newPassword; } else { diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs index 7cfbca8034..bea3626d34 100644 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ b/src/neo/Wallets/SQLite/UserWallet.cs @@ -170,10 +170,10 @@ private void BuildDatabase() } } - public bool ChangePassword(string password_old, string password_new) + public override bool ChangePassword(string oldPassword, string newPassword) { - if (!VerifyPassword(password_old)) return false; - byte[] passwordKey = password_new.ToAesKey(); + if (!VerifyPassword(oldPassword)) return false; + byte[] passwordKey = newPassword.ToAesKey(); try { SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 0c8151a257..4831a82c96 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -23,6 +23,7 @@ public abstract class Wallet public abstract string Name { get; } public abstract Version Version { get; } + public abstract bool ChangePassword(string oldPassword, string newPassword); public abstract bool Contains(UInt160 scriptHash); public abstract WalletAccount CreateAccount(byte[] privateKey); public abstract WalletAccount CreateAccount(Contract contract, KeyPair key = null); diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index a210cf581e..15a1cc8844 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -21,6 +21,11 @@ internal class MyWallet : Wallet Dictionary accounts = new Dictionary(); + public override bool ChangePassword(string oldPassword, string newPassword) + { + throw new NotImplementedException(); + } + public override bool Contains(UInt160 scriptHash) { return accounts.ContainsKey(scriptHash); From 6ce1f96b29a943bb409893b6ba71ca7d02bf88e6 Mon Sep 17 00:00:00 2001 From: Shargon Date: Sat, 11 Apr 2020 10:41:22 +0200 Subject: [PATCH 233/305] Network dependant signature (#1550) --- src/neo/Network/P2P/Helper.cs | 1 + tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 6 +++--- tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs | 10 +++++----- .../Network/P2P/Payloads/UT_Transaction.cs | 2 +- tests/neo.UnitTests/SmartContract/UT_Syscalls.cs | 4 ++-- tests/neo.UnitTests/UT_Helper.cs | 7 +------ 6 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/neo/Network/P2P/Helper.cs b/src/neo/Network/P2P/Helper.cs index 5accd35631..1d4797cf9e 100644 --- a/src/neo/Network/P2P/Helper.cs +++ b/src/neo/Network/P2P/Helper.cs @@ -10,6 +10,7 @@ public static byte[] GetHashData(this IVerifiable verifiable) using (MemoryStream ms = new MemoryStream()) using (BinaryWriter writer = new BinaryWriter(ms)) { + writer.Write(ProtocolSettings.Default.Magic); verifiable.SerializeUnsigned(writer); writer.Flush(); return ms.ToArray(); diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index 11456371ae..cf421b99cf 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -70,13 +70,13 @@ public void TestContainsTransaction() [TestMethod] public void TestGetCurrentBlockHash() { - Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0x2b8a21dfaf989dc1a5f2694517aefdbda1dd340f3cf177187d73e038a58ad2bb")); + Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0xdba446947a90b2862ef050703b44828ad8b02d11978f8ef59bd3e1c97aabf6e5")); } [TestMethod] public void TestGetCurrentHeaderHash() { - Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0x2b8a21dfaf989dc1a5f2694517aefdbda1dd340f3cf177187d73e038a58ad2bb")); + Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0xdba446947a90b2862ef050703b44828ad8b02d11978f8ef59bd3e1c97aabf6e5")); } [TestMethod] @@ -88,7 +88,7 @@ public void TestGetBlock() [TestMethod] public void TestGetBlockHash() { - Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0x2b8a21dfaf989dc1a5f2694517aefdbda1dd340f3cf177187d73e038a58ad2bb")); + Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0xdba446947a90b2862ef050703b44828ad8b02d11978f8ef59bd3e1c97aabf6e5")); Blockchain.Singleton.GetBlockHash(10).Should().BeNull(); } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index 2090acda87..fb9bc84a5d 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 = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c000000000000000000000000000000000000000000000000000000010001110200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; + var hex = "000000000000000000000000000000000000000000000000000000000000000000000000c2f713d5d23bee9da2a298ef1ab6a7a454e51e813265d71c85f68ecafc8f774de913ff854c000000000000000000000000000000000000000000000000000000010001110200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; 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 = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c000000000000000000000000000000000000000000000000000000010001110200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; + var hex = "000000000000000000000000000000000000000000000000000000000000000000000000c2f713d5d23bee9da2a298ef1ab6a7a454e51e813265d71c85f68ecafc8f774de913ff854c000000000000000000000000000000000000000000000000000000010001110200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) using (BinaryReader reader = new BinaryReader(ms)) @@ -199,11 +199,11 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0x865b348426952d2eaa978cf5d37dc85ec5f95bc56c57c1379d1be21898e74c1e"); + jObj["hash"].AsString().Should().Be("0xa8bb713f8a2a1ed7e625e440d954dacdd504be1ff39c133c93bad77f150ffeb3"); jObj["size"].AsNumber().Should().Be(166); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - jObj["merkleroot"].AsString().Should().Be("0x6226416a0e5aca42b5566f5a19ab467692688ba9d47986f6981a7f747bba2772"); + jObj["merkleroot"].AsString().Should().Be("0x4d778ffcca8ef6851cd76532811ee554a4a7b61aef98a2a29dee3bd2d513f7c2"); jObj["time"].AsNumber().Should().Be(328665601001); jObj["index"].AsNumber().Should().Be(0); jObj["nextconsensus"].AsString().Should().Be("NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf"); @@ -214,7 +214,7 @@ public void ToJson() jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; - txObj[0]["hash"].AsString().Should().Be("0xf374032ea013e3d9272f7a25dc1fe497a489ae36e94aa06270b26e60ab693435"); + txObj[0]["hash"].AsString().Should().Be("0x606d39d98d1732fc1c9931f194aba8b2e40a5db5a26fdf4b513d4a6e0d69a8c8"); txObj[0]["size"].AsNumber().Should().Be(52); 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_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 06ead63238..adee349b2b 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -1097,7 +1097,7 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0x11e3ee692015f0cd3cb8b6db7a4fc37568540f020cb9ca497a9917c81f20b62f"); + jObj["hash"].AsString().Should().Be("0x8b86429eb984728752552ee8d69536d36ab985bbe383c6a6eeb2100f6f29b81b"); jObj["size"].AsNumber().Should().Be(83); jObj["version"].AsNumber().Should().Be(0); ((JArray)jObj["attributes"]).Count.Should().Be(0); diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index f7d80a4d84..9979ca8648 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -97,7 +97,7 @@ public void System_Blockchain_GetBlock() Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteString)); Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(), - "5b22366b4139757552614430373634585358466c706674686b436b5954702f6e34623878715057476c6a6659303d222c332c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c322c302c224141414141414141414141414141414141414141414141414141413d222c315d"); + "5b2261564e62466b35384f51717547373870747154766561762f48677941566a72634e41434d4e59705c7530303242366f6f3d222c332c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c22414141414141414141414141414141414141414141414141414141414141414141414141414141414141413d222c322c302c224141414141414141414141414141414141414141414141414141413d222c315d"); Assert.AreEqual(0, engine.ResultStack.Count); // Clean @@ -261,7 +261,7 @@ public void System_ExecutionEngine_GetScriptContainer() Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteString)); Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(), - @"5b225c75303032426b53415959527a4c4b69685a676464414b50596f754655737a63544d7867445a6572584a3172784c37303d222c362c342c222f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f383d222c332c322c352c2241513d3d225d"); + @"5b226770564846625133316969517a614f4c7a33523546394d6256715932596b7a5164324461785536677154303d222c362c342c222f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f383d222c332c322c352c2241513d3d225d"); Assert.AreEqual(0, engine.ResultStack.Count); } } diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index daeaabc43b..d9223afcd9 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -19,12 +19,7 @@ public void GetHashData() { TestVerifiable verifiable = new TestVerifiable(); byte[] res = verifiable.GetHashData(); - res.Length.Should().Be(8); - byte[] requiredData = new byte[] { 7, 116, 101, 115, 116, 83, 116, 114 }; - for (int i = 0; i < requiredData.Length; i++) - { - res[i].Should().Be(requiredData[i]); - } + res.ToHexString().Should().Be("4e454f000774657374537472"); } [TestMethod] From 66512028695435dda9f6b9dfa5f587e3a188159c Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 15 Apr 2020 15:41:43 +0800 Subject: [PATCH 234/305] 3.0.0-preview2 --- src/neo/neo.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 41d80ba88c..dd502e1d4a 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -4,7 +4,7 @@ 2015-2019 The Neo Project Neo 3.0.0 - preview1 + preview2-00 The Neo Project netstandard2.1 true @@ -27,7 +27,7 @@ - + From 62f8152e50358abcf319e66ff4ff54fc266f44fe Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 15 Apr 2020 16:02:54 +0800 Subject: [PATCH 235/305] Vote for committee (#1491) --- src/neo/Consensus/ConsensusContext.cs | 8 +- src/neo/Consensus/RecoveryMessage.cs | 6 +- src/neo/Helper.cs | 52 ---- src/neo/Ledger/Blockchain.cs | 6 +- src/neo/Network/P2P/Payloads/ConsensusData.cs | 2 +- src/neo/ProtocolSettings.cs | 27 ++- .../SmartContract/Native/Tokens/GasToken.cs | 2 +- .../SmartContract/Native/Tokens/NeoToken.cs | 222 ++++++++--------- .../Native/Tokens/UT_GasToken.cs | 25 +- .../Native/Tokens/UT_NeoToken.cs | 227 +++++++----------- tests/neo.UnitTests/UT_Helper.cs | 150 ------------ 11 files changed, 252 insertions(+), 475 deletions(-) diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index 695b92a683..f7f500f3dc 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -109,10 +109,10 @@ public void Deserialize(BinaryReader reader) ViewNumber = reader.ReadByte(); TransactionHashes = reader.ReadSerializableArray(); Transaction[] transactions = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); - PreparationPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); - CommitPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); - ChangeViewPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); - LastChangeViewPayloads = reader.ReadNullableArray(Blockchain.MaxValidators); + PreparationPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); + CommitPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); + ChangeViewPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); + LastChangeViewPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); if (TransactionHashes.Length == 0 && !RequestSentOrReceived) TransactionHashes = null; Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash); diff --git a/src/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs index c25816ee7f..cde0f7aecc 100644 --- a/src/neo/Consensus/RecoveryMessage.cs +++ b/src/neo/Consensus/RecoveryMessage.cs @@ -32,7 +32,7 @@ public RecoveryMessage() : base(ConsensusMessageType.RecoveryMessage) public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); - ChangeViewMessages = reader.ReadSerializableArray(Blockchain.MaxValidators).ToDictionary(p => (int)p.ValidatorIndex); + ChangeViewMessages = reader.ReadSerializableArray(Blockchain.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); if (reader.ReadBoolean()) PrepareRequestMessage = reader.ReadSerializable(); else @@ -42,8 +42,8 @@ public override void Deserialize(BinaryReader reader) PreparationHash = new UInt256(reader.ReadFixedBytes(preparationHashSize)); } - PreparationMessages = reader.ReadSerializableArray(Blockchain.MaxValidators).ToDictionary(p => (int)p.ValidatorIndex); - CommitMessages = reader.ReadSerializableArray(Blockchain.MaxValidators).ToDictionary(p => (int)p.ValidatorIndex); + PreparationMessages = reader.ReadSerializableArray(Blockchain.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); + CommitMessages = reader.ReadSerializableArray(Blockchain.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); } internal ConsensusPayload[] GetChangeViewPayloads(ConsensusContext context, ConsensusPayload payload) diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index be66ce2013..b8f185e2c9 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -258,57 +258,5 @@ internal static IPEndPoint Unmap(this IPEndPoint endPoint) return endPoint; return new IPEndPoint(endPoint.Address.Unmap(), endPoint.Port); } - - internal static BigInteger WeightedAverage(this IEnumerable source, Func valueSelector, Func weightSelector) - { - BigInteger sum_weight = BigInteger.Zero; - BigInteger sum_value = BigInteger.Zero; - foreach (T item in source) - { - BigInteger weight = weightSelector(item); - sum_weight += weight; - sum_value += valueSelector(item) * weight; - } - if (sum_value == BigInteger.Zero) return BigInteger.Zero; - return sum_value / sum_weight; - } - - internal static IEnumerable WeightedFilter(this IList source, double start, double end, Func weightSelector, Func resultSelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (start < 0 || start > 1) throw new ArgumentOutOfRangeException(nameof(start)); - if (end < start || start + end > 1) throw new ArgumentOutOfRangeException(nameof(end)); - if (weightSelector == null) throw new ArgumentNullException(nameof(weightSelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); - if (source.Count == 0 || start == end) yield break; - double amount = (double)source.Select(weightSelector).Sum(); - BigInteger sum = 0; - double current = 0; - foreach (T item in source) - { - if (current >= end) break; - BigInteger weight = weightSelector(item); - sum += weight; - double old = current; - current = (double)sum / amount; - if (current <= start) continue; - if (old < start) - { - if (current > end) - { - weight = (long)((end - start) * amount); - } - else - { - weight = (long)((current - start) * amount); - } - } - else if (current > end) - { - weight = (long)((end - old) * amount); - } - yield return resultSelector(item, weight); - } - } } } diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 54a4f26327..fd76d1ebca 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -30,10 +30,12 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu public static readonly uint MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock; public const uint DecrementInterval = 2000000; - public const int MaxValidators = 1024; public static readonly uint[] GenerationAmount = { 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; public static readonly TimeSpan TimePerBlock = TimeSpan.FromMilliseconds(MillisecondsPerBlock); - public static readonly ECPoint[] StandbyValidators = ProtocolSettings.Default.StandbyValidators.OfType().Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); + public static readonly byte CommitteeMembersCount = (byte)ProtocolSettings.Default.StandbyCommittee.Length; + public static readonly byte ValidatorsCount = ProtocolSettings.Default.ValidatorsCount; + public static readonly ECPoint[] StandbyCommittee = ProtocolSettings.Default.StandbyCommittee.Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); + public static readonly ECPoint[] StandbyValidators = StandbyCommittee[..ValidatorsCount]; public static readonly Block GenesisBlock = new Block { diff --git a/src/neo/Network/P2P/Payloads/ConsensusData.cs b/src/neo/Network/P2P/Payloads/ConsensusData.cs index 1a8019633d..460f6a8098 100644 --- a/src/neo/Network/P2P/Payloads/ConsensusData.cs +++ b/src/neo/Network/P2P/Payloads/ConsensusData.cs @@ -29,7 +29,7 @@ public UInt256 Hash void ISerializable.Deserialize(BinaryReader reader) { - PrimaryIndex = (uint)reader.ReadVarInt(Blockchain.MaxValidators - 1); + PrimaryIndex = (uint)reader.ReadVarInt((ulong)Blockchain.ValidatorsCount - 1); Nonce = reader.ReadUInt64(); } diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index 8f0a48101e..ca6deaf96e 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -9,7 +9,8 @@ public class ProtocolSettings { public uint Magic { get; } public byte AddressVersion { get; } - public string[] StandbyValidators { get; } + public string[] StandbyCommittee { get; } + public byte ValidatorsCount { get; } public string[] SeedList { get; } public uint MillisecondsPerBlock { get; } public int MemoryPoolMaxTransactions { get; } @@ -47,18 +48,36 @@ private ProtocolSettings(IConfigurationSection section) this.AddressVersion = section.GetValue("AddressVersion", (byte)0x35); IConfigurationSection section_sv = section.GetSection("StandbyValidators"); if (section_sv.Exists()) - this.StandbyValidators = section_sv.GetChildren().Select(p => p.Get()).ToArray(); + this.StandbyCommittee = section_sv.GetChildren().Select(p => p.Get()).ToArray(); else - this.StandbyValidators = new[] + this.StandbyCommittee = new[] { + //Validators "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", "02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", "03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", "02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", "024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", "02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", - "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70" + "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", + + //Other Members + "023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", + "03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", + "03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", + "03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", + "02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", + "03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", + "0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", + "020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", + "0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", + "03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", + "02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", + "0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", + "03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", + "02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a" }; + this.ValidatorsCount = section.GetValue("ValidatorsCount", (byte)7); IConfigurationSection section_sl = section.GetSection("SeedList"); if (section_sl.Exists()) this.SeedList = section_sl.GetChildren().Select(p => p.Get()).ToArray(); diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index 1f05114062..459d65950a 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -24,7 +24,7 @@ internal override bool Initialize(ApplicationEngine engine) { if (!base.Initialize(engine)) return false; if (TotalSupply(engine.Snapshot) != BigInteger.Zero) return false; - UInt160 account = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, Blockchain.StandbyValidators).ToScriptHash(); + UInt160 account = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators); Mint(engine, account, 30_000_000 * Factor); return true; } diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index ec19edb85f..ef633ad597 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -9,7 +9,6 @@ using Neo.VM.Types; using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Numerics; using Array = Neo.VM.Types.Array; @@ -25,8 +24,7 @@ public sealed class NeoToken : Nep5Token public override byte Decimals => 0; public BigInteger TotalAmount { get; } - private const byte Prefix_Validator = 33; - private const byte Prefix_ValidatorsCount = 15; + private const byte Prefix_Candidate = 33; private const byte Prefix_NextValidators = 14; internal NeoToken() @@ -43,18 +41,13 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco { DistributeGas(engine, account, state); if (amount.IsZero) return; - if (state.Votes.Length == 0) return; - foreach (ECPoint pubkey in state.Votes) + if (state.VoteTo != null) { - StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Validator, pubkey.ToArray())); - ValidatorState state_validator = ValidatorState.FromByteArray(storage_validator.Value); + StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Candidate, state.VoteTo.ToArray())); + CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value); state_validator.Votes += amount; storage_validator.Value = state_validator.ToByteArray(); } - StorageItem storage_count = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_ValidatorsCount)); - ValidatorsCountState state_count = ValidatorsCountState.FromByteArray(storage_count.Value); - state_count.Votes[state.Votes.Length - 1] += amount; - storage_count.Value = state_count.ToByteArray(); } private void DistributeGas(ApplicationEngine engine, UInt160 account, AccountState state) @@ -101,10 +94,19 @@ internal override bool Initialize(ApplicationEngine engine) { if (!base.Initialize(engine)) return false; if (base.TotalSupply(engine.Snapshot) != BigInteger.Zero) return false; - UInt160 account = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, Blockchain.StandbyValidators).ToScriptHash(); - Mint(engine, account, TotalAmount); - foreach (ECPoint pubkey in Blockchain.StandbyValidators) - RegisterValidator(engine.Snapshot, pubkey); + BigInteger amount = TotalAmount; + for (int i = 0; i < Blockchain.CommitteeMembersCount; i++) + { + ECPoint pubkey = Blockchain.StandbyCommittee[i]; + RegisterCandidate(engine.Snapshot, pubkey); + BigInteger balance = TotalAmount / 2 / (Blockchain.ValidatorsCount * 2 + (Blockchain.CommitteeMembersCount - Blockchain.ValidatorsCount)); + if (i < Blockchain.ValidatorsCount) balance *= 2; + UInt160 account = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); + Mint(engine, account, balance); + Vote(engine.Snapshot, account, pubkey); + amount -= balance; + } + Mint(engine, Blockchain.GetConsensusAddress(Blockchain.StandbyValidators), amount); return true; } @@ -133,20 +135,51 @@ public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) } [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] - private StackItem RegisterValidator(ApplicationEngine engine, Array args) + private StackItem RegisterCandidate(ApplicationEngine engine, Array args) { ECPoint pubkey = args[0].GetSpan().AsSerializable(); - return RegisterValidator(engine.Snapshot, pubkey); + if (!InteropService.Runtime.CheckWitnessInternal(engine, Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) + return false; + return RegisterCandidate(engine.Snapshot, pubkey); } - private bool RegisterValidator(StoreView snapshot, ECPoint pubkey) + private bool RegisterCandidate(StoreView snapshot, ECPoint pubkey) { - StorageKey key = CreateStorageKey(Prefix_Validator, pubkey); - if (snapshot.Storages.TryGet(key) != null) return false; - snapshot.Storages.Add(key, new StorageItem + StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey); + StorageItem item = snapshot.Storages.GetAndChange(key, () => new StorageItem { - Value = new ValidatorState().ToByteArray() + Value = new CandidateState().ToByteArray() }); + CandidateState state = CandidateState.FromByteArray(item.Value); + state.Registered = true; + item.Value = state.ToByteArray(); + return true; + } + + [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] + private StackItem UnregisterCandidate(ApplicationEngine engine, Array args) + { + ECPoint pubkey = args[0].GetSpan().AsSerializable(); + if (!InteropService.Runtime.CheckWitnessInternal(engine, Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) + return false; + return UnregisterCandidate(engine.Snapshot, pubkey); + } + + private bool UnregisterCandidate(StoreView snapshot, ECPoint pubkey) + { + StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey); + if (snapshot.Storages.TryGet(key) is null) return true; + StorageItem item = snapshot.Storages.GetAndChange(key); + CandidateState state = CandidateState.FromByteArray(item.Value); + if (state.Votes.IsZero) + { + snapshot.Storages.Delete(key); + } + else + { + state.Registered = false; + item.Value = state.ToByteArray(); + } return true; } @@ -154,39 +187,37 @@ private bool RegisterValidator(StoreView snapshot, ECPoint pubkey) private StackItem Vote(ApplicationEngine engine, Array args) { UInt160 account = new UInt160(args[0].GetSpan()); - ECPoint[] pubkeys = ((Array)args[1]).Select(p => p.GetSpan().AsSerializable()).ToArray(); + ECPoint voteTo = args[1].IsNull ? null : args[1].GetSpan().AsSerializable(); if (!InteropService.Runtime.CheckWitnessInternal(engine, account)) return false; + return Vote(engine.Snapshot, account, voteTo); + } + + private bool Vote(StoreView snapshot, UInt160 account, ECPoint voteTo) + { StorageKey key_account = CreateAccountKey(account); - if (engine.Snapshot.Storages.TryGet(key_account) is null) return false; - StorageItem storage_account = engine.Snapshot.Storages.GetAndChange(key_account); + if (snapshot.Storages.TryGet(key_account) is null) return false; + StorageItem storage_account = snapshot.Storages.GetAndChange(key_account); AccountState state_account = new AccountState(storage_account.Value); - foreach (ECPoint pubkey in state_account.Votes) + if (state_account.VoteTo != null) { - StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Validator, pubkey.ToArray())); - ValidatorState state_validator = ValidatorState.FromByteArray(storage_validator.Value); + StorageKey key = CreateStorageKey(Prefix_Candidate, state_account.VoteTo.ToArray()); + StorageItem storage_validator = snapshot.Storages.GetAndChange(key); + CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value); state_validator.Votes -= state_account.Balance; - storage_validator.Value = state_validator.ToByteArray(); - } - pubkeys = pubkeys.Distinct().Where(p => engine.Snapshot.Storages.TryGet(CreateStorageKey(Prefix_Validator, p.ToArray())) != null).ToArray(); - if (pubkeys.Length != state_account.Votes.Length) - { - StorageItem storage_count = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_ValidatorsCount), () => new StorageItem - { - Value = new ValidatorsCountState().ToByteArray() - }); - ValidatorsCountState state_count = ValidatorsCountState.FromByteArray(storage_count.Value); - if (state_account.Votes.Length > 0) - state_count.Votes[state_account.Votes.Length - 1] -= state_account.Balance; - if (pubkeys.Length > 0) - state_count.Votes[pubkeys.Length - 1] += state_account.Balance; - storage_count.Value = state_count.ToByteArray(); + if (!state_validator.Registered && state_validator.Votes.IsZero) + snapshot.Storages.Delete(key); + else + storage_validator.Value = state_validator.ToByteArray(); } - state_account.Votes = pubkeys; + state_account.VoteTo = voteTo; storage_account.Value = state_account.ToByteArray(); - foreach (ECPoint pubkey in state_account.Votes) + if (voteTo != null) { - StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Validator, pubkey.ToArray())); - ValidatorState state_validator = ValidatorState.FromByteArray(storage_validator.Value); + StorageKey key = CreateStorageKey(Prefix_Candidate, voteTo.ToArray()); + if (snapshot.Storages.TryGet(key) is null) return false; + StorageItem storage_validator = snapshot.Storages.GetAndChange(key); + CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value); + if (!state_validator.Registered) return false; state_validator.Votes += state_account.Balance; storage_validator.Value = state_validator.ToByteArray(); } @@ -194,19 +225,19 @@ private StackItem Vote(ApplicationEngine engine, Array args) } [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] - private StackItem GetRegisteredValidators(ApplicationEngine engine, Array args) + private StackItem GetCandidates(ApplicationEngine engine, Array args) { - return new Array(engine.ReferenceCounter, GetRegisteredValidators(engine.Snapshot).Select(p => new Struct(engine.ReferenceCounter, new StackItem[] { p.PublicKey.ToArray(), p.Votes }))); + return new Array(engine.ReferenceCounter, GetCandidates(engine.Snapshot).Select(p => new Struct(engine.ReferenceCounter, new StackItem[] { p.PublicKey.ToArray(), p.Votes }))); } - public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetRegisteredValidators(StoreView snapshot) + public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetCandidates(StoreView snapshot) { - byte[] prefix_key = StorageKey.CreateSearchPrefix(Id, new[] { Prefix_Validator }); + byte[] prefix_key = StorageKey.CreateSearchPrefix(Id, new[] { Prefix_Candidate }); return snapshot.Storages.Find(prefix_key).Select(p => ( p.Key.Key.AsSerializable(1), - ValidatorState.FromByteArray(p.Value.Value).Votes - )); + CandidateState.FromByteArray(p.Value.Value) + )).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes)); } [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] @@ -217,21 +248,23 @@ private StackItem GetValidators(ApplicationEngine engine, Array args) public ECPoint[] GetValidators(StoreView snapshot) { - StorageItem storage_count = snapshot.Storages.TryGet(CreateStorageKey(Prefix_ValidatorsCount)); - if (storage_count is null) return Blockchain.StandbyValidators; - ValidatorsCountState state_count = ValidatorsCountState.FromByteArray(storage_count.Value); - int count = (int)state_count.Votes.Select((p, i) => new - { - Count = i, - Votes = p - }).Where(p => p.Votes.Sign > 0).ToArray().WeightedFilter(0.25, 0.75, p => p.Votes, (p, w) => new - { - p.Count, - Weight = w - }).WeightedAverage(p => p.Count, p => p.Weight); - count = Math.Max(count, Blockchain.StandbyValidators.Length); - HashSet sv = new HashSet(Blockchain.StandbyValidators); - return GetRegisteredValidators(snapshot).Where(p => (p.Votes.Sign > 0) || sv.Contains(p.PublicKey)).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count).OrderBy(p => p).ToArray(); + return GetCommitteeMembers(snapshot, Blockchain.ValidatorsCount).OrderBy(p => p).ToArray(); + } + + [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] + private StackItem GetCommittee(ApplicationEngine engine, Array args) + { + return new Array(engine.ReferenceCounter, GetCommittee(engine.Snapshot).Select(p => (StackItem)p.ToArray())); + } + + public ECPoint[] GetCommittee(StoreView snapshot) + { + return GetCommitteeMembers(snapshot, Blockchain.CommitteeMembersCount).OrderBy(p => p).ToArray(); + } + + private IEnumerable GetCommitteeMembers(StoreView snapshot, int count) + { + return GetCandidates(snapshot).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count); } [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] @@ -250,11 +283,10 @@ public ECPoint[] GetNextBlockValidators(StoreView snapshot) public class AccountState : Nep5AccountState { public uint BalanceHeight; - public ECPoint[] Votes; + public ECPoint VoteTo; public AccountState() { - this.Votes = new ECPoint[0]; } public AccountState(byte[] data) @@ -266,66 +298,36 @@ protected override void FromStruct(Struct @struct) { base.FromStruct(@struct); BalanceHeight = (uint)@struct[1].GetBigInteger(); - Votes = @struct[2].GetSpan().AsSerializableArray(Blockchain.MaxValidators); + VoteTo = @struct[2].IsNull ? null : @struct[2].GetSpan().AsSerializable(); } protected override Struct ToStruct() { Struct @struct = base.ToStruct(); @struct.Add(BalanceHeight); - @struct.Add(Votes.ToByteArray()); + @struct.Add(VoteTo?.ToArray() ?? StackItem.Null); return @struct; } } - internal class ValidatorState + internal class CandidateState { + public bool Registered = true; public BigInteger Votes; - public static ValidatorState FromByteArray(byte[] data) + public static CandidateState FromByteArray(byte[] data) { - return new ValidatorState + Struct @struct = (Struct)BinarySerializer.Deserialize(data, 16, 32); + return new CandidateState { - Votes = new BigInteger(data) + Registered = @struct[0].ToBoolean(), + Votes = @struct[1].GetBigInteger() }; } public byte[] ToByteArray() { - return Votes.ToByteArrayStandard(); - } - } - - internal class ValidatorsCountState - { - public BigInteger[] Votes = new BigInteger[Blockchain.MaxValidators]; - - public static ValidatorsCountState FromByteArray(byte[] data) - { - using (MemoryStream ms = new MemoryStream(data, false)) - using (BinaryReader r = new BinaryReader(ms)) - { - BigInteger[] votes = new BigInteger[(int)r.ReadVarInt(Blockchain.MaxValidators)]; - for (int i = 0; i < votes.Length; i++) - votes[i] = new BigInteger(r.ReadVarBytes()); - return new ValidatorsCountState - { - Votes = votes - }; - } - } - - public byte[] ToByteArray() - { - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter w = new BinaryWriter(ms)) - { - w.WriteVarInt(Votes.Length); - foreach (BigInteger vote in Votes) - w.WriteVarBytes(vote.ToByteArrayStandard()); - w.Flush(); - return ms.ToArray(); - } + return BinarySerializer.Serialize(new Struct { Registered, Votes }, 32); } } } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index be6e6a1d77..aa478633c1 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -40,8 +40,7 @@ public void Check_BalanceOfTransferAndBurn() var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; - byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, - Blockchain.StandbyValidators).ToScriptHash().ToArray(); + byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); byte[] to = new byte[20]; @@ -54,16 +53,16 @@ public void Check_BalanceOfTransferAndBurn() // Check unclaim var unclaim = UT_NeoToken.Check_UnclaimedGas(snapshot, from); - unclaim.Value.Should().Be(new BigInteger(600000000000)); + unclaim.Value.Should().Be(new BigInteger(300000048000)); unclaim.State.Should().BeTrue(); // Transfer NativeContract.NEO.Transfer(snapshot, from, to, BigInteger.Zero, true).Should().BeTrue(); - NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(100_000_000); + NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(50000008); NativeContract.NEO.BalanceOf(snapshot, to).Should().Be(0); - NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(3000600000000000); + NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(3000300000048000); NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(0); // Check unclaim @@ -73,7 +72,7 @@ public void Check_BalanceOfTransferAndBurn() unclaim.State.Should().BeTrue(); supply = NativeContract.GAS.TotalSupply(snapshot); - supply.Should().Be(3000600000000000); + supply.Should().Be(3000300000048000); snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 3); // Gas @@ -81,13 +80,13 @@ public void Check_BalanceOfTransferAndBurn() keyCount = snapshot.Storages.GetChangeSet().Count(); - NativeContract.GAS.Transfer(snapshot, from, to, 3000600000000000, false).Should().BeFalse(); // Not signed - NativeContract.GAS.Transfer(snapshot, from, to, 3000600000000001, true).Should().BeFalse(); // More than balance - NativeContract.GAS.Transfer(snapshot, from, to, 3000600000000000, true).Should().BeTrue(); // All balance + NativeContract.GAS.Transfer(snapshot, from, to, 3000300000048000, false).Should().BeFalse(); // Not signed + NativeContract.GAS.Transfer(snapshot, from, to, 3000300000048001, true).Should().BeFalse(); // More than balance + NativeContract.GAS.Transfer(snapshot, from, to, 3000300000048000, true).Should().BeTrue(); // All balance // Balance of - NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000600000000000); + NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000300000048000); NativeContract.GAS.BalanceOf(snapshot, from).Should().Be(0); snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount + 1); // All @@ -103,19 +102,19 @@ public void Check_BalanceOfTransferAndBurn() // Burn more than expected Assert.ThrowsException(() => - NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000600000000001))); + NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000300000048001))); // Real burn NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(1)); - NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000599999999999); + NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000300000047999); keyCount.Should().Be(snapshot.Storages.GetChangeSet().Count()); // Burn all - NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000599999999999)); + NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000300000047999)); (keyCount - 1).Should().Be(snapshot.Storages.GetChangeSet().Count()); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index ad17dffb28..b2696b4888 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -45,24 +45,23 @@ public void Check_Vote() var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; - byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, - Blockchain.StandbyValidators).ToScriptHash().ToArray(); + byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); // No signature - var ret = Check_Vote(snapshot, from, new byte[][] { }, false); + var ret = Check_Vote(snapshot, from, null, false); ret.Result.Should().BeFalse(); ret.State.Should().BeTrue(); // Wrong address - ret = Check_Vote(snapshot, new byte[19], new byte[][] { }, false); + ret = Check_Vote(snapshot, new byte[19], null, false); ret.Result.Should().BeFalse(); ret.State.Should().BeFalse(); // Wrong ec - ret = Check_Vote(snapshot, from, new byte[][] { new byte[19] }, true); + ret = Check_Vote(snapshot, from, new byte[19], true); ret.Result.Should().BeFalse(); ret.State.Should().BeFalse(); @@ -72,7 +71,7 @@ public void Check_Vote() fakeAddr[0] = 0x5F; fakeAddr[5] = 0xFF; - ret = Check_Vote(snapshot, fakeAddr, new byte[][] { }, true); + ret = Check_Vote(snapshot, fakeAddr, null, true); ret.Result.Should().BeFalse(); ret.State.Should().BeTrue(); @@ -85,11 +84,10 @@ public void Check_UnclaimedGas() var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; - byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, - Blockchain.StandbyValidators).ToScriptHash().ToArray(); + byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); var unclaim = Check_UnclaimedGas(snapshot, from); - unclaim.Value.Should().Be(new BigInteger(600000000000)); + unclaim.Value.Should().Be(new BigInteger(300000048000)); unclaim.State.Should().BeTrue(); unclaim = Check_UnclaimedGas(snapshot, new byte[19]); @@ -102,22 +100,14 @@ public void Check_RegisterValidator() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var ret = Check_RegisterValidator(snapshot, new byte[0]); - ret.State.Should().BeFalse(); - ret.Result.Should().BeFalse(); - - ret = Check_RegisterValidator(snapshot, new byte[33]); - ret.State.Should().BeFalse(); - ret.Result.Should().BeFalse(); - var keyCount = snapshot.Storages.GetChangeSet().Count(); var point = Blockchain.StandbyValidators[0].EncodePoint(true); - ret = Check_RegisterValidator(snapshot, point); // Exists + var ret = Check_RegisterValidator(snapshot, point); // Exists ret.State.Should().BeTrue(); - ret.Result.Should().BeFalse(); + ret.Result.Should().BeTrue(); - snapshot.Storages.GetChangeSet().Count().Should().Be(keyCount); // No changes + snapshot.Storages.GetChangeSet().Count().Should().Be(++keyCount); // No changes point[20]++; // fake point ret = Check_RegisterValidator(snapshot, point); // New @@ -129,14 +119,13 @@ public void Check_RegisterValidator() // Check GetRegisteredValidators - var validators = NativeContract.NEO.GetRegisteredValidators(snapshot).OrderBy(u => u.PublicKey).ToArray(); - var check = Blockchain.StandbyValidators.Select(u => u.EncodePoint(true)).ToList(); + var members = NativeContract.NEO.GetCandidates(snapshot).OrderBy(u => u.PublicKey).ToArray(); + var check = Blockchain.StandbyCommittee.Select(u => u.EncodePoint(true)).ToList(); check.Add(point); // Add the new member - for (int x = 0; x < validators.Length; x++) + for (int x = 0; x < members.Length; x++) { - Assert.AreEqual(1, check.RemoveAll(u => u.SequenceEqual(validators[x].PublicKey.EncodePoint(true)))); - Assert.AreEqual(0, validators[x].Votes); + Assert.AreEqual(1, check.RemoveAll(u => u.SequenceEqual(members[x].PublicKey.EncodePoint(true)))); } Assert.AreEqual(0, check.Count); @@ -148,8 +137,7 @@ public void Check_Transfer() var snapshot = Blockchain.Singleton.GetSnapshot(); snapshot.PersistingBlock = new Block() { Index = 1000 }; - byte[] from = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, - Blockchain.StandbyValidators).ToScriptHash().ToArray(); + byte[] from = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); byte[] to = new byte[20]; @@ -158,14 +146,14 @@ public void Check_Transfer() // Check unclaim var unclaim = Check_UnclaimedGas(snapshot, from); - unclaim.Value.Should().Be(new BigInteger(600000000000)); + unclaim.Value.Should().Be(new BigInteger(300000048000)); 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.BalanceOf(snapshot, from).Should().Be(99_999_999); + NativeContract.NEO.BalanceOf(snapshot, from).Should().Be(50000007); NativeContract.NEO.BalanceOf(snapshot, to).Should().Be(1); // Check unclaim @@ -199,10 +187,9 @@ public void Check_Transfer() public void Check_BalanceOf() { var snapshot = Blockchain.Singleton.GetSnapshot(); - byte[] account = Contract.CreateMultiSigRedeemScript(Blockchain.StandbyValidators.Length / 2 + 1, - Blockchain.StandbyValidators).ToScriptHash().ToArray(); + byte[] account = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators).ToArray(); - NativeContract.NEO.BalanceOf(snapshot, account).Should().Be(100_000_000); + NativeContract.NEO.BalanceOf(snapshot, account).Should().Be(50_000_008); account[5]++; // Without existing balance @@ -315,25 +302,24 @@ public void TestGetNextBlockValidators2() [TestMethod] public void TestGetRegisteredValidators1() { - using (ApplicationEngine engine = NativeContract.NEO.TestCall("getRegisteredValidators")) + using (ApplicationEngine engine = NativeContract.NEO.TestCall("getCandidates")) { - var result = engine.ResultStack.Peek(); - result.GetType().Should().Be(typeof(VM.Types.Array)); - ((VM.Types.Array)result).Count.Should().Be(7); - ((VM.Types.Struct)((VM.Types.Array)result)[0])[0].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); - ((VM.Types.Struct)((VM.Types.Array)result)[0])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[1])[0].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - ((VM.Types.Struct)((VM.Types.Array)result)[1])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[2])[0].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - ((VM.Types.Struct)((VM.Types.Array)result)[2])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[3])[0].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - ((VM.Types.Struct)((VM.Types.Array)result)[3])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[4])[0].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - ((VM.Types.Struct)((VM.Types.Array)result)[4])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[5])[0].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - ((VM.Types.Struct)((VM.Types.Array)result)[5])[1].GetBigInteger().Should().Be(new BigInteger(0)); - ((VM.Types.Struct)((VM.Types.Array)result)[6])[0].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - ((VM.Types.Struct)((VM.Types.Array)result)[6])[1].GetBigInteger().Should().Be(new BigInteger(0)); + engine.ResultStack.TryPop(out VM.Types.Array array).Should().BeTrue(); + array.Count.Should().Be(21); + ((VM.Types.Struct)array[0])[0].GetSpan().ToHexString().Should().Be("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639"); + ((VM.Types.Struct)array[0])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); + ((VM.Types.Struct)array[1])[0].GetSpan().ToHexString().Should().Be("0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30"); + ((VM.Types.Struct)array[1])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); + ((VM.Types.Struct)array[2])[0].GetSpan().ToHexString().Should().Be("0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d"); + ((VM.Types.Struct)array[2])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); + ((VM.Types.Struct)array[3])[0].GetSpan().ToHexString().Should().Be("023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe"); + ((VM.Types.Struct)array[3])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); + ((VM.Types.Struct)array[4])[0].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + ((VM.Types.Struct)array[4])[1].GetBigInteger().Should().Be(new BigInteger(3571428)); + ((VM.Types.Struct)array[5])[0].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + ((VM.Types.Struct)array[5])[1].GetBigInteger().Should().Be(new BigInteger(3571428)); + ((VM.Types.Struct)array[6])[0].GetSpan().ToHexString().Should().Be("02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad"); + ((VM.Types.Struct)array[6])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); } } @@ -341,29 +327,29 @@ public void TestGetRegisteredValidators1() public void TestGetRegisteredValidators2() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var result = NativeContract.NEO.GetRegisteredValidators(snapshot).ToArray(); - result.Length.Should().Be(7); - result[0].PublicKey.ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); - result[0].Votes.Should().Be(new BigInteger(0)); - result[1].PublicKey.ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - result[1].Votes.Should().Be(new BigInteger(0)); - result[2].PublicKey.ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - result[2].Votes.Should().Be(new BigInteger(0)); - result[3].PublicKey.ToArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - result[3].Votes.Should().Be(new BigInteger(0)); - result[4].PublicKey.ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - result[4].Votes.Should().Be(new BigInteger(0)); - result[5].PublicKey.ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - result[5].Votes.Should().Be(new BigInteger(0)); - result[6].PublicKey.ToArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - result[6].Votes.Should().Be(new BigInteger(0)); + var result = NativeContract.NEO.GetCandidates(snapshot).ToArray(); + result.Length.Should().Be(21); + result[0].PublicKey.ToArray().ToHexString().Should().Be("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639"); + result[0].Votes.Should().Be(new BigInteger(1785714)); + result[1].PublicKey.ToArray().ToHexString().Should().Be("0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30"); + result[1].Votes.Should().Be(new BigInteger(1785714)); + result[2].PublicKey.ToArray().ToHexString().Should().Be("0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d"); + result[2].Votes.Should().Be(new BigInteger(1785714)); + result[3].PublicKey.ToArray().ToHexString().Should().Be("023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe"); + result[3].Votes.Should().Be(new BigInteger(1785714)); + result[4].PublicKey.ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + result[4].Votes.Should().Be(new BigInteger(3571428)); + result[5].PublicKey.ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + result[5].Votes.Should().Be(new BigInteger(3571428)); + result[6].PublicKey.ToArray().ToHexString().Should().Be("02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad"); + result[6].Votes.Should().Be(new BigInteger(1785714)); StorageKey key = NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G); snapshot.Storages.Add(key, new StorageItem { - Value = new ValidatorState().ToByteArray() + Value = new CandidateState().ToByteArray() }); - NativeContract.NEO.GetRegisteredValidators(snapshot).ToArray().Length.Should().Be(8); + NativeContract.NEO.GetCandidates(snapshot).ToArray().Length.Should().Be(22); } [TestMethod] @@ -374,13 +360,13 @@ public void TestGetValidators1() var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); ((VM.Types.Array)result).Count.Should().Be(7); - ((VM.Types.Array)result)[0].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - ((VM.Types.Array)result)[1].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - ((VM.Types.Array)result)[2].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - ((VM.Types.Array)result)[3].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - ((VM.Types.Array)result)[4].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - ((VM.Types.Array)result)[5].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - ((VM.Types.Array)result)[6].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + ((VM.Types.Array)result)[0].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + ((VM.Types.Array)result)[1].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + ((VM.Types.Array)result)[2].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + ((VM.Types.Array)result)[3].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + ((VM.Types.Array)result)[4].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + ((VM.Types.Array)result)[5].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + ((VM.Types.Array)result)[6].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); } } @@ -389,25 +375,13 @@ public void TestGetValidators2() { var snapshot = Blockchain.Singleton.GetSnapshot(); var result = NativeContract.NEO.GetValidators(snapshot); - result[0].ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - result[1].ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - result[2].ToArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - result[3].ToArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - result[4].ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - result[5].ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - result[6].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); - - StorageKey key = CreateStorageKey(15); - ValidatorsCountState state = new ValidatorsCountState(); - for (int i = 0; i < 100; i++) - { - state.Votes[i] = new BigInteger(i + 1); - } - snapshot.Storages.Add(key, new StorageItem() - { - Value = state.ToByteArray() - }); - NativeContract.NEO.GetValidators(snapshot).ToArray().Length.Should().Be(7); + result[0].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + result[1].ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + result[2].ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + result[3].ToArray().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + result[4].ToArray().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + result[5].ToArray().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + result[6].ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); } [TestMethod] @@ -469,11 +443,11 @@ 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(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, false); + var ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), false); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); - ret = Check_Vote(snapshot, account.ToArray(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, true); + ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); @@ -481,48 +455,40 @@ public void TestVote() { Value = new AccountState().ToByteArray() }); - ret = Check_Vote(snapshot, account.ToArray(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, true); + ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); ret.State.Should().BeTrue(); - ret.Result.Should().BeTrue(); + ret.Result.Should().BeFalse(); snapshot.Storages.Delete(keyAccount); snapshot.Storages.GetAndChange(keyAccount, () => new StorageItem { Value = new AccountState() { - Votes = new ECPoint[] { ECCurve.Secp256r1.G } + VoteTo = ECCurve.Secp256r1.G }.ToByteArray() }); snapshot.Storages.Add(keyValidator, new StorageItem { - Value = new ValidatorState().ToByteArray() + Value = new CandidateState().ToByteArray() }); - ret = Check_Vote(snapshot, account.ToArray(), new byte[][] { ECCurve.Secp256r1.G.ToArray() }, true); + ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); } - [TestMethod] - public void TestValidatorsCountState_FromByteArray() - { - ValidatorsCountState input = new ValidatorsCountState { Votes = new BigInteger[] { new BigInteger(1000) } }; - ValidatorsCountState output = ValidatorsCountState.FromByteArray(input.ToByteArray()); - output.Should().BeEquivalentTo(input); - } - [TestMethod] public void TestValidatorState_FromByteArray() { - ValidatorState input = new ValidatorState { Votes = new BigInteger(1000) }; - ValidatorState output = ValidatorState.FromByteArray(input.ToByteArray()); + CandidateState input = new CandidateState { Votes = new BigInteger(1000) }; + CandidateState output = CandidateState.FromByteArray(input.ToByteArray()); output.Should().BeEquivalentTo(input); } [TestMethod] public void TestValidatorState_ToByteArray() { - ValidatorState input = new ValidatorState { Votes = new BigInteger(1000) }; - input.ToByteArray().ToHexString().Should().Be("e803"); + CandidateState input = new CandidateState { Votes = new BigInteger(1000) }; + input.ToByteArray().ToHexString().Should().Be("410220012102e803"); } internal (bool State, bool Result) Transfer4TesingOnBalanceChanging(BigInteger amount, bool addVotes) @@ -539,23 +505,13 @@ public void TestValidatorState_ToByteArray() { Value = new AccountState() { - Votes = new ECPoint[] { ECCurve.Secp256r1.G }, + VoteTo = ECCurve.Secp256r1.G, Balance = new BigInteger(1000) }.ToByteArray() }); snapshot.Storages.Add(NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G), new StorageItem { - Value = new ValidatorState().ToByteArray() - }); - - ValidatorsCountState state = new ValidatorsCountState(); - for (int i = 0; i < 100; i++) - { - state.Votes[i] = new BigInteger(i + 1); - } - snapshot.Storages.Add(CreateStorageKey(15), new StorageItem() - { - Value = state.ToByteArray() + Value = new CandidateState().ToByteArray() }); } else @@ -577,7 +533,7 @@ public void TestValidatorState_ToByteArray() return (true, result.ToBoolean()); } - internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] account, byte[][] pubkeys, bool signAccount) + internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] account, byte[] pubkey, bool signAccount) { var engine = new ApplicationEngine(TriggerType.Application, new Nep5NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot, 0, true); @@ -586,11 +542,11 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] var script = new ScriptBuilder(); - foreach (var ec in pubkeys) script.EmitPush(ec); - script.EmitPush(pubkeys.Length); - script.Emit(OpCode.PACK); - - script.EmitPush(account.ToArray()); + if (pubkey is null) + script.Emit(OpCode.PUSHNULL); + else + script.EmitPush(pubkey); + script.EmitPush(account); script.EmitPush(2); script.Emit(OpCode.PACK); script.EmitPush("vote"); @@ -609,7 +565,8 @@ internal static (bool State, bool Result) Check_Vote(StoreView snapshot, byte[] internal static (bool State, bool Result) Check_RegisterValidator(StoreView snapshot, byte[] pubkey) { - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + var engine = new ApplicationEngine(TriggerType.Application, + new Nep5NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, 0, true); engine.LoadScript(NativeContract.NEO.Script); @@ -617,7 +574,7 @@ internal static (bool State, bool Result) Check_RegisterValidator(StoreView snap script.EmitPush(pubkey); script.EmitPush(1); script.Emit(OpCode.PACK); - script.EmitPush("registerValidator"); + script.EmitPush("registerCandidate"); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -685,7 +642,7 @@ internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint[] votes) + 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); @@ -694,7 +651,7 @@ internal static void CheckBalance(byte[] account, DataCache(Blockchain.MaxValidators)).Should().BeEquivalentTo(votes); // Votes + st[2].GetSpan().AsSerializable().Should().BeEquivalentTo(voteTo); // Votes trackable.Key.Key.Should().BeEquivalentTo(new byte[] { 20 }.Concat(account)); trackable.Item.IsConstant.Should().Be(false); diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index d9223afcd9..51e74d3750 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -4,7 +4,6 @@ using Neo.SmartContract; using Neo.Wallets; using System; -using System.Collections.Generic; using System.Net; using System.Numerics; using System.Security.Cryptography; @@ -118,154 +117,5 @@ public void TestUnmapForIPEndPoin() var endPoint2 = new IPEndPoint(addr2, 8888); endPoint2.Unmap().Should().Be(endPoint); } - - [TestMethod] - public void TestWeightedAverage() - { - var foo1 = new Foo - { - Value = 1, - Weight = 2 - }; - var foo2 = new Foo - { - Value = 2, - Weight = 3 - }; - var list = new List - { - foo1,foo2 - }; - list.WeightedAverage(p => p.Value, p => p.Weight).Should().Be(new BigInteger(1)); - - var foo3 = new Foo - { - Value = 1, - Weight = 0 - }; - var foo4 = new Foo - { - Value = 2, - Weight = 0 - }; - var list2 = new List - { - foo3, foo4 - }; - list2.WeightedAverage(p => p.Value, p => p.Weight).Should().Be(BigInteger.Zero); - } - - [TestMethod] - public void WeightFilter() - { - var w1 = new Woo - { - Value = 1 - }; - var w2 = new Woo - { - Value = 2 - }; - var list = new List - { - w1, w2 - }; - var ret = list.WeightedFilter(0.3, 0.6, p => p.Value, (p, w) => new Result - { - Info = p, - Weight = w - }); - var sum = BigInteger.Zero; - foreach (Result res in ret) - { - sum = BigInteger.Add(res.Weight, sum); - } - sum.Should().Be(BigInteger.Zero); - - var w3 = new Woo - { - Value = 3 - }; - - var list2 = new List - { - w1, w2, w3 - }; - var ret2 = list2.WeightedFilter(0.3, 0.4, p => p.Value, (p, w) => new Result - { - Info = p, - Weight = w - }); - sum = BigInteger.Zero; - foreach (Result res in ret2) - { - sum = BigInteger.Add(res.Weight, sum); - } - sum.Should().Be(BigInteger.Zero); - - CheckArgumentOutOfRangeException(-1, 0.4, p => p.Value, list2); - - CheckArgumentOutOfRangeException(0.2, 1.4, p => p.Value, list2); - - CheckArgumentOutOfRangeException(0.8, 0.3, p => p.Value, list2); - - CheckArgumentOutOfRangeException(0.3, 0.8, p => p.Value, list2); - - CheckArgumentNullException(0.3, 0.6, null, list2); - - CheckArgumentNullException(0.3, 0.4, p => p.Value, null); - - list2.WeightedFilter(0.3, 0.3, p => p.Value, (p, w) => new Result - { - Info = p, - Weight = w - }).WeightedAverage(p => p.Weight, p => p.Weight).Should().Be(0); - - - var list3 = new List(); - list3.WeightedFilter(0.3, 0.6, p => p.Value, (p, w) => new Result - { - Info = p, - Weight = w - }).WeightedAverage(p => p.Weight, p => p.Weight).Should().Be(0); - - } - - private static void CheckArgumentOutOfRangeException(double start, double end, Func func, List list) - { - Action action = () => list.WeightedFilter(start, end, func, (p, w) => new Result - { - Info = p, - Weight = w - }).WeightedAverage(p => p.Weight, p => p.Weight); - action.Should().Throw(); - } - - private static void CheckArgumentNullException(double start, double end, Func func, List list) - { - Action action = () => list.WeightedFilter(start, end, func, (p, w) => new Result - { - Info = p, - Weight = w - }).WeightedAverage(p => p.Weight, p => p.Weight); - action.Should().Throw(); - } - } - - class Foo - { - public int Weight { set; get; } - public int Value { set; get; } - } - - class Woo - { - public int Value { set; get; } - } - - class Result - { - public Woo Info { set; get; } - public BigInteger Weight { set; get; } } } From 56041a8903b0a8ae0dca713c27b240fa4a3882d9 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 15 Apr 2020 16:10:41 +0800 Subject: [PATCH 236/305] Fix version --- src/neo/neo.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index dd502e1d4a..c7e38117c3 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -4,7 +4,7 @@ 2015-2019 The Neo Project Neo 3.0.0 - preview2-00 + preview2 The Neo Project netstandard2.1 true From 7d3dfdebc759cf56feaf070feccbb8e388dc9066 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 15 Apr 2020 16:33:25 +0800 Subject: [PATCH 237/305] Modify manifest according to the last amendment of NEP-3 (#1481) --- src/neo/SmartContract/ApplicationEngine.cs | 8 +-- src/neo/SmartContract/Helper.cs | 14 ++++-- .../SmartContract/InteropService.Contract.cs | 49 +++++++++++-------- src/neo/SmartContract/Manifest/ContractAbi.cs | 18 ++++--- .../Manifest/ContractManifest.cs | 27 +--------- .../Manifest/ContractMethodDescriptor.cs | 29 ++++------- .../SmartContract/Native/NativeContract.cs | 31 +++++++++--- .../neo.UnitTests/Ledger/UT_ContractState.cs | 4 +- .../Manifest/UT_ContractManifest.cs | 43 ++++++++-------- .../Manifest/UT_ContractPermission.cs | 8 +-- .../Native/Tokens/UT_Nep5Token.cs | 30 ++++-------- .../SmartContract/Native/UT_NativeContract.cs | 21 ++++---- .../SmartContract/UT_InteropService.NEO.cs | 4 +- .../SmartContract/UT_InteropService.cs | 43 ++++++++-------- .../SmartContract/UT_SmartContractHelper.cs | 13 ++++- .../SmartContract/UT_Syscalls.cs | 15 ++++-- tests/neo.UnitTests/TestUtils.cs | 35 +++++++++++-- 17 files changed, 213 insertions(+), 179 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index fda6a3ba84..fc11594a03 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -114,20 +114,20 @@ private static Block CreateDummyBlock(StoreView snapshot) } public static ApplicationEngine Run(byte[] script, StoreView snapshot, - IVerifiable container = null, Block persistingBlock = null, bool testMode = false, long extraGAS = default) + IVerifiable container = null, Block persistingBlock = null, int offset = 0, bool testMode = false, long extraGAS = default) { snapshot.PersistingBlock = persistingBlock ?? snapshot.PersistingBlock ?? CreateDummyBlock(snapshot); ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, container, snapshot, extraGAS, testMode); - engine.LoadScript(script); + engine.LoadScript(script).InstructionPointer = offset; engine.Execute(); return engine; } - public static ApplicationEngine Run(byte[] script, IVerifiable container = null, Block persistingBlock = null, bool testMode = false, long extraGAS = default) + public static ApplicationEngine Run(byte[] script, IVerifiable container = null, Block persistingBlock = null, int offset = 0, bool testMode = false, long extraGAS = default) { using (SnapshotView snapshot = Blockchain.Singleton.GetSnapshot()) { - return Run(script, snapshot, container, persistingBlock, testMode, extraGAS); + return Run(script, snapshot, container, persistingBlock, offset, testMode, extraGAS); } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index fc10de2acf..f2a506da6a 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -1,7 +1,9 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; +using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; +using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; using System; @@ -143,19 +145,25 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snap if (hashes.Length != verifiable.Witnesses.Length) return false; for (int i = 0; i < hashes.Length; i++) { + int offset; byte[] verification = verifiable.Witnesses[i].VerificationScript; if (verification.Length == 0) { - verification = snapshot.Contracts.TryGet(hashes[i])?.Script; - if (verification is null) return false; + ContractState cs = snapshot.Contracts.TryGet(hashes[i]); + if (cs is null) return false; + ContractMethodDescriptor md = cs.Manifest.Abi.GetMethod("verify"); + if (md is null) return false; + verification = cs.Script; + offset = md.Offset; } else { if (hashes[i] != verifiable.Witnesses[i].ScriptHash) return false; + offset = 0; } using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Verification, verifiable, snapshot, gas)) { - engine.LoadScript(verification, CallFlags.ReadOnly); + engine.LoadScript(verification, CallFlags.ReadOnly).InstructionPointer = offset; engine.LoadScript(verifiable.Witnesses[i].InvocationScript, CallFlags.None); if (engine.Execute() == VMState.FAULT) return false; if (!engine.ResultStack.TryPop(out StackItem result) || !result.ToBoolean()) return false; diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index 351345d29b..99dc91c419 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -3,10 +3,12 @@ using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native; using Neo.VM; using Neo.VM.Types; using System; using System.Linq; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract { @@ -111,38 +113,33 @@ private static bool Contract_Destroy(ApplicationEngine engine) private static bool Contract_Call(ApplicationEngine engine) { - StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop(); - StackItem method = engine.CurrentContext.EvaluationStack.Pop(); - StackItem args = engine.CurrentContext.EvaluationStack.Pop(); + if (!engine.TryPop(out ReadOnlySpan contractHash)) return false; + if (!engine.TryPop(out string method)) return false; + if (!engine.TryPop(out Array args)) return false; - return Contract_CallEx(engine, new UInt160(contractHash.GetSpan()), method, args, CallFlags.All); + return Contract_CallEx(engine, new UInt160(contractHash), method, args, CallFlags.All); } private static bool Contract_CallEx(ApplicationEngine engine) { - StackItem contractHash = engine.CurrentContext.EvaluationStack.Pop(); - StackItem method = engine.CurrentContext.EvaluationStack.Pop(); - StackItem args = engine.CurrentContext.EvaluationStack.Pop(); + if (!engine.TryPop(out ReadOnlySpan contractHash)) return false; + if (!engine.TryPop(out string method)) return false; + if (!engine.TryPop(out Array args)) return false; + if (!engine.TryPop(out int flags)) return false; - if (!engine.CurrentContext.EvaluationStack.TryPop(out var flagItem)) - { - return false; - } + if (!Enum.IsDefined(typeof(CallFlags), (CallFlags)flags)) return false; - CallFlags flags = (CallFlags)(int)flagItem.ToBigInteger(); - if (!Enum.IsDefined(typeof(CallFlags), flags)) return false; - - return Contract_CallEx(engine, new UInt160(contractHash.GetSpan()), method, args, flags); + return Contract_CallEx(engine, new UInt160(contractHash), method, args, (CallFlags)flags); } - private static bool Contract_CallEx(ApplicationEngine engine, UInt160 contractHash, StackItem method, StackItem args, CallFlags flags) + private static bool Contract_CallEx(ApplicationEngine engine, UInt160 contractHash, string method, Array args, CallFlags flags) { ContractState contract = engine.Snapshot.Contracts.TryGet(contractHash); if (contract is null) return false; ContractManifest currentManifest = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash)?.Manifest; - if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method.GetString())) + if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method)) return false; if (engine.InvocationCounter.TryGetValue(contract.ScriptHash, out var counter)) @@ -158,13 +155,25 @@ private static bool Contract_CallEx(ApplicationEngine engine, UInt160 contractHa UInt160 callingScriptHash = state.ScriptHash; CallFlags callingFlags = state.CallFlags; - ExecutionContext context_new = engine.LoadScript(contract.Script, 1); + ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); + if (md is null) return false; + int rvcount = md.ReturnType == ContractParameterType.Void ? 0 : 1; + ExecutionContext context_new = engine.LoadScript(contract.Script, rvcount); state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; state.CallFlags = flags & callingFlags; - context_new.EvaluationStack.Push(args); - context_new.EvaluationStack.Push(method); + if (NativeContract.IsNative(contractHash)) + { + context_new.EvaluationStack.Push(args); + context_new.EvaluationStack.Push(method); + } + else + { + for (int i = args.Count - 1; i >= 0; i--) + context_new.EvaluationStack.Push(args[i]); + context_new.InstructionPointer = md.Offset; + } return true; } diff --git a/src/neo/SmartContract/Manifest/ContractAbi.cs b/src/neo/SmartContract/Manifest/ContractAbi.cs index e7fb4a5272..6a5e559b62 100644 --- a/src/neo/SmartContract/Manifest/ContractAbi.cs +++ b/src/neo/SmartContract/Manifest/ContractAbi.cs @@ -1,4 +1,5 @@ using Neo.IO.Json; +using System.Collections.Generic; using System.Linq; namespace Neo.SmartContract.Manifest @@ -8,16 +9,13 @@ namespace Neo.SmartContract.Manifest /// public class ContractAbi { + private IReadOnlyDictionary methodDictionary; + /// /// Hash is the script hash of the contract. It is encoded as a hexadecimal string in big-endian. /// public UInt160 Hash { get; set; } - /// - /// Entrypoint is a Method object which describe the details of the entrypoint of the contract. - /// - public ContractMethodDescriptor EntryPoint { get; set; } - /// /// Methods is an array of Method objects which describe the details of each method in the contract. /// @@ -33,7 +31,6 @@ public ContractAbi Clone() return new ContractAbi { Hash = Hash, - EntryPoint = EntryPoint.Clone(), Methods = Methods.Select(p => p.Clone()).ToArray(), Events = Events.Select(p => p.Clone()).ToArray() }; @@ -49,17 +46,22 @@ public static ContractAbi FromJson(JObject json) return new ContractAbi { Hash = UInt160.Parse(json["hash"].AsString()), - EntryPoint = ContractMethodDescriptor.FromJson(json["entryPoint"]), Methods = ((JArray)json["methods"]).Select(u => ContractMethodDescriptor.FromJson(u)).ToArray(), Events = ((JArray)json["events"]).Select(u => ContractEventDescriptor.FromJson(u)).ToArray() }; } + public ContractMethodDescriptor GetMethod(string name) + { + methodDictionary ??= Methods.ToDictionary(p => p.Name); + methodDictionary.TryGetValue(name, out var method); + return method; + } + public JObject ToJson() { var json = new JObject(); json["hash"] = Hash.ToString(); - json["entryPoint"] = EntryPoint.ToJson(); json["methods"] = new JArray(Methods.Select(u => u.ToJson()).ToArray()); json["events"] = new JArray(Events.Select(u => u.ToJson()).ToArray()); return json; diff --git a/src/neo/SmartContract/Manifest/ContractManifest.cs b/src/neo/SmartContract/Manifest/ContractManifest.cs index 0ad289c9da..da56c1cb0f 100644 --- a/src/neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/neo/SmartContract/Manifest/ContractManifest.cs @@ -15,7 +15,7 @@ public class ContractManifest : ISerializable /// /// Max length for a valid Contract Manifest /// - public const int MaxLength = 2048; + public const int MaxLength = 4096; /// /// Serialized size @@ -64,31 +64,6 @@ public class ContractManifest : ISerializable /// public JObject Extra { get; set; } - /// - /// Create Default Contract manifest - /// - /// Hash - /// Return default manifest for this contract - public static ContractManifest CreateDefault(UInt160 hash) - { - return new ContractManifest() - { - Permissions = new[] { ContractPermission.DefaultPermission }, - Abi = new ContractAbi() - { - Hash = hash, - EntryPoint = ContractMethodDescriptor.DefaultEntryPoint, - Events = new ContractEventDescriptor[0], - Methods = new ContractMethodDescriptor[0] - }, - Features = ContractFeatures.NoProperty, - Groups = new ContractGroup[0], - SafeMethods = WildcardContainer.Create(), - Trusts = WildcardContainer.Create(), - Extra = null, - }; - } - /// /// Return true if is allowed /// diff --git a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs index 6596f86dd6..873999096a 100644 --- a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -6,27 +6,13 @@ namespace Neo.SmartContract.Manifest { public class ContractMethodDescriptor : ContractEventDescriptor { - /// - /// Default entry point - /// - public static readonly ContractMethodDescriptor DefaultEntryPoint = new ContractMethodDescriptor() + private int _offset; + + public int Offset { - Name = "Main", - Parameters = new ContractParameterDefinition[] - { - new ContractParameterDefinition() - { - Name = "operation", - Type = ContractParameterType.String - }, - new ContractParameterDefinition() - { - Name = "args", - Type = ContractParameterType.Array - } - }, - ReturnType = ContractParameterType.Any - }; + 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: @@ -40,6 +26,7 @@ public new ContractMethodDescriptor Clone() { Name = Name, Parameters = Parameters.Select(p => p.Clone()).ToArray(), + Offset = Offset, ReturnType = ReturnType }; } @@ -55,6 +42,7 @@ public new static ContractMethodDescriptor FromJson(JObject json) { 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()), }; } @@ -62,6 +50,7 @@ public new static ContractMethodDescriptor FromJson(JObject json) public override JObject ToJson() { var json = base.ToJson(); + json["offset"] = Offset; json["returnType"] = ReturnType.ToString(); return json; } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index b28f55be14..7e402ac00a 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -17,10 +17,11 @@ namespace Neo.SmartContract.Native { public abstract class NativeContract { - private static readonly List contracts = new List(); + private static readonly List contractsList = new List(); + private static readonly Dictionary contractsDictionary = new Dictionary(); private readonly Dictionary methods = new Dictionary(); - public static IReadOnlyCollection Contracts { get; } = contracts; + public static IReadOnlyCollection Contracts { get; } = contractsList; public static NeoToken NEO { get; } = new NeoToken(); public static GasToken GAS { get; } = new GasToken(); public static PolicyContract Policy { get; } = new PolicyContract(); @@ -42,7 +43,6 @@ protected NativeContract() this.Script = sb.ToArray(); } this.Hash = Script.ToScriptHash(); - this.Manifest = ContractManifest.CreateDefault(this.Hash); List descriptors = new List(); List safeMethods = new List(); foreach (MethodInfo method in GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) @@ -64,9 +64,23 @@ protected NativeContract() RequiredCallFlags = attribute.SafeMethod ? CallFlags.None : CallFlags.AllowModifyStates }); } - this.Manifest.Abi.Methods = descriptors.ToArray(); - this.Manifest.SafeMethods = WildcardContainer.Create(safeMethods.ToArray()); - contracts.Add(this); + this.Manifest = new ContractManifest + { + Permissions = new[] { ContractPermission.DefaultPermission }, + Abi = new ContractAbi() + { + Hash = Hash, + Events = new ContractEventDescriptor[0], + Methods = descriptors.ToArray() + }, + Features = ContractFeatures.NoProperty, + Groups = new ContractGroup[0], + SafeMethods = WildcardContainer.Create(safeMethods.ToArray()), + Trusts = WildcardContainer.Create(), + Extra = null, + }; + contractsList.Add(this); + contractsDictionary.Add(Hash, this); } protected StorageKey CreateStorageKey(byte prefix, byte[] key = null) @@ -102,6 +116,11 @@ internal bool Invoke(ApplicationEngine engine) return true; } + public static bool IsNative(UInt160 hash) + { + return contractsDictionary.ContainsKey(hash); + } + internal long GetPrice(EvaluationStack stack, StoreView snapshot) { return methods.TryGetValue(stack.Peek().GetString(), out ContractMethodMetadata method) ? method.Price : 0; diff --git a/tests/neo.UnitTests/Ledger/UT_ContractState.cs b/tests/neo.UnitTests/Ledger/UT_ContractState.cs index 8f730a20c0..40e4a6a23d 100644 --- a/tests/neo.UnitTests/Ledger/UT_ContractState.cs +++ b/tests/neo.UnitTests/Ledger/UT_ContractState.cs @@ -19,7 +19,7 @@ public class UT_ContractState [TestInitialize] public void TestSetup() { - manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")); + manifest = TestUtils.CreateDefaultManifest(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")); contract = new ContractState { Script = script, @@ -84,7 +84,7 @@ public void TestDeserialize() public void TestGetSize() { ISerializable newContract = contract; - newContract.Size.Should().Be(372); + newContract.Size.Should().Be(239); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs index 6bab91a808..1df0b7ec07 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs @@ -1,6 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; -using Neo.IO.Json; using Neo.SmartContract.Manifest; using System.IO; @@ -12,22 +11,22 @@ public class UT_ContractManifest [TestMethod] public void ParseFromJson_Default() { - var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":null}"; + var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); - Assert.AreEqual(manifest.ToString(), ContractManifest.CreateDefault(UInt160.Zero).ToString()); + Assert.AreEqual(manifest.ToString(), TestUtils.CreateDefaultManifest(UInt160.Zero).ToString()); Assert.IsTrue(manifest.IsValid(UInt160.Zero)); } [TestMethod] public void ParseFromJson_Features() { - var json = @"{""groups"":[],""features"":{""storage"":true,""payable"":true},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":null}"; + var json = @"{""groups"":[],""features"":{""storage"":true,""payable"":true},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToJson().ToString(), json); - var check = ContractManifest.CreateDefault(UInt160.Zero); + var check = TestUtils.CreateDefaultManifest(UInt160.Zero); check.Features = ContractFeatures.HasStorage | ContractFeatures.Payable; Assert.AreEqual(manifest.ToString(), check.ToString()); } @@ -35,11 +34,11 @@ public void ParseFromJson_Features() [TestMethod] public void ParseFromJson_Permissions() { - var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""safeMethods"":[],""extra"":null}"; + var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""0x0000000000000000000000000000000000000000"",""methods"":[""method1"",""method2""]}],""trusts"":[],""safeMethods"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); - var check = ContractManifest.CreateDefault(UInt160.Zero); + var check = TestUtils.CreateDefaultManifest(UInt160.Zero); check.Permissions = new[] { new ContractPermission() @@ -54,11 +53,11 @@ public void ParseFromJson_Permissions() [TestMethod] public void ParseFromJson_SafeMethods() { - var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[""balanceOf""],""extra"":null}"; + var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[""balanceOf""],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); - var check = ContractManifest.CreateDefault(UInt160.Zero); + var check = TestUtils.CreateDefaultManifest(UInt160.Zero); check.SafeMethods = WildcardContainer.Create("balanceOf"); Assert.AreEqual(manifest.ToString(), check.ToString()); } @@ -66,11 +65,11 @@ public void ParseFromJson_SafeMethods() [TestMethod] public void ParseFromJson_Trust() { - var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""safeMethods"":[],""extra"":null}"; + var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[""0x0000000000000000000000000000000000000001""],""safeMethods"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); - var check = ContractManifest.CreateDefault(UInt160.Zero); + var check = TestUtils.CreateDefaultManifest(UInt160.Zero); check.Trusts = WildcardContainer.Create(UInt160.Parse("0x0000000000000000000000000000000000000001")); Assert.AreEqual(manifest.ToString(), check.ToString()); } @@ -78,11 +77,11 @@ public void ParseFromJson_Trust() [TestMethod] public void ParseFromJson_Groups() { - var json = @"{""groups"":[{""pubKey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":null}"; + var json = @"{""groups"":[{""pubKey"":""03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"",""signature"":""QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==""}],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":null}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(manifest.ToString(), json); - var check = ContractManifest.CreateDefault(UInt160.Zero); + var check = TestUtils.CreateDefaultManifest(UInt160.Zero); check.Groups = new ContractGroup[] { new ContractGroup() { PubKey = ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1), Signature = "41414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141".HexToBytes() } }; Assert.AreEqual(manifest.ToString(), check.ToString()); } @@ -90,7 +89,7 @@ public void ParseFromJson_Groups() [TestMethod] public void ParseFromJson_Extra() { - var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""entryPoint"":{""name"":""Main"",""parameters"":[{""name"":""operation"",""type"":""String""},{""name"":""args"",""type"":""Array""}],""returnType"":""Any""},""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":{""key"":""value""}}"; + var json = @"{""groups"":[],""features"":{""storage"":false,""payable"":false},""abi"":{""hash"":""0x0000000000000000000000000000000000000000"",""methods"":[],""events"":[]},""permissions"":[{""contract"":""*"",""methods"":""*""}],""trusts"":[],""safeMethods"":[],""extra"":{""key"":""value""}}"; var manifest = ContractManifest.Parse(json); Assert.AreEqual(json, json); Assert.AreEqual("value", manifest.Extra["key"].AsString(), false); @@ -102,11 +101,11 @@ public void TestDeserializeAndSerialize() MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); BinaryReader reader = new BinaryReader(stream); - var expected = ContractManifest.CreateDefault(UInt160.Zero); + var expected = TestUtils.CreateDefaultManifest(UInt160.Zero); expected.SafeMethods = WildcardContainer.Create(new string[] { "AAA" }); expected.Serialize(writer); stream.Seek(0, SeekOrigin.Begin); - var actual = ContractManifest.CreateDefault(UInt160.Zero); + var actual = TestUtils.CreateDefaultManifest(UInt160.Zero); actual.Deserialize(reader); Assert.AreEqual(expected.SafeMethods.ToString(), actual.SafeMethods.ToString()); Assert.AreEqual(expected.SafeMethods.Count, 1); @@ -115,15 +114,15 @@ public void TestDeserializeAndSerialize() [TestMethod] public void TestGetHash() { - var temp = ContractManifest.CreateDefault(UInt160.Zero); + var temp = TestUtils.CreateDefaultManifest(UInt160.Zero); Assert.AreEqual(temp.Abi.Hash, temp.Hash); } [TestMethod] public void TestGetSize() { - var temp = ContractManifest.CreateDefault(UInt160.Zero); - Assert.AreEqual(366, temp.Size); + var temp = TestUtils.CreateDefaultManifest(UInt160.Zero); + Assert.AreEqual(233, temp.Size); } [TestMethod] @@ -136,15 +135,15 @@ public void TestGenerator() [TestMethod] public void TestCanCall() { - var temp = ContractManifest.CreateDefault(UInt160.Zero); + var temp = TestUtils.CreateDefaultManifest(UInt160.Zero); temp.SafeMethods = WildcardContainer.Create(new string[] { "AAA" }); - Assert.AreEqual(true, temp.CanCall(ContractManifest.CreateDefault(UInt160.Zero), "AAA")); + Assert.AreEqual(true, temp.CanCall(TestUtils.CreateDefaultManifest(UInt160.Zero), "AAA")); } [TestMethod] public void TestClone() { - var expected = ContractManifest.CreateDefault(UInt160.Zero); + var expected = TestUtils.CreateDefaultManifest(UInt160.Zero); expected.SafeMethods = WildcardContainer.Create(new string[] { "AAA" }); var actual = expected.Clone(); Assert.AreEqual(actual.ToString(), expected.ToString()); diff --git a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs index 7517458423..8e147f0117 100644 --- a/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs +++ b/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractPermission.cs @@ -11,13 +11,13 @@ public class UT_ContractPermission [TestMethod] public void TestIsAllowed() { - ContractManifest contractManifest1 = ContractManifest.CreateDefault(UInt160.Zero); + ContractManifest contractManifest1 = TestUtils.CreateDefaultManifest(UInt160.Zero); ContractPermission contractPermission1 = ContractPermission.DefaultPermission; contractPermission1.Contract = ContractPermissionDescriptor.Create(UInt160.Zero); Assert.AreEqual(true, contractPermission1.IsAllowed(contractManifest1, "AAA")); contractPermission1.Contract = ContractPermissionDescriptor.CreateWildcard(); - ContractManifest contractManifest2 = ContractManifest.CreateDefault(UInt160.Zero); + ContractManifest contractManifest2 = TestUtils.CreateDefaultManifest(UInt160.Zero); ContractPermission contractPermission2 = ContractPermission.DefaultPermission; contractPermission2.Contract = ContractPermissionDescriptor.Create(UInt160.Parse("0x0000000000000000000000000000000000000001")); Assert.AreEqual(false, contractPermission2.IsAllowed(contractManifest2, "AAA")); @@ -27,7 +27,7 @@ public void TestIsAllowed() byte[] privateKey3 = new byte[32]; random3.NextBytes(privateKey3); ECPoint publicKey3 = ECCurve.Secp256r1.G * privateKey3; - ContractManifest contractManifest3 = ContractManifest.CreateDefault(UInt160.Zero); + ContractManifest contractManifest3 = TestUtils.CreateDefaultManifest(UInt160.Zero); contractManifest3.Groups = new ContractGroup[] { new ContractGroup() { PubKey = publicKey3 } }; ContractPermission contractPermission3 = ContractPermission.DefaultPermission; contractPermission3.Contract = ContractPermissionDescriptor.Create(publicKey3); @@ -41,7 +41,7 @@ public void TestIsAllowed() byte[] privateKey42 = new byte[32]; random4.NextBytes(privateKey42); ECPoint publicKey42 = ECCurve.Secp256r1.G * privateKey42; - ContractManifest contractManifest4 = ContractManifest.CreateDefault(UInt160.Zero); + ContractManifest contractManifest4 = TestUtils.CreateDefaultManifest(UInt160.Zero); contractManifest4.Groups = new ContractGroup[] { new ContractGroup() { PubKey = publicKey42 } }; ContractPermission contractPermission4 = ContractPermission.DefaultPermission; contractPermission4.Contract = ContractPermissionDescriptor.Create(publicKey41); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs index 1e31914441..f248c5e820 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs @@ -1,3 +1,4 @@ +using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; @@ -11,30 +12,28 @@ namespace Neo.UnitTests.SmartContract.Native.Tokens { [TestClass] - public class UT_Nep5Token + public class UT_Nep5Token : TestKit { + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + } + protected const byte Prefix_TotalSupply = 11; + private static readonly TestNep5Token test = new TestNep5Token(); [TestMethod] public void TestTotalSupply() { var snapshot = Blockchain.Singleton.GetSnapshot(); - TestNep5Token test = new TestNep5Token(); StorageItem item = new StorageItem { Value = new byte[] { 0x01 } }; var key = CreateStorageKey(Prefix_TotalSupply); - var ServiceHash = "test".ToInteropMethodHash(); - byte[] script = null; - using (ScriptBuilder sb = new ScriptBuilder()) - { - sb.EmitSysCall(ServiceHash); - script = sb.ToArray(); - } - var Hash = script.ToScriptHash(); key.Id = test.Id; snapshot.Storages.Add(key, item); @@ -48,7 +47,6 @@ public void TestTotalSupplyDecimal() { var snapshot = Blockchain.Singleton.GetSnapshot(); - TestNep5Token test = new TestNep5Token(); BigInteger totalSupply = 100_000_000; totalSupply *= test.Factor; @@ -58,14 +56,6 @@ public void TestTotalSupplyDecimal() }; var key = CreateStorageKey(Prefix_TotalSupply); - var ServiceHash = "test".ToInteropMethodHash(); - byte[] script = null; - using (ScriptBuilder sb = new ScriptBuilder()) - { - sb.EmitSysCall(ServiceHash); - script = sb.ToArray(); - } - var Hash = script.ToScriptHash(); key.Id = test.Id; snapshot.Storages.Add(key, item); @@ -98,7 +88,7 @@ public class TestNep5Token : Nep5Token public override byte Decimals => 8; - public override string ServiceName => "test"; + public override string ServiceName => "testNep5Token"; public new StackItem TotalSupply(ApplicationEngine engine, VM.Types.Array args) { diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index bbcbcd9d1d..b56527ccd8 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -19,23 +19,25 @@ public void TestSetup() TestBlockchain.InitializeMockNeoSystem(); } + private static readonly TestNativeContract testNativeContract = new TestNativeContract(); + [TestMethod] public void TestInitialize() { ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0); - TestNativeContract pc = new TestNativeContract(); - pc.Initialize(ae).Should().BeTrue(); + + testNativeContract.Initialize(ae).Should().BeTrue(); ae = new ApplicationEngine(TriggerType.System, null, null, 0); - Action action = () => pc.Initialize(ae); + Action action = () => testNativeContract.Initialize(ae); action.Should().Throw(); } [TestMethod] public void TestInvoke() { - ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), 0); - TestNativeContract testNativeContract = new TestNativeContract(); + var snapshot = Blockchain.Singleton.GetSnapshot(); + ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); ScriptBuilder sb1 = new ScriptBuilder(); @@ -43,7 +45,7 @@ public void TestInvoke() engine1.LoadScript(sb1.ToArray()); testNativeContract.Invoke(engine1).Should().BeFalse(); - ApplicationEngine engine2 = new ApplicationEngine(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), 0); + ApplicationEngine engine2 = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); ScriptBuilder sb2 = new ScriptBuilder(); sb2.EmitSysCall("test".ToInteropMethodHash()); @@ -65,14 +67,14 @@ public void TestInvoke() [TestMethod] public void TestOnPersistWithArgs() { - ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), 0); - TestNativeContract testNativeContract = new TestNativeContract(); + var snapshot = Blockchain.Singleton.GetSnapshot(); + ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); VMArray args = new VMArray(); VM.Types.Boolean result1 = new VM.Types.Boolean(false); testNativeContract.TestOnPersist(engine1, args).Should().Be(result1); - ApplicationEngine engine2 = new ApplicationEngine(TriggerType.System, null, Blockchain.Singleton.GetSnapshot(), 0); + ApplicationEngine engine2 = new ApplicationEngine(TriggerType.System, null, snapshot, 0); VM.Types.Boolean result2 = new VM.Types.Boolean(true); testNativeContract.TestOnPersist(engine2, args).Should().Be(result2); } @@ -80,7 +82,6 @@ public void TestOnPersistWithArgs() [TestMethod] public void TestTestCall() { - TestNativeContract testNativeContract = new TestNativeContract(); ApplicationEngine engine = testNativeContract.TestCall("System.Blockchain.GetHeight", 0); engine.ResultStack.Should().BeEmpty(); } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index c187a08043..914e6bb012 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -166,7 +166,7 @@ public void TestContract_Create() engine.CurrentContext.EvaluationStack.Push(script); InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeFalse(); - var manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")); + var manifest = TestUtils.CreateDefaultManifest(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")); engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeFalse(); @@ -205,7 +205,7 @@ public void TestContract_Update() engine.CurrentContext.EvaluationStack.Push(script); InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeFalse(); - var manifest = ContractManifest.CreateDefault(script.ToScriptHash()); + var manifest = TestUtils.CreateDefaultManifest(script.ToScriptHash()); byte[] privkey = { 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}; KeyPair key = new KeyPair(privkey); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index f1420fac23..0db5358f7c 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -45,16 +45,17 @@ public void Runtime_GetNotifications_Test() // Add return script.EmitPush(true); + script.Emit(OpCode.RET); // Mock contract scriptHash2 = script.ToArray().ToScriptHash(); snapshot.Contracts.Delete(scriptHash2); - snapshot.Contracts.Add(scriptHash2, new Neo.Ledger.ContractState() + snapshot.Contracts.Add(scriptHash2, new ContractState() { Script = script.ToArray(), - Manifest = ContractManifest.CreateDefault(scriptHash2), + Manifest = TestUtils.CreateDefaultManifest(scriptHash2, "test"), }); } @@ -87,7 +88,7 @@ public void Runtime_GetNotifications_Test() // Call script - script.EmitAppCall(scriptHash2, "test"); + script.EmitAppCall(scriptHash2, "test", 2, 1); // Drop return @@ -113,13 +114,13 @@ public void Runtime_GetNotifications_Test() // Check syscall result - AssertNotification(array[1], scriptHash2, "test"); + AssertNotification(array[1], scriptHash2, 2); AssertNotification(array[0], currentScriptHash, 13); // Check notifications Assert.AreEqual(scriptHash2, engine.Notifications[1].ScriptHash); - Assert.AreEqual("test", engine.Notifications[1].State.GetString()); + Assert.AreEqual(2, engine.Notifications[1].State.GetBigInteger()); Assert.AreEqual(currentScriptHash, engine.Notifications[0].ScriptHash); Assert.AreEqual(13, engine.Notifications[0].State.GetBigInteger()); @@ -137,7 +138,7 @@ public void Runtime_GetNotifications_Test() // Call script - script.EmitAppCall(scriptHash2, "test"); + script.EmitAppCall(scriptHash2, "test", 2, 1); // Drop return @@ -163,12 +164,12 @@ public void Runtime_GetNotifications_Test() // Check syscall result - AssertNotification(array[0], scriptHash2, "test"); + AssertNotification(array[0], scriptHash2, 2); // Check notifications Assert.AreEqual(scriptHash2, engine.Notifications[1].ScriptHash); - Assert.AreEqual("test", engine.Notifications[1].State.GetString()); + Assert.AreEqual(2, engine.Notifications[1].State.GetBigInteger()); Assert.AreEqual(currentScriptHash, engine.Notifications[0].ScriptHash); Assert.AreEqual(13, engine.Notifications[0].State.GetBigInteger()); @@ -236,15 +237,14 @@ public void TestExecutionEngine_GetCallingScriptHash() var contract = new ContractState() { - Manifest = ContractManifest.CreateDefault(scriptA.ToArray().ToScriptHash()), + Manifest = TestUtils.CreateDefaultManifest(scriptA.ToArray().ToScriptHash(), "test"), Script = scriptA.ToArray() }; - engine = GetEngine(true, true, false); engine.Snapshot.Contracts.Add(contract.ScriptHash, contract); using ScriptBuilder scriptB = new ScriptBuilder(); - scriptB.EmitAppCall(contract.ScriptHash, ""); + scriptB.EmitAppCall(contract.ScriptHash, "test", 0, 1); engine.LoadScript(scriptB.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -736,10 +736,11 @@ public void TestInvoke() public void TestContract_Call() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var state = TestUtils.GetContract(); + var state = TestUtils.GetContract("method"); state.Manifest.Features = ContractFeatures.HasStorage; byte[] method = Encoding.UTF8.GetBytes("method"); - byte[] args = new byte[0]; + var args = new VM.Types.Array { 0, 1 }; + snapshot.Contracts.Add(state.ScriptHash, state); var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); @@ -748,8 +749,8 @@ public void TestContract_Call() engine.CurrentContext.EvaluationStack.Push(method); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); InteropService.Invoke(engine, InteropService.Contract.Call).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(method.ToHexString()); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(args.ToHexString()); + 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.CurrentContext.EvaluationStack.Push(args); @@ -774,12 +775,12 @@ public void TestContract_CallEx() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var state = TestUtils.GetContract(); + var state = TestUtils.GetContract("method"); state.Manifest.Features = ContractFeatures.HasStorage; snapshot.Contracts.Add(state.ScriptHash, state); byte[] method = Encoding.UTF8.GetBytes("method"); - byte[] args = new byte[0]; + var args = new VM.Types.Array { 0, 1 }; foreach (var flags in new CallFlags[] { CallFlags.None, CallFlags.AllowCall, CallFlags.AllowModifyStates, CallFlags.All }) { @@ -791,8 +792,8 @@ public void TestContract_CallEx() engine.CurrentContext.EvaluationStack.Push(method); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); InteropService.Invoke(engine, InteropService.Contract.CallEx).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(method.ToHexString()); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(args.ToHexString()); + engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); + engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); // Contract doesn't exists @@ -809,8 +810,8 @@ public void TestContract_CallEx() engine.CurrentContext.EvaluationStack.Push(method); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); InteropService.Invoke(engine, InteropService.Contract.CallEx).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(method.ToHexString()); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(args.ToHexString()); + engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); + engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); // Check rights diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 8be6866885..ac62d746ab 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -14,10 +14,16 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_SmartContractHelper { + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + } + [TestMethod] public void TestIsMultiSigContract() { - Neo.Cryptography.ECC.ECPoint[] publicKeys1 = new Neo.Cryptography.ECC.ECPoint[20]; + ECPoint[] publicKeys1 = new ECPoint[20]; for (int i = 0; i < 20; i++) { byte[] privateKey1 = new byte[32]; @@ -151,7 +157,10 @@ public void TestVerifyWitnesses() block3.NextConsensus = UInt160.Zero; snapshot3.Blocks.Add(index3, block3); Header header3 = new Header() { PrevHash = index3, Witness = new Witness { VerificationScript = new byte[0] } }; - snapshot3.Contracts.Add(UInt160.Zero, new ContractState()); + snapshot3.Contracts.Add(UInt160.Zero, new ContractState() + { + Manifest = TestUtils.CreateDefaultManifest(UInt160.Zero, "verify"), + }); Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header3, snapshot3, 100)); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 9979ca8648..c516d1a871 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -3,8 +3,10 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; +using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; +using System.Collections.Generic; using System.Linq; namespace Neo.UnitTests.SmartContract @@ -340,6 +342,9 @@ public void System_Runtime_GetInvocationCounter() contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractA.ScriptHash.ToArray())); contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractB.ScriptHash.ToArray())); contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractC.ScriptHash.ToArray())); + contractA.Manifest = TestUtils.CreateDefaultManifest(contractA.ScriptHash, "dummyMain"); + contractB.Manifest = TestUtils.CreateDefaultManifest(contractA.ScriptHash, "dummyMain"); + contractC.Manifest = TestUtils.CreateDefaultManifest(contractA.ScriptHash, "dummyMain"); contracts.Add(contractA.ScriptHash, contractA); contracts.Add(contractB.ScriptHash, contractB); contracts.Add(contractC.ScriptHash, contractC); @@ -349,16 +354,16 @@ public void System_Runtime_GetInvocationCounter() using (var script = new ScriptBuilder()) { - script.EmitSysCall(InteropService.Contract.Call, contractA.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.Contract.Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.Contract.Call, contractB.ScriptHash.ToArray(), "dummyMain", 0); - script.EmitSysCall(InteropService.Contract.Call, contractC.ScriptHash.ToArray(), "dummyMain", 0); + script.EmitAppCall(contractA.ScriptHash, "dummyMain", 0, 1); + script.EmitAppCall(contractB.ScriptHash, "dummyMain", 0, 1); + script.EmitAppCall(contractB.ScriptHash, "dummyMain", 0, 1); + script.EmitAppCall(contractC.ScriptHash, "dummyMain", 0, 1); // Execute var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(script.ToArray()); - Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(VMState.HALT, engine.Execute()); // Check the results diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index cf3a59c92a..0338ff32c1 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -3,8 +3,8 @@ using Neo.IO.Json; using Neo.Ledger; using Neo.Network.P2P.Payloads; -using Neo.SmartContract.Manifest; using Neo.SmartContract; +using Neo.SmartContract.Manifest; using Neo.VM; using Neo.Wallets.NEP6; using System; @@ -17,6 +17,33 @@ public static class TestUtils { public static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism + public static ContractManifest CreateDefaultManifest(UInt160 hash, string method = null) + { + return new ContractManifest() + { + Permissions = new[] { ContractPermission.DefaultPermission }, + Abi = new ContractAbi() + { + Hash = hash, + Events = new ContractEventDescriptor[0], + Methods = method == null ? new ContractMethodDescriptor[0] : new ContractMethodDescriptor[] + { + new ContractMethodDescriptor() + { + Name = method, + Parameters = new ContractParameterDefinition[0], + ReturnType = ContractParameterType.Integer + } + } + }, + Features = ContractFeatures.NoProperty, + Groups = new ContractGroup[0], + SafeMethods = WildcardContainer.Create(), + Trusts = WildcardContainer.Create(), + Extra = null, + }; + } + public static byte[] GetByteArray(int length, byte firstByte) { byte[] array = new byte[length]; @@ -56,13 +83,13 @@ public static Transaction GetTransaction() }; } - internal static ContractState GetContract() + internal static ContractState GetContract(string method = null) { return new ContractState { Id = 0x43000000, Script = new byte[] { 0x01, 0x01, 0x01, 0x01 }, - Manifest = ContractManifest.CreateDefault(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")) + Manifest = CreateDefaultManifest(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), method) }; } @@ -72,7 +99,7 @@ internal static ContractState GetContract(byte[] script) { Id = 1, Script = script, - Manifest = ContractManifest.CreateDefault(script.ToScriptHash()) + Manifest = CreateDefaultManifest(script.ToScriptHash()) }; } From b5d4fd51e6b78486dd36bb1d4e0c0d4ab2821924 Mon Sep 17 00:00:00 2001 From: Luchuan Date: Wed, 15 Apr 2020 17:57:37 +0800 Subject: [PATCH 238/305] Sync try-catch opcodes (#1565) --- src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs | 7 ++++++- src/neo/neo.csproj | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index 4bfa5775bf..41bd1f7a29 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -58,9 +58,14 @@ partial class ApplicationEngine [OpCode.CALL] = 22000, [OpCode.CALL_L] = 22000, [OpCode.CALLA] = 22000, - [OpCode.THROW] = 30, [OpCode.ABORT] = 30, [OpCode.ASSERT] = 30, + [OpCode.THROW] = 22000, + [OpCode.TRY] = 100, + [OpCode.TRY_L] = 100, + [OpCode.ENDTRY] = 100, + [OpCode.ENDTRY_L] = 100, + [OpCode.ENDFINALLY] = 100, [OpCode.RET] = 0, [OpCode.SYSCALL] = 0, [OpCode.DEPTH] = 60, diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index c7e38117c3..df025e9ab3 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -27,7 +27,7 @@ - + From 9fd8a2a79d1a58656d6a0a0970b81fedf846cd29 Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Thu, 16 Apr 2020 12:46:35 +0800 Subject: [PATCH 239/305] Fix plugin configuration falure (#1564) --- src/neo/Plugins/Plugin.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs index 61af339936..b8d775b95c 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -66,7 +66,11 @@ private static void ConfigWatcher_Changed(object sender, FileSystemEventArgs e) switch (GetExtension(e.Name)) { case ".json": - Plugins.FirstOrDefault(p => p.ConfigFile == e.FullPath)?.Configure(); + try + { + Plugins.FirstOrDefault(p => p.ConfigFile == e.FullPath)?.Configure(); + } + catch (FormatException) { } break; case ".dll": if (e.ChangeType != WatcherChangeTypes.Created) return; From 11701436ab8a6f6b44fe2528b984a8c5d365588b Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 16 Apr 2020 12:51:39 +0800 Subject: [PATCH 240/305] Add StackItem.ToJson() (#1569) --- src/neo/IO/ReferenceEqualityComparer.cs | 25 +++++++++++++ src/neo/VM/Helper.cs | 47 +++++++++++++++++++++++++ tests/neo.UnitTests/VM/UT_Helper.cs | 21 +++++++++++ 3 files changed, 93 insertions(+) create mode 100644 src/neo/IO/ReferenceEqualityComparer.cs diff --git a/src/neo/IO/ReferenceEqualityComparer.cs b/src/neo/IO/ReferenceEqualityComparer.cs new file mode 100644 index 0000000000..4c736c1874 --- /dev/null +++ b/src/neo/IO/ReferenceEqualityComparer.cs @@ -0,0 +1,25 @@ +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace Neo.IO +{ + internal sealed class ReferenceEqualityComparer : IEqualityComparer, IEqualityComparer + { + public static readonly ReferenceEqualityComparer Default = new ReferenceEqualityComparer(); + + private ReferenceEqualityComparer() + { + } + + public new bool Equals(object x, object y) + { + return x == y; + } + + public int GetHashCode(object obj) + { + return RuntimeHelpers.GetHashCode(obj); + } + } +} diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 5ccfe0554e..eaed72fb31 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -1,5 +1,6 @@ using Neo.Cryptography.ECC; using Neo.IO; +using Neo.IO.Json; using Neo.SmartContract; using Neo.VM.Types; using System; @@ -219,6 +220,52 @@ public static byte[] MakeScript(this UInt160 scriptHash, string operation, param } } + public static JObject ToJson(this StackItem item) + { + return ToJson(item, null); + } + + private static JObject ToJson(StackItem item, HashSet context) + { + JObject json = new JObject(); + json["type"] = item.Type; + switch (item) + { + case Array array: + context ??= new HashSet(ReferenceEqualityComparer.Default); + if (!context.Add(array)) throw new InvalidOperationException(); + json["value"] = new JArray(array.Select(p => ToJson(p, context))); + break; + case Boolean boolean: + json["value"] = boolean.ToBoolean(); + break; + case Buffer buffer: + json["value"] = Convert.ToBase64String(buffer.InnerBuffer); + break; + case ByteString byteString: + json["value"] = Convert.ToBase64String(byteString.Span); + break; + case Integer integer: + json["value"] = integer.ToBigInteger().ToString(); + break; + case Map map: + context ??= new HashSet(ReferenceEqualityComparer.Default); + if (!context.Add(map)) throw new InvalidOperationException(); + json["value"] = new JArray(map.Select(p => + { + JObject item = new JObject(); + item["key"] = ToJson(p.Key, context); + item["value"] = ToJson(p.Value, context); + return item; + })); + break; + case Pointer pointer: + json["value"] = pointer.Position; + break; + } + return json; + } + public static ContractParameter ToParameter(this StackItem item) { return ToParameter(item, null); diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 64e36f194f..498ff433f2 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -26,6 +26,27 @@ public void TestEmit() CollectionAssert.AreEqual(new[] { (byte)OpCode.PUSH0 }, sb.ToArray()); } + [TestMethod] + public void TestToJson() + { + var item = new VM.Types.Array(); + item.Add(5); + item.Add("hello world"); + item.Add(new byte[] { 1, 2, 3 }); + item.Add(true); + + Assert.AreEqual("{\"type\":\"Integer\",\"value\":\"5\"}", item[0].ToJson().ToString()); + Assert.AreEqual("{\"type\":\"ByteString\",\"value\":\"aGVsbG8gd29ybGQ=\"}", item[1].ToJson().ToString()); + Assert.AreEqual("{\"type\":\"ByteString\",\"value\":\"AQID\"}", item[2].ToJson().ToString()); + Assert.AreEqual("{\"type\":\"Boolean\",\"value\":true}", item[3].ToJson().ToString()); + Assert.AreEqual("{\"type\":\"Array\",\"value\":[{\"type\":\"Integer\",\"value\":\"5\"},{\"type\":\"ByteString\",\"value\":\"aGVsbG8gd29ybGQ=\"},{\"type\":\"ByteString\",\"value\":\"AQID\"},{\"type\":\"Boolean\",\"value\":true}]}", item.ToJson().ToString()); + + var item2 = new VM.Types.Map(); + item2[1] = new Pointer(new Script(new byte[0]), 0); + + Assert.AreEqual("{\"type\":\"Map\",\"value\":[{\"key\":{\"type\":\"Integer\",\"value\":\"1\"},\"value\":{\"type\":\"Pointer\",\"value\":0}}]}", item2.ToJson().ToString()); + } + [TestMethod] public void TestEmitAppCall1() { From 6f63594c6047500cf761966bbad87a0bf359f950 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 18 Apr 2020 14:37:50 +0800 Subject: [PATCH 241/305] Call _initialize() automatically (#1572) --- src/neo/SmartContract/InteropService.Contract.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index 99dc91c419..88260a0407 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -134,6 +134,8 @@ private static bool Contract_CallEx(ApplicationEngine engine) private static bool Contract_CallEx(ApplicationEngine engine, UInt160 contractHash, string method, Array args, CallFlags flags) { + if (method.StartsWith('_')) return false; + ContractState contract = engine.Snapshot.Contracts.TryGet(contractHash); if (contract is null) return false; @@ -174,6 +176,10 @@ private static bool Contract_CallEx(ApplicationEngine engine, UInt160 contractHa context_new.EvaluationStack.Push(args[i]); context_new.InstructionPointer = md.Offset; } + + md = contract.Manifest.Abi.GetMethod("_initialize"); + if (md != null) engine.LoadClonedContext(md.Offset); + return true; } From 53ea390feebc7f174fe7dd9b1bbcc15efa629099 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 18 Apr 2020 14:57:05 +0800 Subject: [PATCH 242/305] Remove grouping (#1566) --- src/neo/IO/Helper.cs | 39 ----------------- src/neo/Ledger/StorageKey.cs | 30 +++++-------- tests/neo.UnitTests/IO/UT_IOHelper.cs | 61 --------------------------- 3 files changed, 11 insertions(+), 119 deletions(-) diff --git a/src/neo/IO/Helper.cs b/src/neo/IO/Helper.cs index e44e5bbe0a..ab8b2ce404 100644 --- a/src/neo/IO/Helper.cs +++ b/src/neo/IO/Helper.cs @@ -12,8 +12,6 @@ namespace Neo.IO { public static class Helper { - public const int GroupingSizeInBytes = 16; - public static T AsSerializable(this byte[] value, int start = 0) where T : ISerializable, new() { using (MemoryStream ms = new MemoryStream(value, start, value.Length - start, false)) @@ -144,24 +142,6 @@ public static int GetVarSize(this string value) return GetVarSize(size) + size; } - public static byte[] ReadBytesWithGrouping(this BinaryReader reader) - { - using (MemoryStream ms = new MemoryStream()) - { - int count; - do - { - byte[] group = reader.ReadFixedBytes(GroupingSizeInBytes); - count = reader.ReadByte(); - if (count > GroupingSizeInBytes) - throw new FormatException(); - if (count > 0) - ms.Write(group, 0, count); - } while (count == GroupingSizeInBytes); - return ms.ToArray(); - } - } - public static byte[] ReadFixedBytes(this BinaryReader reader, int size) { var index = 0; @@ -277,25 +257,6 @@ public static void Write(this BinaryWriter writer, ISerializable value) } } - public static void WriteBytesWithGrouping(this BinaryWriter writer, byte[] value) - { - int index = 0; - int remain = value.Length; - while (remain >= GroupingSizeInBytes) - { - writer.Write(value, index, GroupingSizeInBytes); - writer.Write((byte)GroupingSizeInBytes); - index += GroupingSizeInBytes; - remain -= GroupingSizeInBytes; - } - if (remain > 0) - writer.Write(value, index, remain); - int padding = GroupingSizeInBytes - remain; - for (int i = 0; i < padding; i++) - writer.Write((byte)0); - writer.Write((byte)remain); - } - public static void WriteFixedString(this BinaryWriter writer, string value, int length) { if (value == null) diff --git a/src/neo/Ledger/StorageKey.cs b/src/neo/Ledger/StorageKey.cs index af7258f2ce..bf1e3c447b 100644 --- a/src/neo/Ledger/StorageKey.cs +++ b/src/neo/Ledger/StorageKey.cs @@ -1,6 +1,7 @@ using Neo.Cryptography; using Neo.IO; using System; +using System.Buffers.Binary; using System.IO; namespace Neo.Ledger @@ -10,31 +11,22 @@ public class StorageKey : IEquatable, ISerializable public int Id; public byte[] Key; - int ISerializable.Size => sizeof(int) + (Key.Length / 16 + 1) * 17; + int ISerializable.Size => sizeof(int) + Key.Length; - internal static byte[] CreateSearchPrefix(int id, byte[] prefix) + internal static byte[] CreateSearchPrefix(int id, ReadOnlySpan prefix) { - using (MemoryStream ms = new MemoryStream()) - { - int index = 0; - int remain = prefix.Length; - while (remain >= 16) - { - ms.Write(prefix, index, 16); - ms.WriteByte(16); - index += 16; - remain -= 16; - } - if (remain > 0) - ms.Write(prefix, index, remain); - return Helper.Concat(BitConverter.GetBytes(id), ms.ToArray()); - } + byte[] buffer = new byte[sizeof(int) + prefix.Length]; + BinaryPrimitives.WriteInt32LittleEndian(buffer, id); + prefix.CopyTo(buffer.AsSpan(sizeof(int))); + return buffer; } + //If the base stream of the reader doesn't support seeking, a NotSupportedException is thrown. + //But StorageKey never works with NetworkStream, so it doesn't matter. void ISerializable.Deserialize(BinaryReader reader) { Id = reader.ReadInt32(); - Key = reader.ReadBytesWithGrouping(); + Key = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); } public bool Equals(StorageKey other) @@ -60,7 +52,7 @@ public override int GetHashCode() void ISerializable.Serialize(BinaryWriter writer) { writer.Write(Id); - writer.WriteBytesWithGrouping(Key); + writer.Write(Key); } } } diff --git a/tests/neo.UnitTests/IO/UT_IOHelper.cs b/tests/neo.UnitTests/IO/UT_IOHelper.cs index 3b36695b2d..1128270716 100644 --- a/tests/neo.UnitTests/IO/UT_IOHelper.cs +++ b/tests/neo.UnitTests/IO/UT_IOHelper.cs @@ -285,45 +285,6 @@ public void TestGetVarSizeString() Assert.AreEqual(3, result); } - [TestMethod] - public void TestReadBytesWithGrouping() - { - for (int i = 0; i < 2; i++) - { - if (i == 0) - { - byte[] caseArray = new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA}; - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - Neo.IO.Helper.WriteBytesWithGrouping(writer, caseArray); - stream.Seek(0, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); - byte[] result = Neo.IO.Helper.ReadBytesWithGrouping(reader); - Assert.AreEqual(Encoding.Default.GetString(caseArray), Encoding.Default.GetString(result)); - } - else - { - byte[] caseArray = new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x10, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x10, - 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x11}; - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - writer.Write(caseArray); - stream.Seek(0, SeekOrigin.Begin); - BinaryReader reader = new BinaryReader(stream); - Action action = () => Neo.IO.Helper.ReadBytesWithGrouping(reader); - action.Should().Throw(); - } - } - } - [TestMethod] public void TestReadFixedString() { @@ -474,28 +435,6 @@ public void TestWriteGeneric() 0x00,0x00,0x00,0x00,0x00}), Encoding.Default.GetString(byteArray)); } - - [TestMethod] - public void TestWriteBytesWithGrouping() - { - MemoryStream stream = new MemoryStream(); - BinaryWriter writer = new BinaryWriter(stream); - Neo.IO.Helper.WriteBytesWithGrouping(writer, new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA}); - stream.Seek(0, SeekOrigin.Begin); - byte[] byteArray = new byte[stream.Length]; - stream.Read(byteArray, 0, (int)stream.Length); - Assert.AreEqual(Encoding.Default.GetString(new byte[] { 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x10, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, - 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,0x10, - 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x04}), Encoding.Default.GetString(byteArray)); - } - [TestMethod] public void TestWriteFixedString() { From 8d233228a877233f53b882fb28dab3851eedca2b Mon Sep 17 00:00:00 2001 From: Shargon Date: Sun, 19 Apr 2020 15:00:41 +0200 Subject: [PATCH 243/305] Add GetCallFlags (#1587) --- src/neo/SmartContract/InteropService.Contract.cs | 9 +++++++++ tests/neo.UnitTests/SmartContract/UT_InteropService.cs | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index 88260a0407..d66fd27d7b 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -24,6 +24,8 @@ public static class Contract public static readonly InteropDescriptor Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); public static readonly InteropDescriptor CallEx = Register("System.Contract.CallEx", Contract_CallEx, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor GetCallFlags = Register("System.Contract.GetCallFlags", Contract_GetCallFlags, 0_00030000, TriggerType.All, CallFlags.None); + /// /// Calculate corresponding account scripthash for given public key /// Warning: check first that input public key is valid, before creating the script. @@ -36,6 +38,13 @@ private static long GetDeploymentPrice(EvaluationStack stack, StoreView snapshot return Storage.GasPerByte * size; } + private static bool Contract_GetCallFlags(ApplicationEngine engine) + { + var state = engine.CurrentContext.GetState(); + engine.Push((int)state.CallFlags); + return true; + } + private static bool Contract_Create(ApplicationEngine engine) { if (!engine.TryPop(out ReadOnlySpan script)) return false; diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 0db5358f7c..5fb8cc4220 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -261,6 +261,15 @@ public void TestExecutionEngine_GetEntryScriptHash() .Should().Be(engine.EntryScriptHash.ToArray().ToHexString()); } + [TestMethod] + public void TestContract_GetCallFlags() + { + var engine = GetEngine(); + InteropService.Invoke(engine, InteropService.Contract.GetCallFlags).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Pop().GetBigInteger() + .Should().Be(((int)CallFlags.All)); + } + [TestMethod] public void TestRuntime_Platform() { From e0da56601559a0baa4243687ce6bc61d51a5c710 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 20 Apr 2020 14:09:54 +0800 Subject: [PATCH 244/305] Fix CallFlags (#1589) --- src/neo/SmartContract/InteropService.Contract.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index d66fd27d7b..36b3cad6ad 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -134,11 +134,12 @@ private static bool Contract_CallEx(ApplicationEngine engine) if (!engine.TryPop(out ReadOnlySpan contractHash)) return false; if (!engine.TryPop(out string method)) return false; if (!engine.TryPop(out Array args)) return false; - if (!engine.TryPop(out int flags)) return false; + if (!engine.TryPop(out int flagsValue)) return false; - if (!Enum.IsDefined(typeof(CallFlags), (CallFlags)flags)) return false; + CallFlags flags = (CallFlags)flagsValue; + if ((flags & ~CallFlags.All) != 0) return false; - return Contract_CallEx(engine, new UInt160(contractHash), method, args, (CallFlags)flags); + return Contract_CallEx(engine, new UInt160(contractHash), method, args, flags); } private static bool Contract_CallEx(ApplicationEngine engine, UInt160 contractHash, string method, Array args, CallFlags flags) From 62237fd5d93cf2c5778c7b68d76a2b9b9c8e0601 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 21 Apr 2020 00:17:50 +0800 Subject: [PATCH 245/305] Simplify access to storage for native contracts. (#1583) --- src/neo/IO/Caching/DataCache.cs | 4 +- src/neo/Ledger/ContractState.cs | 5 + src/neo/Ledger/StorageItem.cs | 46 ++++++++- src/neo/Network/P2P/Payloads/Block.cs | 5 + src/neo/Network/P2P/Payloads/Transaction.cs | 5 + src/neo/SmartContract/IInteroperable.cs | 1 + .../SmartContract/Native/Tokens/NeoToken.cs | 68 ++++--------- .../Native/Tokens/Nep5AccountState.cs | 29 +----- .../SmartContract/Native/Tokens/Nep5Token.cs | 43 ++------ tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 11 +-- .../Network/P2P/Payloads/UT_Transaction.cs | 99 ++++--------------- .../Native/Tokens/UT_NeoToken.cs | 87 ++++------------ tests/neo.UnitTests/Wallets/UT_Wallet.cs | 85 +++------------- 13 files changed, 154 insertions(+), 334 deletions(-) diff --git a/src/neo/IO/Caching/DataCache.cs b/src/neo/IO/Caching/DataCache.cs index 4529978a19..d20d1bba2e 100644 --- a/src/neo/IO/Caching/DataCache.cs +++ b/src/neo/IO/Caching/DataCache.cs @@ -219,7 +219,7 @@ public TValue GetAndChange(TKey key, Func factory = null) { if (trackable.State == TrackState.Deleted) { - if (factory == null) throw new KeyNotFoundException(); + if (factory == null) return null; trackable.Item = factory(); trackable.State = TrackState.Changed; } @@ -237,7 +237,7 @@ public TValue GetAndChange(TKey key, Func factory = null) }; if (trackable.Item == null) { - if (factory == null) throw new KeyNotFoundException(); + if (factory == null) return null; trackable.Item = factory(); trackable.State = TrackState.Added; } diff --git a/src/neo/Ledger/ContractState.cs b/src/neo/Ledger/ContractState.cs index a47a6366f7..504175ee4b 100644 --- a/src/neo/Ledger/ContractState.cs +++ b/src/neo/Ledger/ContractState.cs @@ -58,6 +58,11 @@ void ICloneable.FromReplica(ContractState replica) Manifest = replica.Manifest.Clone(); } + void IInteroperable.FromStackItem(StackItem stackItem) + { + throw new NotSupportedException(); + } + void ISerializable.Serialize(BinaryWriter writer) { writer.Write(Id); diff --git a/src/neo/Ledger/StorageItem.cs b/src/neo/Ledger/StorageItem.cs index 49ea93d8a1..801f0c72a0 100644 --- a/src/neo/Ledger/StorageItem.cs +++ b/src/neo/Ledger/StorageItem.cs @@ -1,15 +1,48 @@ using Neo.IO; +using Neo.SmartContract; using System.IO; namespace Neo.Ledger { public class StorageItem : ICloneable, ISerializable { - public byte[] Value; + private byte[] value; + private IInteroperable interoperable; public bool IsConstant; public int Size => Value.GetVarSize() + sizeof(bool); + public byte[] Value + { + get + { + if (value is null && interoperable != null) + value = BinarySerializer.Serialize(interoperable.ToStackItem(null), 4096); + return value; + } + set + { + interoperable = null; + this.value = value; + } + } + + public StorageItem() + { + } + + public StorageItem(byte[] value, bool isConstant = false) + { + this.value = value; + this.IsConstant = isConstant; + } + + public StorageItem(IInteroperable interoperable, bool isConstant = false) + { + this.interoperable = interoperable; + this.IsConstant = isConstant; + } + StorageItem ICloneable.Clone() { return new StorageItem @@ -31,6 +64,17 @@ void ICloneable.FromReplica(StorageItem replica) IsConstant = replica.IsConstant; } + public T GetInteroperable() where T : IInteroperable, new() + { + if (interoperable is null) + { + interoperable = new T(); + interoperable.FromStackItem(BinarySerializer.Deserialize(value, 16, 34)); + } + value = null; + return (T)interoperable; + } + public void Serialize(BinaryWriter writer) { writer.WriteVarBytes(Value); diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index a3623a4c94..2663eb28f4 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -81,6 +81,11 @@ 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(); diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 6035ac2285..48b65b02cd 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -195,6 +195,11 @@ public override bool Equals(object obj) return Equals(obj as Transaction); } + void IInteroperable.FromStackItem(StackItem stackItem) + { + throw new NotSupportedException(); + } + public override int GetHashCode() { return Hash.GetHashCode(); diff --git a/src/neo/SmartContract/IInteroperable.cs b/src/neo/SmartContract/IInteroperable.cs index 41807cd10a..ec53ef48c0 100644 --- a/src/neo/SmartContract/IInteroperable.cs +++ b/src/neo/SmartContract/IInteroperable.cs @@ -5,6 +5,7 @@ namespace Neo.SmartContract { public interface IInteroperable { + void FromStackItem(StackItem stackItem); StackItem ToStackItem(ReferenceCounter referenceCounter); } } diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index ef633ad597..4c9147220e 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -44,9 +44,8 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco if (state.VoteTo != null) { StorageItem storage_validator = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Candidate, state.VoteTo.ToArray())); - CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value); + CandidateState state_validator = storage_validator.GetInteroperable(); state_validator.Votes += amount; - storage_validator.Value = state_validator.ToByteArray(); } } @@ -55,7 +54,6 @@ private void DistributeGas(ApplicationEngine engine, UInt160 account, AccountSta BigInteger gas = CalculateBonus(engine.Snapshot, state.Balance, state.BalanceHeight, engine.Snapshot.PersistingBlock.Index); state.BalanceHeight = engine.Snapshot.PersistingBlock.Index; GAS.Mint(engine, account, gas); - engine.Snapshot.Storages.GetAndChange(CreateAccountKey(account)).Value = state.ToByteArray(); } private BigInteger CalculateBonus(StoreView snapshot, BigInteger value, uint start, uint end) @@ -130,7 +128,7 @@ public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) { StorageItem storage = snapshot.Storages.TryGet(CreateAccountKey(account)); if (storage is null) return BigInteger.Zero; - AccountState state = new AccountState(storage.Value); + AccountState state = storage.GetInteroperable(); return CalculateBonus(snapshot, state.Balance, state.BalanceHeight, end); } @@ -146,13 +144,9 @@ private StackItem RegisterCandidate(ApplicationEngine engine, Array args) private bool RegisterCandidate(StoreView snapshot, ECPoint pubkey) { StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey); - StorageItem item = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new CandidateState().ToByteArray() - }); - CandidateState state = CandidateState.FromByteArray(item.Value); + StorageItem item = snapshot.Storages.GetAndChange(key, () => new StorageItem(new CandidateState())); + CandidateState state = item.GetInteroperable(); state.Registered = true; - item.Value = state.ToByteArray(); return true; } @@ -170,16 +164,11 @@ private bool UnregisterCandidate(StoreView snapshot, ECPoint pubkey) StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey); if (snapshot.Storages.TryGet(key) is null) return true; StorageItem item = snapshot.Storages.GetAndChange(key); - CandidateState state = CandidateState.FromByteArray(item.Value); + CandidateState state = item.GetInteroperable(); if (state.Votes.IsZero) - { snapshot.Storages.Delete(key); - } else - { state.Registered = false; - item.Value = state.ToByteArray(); - } return true; } @@ -197,29 +186,25 @@ private bool Vote(StoreView snapshot, UInt160 account, ECPoint voteTo) StorageKey key_account = CreateAccountKey(account); if (snapshot.Storages.TryGet(key_account) is null) return false; StorageItem storage_account = snapshot.Storages.GetAndChange(key_account); - AccountState state_account = new AccountState(storage_account.Value); + AccountState state_account = storage_account.GetInteroperable(); if (state_account.VoteTo != null) { StorageKey key = CreateStorageKey(Prefix_Candidate, state_account.VoteTo.ToArray()); StorageItem storage_validator = snapshot.Storages.GetAndChange(key); - CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value); + CandidateState state_validator = storage_validator.GetInteroperable(); state_validator.Votes -= state_account.Balance; if (!state_validator.Registered && state_validator.Votes.IsZero) snapshot.Storages.Delete(key); - else - storage_validator.Value = state_validator.ToByteArray(); } state_account.VoteTo = voteTo; - storage_account.Value = state_account.ToByteArray(); if (voteTo != null) { StorageKey key = CreateStorageKey(Prefix_Candidate, voteTo.ToArray()); if (snapshot.Storages.TryGet(key) is null) return false; StorageItem storage_validator = snapshot.Storages.GetAndChange(key); - CandidateState state_validator = CandidateState.FromByteArray(storage_validator.Value); + CandidateState state_validator = storage_validator.GetInteroperable(); if (!state_validator.Registered) return false; state_validator.Votes += state_account.Balance; - storage_validator.Value = state_validator.ToByteArray(); } return true; } @@ -236,7 +221,7 @@ public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetCandidates(StoreVie return snapshot.Storages.Find(prefix_key).Select(p => ( p.Key.Key.AsSerializable(1), - CandidateState.FromByteArray(p.Value.Value) + p.Value.GetInteroperable() )).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes)); } @@ -285,49 +270,38 @@ public class AccountState : Nep5AccountState public uint BalanceHeight; public ECPoint VoteTo; - public AccountState() - { - } - - public AccountState(byte[] data) - : base(data) - { - } - - protected override void FromStruct(Struct @struct) + public override void FromStackItem(StackItem stackItem) { - base.FromStruct(@struct); + base.FromStackItem(stackItem); + Struct @struct = (Struct)stackItem; BalanceHeight = (uint)@struct[1].GetBigInteger(); VoteTo = @struct[2].IsNull ? null : @struct[2].GetSpan().AsSerializable(); } - protected override Struct ToStruct() + public override StackItem ToStackItem(ReferenceCounter referenceCounter) { - Struct @struct = base.ToStruct(); + Struct @struct = (Struct)base.ToStackItem(referenceCounter); @struct.Add(BalanceHeight); @struct.Add(VoteTo?.ToArray() ?? StackItem.Null); return @struct; } } - internal class CandidateState + internal class CandidateState : IInteroperable { public bool Registered = true; public BigInteger Votes; - public static CandidateState FromByteArray(byte[] data) + public void FromStackItem(StackItem stackItem) { - Struct @struct = (Struct)BinarySerializer.Deserialize(data, 16, 32); - return new CandidateState - { - Registered = @struct[0].ToBoolean(), - Votes = @struct[1].GetBigInteger() - }; + Struct @struct = (Struct)stackItem; + Registered = @struct[0].ToBoolean(); + Votes = @struct[1].GetBigInteger(); } - public byte[] ToByteArray() + public StackItem ToStackItem(ReferenceCounter referenceCounter) { - return BinarySerializer.Serialize(new Struct { Registered, Votes }, 32); + return new Struct(referenceCounter) { Registered, Votes }; } } } diff --git a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs index b2d62be6d1..cb46d0bd94 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs @@ -4,37 +4,18 @@ namespace Neo.SmartContract.Native.Tokens { - public class Nep5AccountState + public class Nep5AccountState : IInteroperable { public BigInteger Balance; - public Nep5AccountState() + public virtual void FromStackItem(StackItem stackItem) { + Balance = ((Struct)stackItem)[0].GetBigInteger(); } - public Nep5AccountState(byte[] data) + public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) { - FromByteArray(data); - } - - public void FromByteArray(byte[] data) - { - FromStruct((Struct)BinarySerializer.Deserialize(data, 16, 34)); - } - - protected virtual void FromStruct(Struct @struct) - { - Balance = @struct[0].GetBigInteger(); - } - - public byte[] ToByteArray() - { - return BinarySerializer.Serialize(ToStruct(), 4096); - } - - protected virtual Struct ToStruct() - { - return new Struct(new StackItem[] { Balance }); + return new Struct(referenceCounter) { Balance }; } } } diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index 3e331de487..b4e389676f 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -70,15 +70,10 @@ 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(CreateAccountKey(account), () => new StorageItem - { - Value = new TState().ToByteArray() - }); - TState state = new TState(); - state.FromByteArray(storage.Value); + StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateAccountKey(account), () => new StorageItem(new TState())); + TState state = storage.GetInteroperable(); OnBalanceChanging(engine, account, state, amount); state.Balance += amount; - storage.Value = state.ToByteArray(); storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem { Value = BigInteger.Zero.ToByteArrayStandard() @@ -95,19 +90,13 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, if (amount.IsZero) return; StorageKey key = CreateAccountKey(account); StorageItem storage = engine.Snapshot.Storages.GetAndChange(key); - TState state = new TState(); - state.FromByteArray(storage.Value); + 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); - } else - { state.Balance -= amount; - storage.Value = state.ToByteArray(); - } storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_TotalSupply)); BigInteger totalSupply = new BigInteger(storage.Value); totalSupply -= amount; @@ -156,8 +145,7 @@ public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) { StorageItem storage = snapshot.Storages.TryGet(CreateAccountKey(account)); if (storage is null) return BigInteger.Zero; - Nep5AccountState state = new Nep5AccountState(storage.Value); - return state.Balance; + return storage.GetInteroperable().Balance; } [ContractMethod(0_08000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" })] @@ -177,21 +165,19 @@ protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 ContractState contract_to = engine.Snapshot.Contracts.TryGet(to); if (contract_to?.Payable == false) return false; StorageKey key_from = CreateAccountKey(from); - StorageItem storage_from = engine.Snapshot.Storages.TryGet(key_from); + StorageItem storage_from = engine.Snapshot.Storages.GetAndChange(key_from); if (amount.IsZero) { if (storage_from != null) { - TState state_from = new TState(); - state_from.FromByteArray(storage_from.Value); + TState state_from = storage_from.GetInteroperable(); OnBalanceChanging(engine, from, state_from, amount); } } else { if (storage_from is null) return false; - TState state_from = new TState(); - state_from.FromByteArray(storage_from.Value); + TState state_from = storage_from.GetInteroperable(); if (state_from.Balance < amount) return false; if (from.Equals(to)) { @@ -201,25 +187,14 @@ 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); - } else - { state_from.Balance -= amount; - storage_from = engine.Snapshot.Storages.GetAndChange(key_from); - storage_from.Value = state_from.ToByteArray(); - } StorageKey key_to = CreateAccountKey(to); - StorageItem storage_to = engine.Snapshot.Storages.GetAndChange(key_to, () => new StorageItem - { - Value = new TState().ToByteArray() - }); - TState state_to = new TState(); - state_to.FromByteArray(storage_to.Value); + StorageItem storage_to = engine.Snapshot.Storages.GetAndChange(key_to, () => new StorageItem(new TState())); + TState state_to = storage_to.GetInteroperable(); OnBalanceChanging(engine, to, state_to, amount); state_to.Balance += amount; - storage_to.Value = state_to.ToByteArray(); } } engine.SendNotification(Hash, new Array(new StackItem[] { "Transfer", from.ToArray(), to.ToArray(), amount })); diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index cf421b99cf..b38c71e2a5 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -113,16 +113,9 @@ public void TestValidTransaction() // Fake balance var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.Value = new Nep5AccountState() - { - Balance = 100_000_000 * NativeContract.GAS.Factor - } - .ToByteArray(); + entry.GetInteroperable().Balance = 100_000_000 * NativeContract.GAS.Factor; snapshot.Commit(); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index adee349b2b..4fc2f9cc4a 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -107,16 +107,9 @@ public void FeeIsMultiSigContract() // Fake balance var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -184,16 +177,9 @@ public void FeeIsSignatureContractDetailed() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -302,16 +288,9 @@ public void FeeIsSignatureContract_TestScope_Global() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -395,16 +374,9 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -489,16 +461,9 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -586,16 +551,9 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; // Make transaction // Manually creating script @@ -644,16 +602,9 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -743,16 +694,9 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; // Make transaction // Manually creating script @@ -1001,16 +945,9 @@ public void FeeIsSignatureContract_TestScope_Global_Default() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index b2696b4888..6d26b5d9ef 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -238,23 +238,17 @@ public void TestCalculateBonus() { var snapshot = Blockchain.Singleton.GetSnapshot(); StorageKey key = CreateStorageKey(20, UInt160.Zero.ToArray()); - snapshot.Storages.Add(key, new StorageItem + snapshot.Storages.Add(key, new StorageItem(new AccountState { - Value = new AccountState() - { - Balance = -100 - }.ToByteArray() - }); + Balance = -100 + })); Action action = () => NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); action.Should().Throw(); snapshot.Storages.Delete(key); - snapshot.Storages.GetAndChange(key, () => new StorageItem + snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState { - Value = new AccountState() - { - Balance = 100 - }.ToByteArray() - }); + Balance = 100 + })); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 30 * Blockchain.DecrementInterval).Should().Be(new BigInteger(7000000000)); } @@ -345,10 +339,7 @@ public void TestGetRegisteredValidators2() result[6].Votes.Should().Be(new BigInteger(1785714)); StorageKey key = NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G); - snapshot.Storages.Add(key, new StorageItem - { - Value = new CandidateState().ToByteArray() - }); + snapshot.Storages.Add(key, new StorageItem(new CandidateState())); NativeContract.NEO.GetCandidates(snapshot).ToArray().Length.Should().Be(22); } @@ -429,10 +420,7 @@ public void TestUnclaimedGas() { var snapshot = Blockchain.Singleton.GetSnapshot(); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); - snapshot.Storages.Add(CreateStorageKey(20, UInt160.Zero.ToArray()), new StorageItem - { - Value = new AccountState().ToByteArray() - }); + snapshot.Storages.Add(CreateStorageKey(20, UInt160.Zero.ToArray()), new StorageItem(new AccountState())); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); } @@ -451,46 +439,22 @@ public void TestVote() ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); - snapshot.Storages.Add(keyAccount, new StorageItem - { - Value = new AccountState().ToByteArray() - }); + snapshot.Storages.Add(keyAccount, new StorageItem(new AccountState())); ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); snapshot.Storages.Delete(keyAccount); - snapshot.Storages.GetAndChange(keyAccount, () => new StorageItem + snapshot.Storages.GetAndChange(keyAccount, () => new StorageItem(new AccountState { - Value = new AccountState() - { - VoteTo = ECCurve.Secp256r1.G - }.ToByteArray() - }); - snapshot.Storages.Add(keyValidator, new StorageItem - { - Value = new CandidateState().ToByteArray() - }); + VoteTo = ECCurve.Secp256r1.G + })); + snapshot.Storages.Add(keyValidator, new StorageItem(new CandidateState())); ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); } - [TestMethod] - public void TestValidatorState_FromByteArray() - { - CandidateState input = new CandidateState { Votes = new BigInteger(1000) }; - CandidateState output = CandidateState.FromByteArray(input.ToByteArray()); - output.Should().BeEquivalentTo(input); - } - - [TestMethod] - public void TestValidatorState_ToByteArray() - { - CandidateState input = new CandidateState { Votes = new BigInteger(1000) }; - input.ToByteArray().ToHexString().Should().Be("410220012102e803"); - } - internal (bool State, bool Result) Transfer4TesingOnBalanceChanging(BigInteger amount, bool addVotes) { var snapshot = Blockchain.Singleton.GetSnapshot(); @@ -501,28 +465,19 @@ public void TestValidatorState_ToByteArray() UInt160 from = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot)[0]; if (addVotes) { - snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem - { - Value = new AccountState() - { - VoteTo = ECCurve.Secp256r1.G, - Balance = new BigInteger(1000) - }.ToByteArray() - }); - snapshot.Storages.Add(NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G), new StorageItem + snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem(new AccountState { - Value = new CandidateState().ToByteArray() - }); + VoteTo = ECCurve.Secp256r1.G, + Balance = new BigInteger(1000) + })); + snapshot.Storages.Add(NativeContract.NEO.CreateStorageKey(33, ECCurve.Secp256r1.G), new StorageItem(new CandidateState())); } else { - snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem + snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem(new AccountState { - Value = new AccountState() - { - Balance = new BigInteger(1000) - }.ToByteArray() - }); + Balance = new BigInteger(1000) + })); } sb.EmitAppCall(NativeContract.NEO.Hash, "transfer", from, UInt160.Zero, amount); diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 15a1cc8844..63ddb327fe 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -203,24 +203,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 - { - Value = new Nep5AccountState().ToByteArray() - }); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); wallet.GetAvailable(NativeContract.GAS.Hash).Should().Be(new BigDecimal(1000000000000, 8)); - entry.Value = new Nep5AccountState() - { - Balance = 0 - } - .ToByteArray(); + entry.GetInteroperable().Balance = 0; snapshot.Commit(); } @@ -235,25 +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 - { - Value = new Nep5AccountState().ToByteArray() - }); - entry.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + 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.Value = new Nep5AccountState() - { - Balance = 0 - } - .ToByteArray(); + entry.GetInteroperable().Balance = 0; snapshot.Commit(); } @@ -345,26 +323,12 @@ 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 - { - Value = new Nep5AccountState().ToByteArray() - }); - entry1.Value = new Nep5AccountState() - { - Balance = 10000 * NativeContract.GAS.Factor - } - .ToByteArray(); + var entry1 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + entry1.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; key = NativeContract.NEO.CreateStorageKey(20, account.ScriptHash); - var entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem - { - Value = new Nep5AccountState().ToByteArray() - }); - entry2.Value = new NeoToken.AccountState() - { - Balance = 10000 * NativeContract.NEO.Factor - } - .ToByteArray(); + var entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoToken.AccountState())); + entry2.GetInteroperable().Balance = 10000 * NativeContract.NEO.Factor; snapshot.Commit(); @@ -390,16 +354,8 @@ public void TestMakeTransaction1() }); tx.Should().NotBeNull(); - entry1.Value = new Nep5AccountState() - { - Balance = 0 - } - .ToByteArray(); - entry2.Value = new NeoToken.AccountState() - { - Balance = 0 - } - .ToByteArray(); + entry1.GetInteroperable().Balance = 0; + entry2.GetInteroperable().Balance = 0; snapshot.Commit(); } @@ -417,15 +373,8 @@ 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 - { - Value = new Nep5AccountState().ToByteArray() - }); - entry.Value = new Nep5AccountState() - { - Balance = 1000000 * NativeContract.GAS.Factor - } - .ToByteArray(); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + entry.GetInteroperable().Balance = 1000000 * NativeContract.GAS.Factor; snapshot.Commit(); var tx = wallet.MakeTransaction(new byte[] { }, account.ScriptHash, new TransactionAttribute[] { }); @@ -434,11 +383,7 @@ public void TestMakeTransaction2() tx = wallet.MakeTransaction(new byte[] { }, null, new TransactionAttribute[] { }); tx.Should().NotBeNull(); - entry.Value = new NeoToken.AccountState() - { - Balance = 0 - } - .ToByteArray(); + entry.GetInteroperable().Balance = 0; snapshot.Commit(); } From 29b384d62122e66eaeeb41a56483dd211ea5faf2 Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Sun, 26 Apr 2020 13:22:33 +0800 Subject: [PATCH 246/305] Cache most recent block (#1592) --- src/neo/Ledger/Blockchain.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index fd76d1ebca..0a535f9a5b 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -331,6 +331,7 @@ private VerifyResult OnNewBlock(Block block) if (!block.Hash.Equals(header_index[(int)block.Index])) return VerifyResult.Invalid; } + block_cache.TryAdd(block.Hash, block); if (block.Index == Height + 1) { Block block_persist = block; @@ -370,7 +371,6 @@ private VerifyResult OnNewBlock(Block block) } else { - block_cache.Add(block.Hash, block); if (block.Index + 100 >= header_index.Count) system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = block }); if (block.Index == header_index.Count) @@ -430,7 +430,7 @@ private VerifyResult OnNewTransaction(Transaction transaction) private void OnPersistCompleted(Block block) { - block_cache.Remove(block.Hash); + block_cache.Remove(block.PrevHash); MemPool.UpdatePoolForBlockPersisted(block, currentSnapshot); Context.System.EventStream.Publish(new PersistCompleted { Block = block }); } From 7c3faaf584e9032367d5e16ca12227d7a970b418 Mon Sep 17 00:00:00 2001 From: Shargon Date: Sun, 26 Apr 2020 07:43:52 +0200 Subject: [PATCH 247/305] Add StateLess to CallFlags (#1549) --- src/neo/SmartContract/CallFlags.cs | 11 ++++++----- .../SmartContract/InteropService.Blockchain.cs | 12 ++++++------ .../SmartContract/InteropService.Runtime.cs | 4 ++-- .../SmartContract/InteropService.Storage.cs | 10 +++++----- .../Native/ContractMethodAttribute.cs | 5 +++-- src/neo/SmartContract/Native/NativeContract.cs | 8 ++++---- src/neo/SmartContract/Native/PolicyContract.cs | 18 +++++++++--------- .../SmartContract/Native/Tokens/NeoToken.cs | 16 ++++++++-------- .../SmartContract/Native/Tokens/Nep5Token.cs | 12 ++++++------ 9 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/neo/SmartContract/CallFlags.cs b/src/neo/SmartContract/CallFlags.cs index 99af3e0599..86361e9b15 100644 --- a/src/neo/SmartContract/CallFlags.cs +++ b/src/neo/SmartContract/CallFlags.cs @@ -7,11 +7,12 @@ public enum CallFlags : byte { None = 0, - AllowModifyStates = 0b00000001, - AllowCall = 0b00000010, - AllowNotify = 0b00000100, + AllowStates = 0b00000001, + AllowModifyStates = 0b00000010, + AllowCall = 0b00000100, + AllowNotify = 0b00001000, - ReadOnly = AllowCall | AllowNotify, - All = AllowModifyStates | AllowCall | AllowNotify + ReadOnly = AllowStates | AllowCall | AllowNotify, + All = AllowStates | AllowModifyStates | AllowCall | AllowNotify } } diff --git a/src/neo/SmartContract/InteropService.Blockchain.cs b/src/neo/SmartContract/InteropService.Blockchain.cs index 1183c5e462..a8ada65f45 100644 --- a/src/neo/SmartContract/InteropService.Blockchain.cs +++ b/src/neo/SmartContract/InteropService.Blockchain.cs @@ -14,12 +14,12 @@ public static class Blockchain { public const uint MaxTraceableBlocks = Transaction.MaxValidUntilBlockIncrement; - public static readonly InteropDescriptor GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application, CallFlags.None); - public static readonly InteropDescriptor GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application, CallFlags.None); - public static readonly InteropDescriptor GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application, CallFlags.None); - public static readonly InteropDescriptor GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application, CallFlags.None); - public static readonly InteropDescriptor GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application, CallFlags.None); - public static readonly InteropDescriptor GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application, CallFlags.AllowStates); private static bool Blockchain_GetHeight(ApplicationEngine engine) { diff --git a/src/neo/SmartContract/InteropService.Runtime.cs b/src/neo/SmartContract/InteropService.Runtime.cs index aa525c1600..9942555cba 100644 --- a/src/neo/SmartContract/InteropService.Runtime.cs +++ b/src/neo/SmartContract/InteropService.Runtime.cs @@ -19,12 +19,12 @@ public static class Runtime public static readonly InteropDescriptor Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application, CallFlags.AllowStates); public static readonly InteropDescriptor GetScriptContainer = Register("System.Runtime.GetScriptContainer", Runtime_GetScriptContainer, 0_00000250, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", Runtime_GetExecutingScriptHash, 0_00000400, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", Runtime_GetCallingScriptHash, 0_00000400, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", Runtime_GetEntryScriptHash, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All, CallFlags.AllowStates); public static readonly InteropDescriptor GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor Log = Register("System.Runtime.Log", Runtime_Log, 0_01000000, TriggerType.All, CallFlags.AllowNotify); public static readonly InteropDescriptor Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_01000000, TriggerType.All, CallFlags.AllowNotify); diff --git a/src/neo/SmartContract/InteropService.Storage.cs b/src/neo/SmartContract/InteropService.Storage.cs index b7df35ea26..c23eebeb41 100644 --- a/src/neo/SmartContract/InteropService.Storage.cs +++ b/src/neo/SmartContract/InteropService.Storage.cs @@ -16,11 +16,11 @@ public static class Storage public const int MaxKeySize = 64; public const int MaxValueSize = ushort.MaxValue; - public static readonly InteropDescriptor GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application, CallFlags.None); - public static readonly InteropDescriptor GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400, TriggerType.Application, CallFlags.None); - public static readonly InteropDescriptor AsReadOnly = Register("System.Storage.AsReadOnly", Storage_AsReadOnly, 0_00000400, TriggerType.Application, CallFlags.None); - public static readonly InteropDescriptor Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application, CallFlags.None); - public static readonly InteropDescriptor Find = Register("System.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor AsReadOnly = Register("System.Storage.AsReadOnly", Storage_AsReadOnly, 0_00000400, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor Find = Register("System.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application, CallFlags.AllowStates); public static readonly InteropDescriptor Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); public static readonly InteropDescriptor PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); public static readonly InteropDescriptor Delete = Register("System.Storage.Delete", Storage_Delete, 1 * GasPerByte, TriggerType.Application, CallFlags.AllowModifyStates); diff --git a/src/neo/SmartContract/Native/ContractMethodAttribute.cs b/src/neo/SmartContract/Native/ContractMethodAttribute.cs index 22051d72f7..6cb745fee5 100644 --- a/src/neo/SmartContract/Native/ContractMethodAttribute.cs +++ b/src/neo/SmartContract/Native/ContractMethodAttribute.cs @@ -10,12 +10,13 @@ internal class ContractMethodAttribute : Attribute public ContractParameterType ReturnType { get; } public ContractParameterType[] ParameterTypes { get; set; } = Array.Empty(); public string[] ParameterNames { get; set; } = Array.Empty(); - public bool SafeMethod { get; set; } = false; + public CallFlags RequiredCallFlags { get; } - public ContractMethodAttribute(long price, ContractParameterType returnType) + public ContractMethodAttribute(long price, ContractParameterType returnType, CallFlags requiredCallFlags) { this.Price = price; this.ReturnType = returnType; + this.RequiredCallFlags = requiredCallFlags; } } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 7e402ac00a..ce34011e32 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -56,12 +56,12 @@ protected NativeContract() ReturnType = attribute.ReturnType, Parameters = attribute.ParameterTypes.Zip(attribute.ParameterNames, (t, n) => new ContractParameterDefinition { Type = t, Name = n }).ToArray() }); - if (attribute.SafeMethod) safeMethods.Add(name); + if (!attribute.RequiredCallFlags.HasFlag(CallFlags.AllowModifyStates)) safeMethods.Add(name); methods.Add(name, new ContractMethodMetadata { Delegate = (Func)method.CreateDelegate(typeof(Func), this), Price = attribute.Price, - RequiredCallFlags = attribute.SafeMethod ? CallFlags.None : CallFlags.AllowModifyStates + RequiredCallFlags = attribute.RequiredCallFlags }); } this.Manifest = new ContractManifest @@ -133,7 +133,7 @@ internal virtual bool Initialize(ApplicationEngine engine) return true; } - [ContractMethod(0, ContractParameterType.Boolean)] + [ContractMethod(0, ContractParameterType.Boolean, CallFlags.AllowModifyStates)] protected StackItem OnPersist(ApplicationEngine engine, Array args) { if (engine.Trigger != TriggerType.System) return false; @@ -145,7 +145,7 @@ protected virtual bool OnPersist(ApplicationEngine engine) return true; } - [ContractMethod(0, ContractParameterType.Array, Name = "supportedStandards", SafeMethod = true)] + [ContractMethod(0, ContractParameterType.Array, CallFlags.None, Name = "supportedStandards")] protected StackItem SupportedStandardsMethod(ApplicationEngine engine, Array args) { return new Array(engine.ReferenceCounter, SupportedStandards.Select(p => (StackItem)p)); diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index ee5f45376e..ac9e9de471 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -67,7 +67,7 @@ internal override bool Initialize(ApplicationEngine engine) return true; } - [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] + [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] private StackItem GetMaxTransactionsPerBlock(ApplicationEngine engine, Array args) { return GetMaxTransactionsPerBlock(engine.Snapshot); @@ -78,7 +78,7 @@ public uint GetMaxTransactionsPerBlock(StoreView snapshot) return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxTransactionsPerBlock)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] + [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] private StackItem GetMaxBlockSize(ApplicationEngine engine, Array args) { return GetMaxBlockSize(engine.Snapshot); @@ -89,7 +89,7 @@ public uint GetMaxBlockSize(StoreView snapshot) return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxBlockSize)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] + [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] private StackItem GetFeePerByte(ApplicationEngine engine, Array args) { return GetFeePerByte(engine.Snapshot); @@ -100,7 +100,7 @@ public long GetFeePerByte(StoreView snapshot) return BitConverter.ToInt64(snapshot.Storages[CreateStorageKey(Prefix_FeePerByte)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Array, SafeMethod = true)] + [ContractMethod(0_01000000, ContractParameterType.Array, CallFlags.AllowStates)] private StackItem GetBlockedAccounts(ApplicationEngine engine, Array args) { return new Array(engine.ReferenceCounter, GetBlockedAccounts(engine.Snapshot).Select(p => (StackItem)p.ToArray())); @@ -111,7 +111,7 @@ public UInt160[] GetBlockedAccounts(StoreView snapshot) return snapshot.Storages[CreateStorageKey(Prefix_BlockedAccounts)].Value.AsSerializableArray(); } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] + [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetMaxBlockSize(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; @@ -122,7 +122,7 @@ private StackItem SetMaxBlockSize(ApplicationEngine engine, Array args) return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] + [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; @@ -132,7 +132,7 @@ private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, Array arg return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] + [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetFeePerByte(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; @@ -142,7 +142,7 @@ private StackItem SetFeePerByte(ApplicationEngine engine, Array args) return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] + [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] private StackItem BlockAccount(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; @@ -156,7 +156,7 @@ private StackItem BlockAccount(ApplicationEngine engine, Array args) return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] + [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] private StackItem UnblockAccount(ApplicationEngine engine, Array args) { if (!CheckValidators(engine)) return false; diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 4c9147220e..7d3b82c797 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -116,7 +116,7 @@ protected override bool OnPersist(ApplicationEngine engine) return true; } - [ContractMethod(0_03000000, ContractParameterType.Integer, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "account", "end" }, SafeMethod = true)] + [ContractMethod(0_03000000, ContractParameterType.Integer, CallFlags.AllowStates, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "account", "end" })] private StackItem UnclaimedGas(ApplicationEngine engine, Array args) { UInt160 account = new UInt160(args[0].GetSpan()); @@ -132,7 +132,7 @@ public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) return CalculateBonus(snapshot, state.Balance, state.BalanceHeight, end); } - [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] + [ContractMethod(0_05000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] private StackItem RegisterCandidate(ApplicationEngine engine, Array args) { ECPoint pubkey = args[0].GetSpan().AsSerializable(); @@ -150,7 +150,7 @@ private bool RegisterCandidate(StoreView snapshot, ECPoint pubkey) return true; } - [ContractMethod(0_05000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] + [ContractMethod(0_05000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] private StackItem UnregisterCandidate(ApplicationEngine engine, Array args) { ECPoint pubkey = args[0].GetSpan().AsSerializable(); @@ -172,7 +172,7 @@ private bool UnregisterCandidate(StoreView snapshot, ECPoint pubkey) return true; } - [ContractMethod(5_00000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" })] + [ContractMethod(5_00000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" })] private StackItem Vote(ApplicationEngine engine, Array args) { UInt160 account = new UInt160(args[0].GetSpan()); @@ -209,7 +209,7 @@ private bool Vote(StoreView snapshot, UInt160 account, ECPoint voteTo) return true; } - [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] + [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] private StackItem GetCandidates(ApplicationEngine engine, Array args) { return new Array(engine.ReferenceCounter, GetCandidates(engine.Snapshot).Select(p => new Struct(engine.ReferenceCounter, new StackItem[] { p.PublicKey.ToArray(), p.Votes }))); @@ -225,7 +225,7 @@ public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetCandidates(StoreVie )).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes)); } - [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] + [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] private StackItem GetValidators(ApplicationEngine engine, Array args) { return new Array(engine.ReferenceCounter, GetValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray())); @@ -236,7 +236,7 @@ public ECPoint[] GetValidators(StoreView snapshot) return GetCommitteeMembers(snapshot, Blockchain.ValidatorsCount).OrderBy(p => p).ToArray(); } - [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] + [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] private StackItem GetCommittee(ApplicationEngine engine, Array args) { return new Array(engine.ReferenceCounter, GetCommittee(engine.Snapshot).Select(p => (StackItem)p.ToArray())); @@ -252,7 +252,7 @@ private IEnumerable GetCommitteeMembers(StoreView snapshot, int count) return GetCandidates(snapshot).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count); } - [ContractMethod(1_00000000, ContractParameterType.Array, SafeMethod = true)] + [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] private StackItem GetNextBlockValidators(ApplicationEngine engine, Array args) { return new Array(engine.ReferenceCounter, GetNextBlockValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray())); diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index b4e389676f..6f1164de8c 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -104,25 +104,25 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, engine.SendNotification(Hash, new Array(new StackItem[] { "Transfer", account.ToArray(), StackItem.Null, amount })); } - [ContractMethod(0, ContractParameterType.String, Name = "name", SafeMethod = true)] + [ContractMethod(0, ContractParameterType.String, CallFlags.None, Name = "name")] protected StackItem NameMethod(ApplicationEngine engine, Array args) { return Name; } - [ContractMethod(0, ContractParameterType.String, Name = "symbol", SafeMethod = true)] + [ContractMethod(0, ContractParameterType.String, CallFlags.None, Name = "symbol")] protected StackItem SymbolMethod(ApplicationEngine engine, Array args) { return Symbol; } - [ContractMethod(0, ContractParameterType.Integer, Name = "decimals", SafeMethod = true)] + [ContractMethod(0, ContractParameterType.Integer, CallFlags.None, Name = "decimals")] protected StackItem DecimalsMethod(ApplicationEngine engine, Array args) { return (uint)Decimals; } - [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)] + [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] protected StackItem TotalSupply(ApplicationEngine engine, Array args) { return TotalSupply(engine.Snapshot); @@ -135,7 +135,7 @@ public virtual BigInteger TotalSupply(StoreView snapshot) return new BigInteger(storage.Value); } - [ContractMethod(0_01000000, ContractParameterType.Integer, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" }, SafeMethod = true)] + [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] protected StackItem BalanceOf(ApplicationEngine engine, Array args) { return BalanceOf(engine.Snapshot, new UInt160(args[0].GetSpan())); @@ -148,7 +148,7 @@ public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) return storage.GetInteroperable().Balance; } - [ContractMethod(0_08000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" })] + [ContractMethod(0_08000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" })] protected StackItem Transfer(ApplicationEngine engine, Array args) { UInt160 from = new UInt160(args[0].GetSpan()); From 67ec9be29dc6abc48c16d7b55df268a39a327ad2 Mon Sep 17 00:00:00 2001 From: Shargon Date: Sun, 26 Apr 2020 07:51:06 +0200 Subject: [PATCH 248/305] Add standard cryptography primitives (#1419) --- src/neo/Consensus/ConsensusService.cs | 5 +- src/neo/Cryptography/Crypto.cs | 78 +++++++++---- src/neo/Cryptography/ECC/ECDsa.cs | 107 ++++++++++++++++++ src/neo/SmartContract/Contract.cs | 4 +- src/neo/SmartContract/Helper.cs | 4 +- .../SmartContract/InteropService.Crypto.cs | 52 +++++++-- .../SmartContract/Manifest/ContractGroup.cs | 2 +- src/neo/Wallets/Wallet.cs | 4 +- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 16 +-- tests/neo.UnitTests/Cryptography/UT_Crypto.cs | 35 +++++- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 6 +- .../SmartContract/UT_Contract.cs | 12 +- .../UT_ContractParameterContext.cs | 12 +- .../SmartContract/UT_InteropService.NEO.cs | 14 +-- .../SmartContract/UT_InteropService.cs | 6 +- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 4 +- 16 files changed, 283 insertions(+), 78 deletions(-) create mode 100644 src/neo/Cryptography/ECC/ECDsa.cs diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 9e59ba7283..788719ae14 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -231,8 +231,7 @@ private void OnCommitReceived(ConsensusPayload payload, Commit commit) { existingCommitPayload = payload; } - else if (Crypto.VerifySignature(hashData, commit.Signature, - context.Validators[payload.ValidatorIndex].EncodePoint(false))) + else if (Crypto.VerifySignature(hashData, commit.Signature, context.Validators[payload.ValidatorIndex])) { existingCommitPayload = payload; CheckCommits(); @@ -433,7 +432,7 @@ private void OnPrepareRequestReceived(ConsensusPayload payload, PrepareRequest m 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].EncodePoint(false))) + if (!Crypto.VerifySignature(hashData, context.CommitPayloads[i].GetDeserializedMessage().Signature, context.Validators[i])) context.CommitPayloads[i] = null; if (context.TransactionHashes.Length == 0) diff --git a/src/neo/Cryptography/Crypto.cs b/src/neo/Cryptography/Crypto.cs index ee7b1418c8..ddd010cc63 100644 --- a/src/neo/Cryptography/Crypto.cs +++ b/src/neo/Cryptography/Crypto.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using System.Security.Cryptography; namespace Neo.Cryptography @@ -32,38 +33,75 @@ public static byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey) } } - public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ReadOnlySpan pubkey) + public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ECC.ECPoint pubkey) { - if (pubkey.Length == 33 && (pubkey[0] == 0x02 || pubkey[0] == 0x03)) + if (pubkey.Curve == ECC.ECCurve.Secp256r1) { - try + byte[] buffer = pubkey.EncodePoint(false); + using (var ecdsa = ECDsa.Create(new ECParameters { - pubkey = ECC.ECPoint.DecodePoint(pubkey, ECC.ECCurve.Secp256r1).EncodePoint(false).AsSpan(1); - } - catch + Curve = ECCurve.NamedCurves.nistP256, + Q = new ECPoint + { + X = buffer[1..33], + Y = buffer[33..] + } + })) { - return false; + return ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256); } } - else if (pubkey.Length == 65 && pubkey[0] == 0x04) - { - pubkey = pubkey[1..]; - } - else if (pubkey.Length != 64) + else { - throw new ArgumentException(); + var ecdsa = new ECC.ECDsa(pubkey); + var r = new BigInteger(signature[..32], true, true); + var s = new BigInteger(signature[32..], true, true); + return ecdsa.VerifySignature(message.Sha256(), r, s); } - using (var ecdsa = ECDsa.Create(new ECParameters + } + + public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ReadOnlySpan pubkey, ECC.ECCurve curve) + { + if (curve == ECC.ECCurve.Secp256r1) { - Curve = ECCurve.NamedCurves.nistP256, - Q = new ECPoint + if (pubkey.Length == 33 && (pubkey[0] == 0x02 || pubkey[0] == 0x03)) { - X = pubkey[..32].ToArray(), - Y = pubkey[32..].ToArray() + try + { + pubkey = ECC.ECPoint.DecodePoint(pubkey, curve).EncodePoint(false).AsSpan(1); + } + catch + { + return false; + } } - })) + else if (pubkey.Length == 65 && pubkey[0] == 0x04) + { + pubkey = pubkey[1..]; + } + else + { + throw new ArgumentException(); + } + using (var ecdsa = ECDsa.Create(new ECParameters + { + Curve = ECCurve.NamedCurves.nistP256, + Q = new ECPoint + { + X = pubkey[..32].ToArray(), + Y = pubkey[32..].ToArray() + } + })) + { + return ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256); + } + } + else { - return ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256); + var ecdsa = new ECC.ECDsa(ECC.ECPoint.DecodePoint(pubkey, curve)); + var r = new BigInteger(signature[..32], true, true); + var s = new BigInteger(signature[32..], true, true); + return ecdsa.VerifySignature(message.Sha256(), r, s); } } } diff --git a/src/neo/Cryptography/ECC/ECDsa.cs b/src/neo/Cryptography/ECC/ECDsa.cs new file mode 100644 index 0000000000..08f48b1dc3 --- /dev/null +++ b/src/neo/Cryptography/ECC/ECDsa.cs @@ -0,0 +1,107 @@ +using System; +using System.Numerics; +using System.Security.Cryptography; + +namespace Neo.Cryptography.ECC +{ + public class ECDsa + { + private readonly byte[] privateKey; + private readonly ECPoint publicKey; + private readonly ECCurve curve; + + public ECDsa(byte[] privateKey, ECCurve curve) + : this(curve.G * privateKey) + { + this.privateKey = privateKey; + } + + public ECDsa(ECPoint publicKey) + { + this.publicKey = publicKey; + this.curve = publicKey.Curve; + } + + private BigInteger CalculateE(BigInteger n, ReadOnlySpan message) + { + int messageBitLength = message.Length * 8; + BigInteger trunc = new BigInteger(message, isUnsigned: true, isBigEndian: true); + if (n.GetBitLength() < messageBitLength) + { + trunc >>= messageBitLength - n.GetBitLength(); + } + return trunc; + } + + public BigInteger[] GenerateSignature(ReadOnlySpan message) + { + if (privateKey == null) throw new InvalidOperationException(); + BigInteger e = CalculateE(curve.N, message); + BigInteger d = new BigInteger(privateKey, isUnsigned: true, isBigEndian: true); + BigInteger r, s; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + do + { + BigInteger k; + do + { + do + { + k = rng.NextBigInteger(curve.N.GetBitLength()); + } + while (k.Sign == 0 || k.CompareTo(curve.N) >= 0); + ECPoint p = ECPoint.Multiply(curve.G, k); + BigInteger x = p.X.Value; + r = x.Mod(curve.N); + } + while (r.Sign == 0); + s = (k.ModInverse(curve.N) * (e + d * r)).Mod(curve.N); + if (s > curve.N / 2) + { + s = curve.N - s; + } + } + while (s.Sign == 0); + } + return new BigInteger[] { r, s }; + } + + private static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) + { + int m = Math.Max(k.GetBitLength(), l.GetBitLength()); + ECPoint Z = P + Q; + ECPoint R = P.Curve.Infinity; + for (int i = m - 1; i >= 0; --i) + { + R = R.Twice(); + if (k.TestBit(i)) + { + if (l.TestBit(i)) + R = R + Z; + else + R = R + P; + } + else + { + if (l.TestBit(i)) + R = R + Q; + } + } + return R; + } + + public bool VerifySignature(ReadOnlySpan message, BigInteger r, BigInteger s) + { + if (r.Sign < 1 || s.Sign < 1 || r.CompareTo(curve.N) >= 0 || s.CompareTo(curve.N) >= 0) + return false; + BigInteger e = CalculateE(curve.N, message); + BigInteger c = s.ModInverse(curve.N); + BigInteger u1 = (e * c).Mod(curve.N); + BigInteger u2 = (r * c).Mod(curve.N); + ECPoint point = SumOfTwoMultiplies(curve.G, u1, publicKey, u2); + BigInteger v = point.X.Value.Mod(curve.N); + return v.Equals(r); + } + } +} diff --git a/src/neo/SmartContract/Contract.cs b/src/neo/SmartContract/Contract.cs index 022e6a4c3f..32f409eab8 100644 --- a/src/neo/SmartContract/Contract.cs +++ b/src/neo/SmartContract/Contract.cs @@ -82,7 +82,7 @@ public static byte[] CreateMultiSigRedeemScript(int m, params ECPoint[] publicKe } sb.EmitPush(publicKeys.Length); sb.Emit(OpCode.PUSHNULL); - sb.EmitSysCall(InteropService.Crypto.ECDsaCheckMultiSig); + sb.EmitSysCall(InteropService.Crypto.CheckMultisigWithECDsaSecp256r1); return sb.ToArray(); } } @@ -102,7 +102,7 @@ public static byte[] CreateSignatureRedeemScript(ECPoint publicKey) { sb.EmitPush(publicKey.EncodePoint(true)); sb.Emit(OpCode.PUSHNULL); - sb.EmitSysCall(InteropService.Crypto.ECDsaVerify); + sb.EmitSysCall(InteropService.Crypto.VerifyWithECDsaSecp256r1); return sb.ToArray(); } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index f2a506da6a..33c6b260cf 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -92,7 +92,7 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List if (script[i++] != (byte)OpCode.PUSHNULL) return false; if (script[i++] != (byte)OpCode.SYSCALL) return false; if (script.Length != i + 4) return false; - if (BitConverter.ToUInt32(script, i) != InteropService.Crypto.ECDsaCheckMultiSig) + if (BitConverter.ToUInt32(script, i) != InteropService.Crypto.CheckMultisigWithECDsaSecp256r1) return false; return true; } @@ -104,7 +104,7 @@ public static bool IsSignatureContract(this byte[] script) || script[1] != 33 || script[35] != (byte)OpCode.PUSHNULL || script[36] != (byte)OpCode.SYSCALL - || BitConverter.ToUInt32(script, 37) != InteropService.Crypto.ECDsaVerify) + || BitConverter.ToUInt32(script, 37) != InteropService.Crypto.VerifyWithECDsaSecp256r1) return false; return true; } diff --git a/src/neo/SmartContract/InteropService.Crypto.cs b/src/neo/SmartContract/InteropService.Crypto.cs index 9678f321ce..99b585885e 100644 --- a/src/neo/SmartContract/InteropService.Crypto.cs +++ b/src/neo/SmartContract/InteropService.Crypto.cs @@ -14,8 +14,12 @@ partial class InteropService { public static class Crypto { - public static readonly InteropDescriptor ECDsaVerify = Register("Neo.Crypto.ECDsaVerify", Crypto_ECDsaVerify, 0_01000000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor ECDsaCheckMultiSig = Register("Neo.Crypto.ECDsaCheckMultiSig", Crypto_ECDsaCheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor SHA256 = Register("Neo.Crypto.SHA256", Crypto_SHA256, 0_01000000, TriggerType.All, CallFlags.None); + + public static readonly InteropDescriptor VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.ECDsa.Secp256r1.Verify", Crypto_ECDsaSecp256r1Verify, 0_01000000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.ECDsa.Secp256k1.Verify", Crypto_ECDsaSecp256k1Verify, 0_01000000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.ECDsa.Secp256r1.CheckMultiSig", Crypto_ECDsaSecp256r1CheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.ECDsa.Secp256k1.CheckMultiSig", Crypto_ECDsaSecp256k1CheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None); private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack, StoreView snapshot) { @@ -25,10 +29,34 @@ private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack, StoreView if (item is Array array) n = array.Count; else n = (int)item.GetBigInteger(); if (n < 1) return 0; - return ECDsaVerify.Price * n; + return VerifyWithECDsaSecp256r1.Price * n; + } + + private static bool Crypto_SHA256(ApplicationEngine engine) + { + StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); + ReadOnlySpan value = item0 switch + { + InteropInterface _interface => _interface.GetInterface().GetHashData(), + Null _ => engine.ScriptContainer.GetHashData(), + _ => item0.GetSpan() + }; + + engine.CurrentContext.EvaluationStack.Push(value.ToArray().Sha256()); + return true; } - private static bool Crypto_ECDsaVerify(ApplicationEngine engine) + private static bool Crypto_ECDsaSecp256r1Verify(ApplicationEngine engine) + { + return Crypto_ECDsaVerify(engine, Cryptography.ECC.ECCurve.Secp256r1); + } + + private static bool Crypto_ECDsaSecp256k1Verify(ApplicationEngine engine) + { + return Crypto_ECDsaVerify(engine, Cryptography.ECC.ECCurve.Secp256k1); + } + + private static bool Crypto_ECDsaVerify(ApplicationEngine engine, Cryptography.ECC.ECCurve curve) { StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); ReadOnlySpan message = item0 switch @@ -41,7 +69,7 @@ private static bool Crypto_ECDsaVerify(ApplicationEngine engine) ReadOnlySpan signature = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); try { - engine.CurrentContext.EvaluationStack.Push(Cryptography.Crypto.VerifySignature(message, signature, pubkey)); + engine.CurrentContext.EvaluationStack.Push(Cryptography.Crypto.VerifySignature(message, signature, pubkey, curve)); } catch (ArgumentException) { @@ -50,7 +78,17 @@ private static bool Crypto_ECDsaVerify(ApplicationEngine engine) return true; } - private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) + private static bool Crypto_ECDsaSecp256r1CheckMultiSig(ApplicationEngine engine) + { + return Crypto_ECDsaCheckMultiSig(engine, Cryptography.ECC.ECCurve.Secp256r1); + } + + private static bool Crypto_ECDsaSecp256k1CheckMultiSig(ApplicationEngine engine) + { + return Crypto_ECDsaCheckMultiSig(engine, Cryptography.ECC.ECCurve.Secp256k1); + } + + private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine, Cryptography.ECC.ECCurve curve) { StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); ReadOnlySpan message = item0 switch @@ -98,7 +136,7 @@ private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine) { for (int i = 0, j = 0; fSuccess && i < m && j < n;) { - if (Cryptography.Crypto.VerifySignature(message, signatures[i], pubkeys[j])) + if (Cryptography.Crypto.VerifySignature(message, signatures[i], pubkeys[j], curve)) i++; j++; if (m - i > n - j) diff --git a/src/neo/SmartContract/Manifest/ContractGroup.cs b/src/neo/SmartContract/Manifest/ContractGroup.cs index c10bcba283..8cb6ced3b8 100644 --- a/src/neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/neo/SmartContract/Manifest/ContractGroup.cs @@ -52,7 +52,7 @@ public static ContractGroup FromJson(JObject json) /// Return true or false public bool IsValid(UInt160 hash) { - return Crypto.VerifySignature(hash.ToArray(), Signature, PubKey.EncodePoint(false)); + return Crypto.VerifySignature(hash.ToArray(), Signature, PubKey); } public virtual JObject ToJson() diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 4831a82c96..56d0076140 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -346,7 +346,7 @@ public static long CalculateNetworkFee(byte[] witness_script, ref int size) if (witness_script.IsSignatureContract()) { size += 67 + witness_script.GetVarSize(); - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null, null); + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.VerifyWithECDsaSecp256r1, null, null); } else if (witness_script.IsMultiSigContract(out int m, out int n)) { @@ -358,7 +358,7 @@ public static long CalculateNetworkFee(byte[] witness_script, ref int size) networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * n; using (ScriptBuilder sb = new ScriptBuilder()) networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.ECDsaVerify, null, null) * n; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.VerifyWithECDsaSecp256r1, null, null) * n; } else { diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 4bdb0da2c5..1baf64acb8 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -10,6 +10,7 @@ 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; @@ -18,7 +19,6 @@ using System.Reflection; using System.Security.Cryptography; using ECPoint = Neo.Cryptography.ECC.ECPoint; -using Neo.SmartContract.Native; namespace Neo.UnitTests.Consensus { @@ -153,8 +153,8 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm 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("0x9412c3107a59fa732ccd94866976f7bbb3d9c372")); - mockContext.Object.Block.NextConsensus.Should().Be(UInt160.Parse("0x9412c3107a59fa732ccd94866976f7bbb3d9c372")); + originalContract.ScriptHash.Should().Be(UInt160.Parse("0x7ab841144dcdbf228ff57f7068f795e2afd1a3c1")); + mockContext.Object.Block.NextConsensus.Should().Be(UInt160.Parse("0x7ab841144dcdbf228ff57f7068f795e2afd1a3c1")); Console.WriteLine("\n=========================="); Console.WriteLine("will trigger OnPersistCompleted again with OnStart flag!"); @@ -175,7 +175,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("will create template MakePrepareRequest..."); mockContext.Object.PrevHeader.Timestamp = defaultTimestamp; - mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0x9412c3107a59fa732ccd94866976f7bbb3d9c372")); + mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0x7ab841144dcdbf228ff57f7068f795e2afd1a3c1")); var prepReq = mockContext.Object.MakePrepareRequest(); var ppToSend = (PrepareRequest)prepReq.ConsensusMessage; // Forcing hashes to 0 because mempool is currently shared @@ -292,10 +292,10 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("\nBasic commits Signatures verification"); // Basic tests for understanding signatures and ensuring signatures of commits are correct on tests var cmPayloadTemp = GetCommitPayloadModifiedAndSignedCopy(commitPayload, 6, kp_array[6], updatedBlockHashData); - Crypto.VerifySignature(originalBlockHashData, cm.Signature, mockContext.Object.Validators[0].EncodePoint(false)).Should().BeFalse(); - Crypto.VerifySignature(updatedBlockHashData, cm.Signature, mockContext.Object.Validators[0].EncodePoint(false)).Should().BeFalse(); - Crypto.VerifySignature(originalBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[6].EncodePoint(false)).Should().BeFalse(); - Crypto.VerifySignature(updatedBlockHashData, ((Commit)cmPayloadTemp.ConsensusMessage).Signature, mockContext.Object.Validators[6].EncodePoint(false)).Should().BeTrue(); + 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(); Console.WriteLine("\n=========================="); Console.WriteLine("\n=========================="); diff --git a/tests/neo.UnitTests/Cryptography/UT_Crypto.cs b/tests/neo.UnitTests/Cryptography/UT_Crypto.cs index 38f2006a7f..0154fea65a 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Crypto.cs +++ b/tests/neo.UnitTests/Cryptography/UT_Crypto.cs @@ -44,21 +44,44 @@ public void TestVerifySignature() { byte[] message = System.Text.Encoding.Default.GetBytes("HelloWorld"); byte[] signature = Crypto.Sign(message, key.PrivateKey, key.PublicKey.EncodePoint(false).Skip(1).ToArray()); - Crypto.VerifySignature(message, signature, key.PublicKey.EncodePoint(false)).Should().BeTrue(); - Crypto.VerifySignature(message, signature, key.PublicKey.EncodePoint(false).Skip(1).ToArray()).Should().BeTrue(); - Crypto.VerifySignature(message, signature, key.PublicKey.EncodePoint(false).Skip(1).ToArray()).Should().BeTrue(); + Crypto.VerifySignature(message, signature, key.PublicKey).Should().BeTrue(); byte[] wrongKey = new byte[33]; wrongKey[0] = 0x02; - Crypto.VerifySignature(message, signature, wrongKey).Should().BeFalse(); + Crypto.VerifySignature(message, signature, wrongKey, Neo.Cryptography.ECC.ECCurve.Secp256r1).Should().BeFalse(); wrongKey[0] = 0x03; for (int i = 1; i < 33; i++) wrongKey[i] = byte.MaxValue; - Crypto.VerifySignature(message, signature, wrongKey).Should().BeFalse(); + Crypto.VerifySignature(message, signature, wrongKey, Neo.Cryptography.ECC.ECCurve.Secp256r1).Should().BeFalse(); wrongKey = new byte[36]; - Action action = () => Crypto.VerifySignature(message, signature, wrongKey).Should().BeFalse(); + Action action = () => Crypto.VerifySignature(message, signature, wrongKey, Neo.Cryptography.ECC.ECCurve.Secp256r1); action.Should().Throw(); } + + [TestMethod] + public void TestSecp256k1() + { + byte[] message = System.Text.Encoding.Default.GetBytes("hello"); + byte[] signature = "5331be791532d157df5b5620620d938bcb622ad02c81cfc184c460efdad18e695480d77440c511e9ad02ea30d773cb54e88f8cbb069644aefa283957085f38b5".HexToBytes(); + byte[] pubKey = "03ea01cb94bdaf0cd1c01b159d474f9604f4af35a3e2196f6bdfdb33b2aa4961fa".HexToBytes(); + + Crypto.VerifySignature(message, signature, pubKey, Neo.Cryptography.ECC.ECCurve.Secp256k1) + .Should().BeTrue(); + + message = System.Text.Encoding.Default.GetBytes("world"); + signature = "b1e6ff4f40536fb7ed706b0f7567903cc227a5241a079fb86f3de51b8321c1e690f37ad0c788848605c1653567935845f0d35a8a1a37174dcbbd235caac8e969".HexToBytes(); + pubKey = "03661b86d54eb3a8e7ea2399e0db36ab65753f95fff661da53ae0121278b881ad0".HexToBytes(); + + Crypto.VerifySignature(message, signature, pubKey, Neo.Cryptography.ECC.ECCurve.Secp256k1) + .Should().BeTrue(); + + message = System.Text.Encoding.Default.GetBytes("中文"); + signature = "b8cba1ff42304d74d083e87706058f59cdd4f755b995926d2cd80a734c5a3c37e4583bfd4339ac762c1c91eee3782660a6baf62cd29e407eccd3da3e9de55a02".HexToBytes(); + pubKey = "03661b86d54eb3a8e7ea2399e0db36ab65753f95fff661da53ae0121278b881ad0".HexToBytes(); + + Crypto.VerifySignature(message, signature, pubKey, Neo.Cryptography.ECC.ECCurve.Secp256k1) + .Should().BeTrue(); + } } } diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index b38c71e2a5..681e36489f 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -70,13 +70,13 @@ public void TestContainsTransaction() [TestMethod] public void TestGetCurrentBlockHash() { - Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0xdba446947a90b2862ef050703b44828ad8b02d11978f8ef59bd3e1c97aabf6e5")); + Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0xc9387b803c8b4c6c1f69f6c876ed7848482c414b0225eb2a3a5395af39425455")); } [TestMethod] public void TestGetCurrentHeaderHash() { - Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0xdba446947a90b2862ef050703b44828ad8b02d11978f8ef59bd3e1c97aabf6e5")); + Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0xc9387b803c8b4c6c1f69f6c876ed7848482c414b0225eb2a3a5395af39425455")); } [TestMethod] @@ -88,7 +88,7 @@ public void TestGetBlock() [TestMethod] public void TestGetBlockHash() { - Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0xdba446947a90b2862ef050703b44828ad8b02d11978f8ef59bd3e1c97aabf6e5")); + Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0xc9387b803c8b4c6c1f69f6c876ed7848482c414b0225eb2a3a5395af39425455")); Blockchain.Singleton.GetBlockHash(10).Should().BeNull(); } diff --git a/tests/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/neo.UnitTests/SmartContract/UT_Contract.cs index 294094ccae..ee943c6477 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Contract.cs @@ -26,7 +26,7 @@ public void TestGetAddress() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); Assert.AreEqual(expectedArray.ToScriptHash().ToAddress(), contract.Address); } @@ -44,7 +44,7 @@ public void TestGetScriptHash() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); Assert.AreEqual(expectedArray.ToScriptHash(), contract.ScriptHash); } @@ -86,7 +86,7 @@ public void TestCreateMultiSigContract() expectedArray[71] = (byte)OpCode.PUSH2; expectedArray[72] = (byte)OpCode.PUSHNULL; expectedArray[73] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaCheckMultiSig), 0, expectedArray, 74, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.CheckMultisigWithECDsaSecp256r1), 0, expectedArray, 74, 4); CollectionAssert.AreEqual(expectedArray, contract.Script); Assert.AreEqual(2, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); @@ -122,7 +122,7 @@ public void TestCreateMultiSigRedeemScript() expectedArray[71] = (byte)OpCode.PUSH2; expectedArray[72] = (byte)OpCode.PUSHNULL; expectedArray[73] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaCheckMultiSig), 0, expectedArray, 74, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.CheckMultisigWithECDsaSecp256r1), 0, expectedArray, 74, 4); CollectionAssert.AreEqual(expectedArray, script); } @@ -140,7 +140,7 @@ public void TestCreateSignatureContract() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); CollectionAssert.AreEqual(expectedArray, contract.Script); Assert.AreEqual(1, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); @@ -160,7 +160,7 @@ public void TestCreateSignatureRedeemScript() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.ECDsaVerify), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(InteropService.Crypto.VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); CollectionAssert.AreEqual(expectedArray, script); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index f5110b00ea..bfb0a53391 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -47,7 +47,7 @@ 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"":""AAAAAABmUJDLobcPtqo9vZKIdjXsd8fVGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA=="",""items"":{""0x1bd5c777ec35768892bd3daab60fb7a1cb905066"":{""script"":""DCECb/A7lJJBzh2t1DUZ5pYOCoW0GmmgXDKBA6orzhWUyhYLQQqQatQ="",""parameters"":[{""type"":""Signature"",""value"":""AQ==""}]}}}"); + str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""hex"":""AAAAAABmUJDLobcPtqo9vZKIdjXsd8fVGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA=="",""items"":{}}"); } [TestMethod] @@ -72,7 +72,7 @@ public void TestAdd() var context1 = new ContractParametersContext(tx); context1.Add(contract, 0, new byte[] { 0x01 }).Should().BeFalse(); - tx.Sender = UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); + tx.Sender = UInt160.Parse("0xcd4ced947d791e887559b3829c3bc08fe37b0a64"); var context2 = new ContractParametersContext(tx); context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); //test repeatlly createItem @@ -83,7 +83,7 @@ public void TestAdd() public void TestGetParameter() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); + tx.Sender = UInt160.Parse("0xcd4ced947d791e887559b3829c3bc08fe37b0a64"); var context = new ContractParametersContext(tx); context.GetParameter(tx.Sender, 0).Should().BeNull(); @@ -96,7 +96,7 @@ public void TestGetParameter() public void TestGetWitnesses() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); + tx.Sender = UInt160.Parse("0xcd4ced947d791e887559b3829c3bc08fe37b0a64"); var context = new ContractParametersContext(tx); context.Add(contract, 0, new byte[] { 0x01 }); Witness[] witnesses = context.GetWitnesses(); @@ -109,7 +109,7 @@ public void TestGetWitnesses() public void TestAddSignature() { Transaction tx = TestUtils.GetTransaction(); - var singleSender = UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); + var singleSender = UInt160.Parse("0xcd4ced947d791e887559b3829c3bc08fe37b0a64"); tx.Sender = singleSender; //singleSign @@ -139,7 +139,7 @@ public void TestAddSignature() key.PublicKey, key2.PublicKey }); - var multiSender = UInt160.Parse("0xd8e21c5f8b2e48c409220a3aff34a7fc4c87fbe9"); + var multiSender = UInt160.Parse("0x6bb1ea23cefb73dd959775c035a114018c2c1119"); tx.Sender = multiSender; context = new ContractParametersContext(tx); context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 914e6bb012..84c80288d4 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -35,13 +35,13 @@ public void TestCheckSig() engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.ECDsaVerify).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.VerifyWithECDsaSecp256r1).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(new byte[70]); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.ECDsaVerify).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.VerifyWithECDsaSecp256r1).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); } @@ -77,14 +77,14 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.ECDsaCheckMultiSig).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.CheckMultisigWithECDsaSecp256r1).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); pubkeys = new VMArray(); engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.ECDsaCheckMultiSig).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Crypto.CheckMultisigWithECDsaSecp256r1).Should().BeFalse(); pubkeys = new VMArray { @@ -95,7 +95,7 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.ECDsaCheckMultiSig).Should().BeFalse(); + InteropService.Invoke(engine, InteropService.Crypto.CheckMultisigWithECDsaSecp256r1).Should().BeFalse(); pubkeys = new VMArray { @@ -110,7 +110,7 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.ECDsaCheckMultiSig).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.CheckMultisigWithECDsaSecp256r1).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); pubkeys = new VMArray @@ -126,7 +126,7 @@ public void TestCrypto_CheckMultiSig() engine.CurrentContext.EvaluationStack.Push(signatures); engine.CurrentContext.EvaluationStack.Push(pubkeys); engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.ECDsaCheckMultiSig).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.CheckMultisigWithECDsaSecp256r1).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 5fb8cc4220..b599016336 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -391,7 +391,7 @@ public void TestCrypto_Verify() engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); engine.CurrentContext.EvaluationStack.Push(message); - InteropService.Invoke(engine, InteropService.Crypto.ECDsaVerify).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.VerifyWithECDsaSecp256r1).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); byte[] wrongkey = pubkey.EncodePoint(false); @@ -399,7 +399,7 @@ public void TestCrypto_Verify() engine.CurrentContext.EvaluationStack.Push(signature); engine.CurrentContext.EvaluationStack.Push(wrongkey); engine.CurrentContext.EvaluationStack.Push(new InteropInterface(engine.ScriptContainer)); - InteropService.Invoke(engine, InteropService.Crypto.ECDsaVerify).Should().BeTrue(); + InteropService.Invoke(engine, InteropService.Crypto.VerifyWithECDsaSecp256r1).Should().BeTrue(); engine.CurrentContext.EvaluationStack.Peek().ToBoolean().Should().BeFalse(); } @@ -879,7 +879,7 @@ public void TestContract_CreateStandardAccount() engine.CurrentContext.EvaluationStack.Push(data); InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray().Should().BeEquivalentTo(UInt160.Parse("0x2c847208959ec1cc94dd13bfe231fa622a404a8a").ToArray()); + engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray().ToHexString().Should().Be("68f96a15748750cccd548feb71be766e8a2c2733"); data = "064b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e".HexToBytes(); engine.CurrentContext.EvaluationStack.Push(data); diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 63ddb327fe..091caa2b65 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -171,9 +171,9 @@ public void TestGetVersion() public void TestGetAccount1() { MyWallet wallet = new MyWallet(); - wallet.CreateAccount(UInt160.Parse("0x7e471cf52f27edc291e29ec8f2d1ea2d210d6725")); + wallet.CreateAccount(UInt160.Parse("0xb3f1526d9f9670df1a21a5953d5296c3a9c9173c")); WalletAccount account = wallet.GetAccount(ECCurve.Secp256r1.G); - account.ScriptHash.Should().Be(UInt160.Parse("0x7e471cf52f27edc291e29ec8f2d1ea2d210d6725")); + account.ScriptHash.Should().Be(UInt160.Parse("0xb3f1526d9f9670df1a21a5953d5296c3a9c9173c")); } [TestMethod] From c044562b40ac741810dcdec23cdbf20b2545ee41 Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Mon, 27 Apr 2020 15:57:47 +0800 Subject: [PATCH 249/305] Datacache correction (#1611) --- src/neo/IO/Caching/DataCache.cs | 8 ++++++++ tests/neo.UnitTests/Wallets/UT_Wallet.cs | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/neo/IO/Caching/DataCache.cs b/src/neo/IO/Caching/DataCache.cs index d20d1bba2e..dd0d5221c8 100644 --- a/src/neo/IO/Caching/DataCache.cs +++ b/src/neo/IO/Caching/DataCache.cs @@ -75,19 +75,27 @@ public void Add(TKey key, TValue value) /// public void Commit() { + LinkedList deletedItem = new LinkedList(); foreach (Trackable trackable in GetChangeSet()) switch (trackable.State) { case TrackState.Added: AddInternal(trackable.Key, trackable.Item); + trackable.State = TrackState.None; break; case TrackState.Changed: UpdateInternal(trackable.Key, trackable.Item); + trackable.State = TrackState.None; break; case TrackState.Deleted: DeleteInternal(trackable.Key); + deletedItem.AddFirst(trackable.Key); break; } + foreach (TKey key in deletedItem) + { + dictionary.Remove(key); + } } public DataCache CreateSnapshot() diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 091caa2b65..e8cd43a46a 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -209,6 +209,7 @@ public void TestGetAvailable() wallet.GetAvailable(NativeContract.GAS.Hash).Should().Be(new BigDecimal(1000000000000, 8)); + entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); entry.GetInteroperable().Balance = 0; snapshot.Commit(); } @@ -231,6 +232,7 @@ public void TestGetBalance() 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 Nep5AccountState())); entry.GetInteroperable().Balance = 0; snapshot.Commit(); } @@ -354,6 +356,8 @@ public void TestMakeTransaction1() }); tx.Should().NotBeNull(); + entry1 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); entry1.GetInteroperable().Balance = 0; entry2.GetInteroperable().Balance = 0; snapshot.Commit(); @@ -383,6 +387,7 @@ public void TestMakeTransaction2() tx = wallet.MakeTransaction(new byte[] { }, null, new TransactionAttribute[] { }); tx.Should().NotBeNull(); + entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); entry.GetInteroperable().Balance = 0; snapshot.Commit(); } From 021b5f2b7f87ba93b8cf91a6796c2416cb0aad6d Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Thu, 30 Apr 2020 02:13:35 +0800 Subject: [PATCH 250/305] Fix LocalNode (#1616) * Remove ProtocolSettings Initialize * Revert "Remove ProtocolSettings Initialize" This reverts commit 58dda0b23e09a9b8a03ac9528fc6eb5908c7bf9e. * Avoid calling ProtocolSettings.Default in multiple threads Co-authored-by: Jin Qiao --- src/neo/Network/P2P/LocalNode.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index c7f0e472e7..f7b9093134 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -59,11 +59,11 @@ public LocalNode(NeoSystem system) singleton = this; // Start dns resolution in parallel - - for (int i = 0; i < ProtocolSettings.Default.SeedList.Length; i++) + string[] seedList = ProtocolSettings.Default.SeedList; + for (int i = 0; i < seedList.Length; i++) { int index = i; - Task.Run(() => SeedList[index] = GetIpEndPoint(ProtocolSettings.Default.SeedList[index])); + Task.Run(() => SeedList[index] = GetIpEndPoint(seedList[index])); } } } From b9b853ebf4ca4148fffa3092aa315b7b961782c4 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 1 May 2020 14:38:47 +0200 Subject: [PATCH 251/305] Update neo-vm package (#1624) * Update neo.csproj * Update all nugets --- src/neo/neo.csproj | 8 ++++---- tests/neo.UnitTests/neo.UnitTests.csproj | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index df025e9ab3..ae25d748cb 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -21,13 +21,13 @@ - + - - - + + + diff --git a/tests/neo.UnitTests/neo.UnitTests.csproj b/tests/neo.UnitTests/neo.UnitTests.csproj index 25aa788129..c1d5a7200d 100644 --- a/tests/neo.UnitTests/neo.UnitTests.csproj +++ b/tests/neo.UnitTests/neo.UnitTests.csproj @@ -15,13 +15,13 @@ - - - - - - - + + + + + + + From 3cec48ba91dfbe777bfcdeaf5a3d658970f54519 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 1 May 2020 18:32:24 +0200 Subject: [PATCH 252/305] Unify akka logs with neo logs (#1623) * Unify akka logs * Add event * dotnet format * Remove event * Remove overload * Clean changes * Fix compilation error * Simplify Logger Co-authored-by: erikzhang --- src/neo/LogLevel.cs | 12 +++++++----- src/neo/NeoSystem.cs | 2 +- src/neo/Utility.cs | 11 +++++++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/neo/LogLevel.cs b/src/neo/LogLevel.cs index 1aeef70a10..0c1c84334d 100644 --- a/src/neo/LogLevel.cs +++ b/src/neo/LogLevel.cs @@ -1,11 +1,13 @@ +using static Akka.Event.LogLevel; + namespace Neo { public enum LogLevel : byte { - Fatal, - Error, - Warning, - Info, - Debug + Debug = DebugLevel, + Info = InfoLevel, + Warning = WarningLevel, + Error = ErrorLevel, + Fatal = Error + 1 } } diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 301df2c5d6..aa18723e01 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -12,7 +12,7 @@ namespace Neo public class NeoSystem : IDisposable { public ActorSystem ActorSystem { get; } = ActorSystem.Create(nameof(NeoSystem), - $"akka {{ log-dead-letters = off }}" + + $"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}\" }}" + diff --git a/src/neo/Utility.cs b/src/neo/Utility.cs index 984422512e..c5145ad760 100644 --- a/src/neo/Utility.cs +++ b/src/neo/Utility.cs @@ -1,3 +1,5 @@ +using Akka.Actor; +using Akka.Event; using Microsoft.Extensions.Configuration; using Neo.Plugins; using System; @@ -6,6 +8,15 @@ namespace Neo { public static class Utility { + internal class Logger : ReceiveActor + { + public Logger() + { + Receive(_ => Sender.Tell(new LoggerInitialized())); + Receive(e => Log(e.LogSource, (LogLevel)e.LogLevel(), e.Message.ToString())); + } + } + /// /// Load configuration with different Environment Variable /// From 04065b0ad9b91a3a383ff337789aabf94e8ed82f Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 3 May 2020 15:42:27 +0800 Subject: [PATCH 253/305] Remove FromJson (#1625) --- src/neo/Network/P2P/Payloads/Block.cs | 10 -------- src/neo/Network/P2P/Payloads/BlockBase.cs | 12 ---------- src/neo/Network/P2P/Payloads/ConsensusData.cs | 10 -------- src/neo/Network/P2P/Payloads/Cosigner.cs | 12 ---------- src/neo/Network/P2P/Payloads/Header.cs | 10 -------- src/neo/Network/P2P/Payloads/Transaction.cs | 16 ------------- .../P2P/Payloads/TransactionAttribute.cs | 10 -------- src/neo/Network/P2P/Payloads/Witness.cs | 8 ------- .../Network/P2P/Payloads/UT_Cosigner.cs | 23 ------------------- 9 files changed, 111 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index 2663eb28f4..82b25252d3 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -113,16 +113,6 @@ public override JObject ToJson() return json; } - public new static Block FromJson(JObject json) - { - Block block = new Block(); - BlockBase blockBase = block; - blockBase.FromJson(json); - block.ConsensusData = ConsensusData.FromJson(json["consensus_data"]); - block.Transactions = ((JArray)json["tx"]).Select(p => Transaction.FromJson(p)).ToArray(); - return block; - } - public TrimmedBlock Trim() { return new TrimmedBlock diff --git a/src/neo/Network/P2P/Payloads/BlockBase.cs b/src/neo/Network/P2P/Payloads/BlockBase.cs index c41869bab4..e737466602 100644 --- a/src/neo/Network/P2P/Payloads/BlockBase.cs +++ b/src/neo/Network/P2P/Payloads/BlockBase.cs @@ -6,7 +6,6 @@ using Neo.Wallets; using System; using System.IO; -using System.Linq; namespace Neo.Network.P2P.Payloads { @@ -113,17 +112,6 @@ public virtual JObject ToJson() return json; } - public void FromJson(JObject json) - { - Version = (uint)json["version"].AsNumber(); - PrevHash = UInt256.Parse(json["previousblockhash"].AsString()); - MerkleRoot = UInt256.Parse(json["merkleroot"].AsString()); - Timestamp = (ulong)json["time"].AsNumber(); - Index = (uint)json["index"].AsNumber(); - NextConsensus = json["nextconsensus"].AsString().ToScriptHash(); - Witness = ((JArray)json["witnesses"]).Select(p => Witness.FromJson(p)).FirstOrDefault(); - } - public virtual bool Verify(StoreView snapshot) { Header prev_header = snapshot.GetHeader(PrevHash); diff --git a/src/neo/Network/P2P/Payloads/ConsensusData.cs b/src/neo/Network/P2P/Payloads/ConsensusData.cs index 460f6a8098..35dfc403af 100644 --- a/src/neo/Network/P2P/Payloads/ConsensusData.cs +++ b/src/neo/Network/P2P/Payloads/ConsensusData.cs @@ -2,7 +2,6 @@ using Neo.IO; using Neo.IO.Json; using Neo.Ledger; -using System.Globalization; using System.IO; namespace Neo.Network.P2P.Payloads @@ -46,14 +45,5 @@ public JObject ToJson() json["nonce"] = Nonce.ToString("x16"); return json; } - - public static ConsensusData FromJson(JObject json) - { - ConsensusData block = new ConsensusData(); - block.PrimaryIndex = (uint)json["primary"].AsNumber(); - block.Nonce = ulong.Parse(json["nonce"].AsString(), NumberStyles.HexNumber); - return block; - } - } } diff --git a/src/neo/Network/P2P/Payloads/Cosigner.cs b/src/neo/Network/P2P/Payloads/Cosigner.cs index 99672ecf4d..f75b8fa7eb 100644 --- a/src/neo/Network/P2P/Payloads/Cosigner.cs +++ b/src/neo/Network/P2P/Payloads/Cosigner.cs @@ -1,7 +1,6 @@ using Neo.Cryptography.ECC; using Neo.IO; using Neo.IO.Json; -using System; using System.IO; using System.Linq; @@ -61,16 +60,5 @@ public JObject ToJson() json["allowedGroups"] = AllowedGroups.Select(p => (JObject)p.ToString()).ToArray(); return json; } - - public static Cosigner FromJson(JObject json) - { - return new Cosigner - { - Account = UInt160.Parse(json["account"].AsString()), - Scopes = (WitnessScope)Enum.Parse(typeof(WitnessScope), json["scopes"].AsString()), - AllowedContracts = ((JArray)json["allowedContracts"])?.Select(p => UInt160.Parse(p.AsString())).ToArray(), - AllowedGroups = ((JArray)json["allowedGroups"])?.Select(p => ECPoint.Parse(p.AsString(), ECCurve.Secp256r1)).ToArray() - }; - } } } diff --git a/src/neo/Network/P2P/Payloads/Header.cs b/src/neo/Network/P2P/Payloads/Header.cs index 193b6b977e..d227860bb0 100644 --- a/src/neo/Network/P2P/Payloads/Header.cs +++ b/src/neo/Network/P2P/Payloads/Header.cs @@ -1,4 +1,3 @@ -using Neo.IO.Json; using Neo.Ledger; using System; using System.IO; @@ -52,14 +51,5 @@ public TrimmedBlock Trim() Hashes = new UInt256[0] }; } - - public new static Header FromJson(JObject json) - { - Header header = new Header(); - BlockBase blockBase = header; - blockBase.FromJson(json); - return header; - } - } } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 48b65b02cd..45f5ff0b3f 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -249,22 +249,6 @@ public JObject ToJson() return json; } - public static Transaction FromJson(JObject json) - { - Transaction tx = new Transaction(); - tx.Version = byte.Parse(json["version"].AsString()); - tx.Nonce = uint.Parse(json["nonce"].AsString()); - tx.Sender = json["sender"].AsString().ToScriptHash(); - tx.SystemFee = long.Parse(json["sys_fee"].AsString()); - tx.NetworkFee = long.Parse(json["net_fee"].AsString()); - tx.ValidUntilBlock = uint.Parse(json["valid_until_block"].AsString()); - tx.Attributes = ((JArray)json["attributes"]).Select(p => TransactionAttribute.FromJson(p)).ToArray(); - tx.Cosigners = ((JArray)json["cosigners"]).Select(p => Cosigner.FromJson(p)).ToArray(); - tx.Script = Convert.FromBase64String(json["script"].AsString()); - tx.Witnesses = ((JArray)json["witnesses"]).Select(p => Witness.FromJson(p)).ToArray(); - return tx; - } - bool IInventory.Verify(StoreView snapshot) { return Verify(snapshot, BigInteger.Zero) == VerifyResult.Succeed; diff --git a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs index d4c31b2e49..dd592284e6 100644 --- a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -33,15 +33,5 @@ public JObject ToJson() json["data"] = Convert.ToBase64String(Data); return json; } - - public static TransactionAttribute FromJson(JObject json) - { - TransactionAttribute transactionAttribute = new TransactionAttribute(); - transactionAttribute.Usage = (TransactionAttributeUsage)byte.Parse(json["usage"].AsString()); - if (!Enum.IsDefined(typeof(TransactionAttributeUsage), transactionAttribute.Usage)) - throw new ArgumentException(); - transactionAttribute.Data = Convert.FromBase64String(json["data"].AsString()); - return transactionAttribute; - } } } diff --git a/src/neo/Network/P2P/Payloads/Witness.cs b/src/neo/Network/P2P/Payloads/Witness.cs index 55b2c7ae4b..34121a6a6a 100644 --- a/src/neo/Network/P2P/Payloads/Witness.cs +++ b/src/neo/Network/P2P/Payloads/Witness.cs @@ -48,13 +48,5 @@ public JObject ToJson() json["verification"] = Convert.ToBase64String(VerificationScript); return json; } - - public static Witness FromJson(JObject json) - { - Witness witness = new Witness(); - witness.InvocationScript = Convert.FromBase64String(json["invocation"].AsString()); - witness.VerificationScript = Convert.FromBase64String(json["verification"].AsString()); - return witness; - } } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs index e21f468575..50cdfcea25 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.IO.Json; using Neo.Network.P2P.Payloads; namespace Neo.UnitTests.Network.P2P.Payloads @@ -97,11 +96,6 @@ public void Json_Global() var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"Global\"}"; attr.ToJson().ToString().Should().Be(json); - - var copy = Cosigner.FromJson(JObject.Parse(json)); - - Assert.AreEqual(attr.Scopes, copy.Scopes); - Assert.AreEqual(attr.Account, copy.Account); } [TestMethod] @@ -115,11 +109,6 @@ public void Json_CalledByEntry() var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CalledByEntry\"}"; attr.ToJson().ToString().Should().Be(json); - - var copy = Cosigner.FromJson(JObject.Parse(json)); - - Assert.AreEqual(attr.Scopes, copy.Scopes); - Assert.AreEqual(attr.Account, copy.Account); } [TestMethod] @@ -134,12 +123,6 @@ public void Json_CustomContracts() var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CustomContracts\",\"allowedContracts\":[\"0x0000000000000000000000000000000000000000\"]}"; attr.ToJson().ToString().Should().Be(json); - - var copy = Cosigner.FromJson(JObject.Parse(json)); - - Assert.AreEqual(attr.Scopes, copy.Scopes); - CollectionAssert.AreEqual(attr.AllowedContracts, copy.AllowedContracts); - Assert.AreEqual(attr.Account, copy.Account); } [TestMethod] @@ -154,12 +137,6 @@ public void Json_CustomGroups() var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CustomGroups\",\"allowedGroups\":[\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\"]}"; attr.ToJson().ToString().Should().Be(json); - - var copy = Cosigner.FromJson(JObject.Parse(json)); - - Assert.AreEqual(attr.Scopes, copy.Scopes); - CollectionAssert.AreEqual(attr.AllowedGroups, copy.AllowedGroups); - Assert.AreEqual(attr.Account, copy.Account); } } } From dfc3f63ba37c03af78db2d42fec60cb92042b5f0 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 4 May 2020 11:33:26 +0200 Subject: [PATCH 254/305] Unify unhandled exceptions (#1626) * Core handle errors * Unify UnhandledExceptions * Remove empty line * Move to NeoSystem * Update NeoSystem.cs * Allow log objects * Change the source of UnhandledException Co-authored-by: Erik Zhang --- src/neo/NeoSystem.cs | 11 +++++++++++ src/neo/Plugins/ILogPlugin.cs | 2 +- src/neo/Plugins/Plugin.cs | 6 +++--- src/neo/Utility.cs | 4 ++-- tests/neo.UnitTests/Plugins/TestLogPlugin.cs | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index aa18723e01..d666a5d1a0 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -26,6 +26,12 @@ public class NeoSystem : IDisposable private ChannelsConfig start_message = null; private bool suspend = false; + static NeoSystem() + { + // Unify unhandled exceptions + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + } + public NeoSystem(string storageEngine = null) { Plugin.LoadPlugins(this); @@ -39,6 +45,11 @@ public NeoSystem(string storageEngine = null) plugin.OnPluginsLoaded(); } + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + Utility.Log("UnhandledException", LogLevel.Fatal, e.ExceptionObject); + } + public void Dispose() { foreach (var p in Plugin.Plugins) diff --git a/src/neo/Plugins/ILogPlugin.cs b/src/neo/Plugins/ILogPlugin.cs index c0733f9143..0934cc414a 100644 --- a/src/neo/Plugins/ILogPlugin.cs +++ b/src/neo/Plugins/ILogPlugin.cs @@ -2,6 +2,6 @@ namespace Neo.Plugins { public interface ILogPlugin { - void Log(string source, LogLevel level, string message); + void Log(string source, LogLevel level, object message); } } diff --git a/src/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs index b8d775b95c..26c0befe3e 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -109,7 +109,7 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven } catch (Exception ex) { - Utility.Log(nameof(Plugin), LogLevel.Error, $"Failed to resolve assembly or its dependency: {ex.Message}"); + Utility.Log(nameof(Plugin), LogLevel.Error, ex); return null; } } @@ -137,7 +137,7 @@ private static void LoadPlugin(Assembly assembly) } catch (Exception ex) { - Utility.Log(nameof(Plugin), LogLevel.Error, $"Failed to initialize plugin: {ex.Message}"); + Utility.Log(nameof(Plugin), LogLevel.Error, ex); } } } @@ -161,7 +161,7 @@ internal static void LoadPlugins(NeoSystem system) } } - protected void Log(string message, LogLevel level = LogLevel.Info) + protected void Log(object message, LogLevel level = LogLevel.Info) { Utility.Log($"{nameof(Plugin)}:{Name}", level, message); } diff --git a/src/neo/Utility.cs b/src/neo/Utility.cs index c5145ad760..c56629f567 100644 --- a/src/neo/Utility.cs +++ b/src/neo/Utility.cs @@ -13,7 +13,7 @@ internal class Logger : ReceiveActor public Logger() { Receive(_ => Sender.Tell(new LoggerInitialized())); - Receive(e => Log(e.LogSource, (LogLevel)e.LogLevel(), e.Message.ToString())); + Receive(e => Log(e.LogSource, (LogLevel)e.LogLevel(), e.Message)); } } @@ -31,7 +31,7 @@ public static IConfigurationRoot LoadConfig(string config) .Build(); } - public static void Log(string source, LogLevel level, string message) + public static void Log(string source, LogLevel level, object message) { foreach (ILogPlugin plugin in Plugin.Loggers) plugin.Log(source, level, message); diff --git a/tests/neo.UnitTests/Plugins/TestLogPlugin.cs b/tests/neo.UnitTests/Plugins/TestLogPlugin.cs index 215715d236..90d9d2c4df 100644 --- a/tests/neo.UnitTests/Plugins/TestLogPlugin.cs +++ b/tests/neo.UnitTests/Plugins/TestLogPlugin.cs @@ -11,7 +11,7 @@ public class TestLogPlugin : Plugin, ILogPlugin protected override void Configure() { } - void ILogPlugin.Log(string source, LogLevel level, string message) + void ILogPlugin.Log(string source, LogLevel level, object message) { Output = source + "_" + level.ToString() + "_" + message; } From bc1fcd3fc9aa24094ecca43bfd6af79e3100b08b Mon Sep 17 00:00:00 2001 From: belane Date: Wed, 6 May 2020 13:19:59 +0200 Subject: [PATCH 255/305] Make NEP6Wallet.path public (#1627) * wallet-backup * make path public * case * Abstract Path * Improve * Revert UT Co-authored-by: Shargon Co-authored-by: erikzhang --- src/neo/Wallets/NEP6/NEP6Wallet.cs | 9 +++------ src/neo/Wallets/SQLite/UserWallet.cs | 23 ++++++++++------------- src/neo/Wallets/Wallet.cs | 10 ++++++++++ 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/neo/Wallets/NEP6/NEP6Wallet.cs b/src/neo/Wallets/NEP6/NEP6Wallet.cs index 423e079dbf..3a5af6d7af 100644 --- a/src/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/neo/Wallets/NEP6/NEP6Wallet.cs @@ -13,7 +13,6 @@ namespace Neo.Wallets.NEP6 { public class NEP6Wallet : Wallet { - private readonly string path; private string password; private string name; private Version version; @@ -24,9 +23,8 @@ public class NEP6Wallet : Wallet public override string Name => name; public override Version Version => version; - public NEP6Wallet(string path, string name = null) + public NEP6Wallet(string path, string name = null) : base(path) { - this.path = path; if (File.Exists(path)) { JObject wallet = JObject.Parse(File.ReadAllBytes(path)); @@ -42,9 +40,8 @@ public NEP6Wallet(string path, string name = null) } } - public NEP6Wallet(JObject wallet) + internal NEP6Wallet(JObject wallet) : base(null) { - this.path = ""; LoadFromJson(wallet, out Scrypt, out accounts, out extra); } @@ -262,7 +259,7 @@ public void Save() wallet["scrypt"] = Scrypt.ToJson(); wallet["accounts"] = new JArray(accounts.Values.Select(p => p.ToJson())); wallet["extra"] = extra; - File.WriteAllText(path, wallet.ToString()); + File.WriteAllText(Path, wallet.ToString()); } public IDisposable Unlock(string password) diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs index bea3626d34..8d69d941c4 100644 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ b/src/neo/Wallets/SQLite/UserWallet.cs @@ -6,26 +6,25 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; using System.Security; using System.Security.Cryptography; using System.Text; +using static System.IO.Path; namespace Neo.Wallets.SQLite { public class UserWallet : Wallet { private readonly object db_lock = new object(); - private readonly string path; private readonly byte[] iv; private readonly byte[] salt; private readonly byte[] masterKey; private readonly ScryptParameters scrypt; private readonly Dictionary accounts; - public override string Name => Path.GetFileNameWithoutExtension(path); + public override string Name => GetFileNameWithoutExtension(Path); public override Version Version { @@ -46,9 +45,8 @@ public override Version Version /// /// Path /// Password Key - private UserWallet(string path, byte[] passwordKey) + private UserWallet(string path, byte[] passwordKey) : base(path) { - this.path = path; this.salt = LoadStoredData("Salt"); byte[] passwordHash = LoadStoredData("PasswordHash"); if (passwordHash != null && !passwordHash.SequenceEqual(passwordKey.Concat(salt).ToArray().Sha256())) @@ -70,9 +68,8 @@ private UserWallet(string path, byte[] passwordKey) /// Path /// Password Key /// Scrypt initialization value - private UserWallet(string path, byte[] passwordKey, ScryptParameters scrypt) + private UserWallet(string path, byte[] passwordKey, ScryptParameters scrypt) : base(path) { - this.path = path; this.iv = new byte[16]; this.salt = new byte[20]; this.masterKey = new byte[32]; @@ -110,7 +107,7 @@ private void AddAccount(UserWalletAccount account) accounts[account.ScriptHash] = account; } lock (db_lock) - using (WalletDataContext ctx = new WalletDataContext(path)) + using (WalletDataContext ctx = new WalletDataContext(Path)) { if (account.HasKey) { @@ -163,7 +160,7 @@ private void AddAccount(UserWalletAccount account) private void BuildDatabase() { - using (WalletDataContext ctx = new WalletDataContext(path)) + using (WalletDataContext ctx = new WalletDataContext(Path)) { ctx.Database.EnsureDeleted(); ctx.Database.EnsureCreated(); @@ -259,7 +256,7 @@ public override bool DeleteAccount(UInt160 scriptHash) if (account != null) { lock (db_lock) - using (WalletDataContext ctx = new WalletDataContext(path)) + using (WalletDataContext ctx = new WalletDataContext(Path)) { if (account.HasKey) { @@ -303,7 +300,7 @@ public override IEnumerable GetAccounts() private Dictionary LoadAccounts() { - using (WalletDataContext ctx = new WalletDataContext(path)) + using (WalletDataContext ctx = new WalletDataContext(Path)) { string passphrase = Encoding.UTF8.GetString(masterKey); Dictionary accounts = ctx.Addresses.Select(p => p.ScriptHash).AsEnumerable().Select(p => new UserWalletAccount(new UInt160(p))).ToDictionary(p => p.ScriptHash); @@ -320,7 +317,7 @@ public override IEnumerable GetAccounts() private byte[] LoadStoredData(string name) { - using (WalletDataContext ctx = new WalletDataContext(path)) + using (WalletDataContext ctx = new WalletDataContext(Path)) { return ctx.Keys.FirstOrDefault(p => p.Name == name)?.Value; } @@ -339,7 +336,7 @@ public static UserWallet Open(string path, SecureString password) private void SaveStoredData(string name, byte[] value) { lock (db_lock) - using (WalletDataContext ctx = new WalletDataContext(path)) + using (WalletDataContext ctx = new WalletDataContext(Path)) { SaveStoredData(ctx, name, value); ctx.SaveChanges(); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 56d0076140..adf5dcde7a 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -21,6 +21,7 @@ namespace Neo.Wallets public abstract class Wallet { public abstract string Name { get; } + public string Path { get; } public abstract Version Version { get; } public abstract bool ChangePassword(string oldPassword, string newPassword); @@ -32,6 +33,15 @@ public abstract class Wallet public abstract WalletAccount GetAccount(UInt160 scriptHash); public abstract IEnumerable GetAccounts(); + internal Wallet() + { + } + + protected Wallet(string path) + { + this.Path = path; + } + public WalletAccount CreateAccount() { byte[] privateKey = new byte[32]; From 405082299dcd5a71ad08d15c5e457cdfee8b77ab Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Thu, 7 May 2020 14:52:13 +0800 Subject: [PATCH 256/305] Add a change list to optimize datacache commit (#1619) --- src/neo/IO/Caching/DataCache.cs | 26 +++++++++++-------- .../neo.UnitTests/IO/Caching/UT_DataCache.cs | 17 ------------ .../SmartContract/UT_Syscalls.cs | 8 +++--- tests/neo.UnitTests/UT_DataCache.cs | 18 +++---------- 4 files changed, 22 insertions(+), 47 deletions(-) diff --git a/src/neo/IO/Caching/DataCache.cs b/src/neo/IO/Caching/DataCache.cs index dd0d5221c8..687aae8f71 100644 --- a/src/neo/IO/Caching/DataCache.cs +++ b/src/neo/IO/Caching/DataCache.cs @@ -16,6 +16,7 @@ public class Trackable } private readonly Dictionary dictionary = new Dictionary(); + private readonly HashSet changeSet = new HashSet(); public TValue this[TKey key] { @@ -65,6 +66,7 @@ public void Add(TKey key, TValue value) Item = value, State = trackable == null ? TrackState.Added : TrackState.Changed }; + changeSet.Add(key); } } @@ -96,6 +98,7 @@ public void Commit() { dictionary.Remove(key); } + changeSet.Clear(); } public DataCache CreateSnapshot() @@ -114,9 +117,15 @@ public void Delete(TKey key) if (dictionary.TryGetValue(key, out Trackable trackable)) { if (trackable.State == TrackState.Added) + { dictionary.Remove(key); + changeSet.Remove(key); + } else + { trackable.State = TrackState.Deleted; + changeSet.Add(key); + } } else { @@ -128,21 +137,13 @@ public void Delete(TKey key) Item = item, State = TrackState.Deleted }); + changeSet.Add(key); } } } protected abstract void DeleteInternal(TKey key); - public void DeleteWhere(Func predicate) - { - lock (dictionary) - { - foreach (Trackable trackable in dictionary.Where(p => p.Value.State != TrackState.Deleted && predicate(p.Key, p.Value.Item)).Select(p => p.Value)) - trackable.State = TrackState.Deleted; - } - } - /// /// Find the entries that start with the `key_prefix` /// @@ -204,8 +205,8 @@ public IEnumerable GetChangeSet() { lock (dictionary) { - foreach (Trackable trackable in dictionary.Values.Where(p => p.State != TrackState.None)) - yield return trackable; + foreach (TKey key in changeSet) + yield return dictionary[key]; } } @@ -234,6 +235,7 @@ public TValue GetAndChange(TKey key, Func factory = null) else if (trackable.State == TrackState.None) { trackable.State = TrackState.Changed; + changeSet.Add(key); } } else @@ -254,6 +256,7 @@ public TValue GetAndChange(TKey key, Func factory = null) trackable.State = TrackState.Changed; } dictionary.Add(key, trackable); + changeSet.Add(key); } return trackable.Item; } @@ -282,6 +285,7 @@ public TValue GetOrAdd(TKey key, Func factory) { trackable.Item = factory(); trackable.State = TrackState.Added; + changeSet.Add(key); } else { diff --git a/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs b/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs index cd9752c819..e3cf329d43 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs +++ b/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs @@ -246,23 +246,6 @@ public void TestDelete() myDataCache.InnerDict.ContainsKey(new MyKey("key2")).Should().BeFalse(); } - [TestMethod] - public void TestDeleteWhere() - { - myDataCache.Add(new MyKey("key1"), new MyValue("value1")); - myDataCache.Add(new MyKey("key2"), new MyValue("value2")); - - myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); - myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value4")); - - myDataCache.DeleteWhere((k, v) => k.Key.StartsWith("key")); - myDataCache.Commit(); - myDataCache.TryGet(new MyKey("key1")).Should().BeNull(); - myDataCache.TryGet(new MyKey("key2")).Should().BeNull(); - myDataCache.InnerDict.ContainsKey(new MyKey("key1")).Should().BeFalse(); - myDataCache.InnerDict.ContainsKey(new MyKey("key2")).Should().BeFalse(); - } - [TestMethod] public void TestFind() { diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index c516d1a871..4a6bd90689 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -323,7 +323,7 @@ public void System_Runtime_GasLeft() public void System_Runtime_GetInvocationCounter() { ContractState contractA, contractB, contractC; - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); var contracts = snapshot.Contracts; // Create dummy contracts @@ -339,9 +339,9 @@ public void System_Runtime_GetInvocationCounter() // Init A,B,C contracts // First two drops is for drop method and arguments - contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractA.ScriptHash.ToArray())); - contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractB.ScriptHash.ToArray())); - contracts.DeleteWhere((a, b) => a.ToArray().SequenceEqual(contractC.ScriptHash.ToArray())); + contracts.Delete(contractA.ScriptHash); + contracts.Delete(contractB.ScriptHash); + contracts.Delete(contractC.ScriptHash); contractA.Manifest = TestUtils.CreateDefaultManifest(contractA.ScriptHash, "dummyMain"); contractB.Manifest = TestUtils.CreateDefaultManifest(contractA.ScriptHash, "dummyMain"); contractC.Manifest = TestUtils.CreateDefaultManifest(contractA.ScriptHash, "dummyMain"); diff --git a/tests/neo.UnitTests/UT_DataCache.cs b/tests/neo.UnitTests/UT_DataCache.cs index 4b8ad1de74..22bcdaa26d 100644 --- a/tests/neo.UnitTests/UT_DataCache.cs +++ b/tests/neo.UnitTests/UT_DataCache.cs @@ -18,11 +18,9 @@ public void TestSetup() public void TestCachedFind_Between() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var storages = snapshot.Storages; + var storages = snapshot.Storages.CreateSnapshot(); var cache = new CloneCache(storages); - storages.DeleteWhere((k, v) => k.Id == 0); - storages.Add ( new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }, @@ -53,19 +51,15 @@ public void TestCachedFind_Between() cache.Find(new byte[5]).Select(u => u.Key.Key[1]).ToArray(), new byte[] { 0x01, 0x02, 0x03 } ); - - storages.DeleteWhere((k, v) => k.Id == 0); } [TestMethod] public void TestCachedFind_Last() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var storages = snapshot.Storages; + var storages = snapshot.Storages.CreateSnapshot(); var cache = new CloneCache(storages); - storages.DeleteWhere((k, v) => k.Id == 0); - storages.Add ( new StorageKey() { Key = new byte[] { 0x00, 0x01 }, Id = 0 }, @@ -89,19 +83,15 @@ public void TestCachedFind_Last() CollectionAssert.AreEqual(cache.Find(new byte[5]).Select(u => u.Key.Key[1]).ToArray(), new byte[] { 0x01, 0x02 } ); - - storages.DeleteWhere((k, v) => k.Id == 0); } [TestMethod] public void TestCachedFind_Empty() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var storages = snapshot.Storages; + var storages = snapshot.Storages.CreateSnapshot(); var cache = new CloneCache(storages); - storages.DeleteWhere((k, v) => k.Id == 0); - cache.Add ( new StorageKey() { Key = new byte[] { 0x00, 0x02 }, Id = 0 }, @@ -117,8 +107,6 @@ public void TestCachedFind_Empty() cache.Find(new byte[5]).Select(u => u.Key.Key[1]).ToArray(), new byte[] { 0x02 } ); - - storages.DeleteWhere((k, v) => k.Id == 0); } } } From 7b0adda9e2d9df94b001ed93f7332311dd33836b Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 7 May 2020 15:16:57 +0200 Subject: [PATCH 257/305] Move cosigners to transaction attributes (#1620) * First draft * Remove dictonary * Move json methods * format * Fix UT * Fix Size * clean code * Fix UT * Clean code * Remove const * Remove blank line * Pluralize * Allow multiple attributes of the same type * Update src/neo/Wallets/Wallet.cs * Fix some UT * Fix UT * Fix UT * Update src/neo/Network/P2P/Payloads/CosignerAttribute.cs Co-authored-by: Erik Zhang * Singular * Move Cosigner to TransactionCollection * Optimize empty * Use ReflectionCache * Change json format * Rename * Remove FromJson * Optimize TransactionAttribute.ToJson() * Rename * Refactor * Change the value of TransactionAttributeType.Cosigner * Fix UTs * Fix attribute order * Remove TransactionAttributeCollection * Reorder * Revert some changes * Revert some changes Co-authored-by: Erik Zhang --- src/neo/Ledger/Blockchain.cs | 3 +- src/neo/Network/P2P/Payloads/Cosigner.cs | 20 ++++--- src/neo/Network/P2P/Payloads/Transaction.cs | 30 ++++------ .../P2P/Payloads/TransactionAttribute.cs | 43 +++++++++----- .../P2P/Payloads/TransactionAttributeType.cs | 10 ++++ .../P2P/Payloads/TransactionAttributeUsage.cs | 7 --- .../SmartContract/InteropService.Runtime.cs | 16 ++--- src/neo/Wallets/Wallet.cs | 14 ++--- .../Consensus/UT_ConsensusContext.cs | 3 +- .../Cryptography/UT_Cryptography_Helper.cs | 3 +- .../neo.UnitTests/IO/Caching/UT_RelayCache.cs | 4 +- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 6 +- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 6 +- tests/neo.UnitTests/Ledger/UT_PoolItem.cs | 4 +- .../Ledger/UT_SendersFeeMonitor.cs | 3 +- .../Ledger/UT_TransactionState.cs | 2 +- .../Network/P2P/Payloads/UT_Block.cs | 19 +++--- .../Network/P2P/Payloads/UT_Cosigner.cs | 16 ++--- .../Network/P2P/Payloads/UT_Transaction.cs | 59 ++++++++----------- .../Network/P2P/Payloads/UT_Witness.cs | 3 +- .../UT_ContractParameterContext.cs | 6 +- .../SmartContract/UT_Syscalls.cs | 11 ++-- tests/neo.UnitTests/TestUtils.cs | 6 +- 23 files changed, 138 insertions(+), 156 deletions(-) create mode 100644 src/neo/Network/P2P/Payloads/TransactionAttributeType.cs delete mode 100644 src/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 0a535f9a5b..a0153063d1 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -167,8 +167,7 @@ private static Transaction DeployNativeContracts() Script = script, Sender = (new[] { (byte)OpCode.PUSH1 }).ToScriptHash(), SystemFee = 0, - Attributes = new TransactionAttribute[0], - Cosigners = new Cosigner[0], + Attributes = Array.Empty(), Witnesses = new[] { new Witness diff --git a/src/neo/Network/P2P/Payloads/Cosigner.cs b/src/neo/Network/P2P/Payloads/Cosigner.cs index f75b8fa7eb..7a045967fa 100644 --- a/src/neo/Network/P2P/Payloads/Cosigner.cs +++ b/src/neo/Network/P2P/Payloads/Cosigner.cs @@ -6,28 +6,30 @@ namespace Neo.Network.P2P.Payloads { - public class Cosigner : ISerializable + public class Cosigner : TransactionAttribute { + // This limits maximum number of AllowedContracts or AllowedGroups here + private const int MaxSubitems = 16; + public UInt160 Account; public WitnessScope Scopes; public UInt160[] AllowedContracts; public ECPoint[] AllowedGroups; + public override TransactionAttributeType Type => TransactionAttributeType.Cosigner; + public Cosigner() { this.Scopes = WitnessScope.Global; } - // This limits maximum number of AllowedContracts or AllowedGroups here - private int MaxSubitems = 16; - - public int Size => + public override int Size => base.Size + /*Account*/ UInt160.Length + /*Scopes*/ sizeof(WitnessScope) + /*AllowedContracts*/ (Scopes.HasFlag(WitnessScope.CustomContracts) ? AllowedContracts.GetVarSize() : 0) + /*AllowedGroups*/ (Scopes.HasFlag(WitnessScope.CustomGroups) ? AllowedGroups.GetVarSize() : 0); - void ISerializable.Deserialize(BinaryReader reader) + protected override void DeserializeWithoutType(BinaryReader reader) { Account = reader.ReadSerializable(); Scopes = (WitnessScope)reader.ReadByte(); @@ -39,7 +41,7 @@ void ISerializable.Deserialize(BinaryReader reader) : new ECPoint[0]; } - void ISerializable.Serialize(BinaryWriter writer) + protected override void SerializeWithoutType(BinaryWriter writer) { writer.Write(Account); writer.Write((byte)Scopes); @@ -49,9 +51,9 @@ void ISerializable.Serialize(BinaryWriter writer) writer.Write(AllowedGroups); } - public JObject ToJson() + public override JObject ToJson() { - JObject json = new JObject(); + JObject json = base.ToJson(); json["account"] = Account.ToString(); json["scopes"] = Scopes; if (Scopes.HasFlag(WitnessScope.CustomContracts)) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 45f5ff0b3f..1d8cfe539f 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -24,11 +24,7 @@ public class Transaction : IEquatable, IInventory, IInteroperable /// /// Maximum number of attributes that can be contained within a transaction /// - private const int MaxTransactionAttributes = 16; - /// - /// Maximum number of cosigners that can be contained within a transaction - /// - private const int MaxCosigners = 16; + public const int MaxTransactionAttributes = 16; private byte version; private uint nonce; @@ -37,7 +33,6 @@ public class Transaction : IEquatable, IInventory, IInteroperable private long netfee; private uint validUntilBlock; private TransactionAttribute[] attributes; - private Cosigner[] cosigners; private byte[] script; private Witness[] witnesses; @@ -52,14 +47,11 @@ public class Transaction : IEquatable, IInventory, IInteroperable public TransactionAttribute[] Attributes { get => attributes; - set { attributes = value; _hash = null; _size = 0; } + set { attributes = value; _cosigners = null; _hash = null; _size = 0; } } - public Cosigner[] Cosigners - { - get => cosigners; - set { cosigners = value; _hash = null; _size = 0; } - } + private Cosigner[] _cosigners; + public Cosigner[] Cosigners => _cosigners ??= attributes.OfType().ToArray(); /// /// The NetworkFee for the transaction divided by its Size. @@ -117,10 +109,9 @@ public int Size if (_size == 0) { _size = HeaderSize + - Attributes.GetVarSize() + //Attributes - Cosigners.GetVarSize() + //Cosigners - Script.GetVarSize() + //Script - Witnesses.GetVarSize(); //Witnesses + Attributes.GetVarSize() + // Attributes + Script.GetVarSize() + // Script + Witnesses.GetVarSize(); // Witnesses } return _size; } @@ -176,8 +167,9 @@ public void DeserializeUnsigned(BinaryReader reader) if (NetworkFee < 0) throw new FormatException(); if (SystemFee + NetworkFee < SystemFee) throw new FormatException(); ValidUntilBlock = reader.ReadUInt32(); - Attributes = reader.ReadSerializableArray(MaxTransactionAttributes); - Cosigners = reader.ReadSerializableArray(MaxCosigners); + Attributes = new TransactionAttribute[reader.ReadVarInt(MaxTransactionAttributes)]; + for (int i = 0; i < Attributes.Length; i++) + Attributes[i] = TransactionAttribute.DeserializeFrom(reader); if (Cosigners.Select(u => u.Account).Distinct().Count() != Cosigners.Length) throw new FormatException(); Script = reader.ReadVarBytes(ushort.MaxValue); if (Script.Length == 0) throw new FormatException(); @@ -227,7 +219,6 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.Write(NetworkFee); writer.Write(ValidUntilBlock); writer.Write(Attributes); - writer.Write(Cosigners); writer.WriteVarBytes(Script); } @@ -243,7 +234,6 @@ public JObject ToJson() json["net_fee"] = NetworkFee.ToString(); json["valid_until_block"] = ValidUntilBlock; json["attributes"] = Attributes.Select(p => p.ToJson()).ToArray(); - json["cosigners"] = Cosigners.Select(p => p.ToJson()).ToArray(); json["script"] = Convert.ToBase64String(Script); json["witnesses"] = Witnesses.Select(p => p.ToJson()).ToArray(); return json; diff --git a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs index dd592284e6..344c23f052 100644 --- a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -1,37 +1,48 @@ using Neo.IO; +using Neo.IO.Caching; using Neo.IO.Json; using System; using System.IO; namespace Neo.Network.P2P.Payloads { - public class TransactionAttribute : ISerializable + public abstract class TransactionAttribute : ISerializable { - public TransactionAttributeUsage Usage; - public byte[] Data; + public abstract TransactionAttributeType Type { get; } + public virtual int Size => sizeof(TransactionAttributeType); - public int Size => sizeof(TransactionAttributeUsage) + Data.GetVarSize(); + public void Deserialize(BinaryReader reader) + { + if (reader.ReadByte() != (byte)Type) + throw new FormatException(); + DeserializeWithoutType(reader); + } - void ISerializable.Deserialize(BinaryReader reader) + public static TransactionAttribute DeserializeFrom(BinaryReader reader) { - Usage = (TransactionAttributeUsage)reader.ReadByte(); - if (!Enum.IsDefined(typeof(TransactionAttributeUsage), Usage)) + TransactionAttributeType type = (TransactionAttributeType)reader.ReadByte(); + if (!(ReflectionCache.CreateInstance(type) is TransactionAttribute attribute)) throw new FormatException(); - Data = reader.ReadVarBytes(252); + attribute.DeserializeWithoutType(reader); + return attribute; } - void ISerializable.Serialize(BinaryWriter writer) + protected abstract void DeserializeWithoutType(BinaryReader reader); + + public virtual JObject ToJson() { - writer.Write((byte)Usage); - writer.WriteVarBytes(Data); + return new JObject + { + ["type"] = Type + }; } - public JObject ToJson() + public void Serialize(BinaryWriter writer) { - JObject json = new JObject(); - json["usage"] = Usage; - json["data"] = Convert.ToBase64String(Data); - return json; + writer.Write((byte)Type); + SerializeWithoutType(writer); } + + protected abstract void SerializeWithoutType(BinaryWriter writer); } } diff --git a/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs b/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs new file mode 100644 index 0000000000..f1e2d704da --- /dev/null +++ b/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs @@ -0,0 +1,10 @@ +using Neo.IO.Caching; + +namespace Neo.Network.P2P.Payloads +{ + public enum TransactionAttributeType : byte + { + [ReflectionCache(typeof(Cosigner))] + Cosigner = 0x01 + } +} diff --git a/src/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs b/src/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs deleted file mode 100644 index 9bf5cc204c..0000000000 --- a/src/neo/Network/P2P/Payloads/TransactionAttributeUsage.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Neo.Network.P2P.Payloads -{ - public enum TransactionAttributeUsage : byte - { - Url = 0x81 - } -} diff --git a/src/neo/SmartContract/InteropService.Runtime.cs b/src/neo/SmartContract/InteropService.Runtime.cs index 9942555cba..ff58e0a897 100644 --- a/src/neo/SmartContract/InteropService.Runtime.cs +++ b/src/neo/SmartContract/InteropService.Runtime.cs @@ -81,24 +81,24 @@ internal static bool CheckWitnessInternal(ApplicationEngine engine, UInt160 hash { if (engine.ScriptContainer is Transaction tx) { - Cosigner usage = tx.Cosigners.FirstOrDefault(p => p.Account.Equals(hash)); - if (usage is null) return false; - if (usage.Scopes == WitnessScope.Global) return true; - if (usage.Scopes.HasFlag(WitnessScope.CalledByEntry)) + Cosigner cosigner = tx.Cosigners.FirstOrDefault(p => p.Account.Equals(hash)); + if (cosigner is null) return false; + if (cosigner.Scopes == WitnessScope.Global) return true; + if (cosigner.Scopes.HasFlag(WitnessScope.CalledByEntry)) { if (engine.CallingScriptHash == engine.EntryScriptHash) return true; } - if (usage.Scopes.HasFlag(WitnessScope.CustomContracts)) + if (cosigner.Scopes.HasFlag(WitnessScope.CustomContracts)) { - if (usage.AllowedContracts.Contains(engine.CurrentScriptHash)) + if (cosigner.AllowedContracts.Contains(engine.CurrentScriptHash)) return true; } - if (usage.Scopes.HasFlag(WitnessScope.CustomGroups)) + if (cosigner.Scopes.HasFlag(WitnessScope.CustomGroups)) { var contract = engine.Snapshot.Contracts[engine.CallingScriptHash]; // check if current group is the required one - if (contract.Manifest.Groups.Select(p => p.PubKey).Intersect(usage.AllowedGroups).Any()) + if (contract.Manifest.Groups.Select(p => p.PubKey).Intersect(cosigner.AllowedGroups).Any()) return true; } return false; diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index adf5dcde7a..b6665aadf9 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -285,11 +285,11 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null Account = new UInt160(p.ToArray()) }).ToArray(); - return MakeTransaction(snapshot, script, new TransactionAttribute[0], cosigners, balances_gas); + return MakeTransaction(snapshot, script, cosigners, balances_gas); } } - public Transaction MakeTransaction(byte[] script, UInt160 sender = null, TransactionAttribute[] attributes = null, Cosigner[] cosigners = null) + public Transaction MakeTransaction(byte[] script, UInt160 sender = null, TransactionAttribute[] attributes = null) { UInt160[] accounts; if (sender is null) @@ -305,11 +305,11 @@ public Transaction MakeTransaction(byte[] script, UInt160 sender = null, Transac using (SnapshotView 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, attributes ?? new TransactionAttribute[0], cosigners ?? new Cosigner[0], balances_gas); + return MakeTransaction(snapshot, script, attributes ?? new TransactionAttribute[0], balances_gas); } } - private Transaction MakeTransaction(StoreView snapshot, byte[] script, TransactionAttribute[] attributes, Cosigner[] cosigners, List<(UInt160 Account, BigInteger Value)> balances_gas) + private Transaction MakeTransaction(StoreView snapshot, byte[] script, TransactionAttribute[] attributes, List<(UInt160 Account, BigInteger Value)> balances_gas) { Random rand = new Random(); foreach (var (account, value) in balances_gas) @@ -322,8 +322,8 @@ private Transaction MakeTransaction(StoreView snapshot, byte[] script, Transacti Sender = account, ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement, Attributes = attributes, - Cosigners = cosigners }; + // will try to execute 'transfer' script to check if it works using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.Clone(), tx, testMode: true)) { @@ -334,8 +334,8 @@ private Transaction MakeTransaction(StoreView snapshot, byte[] script, Transacti UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); - // base size for transaction: includes const_header + attributes + cosigners with scopes + script + hashes - int size = Transaction.HeaderSize + attributes.GetVarSize() + cosigners.GetVarSize() + script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); + // base size for transaction: includes const_header + attributes + script + hashes + int size = Transaction.HeaderSize + tx.Attributes.GetVarSize() + script.GetVarSize() + IO.Helper.GetVarSize(hashes.Length); foreach (UInt160 hash in hashes) { diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs index 469c28d3ba..5fab8d80d2 100644 --- a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs +++ b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs @@ -98,8 +98,7 @@ private Transaction CreateTransactionWithSize(int v) var r = new Random(); var tx = new Transaction() { - Cosigners = new Cosigner[0], - Attributes = new TransactionAttribute[0], + Attributes = Array.Empty(), NetworkFee = 0, Nonce = (uint)Environment.TickCount, Script = new byte[0], diff --git a/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index b44d0a74d1..0e17d0913c 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -145,8 +145,7 @@ public void TestTest() Script = TestUtils.GetByteArray(32, 0x42), Sender = UInt160.Zero, SystemFee = 4200000000, - Attributes = new TransactionAttribute[0], - Cosigners = new Cosigner[0], + Attributes = Array.Empty(), Witnesses = new[] { new Witness diff --git a/tests/neo.UnitTests/IO/Caching/UT_RelayCache.cs b/tests/neo.UnitTests/IO/Caching/UT_RelayCache.cs index 4608dd0e14..d442a018e9 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_RelayCache.cs +++ b/tests/neo.UnitTests/IO/Caching/UT_RelayCache.cs @@ -2,6 +2,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Caching; using Neo.Network.P2P.Payloads; +using System; namespace Neo.UnitTests.IO.Caching { @@ -27,8 +28,7 @@ public void TestGetKeyForItem() SystemFee = 0, NetworkFee = 0, ValidUntilBlock = 100, - Cosigners = new Cosigner[0], - Attributes = new TransactionAttribute[0], + Attributes = Array.Empty(), Script = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04 }, Witnesses = new Witness[0] }; diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index 681e36489f..6faf336290 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -70,13 +70,13 @@ public void TestContainsTransaction() [TestMethod] public void TestGetCurrentBlockHash() { - Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0xc9387b803c8b4c6c1f69f6c876ed7848482c414b0225eb2a3a5395af39425455")); + Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0x557f5c9d0c865a211a749899681e5b4fbf745b3bcf0c395e6d6a7f1edb0d86f1")); } [TestMethod] public void TestGetCurrentHeaderHash() { - Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0xc9387b803c8b4c6c1f69f6c876ed7848482c414b0225eb2a3a5395af39425455")); + Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0x557f5c9d0c865a211a749899681e5b4fbf745b3bcf0c395e6d6a7f1edb0d86f1")); } [TestMethod] @@ -88,7 +88,7 @@ public void TestGetBlock() [TestMethod] public void TestGetBlockHash() { - Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0xc9387b803c8b4c6c1f69f6c876ed7848482c414b0225eb2a3a5395af39425455")); + Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0x557f5c9d0c865a211a749899681e5b4fbf745b3bcf0c395e6d6a7f1edb0d86f1")); Blockchain.Singleton.GetBlockHash(10).Should().BeNull(); } diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 08d18ce58f..4e96605b7b 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -79,8 +79,7 @@ private Transaction CreateTransactionWithFee(long fee) mock.Object.Script = randomBytes; mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = fee; - mock.Object.Attributes = new TransactionAttribute[0]; - mock.Object.Cosigners = new Cosigner[0]; + mock.Object.Attributes = Array.Empty(); mock.Object.Witnesses = new[] { new Witness @@ -104,8 +103,7 @@ private Transaction CreateTransactionWithFeeAndBalanceVerify(long fee) mock.Object.Script = randomBytes; mock.Object.Sender = sender; mock.Object.NetworkFee = fee; - mock.Object.Attributes = new TransactionAttribute[0]; - mock.Object.Cosigners = new Cosigner[0]; + mock.Object.Attributes = Array.Empty(); mock.Object.Witnesses = new[] { new Witness diff --git a/tests/neo.UnitTests/Ledger/UT_PoolItem.cs b/tests/neo.UnitTests/Ledger/UT_PoolItem.cs index 0d37e6edfd..a3c55712b8 100644 --- a/tests/neo.UnitTests/Ledger/UT_PoolItem.cs +++ b/tests/neo.UnitTests/Ledger/UT_PoolItem.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using Neo.Ledger; -using Neo.IO; using Neo.Network.P2P.Payloads; using System; @@ -121,8 +120,7 @@ public static Transaction GenerateTx(long networkFee, int size, byte[] overrideS Script = overrideScriptBytes ?? new byte[0], Sender = UInt160.Zero, NetworkFee = networkFee, - Attributes = new TransactionAttribute[0], - Cosigners = new Cosigner[0], + Attributes = Array.Empty(), Witnesses = new[] { new Witness diff --git a/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs b/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs index 2b0d7a349b..cd8a5af463 100644 --- a/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs +++ b/tests/neo.UnitTests/Ledger/UT_SendersFeeMonitor.cs @@ -24,8 +24,7 @@ private Transaction CreateTransactionWithFee(long networkFee, long systemFee) mock.Object.Sender = UInt160.Zero; mock.Object.NetworkFee = networkFee; mock.Object.SystemFee = systemFee; - mock.Object.Attributes = new TransactionAttribute[0]; - mock.Object.Cosigners = new Cosigner[0]; + mock.Object.Attributes = Array.Empty(); mock.Object.Witnesses = new[] { new Witness diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionState.cs b/tests/neo.UnitTests/Ledger/UT_TransactionState.cs index 11ef772556..9446b954d2 100644 --- a/tests/neo.UnitTests/Ledger/UT_TransactionState.cs +++ b/tests/neo.UnitTests/Ledger/UT_TransactionState.cs @@ -61,7 +61,7 @@ public void TestDeserialize() [TestMethod] public void TestGetSize() { - ((ISerializable)origin).Size.Should().Be(62); + ((ISerializable)origin).Size.Should().Be(61); } } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index fb9bc84a5d..0e36930f0b 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -59,7 +59,7 @@ public void Size_Get_1_Transaction() TestUtils.GetTransaction() }; - uut.Size.Should().Be(166); + uut.Size.Should().Be(165); } [TestMethod] @@ -75,7 +75,7 @@ public void Size_Get_3_Transaction() TestUtils.GetTransaction() }; - uut.Size.Should().Be(270); + uut.Size.Should().Be(267); } [TestMethod] @@ -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 = "000000000000000000000000000000000000000000000000000000000000000000000000c2f713d5d23bee9da2a298ef1ab6a7a454e51e813265d71c85f68ecafc8f774de913ff854c000000000000000000000000000000000000000000000000000000010001110200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; + var hex = "000000000000000000000000000000000000000000000000000000000000000000000000bc72014eb4f1fcdd27831b79c42ffa71e1b949086a97c87654a644585dd616f6e913ff854c0000000000000000000000000000000000000000000000000000000100011102000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; 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 = "000000000000000000000000000000000000000000000000000000000000000000000000c2f713d5d23bee9da2a298ef1ab6a7a454e51e813265d71c85f68ecafc8f774de913ff854c000000000000000000000000000000000000000000000000000000010001110200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; + var hex = "000000000000000000000000000000000000000000000000000000000000000000000000bc72014eb4f1fcdd27831b79c42ffa71e1b949086a97c87654a644585dd616f6e913ff854c0000000000000000000000000000000000000000000000000000000100011102000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100010000"; using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) using (BinaryReader reader = new BinaryReader(ms)) @@ -199,11 +199,11 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0xa8bb713f8a2a1ed7e625e440d954dacdd504be1ff39c133c93bad77f150ffeb3"); - jObj["size"].AsNumber().Should().Be(166); + jObj["hash"].AsString().Should().Be("0xac84cebc5825cbe78941b301789bc43e8906bb9d86edd80cc94591088a26d9cc"); + jObj["size"].AsNumber().Should().Be(165); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - jObj["merkleroot"].AsString().Should().Be("0x4d778ffcca8ef6851cd76532811ee554a4a7b61aef98a2a29dee3bd2d513f7c2"); + jObj["merkleroot"].AsString().Should().Be("0xf616d65d5844a65476c8976a0849b9e171fa2fc4791b8327ddfcf1b44e0172bc"); jObj["time"].AsNumber().Should().Be(328665601001); jObj["index"].AsNumber().Should().Be(0); jObj["nextconsensus"].AsString().Should().Be("NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf"); @@ -214,11 +214,10 @@ public void ToJson() jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; - txObj[0]["hash"].AsString().Should().Be("0x606d39d98d1732fc1c9931f194aba8b2e40a5db5a26fdf4b513d4a6e0d69a8c8"); - txObj[0]["size"].AsNumber().Should().Be(52); + txObj[0]["hash"].AsString().Should().Be("0x5f9b7409b6cf21fb0bf63c3890f62cccfe5fb9c3277ea33935e0a09f4255407c"); + txObj[0]["size"].AsNumber().Should().Be(51); txObj[0]["version"].AsNumber().Should().Be(0); ((JArray)txObj[0]["attributes"]).Count.Should().Be(0); - ((JArray)txObj[0]["cosigners"]).Count.Should().Be(0); txObj[0]["net_fee"].AsString().Should().Be("0"); } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs index 50cdfcea25..87ea4c8c26 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Cosigner.cs @@ -18,7 +18,7 @@ public void Serialize_Deserialize_Global() Account = UInt160.Zero }; - var hex = "000000000000000000000000000000000000000000"; + var hex = "01000000000000000000000000000000000000000000"; attr.ToArray().ToHexString().Should().Be(hex); var copy = hex.HexToBytes().AsSerializable(); @@ -36,7 +36,7 @@ public void Serialize_Deserialize_CalledByEntry() Account = UInt160.Zero }; - var hex = "000000000000000000000000000000000000000001"; + var hex = "01000000000000000000000000000000000000000001"; attr.ToArray().ToHexString().Should().Be(hex); var copy = hex.HexToBytes().AsSerializable(); @@ -55,7 +55,7 @@ public void Serialize_Deserialize_CustomContracts() Account = UInt160.Zero }; - var hex = "000000000000000000000000000000000000000010010000000000000000000000000000000000000000"; + var hex = "01000000000000000000000000000000000000000010010000000000000000000000000000000000000000"; attr.ToArray().ToHexString().Should().Be(hex); var copy = hex.HexToBytes().AsSerializable(); @@ -75,7 +75,7 @@ public void Serialize_Deserialize_CustomGroups() Account = UInt160.Zero }; - var hex = "0000000000000000000000000000000000000000200103b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"; + var hex = "010000000000000000000000000000000000000000200103b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"; attr.ToArray().ToHexString().Should().Be(hex); var copy = hex.HexToBytes().AsSerializable(); @@ -94,7 +94,7 @@ public void Json_Global() Account = UInt160.Zero }; - var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"Global\"}"; + var json = "{\"type\":\"Cosigner\",\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"Global\"}"; attr.ToJson().ToString().Should().Be(json); } @@ -107,7 +107,7 @@ public void Json_CalledByEntry() Account = UInt160.Zero }; - var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CalledByEntry\"}"; + var json = "{\"type\":\"Cosigner\",\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CalledByEntry\"}"; attr.ToJson().ToString().Should().Be(json); } @@ -121,7 +121,7 @@ public void Json_CustomContracts() Account = UInt160.Zero }; - var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CustomContracts\",\"allowedContracts\":[\"0x0000000000000000000000000000000000000000\"]}"; + var json = "{\"type\":\"Cosigner\",\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CustomContracts\",\"allowedContracts\":[\"0x0000000000000000000000000000000000000000\"]}"; attr.ToJson().ToString().Should().Be(json); } @@ -135,7 +135,7 @@ public void Json_CustomGroups() Account = UInt160.Zero }; - var json = "{\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CustomGroups\",\"allowedGroups\":[\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\"]}"; + var json = "{\"type\":\"Cosigner\",\"account\":\"0x0000000000000000000000000000000000000000\",\"scopes\":\"CustomGroups\",\"allowedGroups\":[\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\"]}"; attr.ToJson().ToString().Should().Be(json); } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 4fc2f9cc4a..4267f8931e 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -64,8 +64,7 @@ public void Size_Get() { uut.Script = TestUtils.GetByteArray(32, 0x42); uut.Sender = UInt160.Zero; - uut.Attributes = new TransactionAttribute[0]; - uut.Cosigners = new Cosigner[0]; + uut.Attributes = Array.Empty(); uut.Witnesses = new[] { new Witness @@ -78,7 +77,7 @@ public void Size_Get() uut.Version.Should().Be(0); uut.Script.Length.Should().Be(32); uut.Script.GetVarSize().Should().Be(33); - uut.Size.Should().Be(83); + uut.Size.Should().Be(82); } [TestMethod] @@ -249,12 +248,12 @@ public void FeeIsSignatureContractDetailed() // Part I Assert.AreEqual(45, Transaction.HeaderSize); // Part II - Assert.AreEqual(1, tx.Attributes.GetVarSize()); - Assert.AreEqual(0, tx.Attributes.Length); + Assert.AreEqual(23, tx.Attributes.GetVarSize()); + Assert.AreEqual(1, tx.Attributes.Length); Assert.AreEqual(1, tx.Cosigners.Length); - Assert.AreEqual(22, tx.Cosigners.GetVarSize()); + Assert.AreEqual(23, tx.Cosigners.GetVarSize()); // Note that Data size and Usage size are different (because of first byte on GetVarSize()) - Assert.AreEqual(21, tx.Cosigners[0].Size); + Assert.AreEqual(22, tx.Cosigners[0].Size); // Part III Assert.AreEqual(86, tx.Script.GetVarSize()); // Part IV @@ -316,7 +315,7 @@ public void FeeIsSignatureContract_TestScope_Global() // using this... - var tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners); + var tx = wallet.MakeTransaction(script, acc.ScriptHash, cosigners); Assert.IsNotNull(tx); Assert.IsNull(tx.Witnesses); @@ -403,7 +402,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() // using this... - var tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners); + var tx = wallet.MakeTransaction(script, acc.ScriptHash, cosigners); Assert.IsNotNull(tx); Assert.IsNull(tx.Witnesses); @@ -493,7 +492,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() // using this... - var tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners); + var tx = wallet.MakeTransaction(script, acc.ScriptHash, cosigners); Assert.IsNotNull(tx); Assert.IsNull(tx.Witnesses); @@ -581,8 +580,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() // expects FAULT on execution of 'transfer' Application script // due to lack of a valid witness validation Transaction tx = null; - Assert.ThrowsException(() => - tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners)); + Assert.ThrowsException(() => tx = wallet.MakeTransaction(script, acc.ScriptHash, cosigners)); Assert.IsNull(tx); } } @@ -631,7 +629,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() // using this... - var tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners); + var tx = wallet.MakeTransaction(script, acc.ScriptHash, cosigners); Assert.IsNotNull(tx); Assert.IsNull(tx.Witnesses); @@ -649,7 +647,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() // only a single witness should exist tx.Witnesses.Length.Should().Be(1); // no attributes must exist - tx.Attributes.Length.Should().Be(0); + tx.Attributes.Length.Should().Be(1); // one cosigner must exist tx.Cosigners.Length.Should().Be(1); @@ -713,14 +711,13 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() // trying with no scope var attributes = new TransactionAttribute[] { }; - var cosigners = new Cosigner[] { }; // using this... // expects FAULT on execution of 'transfer' Application script // due to lack of a valid witness validation Transaction tx = null; - Assert.ThrowsException(() => tx = wallet.MakeTransaction(script, acc.ScriptHash, attributes, cosigners)); + Assert.ThrowsException(() => tx = wallet.MakeTransaction(script, acc.ScriptHash, attributes)); Assert.IsNull(tx); } } @@ -737,8 +734,8 @@ public void Transaction_Reverify_Hashes_Length_Unequal_To_Witnesses_Length() SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS NetworkFee = 0x0000000000000001, ValidUntilBlock = 0x01020304, - Attributes = new TransactionAttribute[0] { }, - Cosigners = new Cosigner[] { + Attributes = new[] + { new Cosigner { Account = UInt160.Parse("0x0001020304050607080900010203040506070809"), @@ -765,8 +762,7 @@ public void Transaction_Serialize_Deserialize_Simple() SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS NetworkFee = 0x0000000000000001, ValidUntilBlock = 0x01020304, - Attributes = new TransactionAttribute[0] { }, - Cosigners = new Cosigner[0] { }, + Attributes = Array.Empty(), Script = new byte[] { (byte)OpCode.PUSH1 }, Witnesses = new Witness[0] { } }; @@ -781,7 +777,6 @@ public void Transaction_Serialize_Deserialize_Simple() "0100000000000000" + // network fee (1 satoshi) "04030201" + // timelimit "00" + // no attributes - "00" + // no cosigners "0111" + // push1 script "00"); // no witnesses @@ -813,8 +808,8 @@ public void Transaction_Serialize_Deserialize_DistinctCosigners() SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS NetworkFee = 0x0000000000000001, ValidUntilBlock = 0x01020304, - Attributes = new TransactionAttribute[0] { }, - Cosigners = new Cosigner[] { + Attributes = new[] + { new Cosigner { Account = UInt160.Parse("0x0001020304050607080900010203040506070809"), @@ -833,7 +828,7 @@ public void Transaction_Serialize_Deserialize_DistinctCosigners() byte[] sTx = txDoubleCosigners.ToArray(); // no need for detailed hexstring here (see basic tests for it) - sTx.ToHexString().Should().Be("0004030201000000000000000000000000000000000000000000e1f505000000000100000000000000040302010002090807060504030201000908070605040302010000090807060504030201000908070605040302010001011100"); + sTx.ToHexString().Should().Be("0004030201000000000000000000000000000000000000000000e1f50500000000010000000000000004030201020109080706050403020100090807060504030201000001090807060504030201000908070605040302010001011100"); // back to transaction (should fail, due to non-distinct cosigners) Transaction tx2 = null; @@ -874,8 +869,7 @@ public void Transaction_Serialize_Deserialize_MaxSizeCosigners() SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS NetworkFee = 0x0000000000000001, ValidUntilBlock = 0x01020304, - Attributes = new TransactionAttribute[0] { }, - Cosigners = cosigners1, // max + 1 (should fail) + Attributes = cosigners1, // max + 1 (should fail) Script = new byte[] { (byte)OpCode.PUSH1 }, Witnesses = new Witness[0] { } }; @@ -909,8 +903,7 @@ public void Transaction_Serialize_Deserialize_MaxSizeCosigners() SystemFee = (long)BigInteger.Pow(10, 8), // 1 GAS NetworkFee = 0x0000000000000001, ValidUntilBlock = 0x01020304, - Attributes = new TransactionAttribute[0] { }, - Cosigners = cosigners, // max + 1 (should fail) + Attributes = cosigners, // max + 1 (should fail) Script = new byte[] { (byte)OpCode.PUSH1 }, Witnesses = new Witness[0] { } }; @@ -972,7 +965,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() // using this... - var tx = wallet.MakeTransaction(script, acc.ScriptHash, new TransactionAttribute[0], cosigners); + var tx = wallet.MakeTransaction(script, acc.ScriptHash, cosigners); Assert.IsNotNull(tx); Assert.IsNull(tx.Witnesses); @@ -1021,8 +1014,7 @@ public void ToJson() uut.Script = TestUtils.GetByteArray(32, 0x42); uut.Sender = UInt160.Zero; uut.SystemFee = 4200000000; - uut.Attributes = new TransactionAttribute[] { }; - uut.Cosigners = new Cosigner[] { }; + uut.Attributes = Array.Empty(); uut.Witnesses = new[] { new Witness @@ -1034,11 +1026,10 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0x8b86429eb984728752552ee8d69536d36ab985bbe383c6a6eeb2100f6f29b81b"); - jObj["size"].AsNumber().Should().Be(83); + jObj["hash"].AsString().Should().Be("0xfe08a23db645733a95914622ead5e738b03918680e927e00928116395e571758"); + jObj["size"].AsNumber().Should().Be(82); jObj["version"].AsNumber().Should().Be(0); ((JArray)jObj["attributes"]).Count.Should().Be(0); - ((JArray)jObj["cosigners"]).Count.Should().Be(0); jObj["net_fee"].AsString().Should().Be("0"); jObj["script"].AsString().Should().Be("QiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA="); jObj["sys_fee"].AsString().Should().Be("4200000000"); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index aebda4d9da..a6cc76682f 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -54,9 +54,8 @@ private Witness PrepareDummyWitness(int maxAccounts) var data = new ContractParametersContext(new Transaction() { - Cosigners = new Cosigner[0], Sender = multiSignContract.ScriptHash, - Attributes = new TransactionAttribute[0], + Attributes = Array.Empty(), NetworkFee = 0, Nonce = 0, Script = new byte[0], diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index bfb0a53391..aaf6511997 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -47,14 +47,14 @@ 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"":""AAAAAABmUJDLobcPtqo9vZKIdjXsd8fVGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA=="",""items"":{}}"); + str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""hex"":""AAAAAABmUJDLobcPtqo9vZKIdjXsd8fVGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA"",""items"":{}}"); } [TestMethod] public void TestParse() { - var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAADyd\\/EUQDWkOJf5\\u002BhFSWOrAFa3KvgAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA==\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}]}}}"); - ret.ScriptHashes[0].ToString().Should().Be("0xbecaad15c0ea585211faf99738a4354014f177f2"); + var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAABmUJDLobcPtqo9vZKIdjXsd8fVGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA\",\"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()); } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 4a6bd90689..0fc198f3d5 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -3,11 +3,10 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; -using Neo.SmartContract.Manifest; using Neo.VM; using Neo.VM.Types; -using System.Collections.Generic; using System.Linq; +using Array = System.Array; namespace Neo.UnitTests.SmartContract { @@ -26,8 +25,7 @@ public void System_Blockchain_GetBlock() var tx = new Transaction() { Script = new byte[] { 0x01 }, - Attributes = new TransactionAttribute[0], - Cosigners = new Cosigner[0], + Attributes = Array.Empty(), NetworkFee = 0x02, SystemFee = 0x03, Nonce = 0x04, @@ -245,8 +243,7 @@ public void System_ExecutionEngine_GetScriptContainer() var tx = new Transaction() { Script = new byte[] { 0x01 }, - Attributes = new TransactionAttribute[0], - Cosigners = new Cosigner[0], + Attributes = Array.Empty(), NetworkFee = 0x02, SystemFee = 0x03, Nonce = 0x04, @@ -263,7 +260,7 @@ public void System_ExecutionEngine_GetScriptContainer() Assert.AreEqual(1, engine.ResultStack.Count); Assert.IsInstanceOfType(engine.ResultStack.Peek(), typeof(ByteString)); Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(), - @"5b226770564846625133316969517a614f4c7a33523546394d6256715932596b7a5164324461785536677154303d222c362c342c222f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f383d222c332c322c352c2241513d3d225d"); + @"5b224435724a376f755c753030324256574845456c5c75303032426e74486b414a424f614c4a6737496776303356337a4953646d6750413d222c362c342c222f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f2f383d222c332c322c352c2241513d3d225d"); Assert.AreEqual(0, engine.ResultStack.Count); } } diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index 0338ff32c1..fd8e6fd620 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -73,8 +73,7 @@ public static Transaction GetTransaction() { Script = new byte[1], Sender = UInt160.Zero, - Attributes = new TransactionAttribute[0], - Cosigners = new Cosigner[0], + Attributes = Array.Empty(), Witnesses = new Witness[]{ new Witness { InvocationScript = new byte[0], @@ -170,8 +169,7 @@ public static Transaction CreateRandomHashTransaction() { Script = randomBytes, Sender = UInt160.Zero, - Attributes = new TransactionAttribute[0], - Cosigners = new Cosigner[0], + Attributes = Array.Empty(), Witnesses = new[] { new Witness From 033c7d52dc2f54e32eaccbbeaf3a75ea81eb7135 Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Fri, 8 May 2020 16:13:06 +0800 Subject: [PATCH 258/305] Fix VerifyWitness (#1628) --- src/neo/SmartContract/Helper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 33c6b260cf..58eecf3d03 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -167,6 +167,7 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snap engine.LoadScript(verifiable.Witnesses[i].InvocationScript, CallFlags.None); if (engine.Execute() == VMState.FAULT) return false; if (!engine.ResultStack.TryPop(out StackItem result) || !result.ToBoolean()) return false; + gas -= engine.GasConsumed; } } return true; From 151e1b035b17adc5d07e95e6c4b9a83684c02026 Mon Sep 17 00:00:00 2001 From: ShawnYun <42930111+ShawnYun@users.noreply.github.com> Date: Tue, 12 May 2020 16:03:53 +0800 Subject: [PATCH 259/305] Modify iterators to public (#1639) * modify iterators * fix * fix * fix --- src/neo/SmartContract/Enumerators/IEnumerator.cs | 2 +- src/neo/SmartContract/Iterators/IIterator.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Enumerators/IEnumerator.cs b/src/neo/SmartContract/Enumerators/IEnumerator.cs index 948bad9371..77da74bd43 100644 --- a/src/neo/SmartContract/Enumerators/IEnumerator.cs +++ b/src/neo/SmartContract/Enumerators/IEnumerator.cs @@ -3,7 +3,7 @@ namespace Neo.SmartContract.Enumerators { - internal interface IEnumerator : IDisposable + public interface IEnumerator : IDisposable { bool Next(); StackItem Value(); diff --git a/src/neo/SmartContract/Iterators/IIterator.cs b/src/neo/SmartContract/Iterators/IIterator.cs index 66b3ef3bc6..c5c866891a 100644 --- a/src/neo/SmartContract/Iterators/IIterator.cs +++ b/src/neo/SmartContract/Iterators/IIterator.cs @@ -3,7 +3,7 @@ namespace Neo.SmartContract.Iterators { - internal interface IIterator : IEnumerator + public interface IIterator : IEnumerator { PrimitiveType Key(); } From ff8c47736f173ba5c2bd68ba5d276719055c6b0c Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 13 May 2020 17:57:06 +0200 Subject: [PATCH 260/305] Revert "Improve gap calculation" This reverts commit 9fa04a45b930dd60f655e98fd77ea124eec18741. --- src/neo/Consensus/ConsensusService.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 788719ae14..b51d9acf7d 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -307,7 +307,9 @@ private void OnConsensusPayload(ConsensusPayload payload) private void OnPersistCompleted(Block block) { Log($"persist block: height={block.Index} hash={block.Hash} tx={block.Transactions.Length}"); - block_received_time = TimeProvider.Current.UtcNow; + DateTime now = TimeProvider.Current.UtcNow; + block_received_gap = now - block_received_time; + block_received_time = now; knownHashes.Clear(); InitializeConsensus(0); } From e78861f73348382f091ef9dfb8aba2ccd2926637 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 13 May 2020 17:57:06 +0200 Subject: [PATCH 261/305] Revert "Revert "Improve gap calculation"" This reverts commit e92073298a90f741faf8ba46923842c4b8be6bd4. --- src/neo/Consensus/ConsensusService.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index b51d9acf7d..788719ae14 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -307,9 +307,7 @@ private void OnConsensusPayload(ConsensusPayload payload) private void OnPersistCompleted(Block block) { Log($"persist block: height={block.Index} hash={block.Hash} tx={block.Transactions.Length}"); - DateTime now = TimeProvider.Current.UtcNow; - block_received_gap = now - block_received_time; - block_received_time = now; + block_received_time = TimeProvider.Current.UtcNow; knownHashes.Clear(); InitializeConsensus(0); } From c21ec37dbd95b78eefa35f8c9a6bd0eceab5cd29 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 15 May 2020 08:58:15 +0200 Subject: [PATCH 262/305] Use config from current working dir (#1622) * Use config from current dir * Search in multiple places --- src/neo/Utility.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/neo/Utility.cs b/src/neo/Utility.cs index c56629f567..61b6c6a71e 100644 --- a/src/neo/Utility.cs +++ b/src/neo/Utility.cs @@ -3,6 +3,8 @@ using Microsoft.Extensions.Configuration; using Neo.Plugins; using System; +using System.IO; +using System.Reflection; namespace Neo { @@ -26,8 +28,27 @@ public static IConfigurationRoot LoadConfig(string config) { var env = Environment.GetEnvironmentVariable("NEO_NETWORK"); var configFile = string.IsNullOrWhiteSpace(env) ? $"{config}.json" : $"{config}.{env}.json"; + + // Working directory + var file = Path.Combine(Environment.CurrentDirectory, configFile); + if (!File.Exists(file)) + { + // EntryPoint folder + file = Path.Combine(Assembly.GetEntryAssembly().Location, configFile); + if (!File.Exists(file)) + { + // neo.dll folder + file = Path.Combine(Assembly.GetExecutingAssembly().Location, configFile); + if (!File.Exists(file)) + { + // default config + return new ConfigurationBuilder().Build(); + } + } + } + return new ConfigurationBuilder() - .AddJsonFile(configFile, true) + .AddJsonFile(file, true) .Build(); } From 758a4d7214590ee92bb31e2d8c883e0b13342eea Mon Sep 17 00:00:00 2001 From: erikzhang Date: Fri, 15 May 2020 18:47:22 +0800 Subject: [PATCH 263/305] Make Plugin.Path be virtual --- 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 26c0befe3e..b2f6fb99d2 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -24,7 +24,7 @@ public abstract class Plugin : IDisposable public virtual string ConfigFile => Combine(PluginsDirectory, GetType().Assembly.GetName().Name, "config.json"); public virtual string Name => GetType().Name; - public string Path => Combine(PluginsDirectory, GetType().Assembly.ManifestModule.ScopeName); + public virtual string Path => Combine(PluginsDirectory, GetType().Assembly.ManifestModule.ScopeName); protected static NeoSystem System { get; private set; } public virtual Version Version => GetType().Assembly.GetName().Version; From 1ca5e62eccd987f4efa41562d012c7f9fb682616 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 15 May 2020 18:16:49 +0200 Subject: [PATCH 264/305] Not found command (#1645) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Not found command * Remove unused method Co-authored-by: Vitor Nazário Coelho --- src/neo/Network/P2P/MessageCommand.cs | 1 + .../Network/P2P/RemoteNode.ProtocolHandler.cs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index ccddacfba2..771a98a459 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(GetBlockDataPayload))] GetBlockData = 0x29, + [ReflectionCache(typeof(InvPayload))] NotFound = 0x2a, [ReflectionCache(typeof(Transaction))] Transaction = 0x2b, diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 91b3921326..2146501e85 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -217,8 +217,8 @@ private void OnGetBlockDataMessageReceived(GetBlockDataPayload payload) /// The payload containing the requested information. private void OnGetDataMessageReceived(InvPayload payload) { - UInt256[] hashes = payload.Hashes.Where(p => sentHashes.Add(p)).ToArray(); - foreach (UInt256 hash in hashes) + var notFound = new List(); + foreach (UInt256 hash in payload.Hashes.Where(p => sentHashes.Add(p))) { switch (payload.Type) { @@ -226,6 +226,8 @@ private void OnGetDataMessageReceived(InvPayload payload) Transaction tx = Blockchain.Singleton.GetTransaction(hash); if (tx != null) EnqueueMessage(Message.Create(MessageCommand.Transaction, tx)); + else + notFound.Add(hash); break; case InventoryType.Block: Block block = Blockchain.Singleton.GetBlock(hash); @@ -241,6 +243,10 @@ private void OnGetDataMessageReceived(InvPayload payload) EnqueueMessage(Message.Create(MessageCommand.MerkleBlock, MerkleBlockPayload.Create(block, flags))); } } + else + { + notFound.Add(hash); + } break; case InventoryType.Consensus: if (Blockchain.Singleton.ConsensusRelayCache.TryGet(hash, out IInventory inventoryConsensus)) @@ -248,6 +254,12 @@ private void OnGetDataMessageReceived(InvPayload payload) break; } } + + if (notFound.Count > 0) + { + foreach (InvPayload entry in InvPayload.CreateGroup(payload.Type, notFound.ToArray())) + EnqueueMessage(Message.Create(MessageCommand.NotFound, entry)); + } } /// From e20658e98d19ad3bdf279af8f760fcb13a6b9e24 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 16 May 2020 01:49:09 +0800 Subject: [PATCH 265/305] Subscribe to RelayResult messages in ConsensusService (#1647) --- src/neo/Consensus/ConsensusService.cs | 8 +++-- src/neo/Ledger/Blockchain.cs | 20 ++++++------ .../Network/P2P/RemoteNode.ProtocolHandler.cs | 6 ++-- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 32 ++++++++++++------- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 788719ae14..5b6661dc08 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -57,6 +57,7 @@ internal ConsensusService(IActorRef localNode, IActorRef taskManager, ConsensusC this.taskManager = taskManager; 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) @@ -506,15 +507,16 @@ protected override void OnReceive(object message) case Timer timer: OnTimer(timer); break; - case ConsensusPayload payload: - OnConsensusPayload(payload); - 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 ConsensusPayload payload) + OnConsensusPayload(payload); + break; } } } diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index a0153063d1..c5b80b8eec 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -64,7 +64,7 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu private uint stored_header_count = 0; private readonly Dictionary block_cache = new Dictionary(); private readonly Dictionary> block_cache_unverified = new Dictionary>(); - internal readonly RelayCache ConsensusRelayCache = new RelayCache(100); + internal readonly RelayCache RelayCache = new RelayCache(100); private SnapshotView currentSnapshot; public IStore Store { get; } @@ -299,8 +299,7 @@ private void OnInventory(IInventory inventory, bool relay = true) { Block block => OnNewBlock(block), Transaction transaction => OnNewTransaction(transaction), - ConsensusPayload payload => OnNewConsensus(payload), - _ => VerifyResult.Unknown + _ => OnNewInventory(inventory) } }; if (relay && rr.Result == VerifyResult.Succeed) @@ -388,14 +387,6 @@ private VerifyResult OnNewBlock(Block block) return VerifyResult.Succeed; } - private VerifyResult OnNewConsensus(ConsensusPayload payload) - { - if (!payload.Verify(currentSnapshot)) return VerifyResult.Invalid; - system.Consensus?.Tell(payload); - ConsensusRelayCache.Add(payload); - return VerifyResult.Succeed; - } - private void OnNewHeaders(Header[] headers) { using (SnapshotView snapshot = GetSnapshot()) @@ -417,6 +408,13 @@ private void OnNewHeaders(Header[] headers) system.TaskManager.Tell(new TaskManager.HeaderTaskCompleted(), Sender); } + private VerifyResult OnNewInventory(IInventory inventory) + { + if (!inventory.Verify(currentSnapshot)) return VerifyResult.Invalid; + RelayCache.Add(inventory); + return VerifyResult.Succeed; + } + private VerifyResult OnNewTransaction(Transaction transaction) { if (ContainsTransaction(transaction.Hash)) return VerifyResult.AlreadyExists; diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 2146501e85..432eba0b35 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -248,9 +248,9 @@ private void OnGetDataMessageReceived(InvPayload payload) notFound.Add(hash); } break; - case InventoryType.Consensus: - if (Blockchain.Singleton.ConsensusRelayCache.TryGet(hash, out IInventory inventoryConsensus)) - EnqueueMessage(Message.Create(MessageCommand.Consensus, inventoryConsensus)); + default: + if (Blockchain.Singleton.RelayCache.TryGet(hash, out IInventory inventory)) + EnqueueMessage(Message.Create((MessageCommand)payload.Type, inventory)); break; } } diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 1baf64acb8..1e8c3f079f 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -1,3 +1,4 @@ +using Akka.Actor; using Akka.TestKit; using Akka.TestKit.Xunit2; using FluentAssertions; @@ -186,7 +187,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.PreparationPayloads[prepReq.ValidatorIndex] = null; Console.WriteLine("will tell prepare request!"); - actorConsensus.Tell(prepReq); + 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; @@ -201,7 +202,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm // cleaning old try with Self ValidatorIndex mockContext.Object.PreparationPayloads[mockContext.Object.MyIndex] = null; - actorConsensus.Tell(prepReq); + TellConsensusPayload(actorConsensus, prepReq); var OnPrepResponse = subscriber.ExpectMsg(); var prepResponsePayload = (ConsensusPayload)OnPrepResponse.Inventory; PrepareResponse prm = (PrepareResponse)prepResponsePayload.ConsensusMessage; @@ -212,7 +213,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(5); // Simulating CN 3 - actorConsensus.Tell(GetPayloadAndModifyValidator(prepResponsePayload, 2)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 2)); //Waiting for RecoveryRequest for a more deterministic UT backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; @@ -225,7 +226,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(4); // Simulating CN 5 - actorConsensus.Tell(GetPayloadAndModifyValidator(prepResponsePayload, 4)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 4)); //Waiting for RecoveryRequest for a more deterministic UT backupOnRecoveryDueToFailedNodes = subscriber.ExpectMsg(); recoveryPayload = (ConsensusPayload)backupOnRecoveryDueToFailedNodes.Inventory; @@ -238,7 +239,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(3); // Simulating CN 4 - actorConsensus.Tell(GetPayloadAndModifyValidator(prepResponsePayload, 3)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(prepResponsePayload, 3)); var onCommitPayload = subscriber.ExpectMsg(); var commitPayload = (ConsensusPayload)onCommitPayload.Inventory; Commit cm = (Commit)commitPayload.ConsensusMessage; @@ -300,7 +301,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("\n=========================="); Console.WriteLine("\nCN7 simulation time"); - actorConsensus.Tell(cmPayloadTemp); + TellConsensusPayload(actorConsensus, cmPayloadTemp); var tempPayloadToBlockAndWait = subscriber.ExpectMsg(); var rmPayload = (ConsensusPayload)tempPayloadToBlockAndWait.Inventory; RecoveryMessage rmm = (RecoveryMessage)rmPayload.ConsensusMessage; @@ -310,7 +311,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(1); Console.WriteLine("\nCN6 simulation time"); - actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 5, kp_array[5], updatedBlockHashData)); + TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 5, kp_array[5], updatedBlockHashData)); tempPayloadToBlockAndWait = subscriber.ExpectMsg(); rmPayload = (ConsensusPayload)tempPayloadToBlockAndWait.Inventory; rmm = (RecoveryMessage)rmPayload.ConsensusMessage; @@ -320,7 +321,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.CountFailed.Should().Be(0); Console.WriteLine("\nCN5 simulation time"); - actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 4, kp_array[4], updatedBlockHashData)); + TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 4, kp_array[4], updatedBlockHashData)); tempPayloadToBlockAndWait = subscriber.ExpectMsg(); Console.WriteLine("\nAsserting CountCommitted is 4..."); mockContext.Object.CountCommitted.Should().Be(4); @@ -329,7 +330,7 @@ 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"); - actorConsensus.Tell(GetPayloadAndModifyValidator(commitPayload, 3)); + TellConsensusPayload(actorConsensus, GetPayloadAndModifyValidator(commitPayload, 3)); Console.WriteLine("\nWaiting for recovery due to failed nodes... "); var backupOnRecoveryMessageAfterCommit = subscriber.ExpectMsg(); rmPayload = (ConsensusPayload)backupOnRecoveryMessageAfterCommit.Inventory; @@ -358,7 +359,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"); - actorConsensus.Tell(GetCommitPayloadModifiedAndSignedCopy(commitPayload, 3, kp_array[3], mockContext.Object.Block.GetHashData())); + TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 3, kp_array[3], mockContext.Object.Block.GetHashData())); Console.WriteLine("\nWait for subscriber Local.Node Relay"); var onBlockRelay = subscriber.ExpectMsg(); @@ -388,7 +389,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm mockContext.Object.LastSeenMessage = new int[] { -1, -1, -1, -1, -1, -1, -1 }; mockContext.Object.CountFailed.Should().Be(7); - actorConsensus.Tell(rmPayload); + TellConsensusPayload(actorConsensus, rmPayload); Console.WriteLine("\nWaiting for RecoveryRequest before final asserts..."); var onRecoveryRequestAfterRecovery = subscriber.ExpectMsg(); @@ -921,5 +922,14 @@ private StorageKey CreateStorageKeyForNativeNeo(byte prefix) storageKey.Key[0] = prefix; return storageKey; } + + private void TellConsensusPayload(IActorRef actor, ConsensusPayload payload) + { + actor.Tell(new Blockchain.RelayResult + { + Inventory = payload, + Result = VerifyResult.Succeed + }); + } } } From 01b90de03c069d934d1053216ebe398751fd83de Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Sat, 16 May 2020 18:30:02 +0800 Subject: [PATCH 266/305] Create only one snapshot for persist (#1602) --- src/neo/Ledger/Blockchain.cs | 13 +++++-- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 43 +++++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index c5b80b8eec..da9fdbdea3 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -485,6 +485,8 @@ private void Persist(Block block) } } snapshot.Blocks.Add(block.Hash, block.Trim()); + StoreView clonedSnapshot = snapshot.Clone(); + // 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 @@ -493,15 +495,20 @@ private void Persist(Block block) Transaction = tx }; - snapshot.Transactions.Add(tx.Hash, state); + clonedSnapshot.Transactions.Add(tx.Hash, state); + clonedSnapshot.Transactions.Commit(); - using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, snapshot.Clone(), tx.SystemFee)) + using (ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, tx, clonedSnapshot, tx.SystemFee)) { engine.LoadScript(tx.Script); state.VMState = engine.Execute(); if (state.VMState == VMState.HALT) { - engine.Snapshot.Commit(); + clonedSnapshot.Commit(); + } + else + { + clonedSnapshot = snapshot.Clone(); } ApplicationExecuted application_executed = new ApplicationExecuted(engine); Context.System.EventStream.Publish(application_executed); diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index 6faf336290..c200bec7c8 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -1,3 +1,4 @@ +using Akka.Actor; using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -8,8 +9,10 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.SmartContract.Native.Tokens; +using Neo.VM; using Neo.Wallets; using Neo.Wallets.NEP6; +using System; using System.Linq; using System.Reflection; @@ -135,6 +138,46 @@ public void TestValidTransaction() } } + [TestMethod] + public void TestInvalidTransactionInPersist() + { + var snapshot = Blockchain.Singleton.GetSnapshot(); + var tx = new Transaction() + { + Attributes = Array.Empty(), + NetworkFee = 0, + Nonce = (uint)Environment.TickCount, + Script = new byte[] { 1 }, + Sender = UInt160.Zero, + 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 + { + Id = NativeContract.NEO.Id, + Key = new byte[sizeof(byte) + (key?.Length ?? 0)] + }; + storageKey.Key[0] = prefix; + key?.CopyTo(storageKey.Key.AsSpan(1)); + return storageKey; + } + private Transaction CreateValidTx(NEP6Wallet wallet, UInt160 account, uint nonce) { var tx = wallet.MakeTransaction(new TransferOutput[] From 3808a85a664716e876cf9d30fcaa6635506260c0 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sat, 16 May 2020 18:34:40 +0800 Subject: [PATCH 267/305] Remove OnPersistCompleted() --- src/neo/Ledger/Blockchain.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index da9fdbdea3..5621079c14 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -425,13 +425,6 @@ private VerifyResult OnNewTransaction(Transaction transaction) return VerifyResult.Succeed; } - private void OnPersistCompleted(Block block) - { - block_cache.Remove(block.PrevHash); - MemPool.UpdatePoolForBlockPersisted(block, currentSnapshot); - Context.System.EventStream.Publish(new PersistCompleted { Block = block }); - } - protected override void OnReceive(object message) { switch (message) @@ -545,7 +538,9 @@ private void Persist(Block block) if (commitExceptions != null) throw new AggregateException(commitExceptions); } UpdateCurrentSnapshot(); - OnPersistCompleted(block); + block_cache.Remove(block.PrevHash); + MemPool.UpdatePoolForBlockPersisted(block, currentSnapshot); + Context.System.EventStream.Publish(new PersistCompleted { Block = block }); } protected override void PostStop() From e5fce2131333758a47932bd83110c913fc03f4f5 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 14:17:34 +0800 Subject: [PATCH 268/305] Optimize Blockchain.cs --- src/neo/Ledger/Blockchain.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 5621079c14..55b7cf85aa 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -447,11 +447,8 @@ protected override void OnReceive(object message) foreach (var tx in transactions) OnInventory(tx, false); break; } - case Transaction transaction: - OnInventory(transaction); - break; - case ConsensusPayload payload: - OnInventory(payload); + case IInventory inventory: + OnInventory(inventory); break; case Idle _: if (MemPool.ReVerifyTopUnverifiedTransactionsIfNeeded(MaxTxToReverifyPerIdle, currentSnapshot)) From 0ba097fbdc8c5399b0f6738b662c60077cf4ff63 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Sun, 17 May 2020 17:02:07 +0800 Subject: [PATCH 269/305] Move namespace --- src/neo/{Persistence => IO}/ByteArrayEqualityComparer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/neo/{Persistence => IO}/ByteArrayEqualityComparer.cs (98%) diff --git a/src/neo/Persistence/ByteArrayEqualityComparer.cs b/src/neo/IO/ByteArrayEqualityComparer.cs similarity index 98% rename from src/neo/Persistence/ByteArrayEqualityComparer.cs rename to src/neo/IO/ByteArrayEqualityComparer.cs index 29d6acbe1e..2a3c64b57a 100644 --- a/src/neo/Persistence/ByteArrayEqualityComparer.cs +++ b/src/neo/IO/ByteArrayEqualityComparer.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Neo.Persistence +namespace Neo.IO { internal class ByteArrayEqualityComparer : IEqualityComparer { From 6e17c69ad99b7305bdb0244210a7702a8c9044de Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 18 May 2020 18:46:17 +0800 Subject: [PATCH 270/305] Decouple NativeContract from InteropService. (#1649) --- src/neo/SmartContract/ApplicationEngine.cs | 2 +- src/neo/SmartContract/Helper.cs | 6 ---- src/neo/SmartContract/InteropDescriptor.cs | 4 ++- .../SmartContract/InteropService.Native.cs | 16 +++++---- .../SmartContract/Native/NativeContract.cs | 35 ++++++++++++------- .../SmartContract/Native/PolicyContract.cs | 2 +- .../SmartContract/Native/Tokens/GasToken.cs | 1 - .../SmartContract/Native/Tokens/NeoToken.cs | 1 - .../SmartContract/Native/Tokens/Nep5Token.cs | 1 - .../Native/Tokens/UT_Nep5Token.cs | 4 +-- .../SmartContract/Native/UT_NativeContract.cs | 30 +++++----------- .../SmartContract/UT_SmartContractHelper.cs | 20 ----------- tests/neo.UnitTests/VM/UT_Helper.cs | 2 +- 13 files changed, 48 insertions(+), 76 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index fc11594a03..53a54f36fd 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -48,7 +48,7 @@ public ApplicationEngine(TriggerType trigger, IVerifiable container, StoreView s return disposable; } - private bool AddGas(long gas) + internal bool AddGas(long gas) { GasConsumed = checked(GasConsumed + gas); return testMode || GasConsumed <= gas_amount; diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 58eecf3d03..c9ca603f68 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.Text; namespace Neo.SmartContract { @@ -114,11 +113,6 @@ public static bool IsStandardContract(this byte[] script) return script.IsSignatureContract() || script.IsMultiSigContract(); } - public static uint ToInteropMethodHash(this string method) - { - return BitConverter.ToUInt32(Encoding.ASCII.GetBytes(method).Sha256(), 0); - } - public static UInt160 ToScriptHash(this byte[] script) { return new UInt160(Crypto.Hash160(script)); diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index ebc94c2f3e..1cbb7bc3d6 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -1,6 +1,8 @@ +using Neo.Cryptography; using Neo.Persistence; using Neo.VM; using System; +using System.Text; namespace Neo.SmartContract { @@ -29,7 +31,7 @@ internal InteropDescriptor(string method, Func handler, private InteropDescriptor(string method, Func handler, TriggerType allowedTriggers, CallFlags requiredCallFlags) { this.Method = method; - this.Hash = method.ToInteropMethodHash(); + this.Hash = BitConverter.ToUInt32(Encoding.ASCII.GetBytes(method).Sha256(), 0); this.Handler = handler; this.AllowedTriggers = allowedTriggers; this.RequiredCallFlags = requiredCallFlags; diff --git a/src/neo/SmartContract/InteropService.Native.cs b/src/neo/SmartContract/InteropService.Native.cs index da3263b4a2..62c3970647 100644 --- a/src/neo/SmartContract/InteropService.Native.cs +++ b/src/neo/SmartContract/InteropService.Native.cs @@ -8,12 +8,7 @@ partial class InteropService internal static class Native { public static readonly InteropDescriptor Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application, CallFlags.AllowModifyStates); - - static Native() - { - foreach (NativeContract contract in NativeContract.Contracts) - Register(contract.ServiceName, contract.Invoke, contract.GetPrice, TriggerType.System | TriggerType.Application, CallFlags.None); - } + public static readonly InteropDescriptor Call = Register("Neo.Native.Call", Native_Call, 0, TriggerType.System | TriggerType.Application, CallFlags.None); private static bool Native_Deploy(ApplicationEngine engine) { @@ -30,6 +25,15 @@ private static bool Native_Deploy(ApplicationEngine engine) } return true; } + + private static bool Native_Call(ApplicationEngine engine) + { + if (!engine.TryPop(out string name)) return false; + NativeContract contract = NativeContract.GetContract(name); + if (contract is null) return false; + contract.Invoke(engine); + return true; + } } } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index ce34011e32..81d8b499ab 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -2,7 +2,6 @@ using Neo.IO; using Neo.Ledger; -using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.SmartContract.Native.Tokens; using Neo.VM; @@ -18,7 +17,8 @@ namespace Neo.SmartContract.Native public abstract class NativeContract { private static readonly List contractsList = new List(); - private static readonly Dictionary contractsDictionary = new Dictionary(); + private static readonly Dictionary contractsNameDictionary = new Dictionary(); + private static readonly Dictionary contractsHashDictionary = new Dictionary(); private readonly Dictionary methods = new Dictionary(); public static IReadOnlyCollection Contracts { get; } = contractsList; @@ -26,8 +26,7 @@ public abstract class NativeContract public static GasToken GAS { get; } = new GasToken(); public static PolicyContract Policy { get; } = new PolicyContract(); - public abstract string ServiceName { get; } - public uint ServiceHash { get; } + public abstract string Name { get; } public byte[] Script { get; } public UInt160 Hash { get; } public abstract int Id { get; } @@ -36,10 +35,10 @@ public abstract class NativeContract protected NativeContract() { - this.ServiceHash = ServiceName.ToInteropMethodHash(); using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitSysCall(ServiceHash); + sb.EmitPush(Name); + sb.EmitSysCall(InteropService.Native.Call); this.Script = sb.ToArray(); } this.Hash = Script.ToScriptHash(); @@ -80,7 +79,8 @@ protected NativeContract() Extra = null, }; contractsList.Add(this); - contractsDictionary.Add(Hash, this); + contractsNameDictionary.Add(Name, this); + contractsHashDictionary.Add(Hash, this); } protected StorageKey CreateStorageKey(byte prefix, byte[] key = null) @@ -100,6 +100,18 @@ internal protected StorageKey CreateStorageKey(byte prefix, ISerializable key) return CreateStorageKey(prefix, key.ToArray()); } + public static NativeContract GetContract(UInt160 hash) + { + contractsHashDictionary.TryGetValue(hash, out var contract); + return contract; + } + + public static NativeContract GetContract(string name) + { + contractsNameDictionary.TryGetValue(name, out var contract); + return contract; + } + internal bool Invoke(ApplicationEngine engine) { if (!engine.CurrentScriptHash.Equals(Hash)) @@ -111,6 +123,8 @@ internal bool Invoke(ApplicationEngine engine) ExecutionContextState state = engine.CurrentContext.GetState(); if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) return false; + if (!engine.AddGas(method.Price)) + return false; StackItem result = method.Delegate(engine, args); engine.CurrentContext.EvaluationStack.Push(result); return true; @@ -118,12 +132,7 @@ internal bool Invoke(ApplicationEngine engine) public static bool IsNative(UInt160 hash) { - return contractsDictionary.ContainsKey(hash); - } - - internal long GetPrice(EvaluationStack stack, StoreView snapshot) - { - return methods.TryGetValue(stack.Peek().GetString(), out ContractMethodMetadata method) ? method.Price : 0; + return contractsHashDictionary.ContainsKey(hash); } internal virtual bool Initialize(ApplicationEngine engine) diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index ac9e9de471..17fc28c0ad 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -17,7 +17,7 @@ namespace Neo.SmartContract.Native { public sealed class PolicyContract : NativeContract { - public override string ServiceName => "Neo.Native.Policy"; + public override string Name => "Policy"; public override int Id => -3; private const byte Prefix_MaxTransactionsPerBlock = 23; diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index 459d65950a..4813e63ffb 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -10,7 +10,6 @@ namespace Neo.SmartContract.Native.Tokens { public sealed class GasToken : Nep5Token { - public override string ServiceName => "Neo.Native.Tokens.GAS"; public override int Id => -2; public override string Name => "GAS"; public override string Symbol => "gas"; diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 7d3b82c797..78ad83c61c 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -17,7 +17,6 @@ namespace Neo.SmartContract.Native.Tokens { public sealed class NeoToken : Nep5Token { - public override string ServiceName => "Neo.Native.Tokens.NEO"; public override int Id => -1; public override string Name => "NEO"; public override string Symbol => "neo"; diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index 6f1164de8c..2a6c54c310 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -17,7 +17,6 @@ public abstract class Nep5Token : NativeContract where TState : Nep5AccountState, new() { public override string[] SupportedStandards { get; } = { "NEP-5", "NEP-10" }; - public abstract string Name { get; } public abstract string Symbol { get; } public abstract byte Decimals { get; } public BigInteger Factor { get; } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs index f248c5e820..cadd4f63a4 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs @@ -82,14 +82,12 @@ public class TestNep5Token : Nep5Token { public override int Id => 0x10000005; - public override string Name => throw new NotImplementedException(); + public override string Name => "testNep5Token"; public override string Symbol => throw new NotImplementedException(); public override byte Decimals => 8; - public override string ServiceName => "testNep5Token"; - public new StackItem TotalSupply(ApplicationEngine engine, VM.Types.Array args) { return base.TotalSupply(engine, args); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index b56527ccd8..b3d5da5cd2 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -3,7 +3,6 @@ using Neo.Ledger; using Neo.SmartContract; using Neo.SmartContract.Native; -using Neo.VM; using Neo.VM.Types; using System; using VMArray = Neo.VM.Types.Array; @@ -37,31 +36,20 @@ public void TestInitialize() public void TestInvoke() { var snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); - - ScriptBuilder sb1 = new ScriptBuilder(); - - sb1.EmitSysCall("null".ToInteropMethodHash()); - engine1.LoadScript(sb1.ToArray()); - testNativeContract.Invoke(engine1).Should().BeFalse(); - - ApplicationEngine engine2 = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); - - ScriptBuilder sb2 = new ScriptBuilder(); - sb2.EmitSysCall("test".ToInteropMethodHash()); - engine2.LoadScript(sb2.ToArray()); + ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine.LoadScript(testNativeContract.Script); ByteString method1 = new ByteString(System.Text.Encoding.Default.GetBytes("wrongMethod")); VMArray args1 = new VMArray(); - engine2.CurrentContext.EvaluationStack.Push(args1); - engine2.CurrentContext.EvaluationStack.Push(method1); - testNativeContract.Invoke(engine2).Should().BeFalse(); + engine.CurrentContext.EvaluationStack.Push(args1); + engine.CurrentContext.EvaluationStack.Push(method1); + testNativeContract.Invoke(engine).Should().BeFalse(); ByteString method2 = new ByteString(System.Text.Encoding.Default.GetBytes("onPersist")); VMArray args2 = new VMArray(); - engine2.CurrentContext.EvaluationStack.Push(args2); - engine2.CurrentContext.EvaluationStack.Push(method2); - testNativeContract.Invoke(engine2).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Push(args2); + engine.CurrentContext.EvaluationStack.Push(method2); + testNativeContract.Invoke(engine).Should().BeTrue(); } [TestMethod] @@ -89,7 +77,7 @@ public void TestTestCall() public class TestNativeContract : NativeContract { - public override string ServiceName => "test"; + public override string Name => "test"; public override int Id => 0x10000006; diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index ac62d746ab..3677041aa2 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -3,10 +3,8 @@ using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.Wallets; -using System; using System.Linq; using System.Security.Cryptography; -using System.Text; using ECPoint = Neo.Cryptography.ECC.ECPoint; namespace Neo.UnitTests.SmartContract @@ -113,24 +111,6 @@ public void TestIsStandardContract() Assert.AreEqual(true, Neo.SmartContract.Helper.IsStandardContract(script2)); } - [TestMethod] - public void TestToInteropMethodHash() - { - byte[] temp1 = Encoding.ASCII.GetBytes("AAAA"); - byte[] temp2 = Neo.Cryptography.Helper.Sha256(temp1); - uint result = BitConverter.ToUInt32(temp2, 0); - Assert.AreEqual(result, Neo.SmartContract.Helper.ToInteropMethodHash("AAAA")); - } - - [TestMethod] - public void TestToScriptHash() - { - byte[] temp1 = Encoding.ASCII.GetBytes("AAAA"); - byte[] temp2 = Neo.Cryptography.Helper.Sha256(temp1); - uint result = BitConverter.ToUInt32(temp2, 0); - Assert.AreEqual(result, Neo.SmartContract.Helper.ToInteropMethodHash("AAAA")); - } - [TestMethod] public void TestVerifyWitnesses() { diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 498ff433f2..428855d0ae 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -117,7 +117,7 @@ public void TestMakeScript() { byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - Assert.AreEqual("0c14000000000000000000000000000000000000000011c00c0962616c616e63654f660c143b7d3711c6f0ccf9b1dca903d1bfa1d896f1238c41627d5b52", + Assert.AreEqual("0c14000000000000000000000000000000000000000011c00c0962616c616e63654f660c14bcaf41d684c7d4ad6ee0d99da9707b9d1f0c8e6641627d5b52", testScript.ToHexString()); } From 1c0a23e41d541946949a9f9857dedd0b3e319255 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 19 May 2020 12:38:50 +0800 Subject: [PATCH 271/305] Optimize the `GetPrice()` mechanism of SYSCALLs (#1650) --- src/neo/SmartContract/ApplicationEngine.cs | 2 - src/neo/SmartContract/InteropDescriptor.cs | 25 +----- .../SmartContract/InteropService.Contract.cs | 15 ++-- .../SmartContract/InteropService.Crypto.cs | 17 +--- .../SmartContract/InteropService.Storage.cs | 45 +++++------ src/neo/SmartContract/InteropService.cs | 16 +--- src/neo/Wallets/Wallet.cs | 4 +- .../SmartContract/UT_InteropDescriptor.cs | 2 +- .../SmartContract/UT_InteropPrices.cs | 79 +++---------------- .../SmartContract/UT_InteropService.NEO.cs | 6 +- .../SmartContract/UT_InteropService.cs | 18 ++--- 11 files changed, 59 insertions(+), 170 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 53a54f36fd..bfec85c8f8 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -80,8 +80,6 @@ public override void Dispose() protected override bool OnSysCall(uint method) { - if (!AddGas(InteropService.GetPrice(method, CurrentContext.EvaluationStack, Snapshot))) - return false; return InteropService.Invoke(this, method); } diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index 1cbb7bc3d6..b5efb42edd 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -1,6 +1,4 @@ using Neo.Cryptography; -using Neo.Persistence; -using Neo.VM; using System; using System.Text; @@ -11,37 +9,20 @@ public class InteropDescriptor public string Method { get; } public uint Hash { get; } internal Func Handler { get; } - public long Price { get; } - public Func PriceCalculator { get; } + public long FixedPrice { get; } public TriggerType AllowedTriggers { get; } public CallFlags RequiredCallFlags { get; } - internal InteropDescriptor(string method, Func handler, long price, TriggerType allowedTriggers, CallFlags requiredCallFlags) - : this(method, handler, allowedTriggers, requiredCallFlags) - { - this.Price = price; - } - - internal InteropDescriptor(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers, CallFlags requiredCallFlags) - : this(method, handler, allowedTriggers, requiredCallFlags) - { - this.PriceCalculator = priceCalculator; - } - - private InteropDescriptor(string method, Func handler, TriggerType allowedTriggers, CallFlags requiredCallFlags) + internal InteropDescriptor(string method, Func handler, long fixedPrice, TriggerType allowedTriggers, CallFlags requiredCallFlags) { this.Method = method; this.Hash = BitConverter.ToUInt32(Encoding.ASCII.GetBytes(method).Sha256(), 0); this.Handler = handler; + this.FixedPrice = fixedPrice; this.AllowedTriggers = allowedTriggers; this.RequiredCallFlags = requiredCallFlags; } - public long GetPrice(EvaluationStack stack, StoreView snapshot) - { - return PriceCalculator is null ? Price : PriceCalculator(stack, snapshot); - } - public static implicit operator uint(InteropDescriptor descriptor) { return descriptor.Hash; diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs index 36b3cad6ad..fdcb7607be 100644 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ b/src/neo/SmartContract/InteropService.Contract.cs @@ -1,7 +1,6 @@ using Neo.Cryptography.ECC; using Neo.IO; using Neo.Ledger; -using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; using Neo.VM; @@ -18,8 +17,8 @@ public static class Contract { public const int MaxLength = 1024 * 1024; - public static readonly InteropDescriptor Create = Register("System.Contract.Create", Contract_Create, GetDeploymentPrice, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor Update = Register("System.Contract.Update", Contract_Update, GetDeploymentPrice, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor Create = Register("System.Contract.Create", Contract_Create, 0, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor Update = Register("System.Contract.Update", Contract_Update, 0, TriggerType.Application, CallFlags.AllowModifyStates); public static readonly InteropDescriptor Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates); public static readonly InteropDescriptor Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); public static readonly InteropDescriptor CallEx = Register("System.Contract.CallEx", Contract_CallEx, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); @@ -32,12 +31,6 @@ public static class Contract /// public static readonly InteropDescriptor CreateStandardAccount = Register("System.Contract.CreateStandardAccount", Contract_CreateStandardAccount, 0_00010000, TriggerType.All, CallFlags.None); - private static long GetDeploymentPrice(EvaluationStack stack, StoreView snapshot) - { - int size = stack.Peek(0).GetByteLength() + stack.Peek(1).GetByteLength(); - return Storage.GasPerByte * size; - } - private static bool Contract_GetCallFlags(ApplicationEngine engine) { var state = engine.CurrentContext.GetState(); @@ -53,6 +46,8 @@ private static bool Contract_Create(ApplicationEngine engine) if (!engine.TryPop(out ReadOnlySpan manifest)) return false; if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) return false; + if (!engine.AddGas(Storage.GasPerByte * (script.Length + manifest.Length))) return false; + UInt160 hash = script.ToScriptHash(); ContractState contract = engine.Snapshot.Contracts.TryGet(hash); if (contract != null) return false; @@ -75,6 +70,8 @@ private static bool Contract_Update(ApplicationEngine engine) if (!engine.TryPop(out StackItem item0)) return false; if (!engine.TryPop(out StackItem item1)) return false; + if (!engine.AddGas(Storage.GasPerByte * (item0.GetByteLength() + item1.GetByteLength()))) return false; + var contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash); if (contract is null) return false; diff --git a/src/neo/SmartContract/InteropService.Crypto.cs b/src/neo/SmartContract/InteropService.Crypto.cs index 99b585885e..73fb99c3d2 100644 --- a/src/neo/SmartContract/InteropService.Crypto.cs +++ b/src/neo/SmartContract/InteropService.Crypto.cs @@ -1,7 +1,6 @@ using Neo.Cryptography; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.VM; using Neo.VM.Types; using System; @@ -18,19 +17,8 @@ public static class Crypto public static readonly InteropDescriptor VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.ECDsa.Secp256r1.Verify", Crypto_ECDsaSecp256r1Verify, 0_01000000, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.ECDsa.Secp256k1.Verify", Crypto_ECDsaSecp256k1Verify, 0_01000000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.ECDsa.Secp256r1.CheckMultiSig", Crypto_ECDsaSecp256r1CheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.ECDsa.Secp256k1.CheckMultiSig", Crypto_ECDsaSecp256k1CheckMultiSig, GetECDsaCheckMultiSigPrice, TriggerType.All, CallFlags.None); - - private static long GetECDsaCheckMultiSigPrice(EvaluationStack stack, StoreView snapshot) - { - if (stack.Count < 2) return 0; - var item = stack.Peek(1); - int n; - if (item is Array array) n = array.Count; - else n = (int)item.GetBigInteger(); - if (n < 1) return 0; - return VerifyWithECDsaSecp256r1.Price * n; - } + public static readonly InteropDescriptor CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.ECDsa.Secp256r1.CheckMultiSig", Crypto_ECDsaSecp256r1CheckMultiSig, 0, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.ECDsa.Secp256k1.CheckMultiSig", Crypto_ECDsaSecp256k1CheckMultiSig, 0, TriggerType.All, CallFlags.None); private static bool Crypto_SHA256(ApplicationEngine engine) { @@ -114,6 +102,7 @@ private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine, Cryptogr for (int i = 0; i < n; i++) pubkeys[i] = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); } + if (!engine.AddGas(VerifyWithECDsaSecp256r1.FixedPrice * n)) return false; int m; byte[][] signatures; item = engine.CurrentContext.EvaluationStack.Pop(); diff --git a/src/neo/SmartContract/InteropService.Storage.cs b/src/neo/SmartContract/InteropService.Storage.cs index c23eebeb41..0525b9a774 100644 --- a/src/neo/SmartContract/InteropService.Storage.cs +++ b/src/neo/SmartContract/InteropService.Storage.cs @@ -1,5 +1,4 @@ using Neo.Ledger; -using Neo.Persistence; using Neo.SmartContract.Iterators; using Neo.VM; using Neo.VM.Types; @@ -21,46 +20,38 @@ public static class Storage public static readonly InteropDescriptor AsReadOnly = Register("System.Storage.AsReadOnly", Storage_AsReadOnly, 0_00000400, TriggerType.Application, CallFlags.AllowStates); public static readonly InteropDescriptor Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application, CallFlags.AllowStates); public static readonly InteropDescriptor Find = Register("System.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor Put = Register("System.Storage.Put", Storage_Put, GetStoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor PutEx = Register("System.Storage.PutEx", Storage_PutEx, GetStoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor Put = Register("System.Storage.Put", Storage_Put, 0, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor PutEx = Register("System.Storage.PutEx", Storage_PutEx, 0, TriggerType.Application, CallFlags.AllowModifyStates); public static readonly InteropDescriptor Delete = Register("System.Storage.Delete", Storage_Delete, 1 * GasPerByte, TriggerType.Application, CallFlags.AllowModifyStates); - private static long GetStoragePrice(EvaluationStack stack, StoreView snapshot) - { - StorageContext context = ((InteropInterface)stack.Peek(0)).GetInterface(); - ReadOnlySpan key = stack.Peek(1).GetSpan(); - ReadOnlySpan value = stack.Peek(2).GetSpan(); - int newDataSize; - StorageKey skey = new StorageKey - { - Id = context.Id, - Key = key.ToArray() - }; - var skeyValue = snapshot.Storages.TryGet(skey); - if (skeyValue is null) - newDataSize = key.Length + value.Length; - else if (value.Length <= skeyValue.Value.Length) - newDataSize = 1; - else - newDataSize = value.Length - skeyValue.Value.Length; - return newDataSize * GasPerByte; - } - private static bool PutExInternal(ApplicationEngine engine, StorageContext context, byte[] key, byte[] value, StorageFlags flags) { if (key.Length > MaxKeySize) return false; if (value.Length > MaxValueSize) return false; if (context.IsReadOnly) return false; + int newDataSize; StorageKey skey = new StorageKey { Id = context.Id, Key = key }; + StorageItem item = engine.Snapshot.Storages.GetAndChange(skey); + if (item is null) + { + newDataSize = key.Length + value.Length; + engine.Snapshot.Storages.Add(skey, item = new StorageItem()); + } + else + { + if (item.IsConstant) return false; + if (value.Length <= item.Value.Length) + newDataSize = 1; + else + newDataSize = value.Length - item.Value.Length; + } + if (!engine.AddGas(newDataSize * GasPerByte)) return false; - if (engine.Snapshot.Storages.TryGet(skey)?.IsConstant == true) return false; - - StorageItem item = engine.Snapshot.Storages.GetAndChange(skey, () => new StorageItem()); item.Value = value; item.IsConstant = flags.HasFlag(StorageFlags.Constant); diff --git a/src/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs index f2a673c82f..e4a399bf94 100644 --- a/src/neo/SmartContract/InteropService.cs +++ b/src/neo/SmartContract/InteropService.cs @@ -1,5 +1,3 @@ -using Neo.Persistence; -using Neo.VM; using System; using System.Collections.Generic; using System.Reflection; @@ -16,11 +14,6 @@ static InteropService() t.GetFields()[0].GetValue(null); } - public static long GetPrice(uint hash, EvaluationStack stack, StoreView snapshot) - { - return methods[hash].GetPrice(stack, snapshot); - } - public static IEnumerable SupportedMethods() { return methods.Values; @@ -35,6 +28,8 @@ internal static bool Invoke(ApplicationEngine engine, uint method) ExecutionContextState state = engine.CurrentContext.GetState(); if (!state.CallFlags.HasFlag(descriptor.RequiredCallFlags)) return false; + if (!engine.AddGas(descriptor.FixedPrice)) + return false; return descriptor.Handler(engine); } @@ -44,12 +39,5 @@ private static InteropDescriptor Register(string method, Func handler, Func priceCalculator, TriggerType allowedTriggers, CallFlags requiredCallFlags) - { - InteropDescriptor descriptor = new InteropDescriptor(method, handler, priceCalculator, allowedTriggers, requiredCallFlags); - methods.Add(descriptor.Hash, descriptor); - return descriptor; - } } } diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index b6665aadf9..2d4f6167cf 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -356,7 +356,7 @@ public static long CalculateNetworkFee(byte[] witness_script, ref int size) if (witness_script.IsSignatureContract()) { size += 67 + witness_script.GetVarSize(); - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.VerifyWithECDsaSecp256r1, null, null); + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.Crypto.VerifyWithECDsaSecp256r1.FixedPrice; } else if (witness_script.IsMultiSigContract(out int m, out int n)) { @@ -368,7 +368,7 @@ public static long CalculateNetworkFee(byte[] witness_script, ref int size) networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * n; using (ScriptBuilder sb = new ScriptBuilder()) networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.GetPrice(InteropService.Crypto.VerifyWithECDsaSecp256r1, null, null) * n; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.Crypto.VerifyWithECDsaSecp256r1.FixedPrice * n; } else { diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs b/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs index aec2ac3dd7..1d2d59d5a2 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs @@ -15,7 +15,7 @@ public void TestGetMethod() TriggerType allowedTriggers = TriggerType.All; InteropDescriptor descriptor = new InteropDescriptor(method, TestHandler, price, allowedTriggers, CallFlags.None); descriptor.Method.Should().Be(method); - descriptor.Price.Should().Be(price); + descriptor.FixedPrice.Should().Be(price); } private bool TestHandler(ApplicationEngine engine) diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index c4c050fd2c..5329463ffe 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -4,7 +4,6 @@ using Neo.SmartContract; using Neo.SmartContract.Manifest; using Neo.VM; -using System; namespace Neo.UnitTests.SmartContract { @@ -25,7 +24,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemRuntimeCheckWitnessHash); - InteropService.GetPrice(InteropService.Runtime.CheckWitness, ae.CurrentContext.EvaluationStack, ae.Snapshot).Should().Be(0_00030000L); + InteropService.Runtime.CheckWitness.FixedPrice.Should().Be(0_00030000L); } // System.Storage.GetContext: 9bf667ce (price is 1) @@ -33,7 +32,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemStorageGetContextHash); - InteropService.GetPrice(InteropService.Storage.GetContext, ae.CurrentContext.EvaluationStack, ae.Snapshot).Should().Be(0_00000400L); + InteropService.Storage.GetContext.FixedPrice.Should().Be(0_00000400L); } // System.Storage.Get: 925de831 (price is 100) @@ -41,48 +40,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemStorageGetHash); - InteropService.GetPrice(InteropService.Storage.Get, ae.CurrentContext.EvaluationStack, ae.Snapshot).Should().Be(0_01000000L); - } - } - - [TestMethod] - public void ApplicationEngineVariablePrices() - { - // Neo.Contract.Create: f66ca56e (requires push properties on fourth position) - byte[] SyscallContractCreateHash00 = new byte[] { (byte)OpCode.PUSHDATA1, 0x01, 0x00, (byte)OpCode.PUSHDATA1, 0x02, 0x00, 0x00, (byte)OpCode.SYSCALL, 0xf6, 0x6c, 0xa5, 0x6e }; - using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0, testMode: true)) - { - Debugger debugger = new Debugger(ae); - ae.LoadScript(SyscallContractCreateHash00); - debugger.StepInto(); // PUSHDATA1 - debugger.StepInto(); // PUSHDATA1 - InteropService.GetPrice(InteropService.Contract.Create, ae.CurrentContext.EvaluationStack, ae.Snapshot).Should().Be(0_00300000L); - } - - // System.Storage.Put: e63f1884 (requires push key and value) - byte[] SyscallStoragePutHash = new byte[] { (byte)OpCode.PUSH3, (byte)OpCode.PUSH3, (byte)OpCode.PUSH0, (byte)OpCode.SYSCALL, 0xe6, 0x3f, 0x18, 0x84 }; - using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0, testMode: true)) - { - Debugger debugger = new Debugger(ae); - ae.LoadScript(SyscallStoragePutHash); - debugger.StepInto(); // push 03 (length 1) - debugger.StepInto(); // push 03 (length 1) - debugger.StepInto(); // push 00 - Action act = () => InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); - act.Should().Throw(); - } - - // System.Storage.PutEx: 73e19b3a (requires push key and value) - byte[] SyscallStoragePutExHash = new byte[] { (byte)OpCode.PUSH3, (byte)OpCode.PUSH3, (byte)OpCode.PUSH0, (byte)OpCode.SYSCALL, 0x73, 0xe1, 0x9b, 0x3a }; - using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0, testMode: true)) - { - Debugger debugger = new Debugger(ae); - ae.LoadScript(SyscallStoragePutExHash); - debugger.StepInto(); // push 03 (length 1) - debugger.StepInto(); // push 03 (length 1) - debugger.StepInto(); // push 00 - Action act = () => InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); - act.Should().Throw(); + InteropService.Storage.Get.FixedPrice.Should().Be(0_01000000L); } } @@ -115,11 +73,8 @@ public void ApplicationEngineRegularPut() debugger.StepInto(); debugger.StepInto(); var setupPrice = ae.GasConsumed; - var defaultDataPrice = InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); - defaultDataPrice.Should().Be(InteropService.Storage.GasPerByte * value.Length); - var expectedCost = defaultDataPrice + setupPrice; debugger.Execute(); - ae.GasConsumed.Should().Be(expectedCost); + (ae.GasConsumed - setupPrice).Should().Be(InteropService.Storage.GasPerByte * value.Length); } } @@ -152,11 +107,8 @@ public void ApplicationEngineReusedStorage_FullReuse() debugger.StepInto(); debugger.StepInto(); var setupPrice = applicationEngine.GasConsumed; - var reusedDataPrice = InteropService.GetPrice(InteropService.Storage.Put, applicationEngine.CurrentContext.EvaluationStack, applicationEngine.Snapshot); - reusedDataPrice.Should().Be(1 * InteropService.Storage.GasPerByte); debugger.Execute(); - var expectedCost = reusedDataPrice + setupPrice; - applicationEngine.GasConsumed.Should().Be(expectedCost); + (applicationEngine.GasConsumed - setupPrice).Should().Be(1 * InteropService.Storage.GasPerByte); } } @@ -191,12 +143,9 @@ public void ApplicationEngineReusedStorage_PartialReuse() debugger.StepInto(); debugger.StepInto(); var setupPrice = ae.GasConsumed; - var reusedDataPrice = InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); - reusedDataPrice.Should().Be(1 * InteropService.Storage.GasPerByte); debugger.StepInto(); - var expectedCost = reusedDataPrice + setupPrice; debugger.StepInto(); - ae.GasConsumed.Should().Be(expectedCost); + (ae.GasConsumed - setupPrice).Should().Be(1 * InteropService.Storage.GasPerByte); } } @@ -227,20 +176,16 @@ public void ApplicationEngineReusedStorage_PartialReuseTwice() { Debugger debugger = new Debugger(ae); ae.LoadScript(script); + debugger.StepInto(); //push value debugger.StepInto(); //push key + debugger.StepInto(); //syscall Storage.GetContext + debugger.StepInto(); //syscall Storage.Put debugger.StepInto(); //push value + debugger.StepInto(); //push key debugger.StepInto(); //syscall Storage.GetContext var setupPrice = ae.GasConsumed; - var incrementDataPrice = InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); - incrementDataPrice.Should().Be(1 * InteropService.Storage.GasPerByte); - debugger.StepInto(); // syscall Storage.Put - - debugger.StepInto(); //push key - debugger.StepInto(); //push value - debugger.StepInto(); - setupPrice = ae.GasConsumed; - var reusedDataPrice = InteropService.GetPrice(InteropService.Storage.Put, ae.CurrentContext.EvaluationStack, ae.Snapshot); - reusedDataPrice.Should().Be(1 * InteropService.Storage.GasPerByte); // = PUT basic fee + debugger.StepInto(); //syscall Storage.Put + (ae.GasConsumed - setupPrice).Should().Be(1 * InteropService.Storage.GasPerByte); // = PUT basic fee } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 84c80288d4..99beb32046 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -145,7 +145,7 @@ public void TestAccount_IsStandard() var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); snapshot.Contracts.Add(state.ScriptHash, state); - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); InteropService.Invoke(engine, InteropService.Contract.IsStandard).Should().BeTrue(); @@ -236,7 +236,7 @@ public void TestContract_Update() }; snapshot.Contracts.Add(state.ScriptHash, state); snapshot.Storages.Add(storageKey, storageItem); - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(state.Script); engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); engine.CurrentContext.EvaluationStack.Push(script); @@ -263,7 +263,7 @@ public void TestStorage_Find() }; snapshot.Contracts.Add(state.ScriptHash, state); snapshot.Storages.Add(storageKey, storageItem); - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index b599016336..1fa5689e7a 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -474,7 +474,7 @@ public void TestBlockchain_GetContract() var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); snapshot.Contracts.Add(state.ScriptHash, state); - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); InteropService.Invoke(engine, InteropService.Blockchain.GetContract).Should().BeTrue(); @@ -532,7 +532,7 @@ public void TestStorage_Get() }; snapshot.Contracts.Add(state.ScriptHash, state); snapshot.Storages.Add(storageKey, storageItem); - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); @@ -607,7 +607,7 @@ public void TestStorage_Put() }; snapshot.Contracts.Add(state.ScriptHash, state); snapshot.Storages.Add(storageKey, storageItem); - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); key = new byte[] { 0x01 }; value = new byte[] { 0x02 }; @@ -655,7 +655,7 @@ public void TestStorage_PutEx() }; snapshot.Contracts.Add(state.ScriptHash, state); snapshot.Storages.Add(storageKey, storageItem); - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); var key = new byte[] { 0x01 }; var value = new byte[] { 0x02 }; @@ -694,7 +694,7 @@ public void TestStorage_Delete() }; snapshot.Contracts.Add(state.ScriptHash, state); snapshot.Storages.Add(storageKey, storageItem); - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); state.Manifest.Features = ContractFeatures.HasStorage; var key = new byte[] { 0x01 }; @@ -751,7 +751,7 @@ public void TestContract_Call() var args = new VM.Types.Array { 0, 1 }; snapshot.Contracts.Add(state.ScriptHash, state); - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push(args); @@ -793,7 +793,7 @@ public void TestContract_CallEx() foreach (var flags in new CallFlags[] { CallFlags.None, CallFlags.AllowCall, CallFlags.AllowModifyStates, CallFlags.All }) { - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); engine.CurrentContext.EvaluationStack.Push((int)CallFlags.All); @@ -855,7 +855,7 @@ public void TestContract_Destroy() }; snapshot.Contracts.Add(scriptHash, state); snapshot.Storages.Add(storageKey, storageItem); - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[0]); InteropService.Invoke(engine, InteropService.Contract.Destroy).Should().BeTrue(); engine.Snapshot.Storages.Find(BitConverter.GetBytes(0x43000000)).Any().Should().BeFalse(); @@ -864,7 +864,7 @@ public void TestContract_Destroy() snapshot = Blockchain.Singleton.GetSnapshot(); state = TestUtils.GetContract(); snapshot.Contracts.Add(scriptHash, state); - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[0]); InteropService.Invoke(engine, InteropService.Contract.Destroy).Should().BeTrue(); engine.Snapshot.Storages.Find(BitConverter.GetBytes(0x43000000)).Any().Should().BeFalse(); From 8c14a9d6893b899373bf373d655fedc356463f98 Mon Sep 17 00:00:00 2001 From: Luchuan Date: Wed, 20 May 2020 18:07:13 +0800 Subject: [PATCH 272/305] fix the way to get the directory path of assembly (#1656) --- src/neo/Utility.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/Utility.cs b/src/neo/Utility.cs index 61b6c6a71e..ee167db70e 100644 --- a/src/neo/Utility.cs +++ b/src/neo/Utility.cs @@ -34,11 +34,11 @@ public static IConfigurationRoot LoadConfig(string config) if (!File.Exists(file)) { // EntryPoint folder - file = Path.Combine(Assembly.GetEntryAssembly().Location, configFile); + file = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), configFile); if (!File.Exists(file)) { // neo.dll folder - file = Path.Combine(Assembly.GetExecutingAssembly().Location, configFile); + file = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), configFile); if (!File.Exists(file)) { // default config From 368f0bf6da70571fed20a0f35bf6a4b692bc43b6 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 25 May 2020 12:50:24 +0200 Subject: [PATCH 273/305] Ask for mempool after sync (#1544) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Query mempool if I have less tx than the remote node * Revert some changes * Wait for verack * Clean file * Move to verack * Wait for block sync * Clean changes * Only accept one mempool message per remote node * Remove one variable * Revert "Only accept one mempool message per remote node" This reverts commit 359a3bb655e7cd82e3d68a7f2d0ee1b4cda68d2b. * Optimize * Check it in TaskManager * Update src/neo/Network/P2P/TaskSession.cs Co-authored-by: Luchuan * Revert * fix MemPoolTaskHash (#1660) * Fix ms bug * private Co-authored-by: Vitor Nazário Coelho Co-authored-by: Luchuan Co-authored-by: Erik Zhang --- src/neo/Network/P2P/TaskManager.cs | 16 ++++++++++++---- src/neo/Network/P2P/TaskSession.cs | 8 +++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 73fa49441a..442efc8a68 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -24,11 +24,13 @@ private class Timer { } private static readonly TimeSpan TimerInterval = TimeSpan.FromSeconds(30); private static readonly TimeSpan TaskTimeout = TimeSpan.FromMinutes(1); + private static readonly UInt256 HeaderTaskHash = UInt256.Zero; + private static readonly UInt256 MemPoolTaskHash = UInt256.Parse("0x0000000000000000000000000000000000000000000000000000000000000001"); private readonly NeoSystem system; private const int MaxConncurrentTasks = 3; - private const int PingCoolingOffPeriod = 60; // in secconds. + private const int PingCoolingOffPeriod = 60_000; // in ms. /// /// A set of known hashes, of inventories or payloads, already received. /// @@ -37,7 +39,6 @@ private class Timer { } private readonly Dictionary sessions = new Dictionary(); private readonly ICancelable timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimerInterval, TimerInterval, Context.Self, new Timer(), ActorRefs.NoSender); - private readonly UInt256 HeaderTaskHash = UInt256.Zero; private bool HasHeaderTask => globalTasks.ContainsKey(HeaderTaskHash); public TaskManager(NeoSystem system) @@ -126,6 +127,8 @@ private void OnRegister(VersionPayload version) { Context.Watch(Sender); TaskSession session = new TaskSession(Sender, version); + if (session.IsFullNode) + session.AvailableTasks.Add(TaskManager.MemPoolTaskHash); sessions.Add(Sender, session); RequestTasks(session); } @@ -183,7 +186,6 @@ private bool IncrementGlobalTask(UInt256 hash) return false; globalTasks[hash] = value + 1; - return true; } @@ -230,6 +232,7 @@ private void RequestTasks(TaskSession session) // Search any similar hash that is on Singleton's knowledge, which means, on the way or already processed session.AvailableTasks.RemoveWhere(p => Blockchain.Singleton.ContainsBlock(p)); HashSet hashes = new HashSet(session.AvailableTasks); + hashes.Remove(MemPoolTaskHash); if (hashes.Count > 0) { foreach (UInt256 hash in hashes.ToArray()) @@ -269,8 +272,13 @@ private void RequestTasks(TaskSession session) session.RemoteNode.Tell(Message.Create(MessageCommand.GetBlocks, GetBlocksPayload.Create(hash))); } else if (Blockchain.Singleton.HeaderHeight >= session.LastBlockIndex - && TimeProvider.Current.UtcNow.ToTimestamp() - PingCoolingOffPeriod >= Blockchain.Singleton.GetBlock(Blockchain.Singleton.CurrentHeaderHash)?.Timestamp) + && TimeProvider.Current.UtcNow.ToTimestampMS() - PingCoolingOffPeriod >= Blockchain.Singleton.GetBlock(Blockchain.Singleton.CurrentHeaderHash)?.Timestamp) { + if (session.AvailableTasks.Remove(MemPoolTaskHash)) + { + session.RemoteNode.Tell(Message.Create(MessageCommand.Mempool)); + } + session.RemoteNode.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(Blockchain.Singleton.Height))); } } diff --git a/src/neo/Network/P2P/TaskSession.cs b/src/neo/Network/P2P/TaskSession.cs index eb0ab631bd..d13c7c82f5 100644 --- a/src/neo/Network/P2P/TaskSession.cs +++ b/src/neo/Network/P2P/TaskSession.cs @@ -16,15 +16,17 @@ internal class TaskSession public bool HasTask => Tasks.Count > 0; public uint StartHeight { get; } + public bool IsFullNode { get; } public uint LastBlockIndex { get; set; } public TaskSession(IActorRef node, VersionPayload version) { + var fullNode = version.Capabilities.OfType().FirstOrDefault(); + + this.IsFullNode = fullNode != null; this.RemoteNode = node; this.Version = version; - this.StartHeight = version.Capabilities - .OfType() - .FirstOrDefault()?.StartHeight ?? 0; + this.StartHeight = fullNode?.StartHeight ?? 0; this.LastBlockIndex = this.StartHeight; } } From 452d872bcd29151db135430e741bfbd0686d3a4b Mon Sep 17 00:00:00 2001 From: erikzhang Date: Tue, 26 May 2020 14:50:02 +0800 Subject: [PATCH 274/305] Remove CommitteeMembersCount and ValidatorsCount from Blockchain.cs --- src/neo/Consensus/ConsensusContext.cs | 8 ++++---- src/neo/Consensus/RecoveryMessage.cs | 7 +++---- src/neo/Ledger/Blockchain.cs | 6 ++---- src/neo/Network/P2P/Payloads/ConsensusData.cs | 3 +-- src/neo/ProtocolSettings.cs | 6 ++++-- src/neo/SmartContract/Native/Tokens/NeoToken.cs | 10 +++++----- 6 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index f7f500f3dc..ee9b3dfe39 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -109,10 +109,10 @@ public void Deserialize(BinaryReader reader) ViewNumber = reader.ReadByte(); TransactionHashes = reader.ReadSerializableArray(); Transaction[] transactions = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); - PreparationPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); - CommitPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); - ChangeViewPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); - LastChangeViewPayloads = reader.ReadNullableArray(Blockchain.ValidatorsCount); + PreparationPayloads = reader.ReadNullableArray(ProtocolSettings.Default.MaxValidatorsCount); + CommitPayloads = reader.ReadNullableArray(ProtocolSettings.Default.MaxValidatorsCount); + ChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.MaxValidatorsCount); + LastChangeViewPayloads = reader.ReadNullableArray(ProtocolSettings.Default.MaxValidatorsCount); if (TransactionHashes.Length == 0 && !RequestSentOrReceived) TransactionHashes = null; Transactions = transactions.Length == 0 && !RequestSentOrReceived ? null : transactions.ToDictionary(p => p.Hash); diff --git a/src/neo/Consensus/RecoveryMessage.cs b/src/neo/Consensus/RecoveryMessage.cs index cde0f7aecc..cadb137b80 100644 --- a/src/neo/Consensus/RecoveryMessage.cs +++ b/src/neo/Consensus/RecoveryMessage.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using System.Collections.Generic; @@ -32,7 +31,7 @@ public RecoveryMessage() : base(ConsensusMessageType.RecoveryMessage) public override void Deserialize(BinaryReader reader) { base.Deserialize(reader); - ChangeViewMessages = reader.ReadSerializableArray(Blockchain.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); + ChangeViewMessages = reader.ReadSerializableArray(ProtocolSettings.Default.MaxValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); if (reader.ReadBoolean()) PrepareRequestMessage = reader.ReadSerializable(); else @@ -42,8 +41,8 @@ public override void Deserialize(BinaryReader reader) PreparationHash = new UInt256(reader.ReadFixedBytes(preparationHashSize)); } - PreparationMessages = reader.ReadSerializableArray(Blockchain.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); - CommitMessages = reader.ReadSerializableArray(Blockchain.ValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); + PreparationMessages = reader.ReadSerializableArray(ProtocolSettings.Default.MaxValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); + CommitMessages = reader.ReadSerializableArray(ProtocolSettings.Default.MaxValidatorsCount).ToDictionary(p => (int)p.ValidatorIndex); } internal ConsensusPayload[] GetChangeViewPayloads(ConsensusContext context, ConsensusPayload payload) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 55b7cf85aa..811db655a6 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -32,10 +32,8 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu public const uint DecrementInterval = 2000000; public static readonly uint[] GenerationAmount = { 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; public static readonly TimeSpan TimePerBlock = TimeSpan.FromMilliseconds(MillisecondsPerBlock); - public static readonly byte CommitteeMembersCount = (byte)ProtocolSettings.Default.StandbyCommittee.Length; - public static readonly byte ValidatorsCount = ProtocolSettings.Default.ValidatorsCount; - public static readonly ECPoint[] StandbyCommittee = ProtocolSettings.Default.StandbyCommittee.Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); - public static readonly ECPoint[] StandbyValidators = StandbyCommittee[..ValidatorsCount]; + public static readonly ECPoint[] StandbyCommittee = ProtocolSettings.Default.StandbyCommittee.Take(ProtocolSettings.Default.MaxCommitteeMembersCount).Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); + public static readonly ECPoint[] StandbyValidators = StandbyCommittee.Take(ProtocolSettings.Default.MaxValidatorsCount).ToArray(); public static readonly Block GenesisBlock = new Block { diff --git a/src/neo/Network/P2P/Payloads/ConsensusData.cs b/src/neo/Network/P2P/Payloads/ConsensusData.cs index 35dfc403af..7bbee151fb 100644 --- a/src/neo/Network/P2P/Payloads/ConsensusData.cs +++ b/src/neo/Network/P2P/Payloads/ConsensusData.cs @@ -1,7 +1,6 @@ using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; -using Neo.Ledger; using System.IO; namespace Neo.Network.P2P.Payloads @@ -28,7 +27,7 @@ public UInt256 Hash void ISerializable.Deserialize(BinaryReader reader) { - PrimaryIndex = (uint)reader.ReadVarInt((ulong)Blockchain.ValidatorsCount - 1); + PrimaryIndex = (uint)reader.ReadVarInt((ulong)ProtocolSettings.Default.MaxValidatorsCount - 1); Nonce = reader.ReadUInt64(); } diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index ca6deaf96e..e004b637a8 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -10,7 +10,8 @@ public class ProtocolSettings public uint Magic { get; } public byte AddressVersion { get; } public string[] StandbyCommittee { get; } - public byte ValidatorsCount { get; } + public byte MaxCommitteeMembersCount { get; } + public byte MaxValidatorsCount { get; } public string[] SeedList { get; } public uint MillisecondsPerBlock { get; } public int MemoryPoolMaxTransactions { get; } @@ -77,7 +78,8 @@ private ProtocolSettings(IConfigurationSection section) "03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", "02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a" }; - this.ValidatorsCount = section.GetValue("ValidatorsCount", (byte)7); + this.MaxCommitteeMembersCount = section.GetValue("MaxCommitteeMembersCount", (byte)21); + this.MaxValidatorsCount = section.GetValue("MaxValidatorsCount", (byte)7); IConfigurationSection section_sl = section.GetSection("SeedList"); if (section_sl.Exists()) this.SeedList = section_sl.GetChildren().Select(p => p.Get()).ToArray(); diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 78ad83c61c..67e6260eee 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -92,12 +92,12 @@ internal override bool Initialize(ApplicationEngine engine) if (!base.Initialize(engine)) return false; if (base.TotalSupply(engine.Snapshot) != BigInteger.Zero) return false; BigInteger amount = TotalAmount; - for (int i = 0; i < Blockchain.CommitteeMembersCount; i++) + for (int i = 0; i < Blockchain.StandbyCommittee.Length; i++) { ECPoint pubkey = Blockchain.StandbyCommittee[i]; RegisterCandidate(engine.Snapshot, pubkey); - BigInteger balance = TotalAmount / 2 / (Blockchain.ValidatorsCount * 2 + (Blockchain.CommitteeMembersCount - Blockchain.ValidatorsCount)); - if (i < Blockchain.ValidatorsCount) balance *= 2; + BigInteger balance = TotalAmount / 2 / (Blockchain.StandbyValidators.Length * 2 + (Blockchain.StandbyCommittee.Length - Blockchain.StandbyValidators.Length)); + if (i < Blockchain.StandbyValidators.Length) balance *= 2; UInt160 account = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); Mint(engine, account, balance); Vote(engine.Snapshot, account, pubkey); @@ -232,7 +232,7 @@ private StackItem GetValidators(ApplicationEngine engine, Array args) public ECPoint[] GetValidators(StoreView snapshot) { - return GetCommitteeMembers(snapshot, Blockchain.ValidatorsCount).OrderBy(p => p).ToArray(); + return GetCommitteeMembers(snapshot, ProtocolSettings.Default.MaxValidatorsCount).OrderBy(p => p).ToArray(); } [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] @@ -243,7 +243,7 @@ private StackItem GetCommittee(ApplicationEngine engine, Array args) public ECPoint[] GetCommittee(StoreView snapshot) { - return GetCommitteeMembers(snapshot, Blockchain.CommitteeMembersCount).OrderBy(p => p).ToArray(); + return GetCommitteeMembers(snapshot, ProtocolSettings.Default.MaxCommitteeMembersCount).OrderBy(p => p).ToArray(); } private IEnumerable GetCommitteeMembers(StoreView snapshot, int count) From 9eaf7b4150eced111246cbb002ce5a41a8dcd75d Mon Sep 17 00:00:00 2001 From: Luchuan Date: Tue, 26 May 2020 23:33:47 +0800 Subject: [PATCH 275/305] Fix policy check (#1668) * fix policy check * Rename * Fix GetCommitteeAddress() Co-authored-by: erikzhang --- src/neo/SmartContract/Native/PolicyContract.cs | 17 ++++++++--------- .../SmartContract/Native/Tokens/NeoToken.cs | 6 ++++++ .../SmartContract/Native/UT_PolicyContract.cs | 18 +++++++++++------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 17fc28c0ad..552227f7be 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -38,11 +38,10 @@ internal bool CheckPolicy(Transaction tx, StoreView snapshot) return true; } - private bool CheckValidators(ApplicationEngine engine) + private bool CheckCommittees(ApplicationEngine engine) { - UInt256 prev_hash = engine.Snapshot.PersistingBlock.PrevHash; - TrimmedBlock prev_block = engine.Snapshot.Blocks[prev_hash]; - return InteropService.Runtime.CheckWitnessInternal(engine, prev_block.NextConsensus); + UInt160 committeeMultiSigAddr = NEO.GetCommitteeAddress(engine.Snapshot); + return InteropService.Runtime.CheckWitnessInternal(engine, committeeMultiSigAddr); } internal override bool Initialize(ApplicationEngine engine) @@ -114,7 +113,7 @@ public UInt160[] GetBlockedAccounts(StoreView snapshot) [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetMaxBlockSize(ApplicationEngine engine, Array args) { - if (!CheckValidators(engine)) return false; + if (!CheckCommittees(engine)) return false; uint value = (uint)args[0].GetBigInteger(); if (Network.P2P.Message.PayloadMaxSize <= value) return false; StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize)); @@ -125,7 +124,7 @@ private StackItem SetMaxBlockSize(ApplicationEngine engine, Array args) [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, Array args) { - if (!CheckValidators(engine)) return false; + if (!CheckCommittees(engine)) return false; uint value = (uint)args[0].GetBigInteger(); StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock)); storage.Value = BitConverter.GetBytes(value); @@ -135,7 +134,7 @@ private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, Array arg [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] private StackItem SetFeePerByte(ApplicationEngine engine, Array args) { - if (!CheckValidators(engine)) return false; + if (!CheckCommittees(engine)) return false; long value = (long)args[0].GetBigInteger(); StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_FeePerByte)); storage.Value = BitConverter.GetBytes(value); @@ -145,7 +144,7 @@ private StackItem SetFeePerByte(ApplicationEngine engine, Array args) [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] private StackItem BlockAccount(ApplicationEngine engine, Array args) { - if (!CheckValidators(engine)) return false; + if (!CheckCommittees(engine)) return false; UInt160 account = new UInt160(args[0].GetSpan()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; @@ -159,7 +158,7 @@ private StackItem BlockAccount(ApplicationEngine engine, Array args) [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] private StackItem UnblockAccount(ApplicationEngine engine, Array args) { - if (!CheckValidators(engine)) return false; + if (!CheckCommittees(engine)) return false; UInt160 account = new UInt160(args[0].GetSpan()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 67e6260eee..3793765c12 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -246,6 +246,12 @@ public ECPoint[] GetCommittee(StoreView snapshot) return GetCommitteeMembers(snapshot, ProtocolSettings.Default.MaxCommitteeMembersCount).OrderBy(p => p).ToArray(); } + public UInt160 GetCommitteeAddress(StoreView snapshot) + { + ECPoint[] committees = GetCommittee(snapshot); + return Contract.CreateMultiSigRedeemScript(committees.Length - (committees.Length - 1) / 2, committees).ToScriptHash(); + } + private IEnumerable GetCommitteeMembers(StoreView snapshot, int count) { return GetCandidates(snapshot).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index a173585182..22442e08cf 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -61,6 +61,8 @@ public void Check_SetMaxBlockSize() snapshot.PersistingBlock = 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); + NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); // Without signature @@ -76,7 +78,7 @@ public void Check_SetMaxBlockSize() // More than expected - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(committeeMultiSigAddr), "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = Neo.Network.P2P.Message.PayloadMaxSize }); ret.Should().BeOfType(); ret.ToBoolean().Should().BeFalse(); @@ -87,7 +89,7 @@ public void Check_SetMaxBlockSize() // With signature - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(committeeMultiSigAddr), "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); ret.Should().BeOfType(); ret.ToBoolean().Should().BeTrue(); @@ -122,7 +124,7 @@ public void Check_SetMaxTransactionsPerBlock() // With signature - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(NativeContract.NEO.GetCommitteeAddress(snapshot)), "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); ret.ToBoolean().Should().BeTrue(); @@ -156,8 +158,8 @@ public void Check_SetFeePerByte() ret.GetBigInteger().Should().Be(1000); // With signature - - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), + UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(committeeMultiSigAddr), "setFeePerByte", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); ret.Should().BeOfType(); ret.ToBoolean().Should().BeTrue(); @@ -177,6 +179,8 @@ public void Check_Block_UnblockAccount() snapshot.PersistingBlock = 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); + NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); // Block without signature @@ -192,7 +196,7 @@ public void Check_Block_UnblockAccount() // Block with signature - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(committeeMultiSigAddr), "blockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); ret.ToBoolean().Should().BeTrue(); @@ -216,7 +220,7 @@ public void Check_Block_UnblockAccount() // Unblock with signature - ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(UInt160.Zero), + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(committeeMultiSigAddr), "unblockAccount", new ContractParameter(ContractParameterType.Hash160) { Value = UInt160.Zero }); ret.Should().BeOfType(); ret.ToBoolean().Should().BeTrue(); From 0aa75cf45116c43773f368a18447d36c62ee55e2 Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Wed, 27 May 2020 12:23:57 +0800 Subject: [PATCH 276/305] Inventory message stream optimization (#1667) --- src/neo/Consensus/ConsensusService.cs | 14 ++++++++------ src/neo/NeoSystem.cs | 2 +- src/neo/Network/P2P/LocalNode.cs | 17 ----------------- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 4 +++- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 8 +++----- 5 files changed, 15 insertions(+), 30 deletions(-) diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 5b6661dc08..4199bb13a0 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -26,6 +26,7 @@ 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 bool started = false; @@ -46,15 +47,16 @@ internal class Timer { public uint Height; public byte ViewNumber; } /// private bool isRecovering = false; - public ConsensusService(IActorRef localNode, IActorRef taskManager, IStore store, Wallet wallet) - : this(localNode, taskManager, new ConsensusContext(wallet, store)) + 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, ConsensusContext context) + 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)); @@ -124,7 +126,7 @@ private void CheckCommits() { Block block = context.CreateBlock(); Log($"relay block: height={block.Index} hash={block.Hash} tx={block.Transactions.Length}"); - localNode.Tell(new LocalNode.Relay { Inventory = block }); + blockchain.Tell(block); } } @@ -602,9 +604,9 @@ protected override void PostStop() base.PostStop(); } - public static Props Props(IActorRef localNode, IActorRef taskManager, IStore store, Wallet wallet) + public static Props Props(IActorRef localNode, IActorRef taskManager, IActorRef blockchain, IStore store, Wallet wallet) { - return Akka.Actor.Props.Create(() => new ConsensusService(localNode, taskManager, store, wallet)).WithMailbox("consensus-service-mailbox"); + return Akka.Actor.Props.Create(() => new ConsensusService(localNode, taskManager, blockchain, store, wallet)).WithMailbox("consensus-service-mailbox"); } private void RequestChangeView(ChangeViewReason reason) diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index d666a5d1a0..88cc93ebcd 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -81,7 +81,7 @@ internal void ResumeNodeStartup() public void StartConsensus(Wallet wallet, IStore consensus_store = null, bool ignoreRecoveryLogs = false) { - Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, consensus_store ?? store, wallet)); + Consensus = ActorSystem.ActorOf(ConsensusService.Props(this.LocalNode, this.TaskManager, this.Blockchain, consensus_store ?? store, wallet)); Consensus.Tell(new ConsensusService.Start { IgnoreRecoveryLogs = ignoreRecoveryLogs }, Blockchain); } diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index f7b9093134..c1e7046725 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -15,7 +15,6 @@ namespace Neo.Network.P2P { public class LocalNode : Peer { - public class Relay { public IInventory Inventory; } internal class RelayDirectly { public IInventory Inventory; } internal class SendDirectly { public IInventory Inventory; } @@ -169,9 +168,6 @@ protected override void OnReceive(object message) case Message msg: BroadcastMessage(msg); break; - case Relay relay: - OnRelay(relay.Inventory); - break; case RelayDirectly relay: OnRelayDirectly(relay.Inventory); break; @@ -181,19 +177,6 @@ protected override void OnReceive(object message) } } - /// - /// For Transaction type of IInventory, it will tell Transaction to the actor of Consensus. - /// Otherwise, tell the inventory to the actor of Blockchain. - /// There are, currently, three implementations of IInventory: TX, Block and ConsensusPayload. - /// - /// The inventory to be relayed. - private void OnRelay(IInventory inventory) - { - if (inventory is Transaction transaction) - system.Consensus?.Tell(transaction); - system.Blockchain.Tell(inventory); - } - private void OnRelayDirectly(IInventory inventory) { var message = new RemoteNode.Relay { Inventory = inventory }; diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 432eba0b35..bdf235bdc0 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -298,7 +298,9 @@ private void OnHeadersMessageReceived(HeadersPayload payload) private void OnInventoryReceived(IInventory inventory) { system.TaskManager.Tell(new TaskManager.TaskCompleted { Hash = inventory.Hash }); - system.LocalNode.Tell(new LocalNode.Relay { Inventory = inventory }); + if (inventory is Transaction transaction) + system.Consensus?.Tell(transaction); + system.Blockchain.Tell(inventory, ActorRefs.NoSender); pendingKnownHashes.Remove(inventory.Hash); knownHashes.Add(inventory.Hash); } diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index 1e8c3f079f..ee26c3ca85 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -100,7 +100,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm 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, mockContext.Object }, null)) + 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 @@ -361,10 +361,8 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("\nCN4 simulation time - Final needed signatures"); TellConsensusPayload(actorConsensus, GetCommitPayloadModifiedAndSignedCopy(commitPayload, 3, kp_array[3], mockContext.Object.Block.GetHashData())); - Console.WriteLine("\nWait for subscriber Local.Node Relay"); - var onBlockRelay = subscriber.ExpectMsg(); - Console.WriteLine("\nAsserting time was Block..."); - var utBlock = (Block)onBlockRelay.Inventory; + Console.WriteLine("\nWait for subscriber Block"); + var utBlock = subscriber.ExpectMsg(); Console.WriteLine("\nAsserting CountCommitted is 5..."); mockContext.Object.CountCommitted.Should().Be(5); From d785e119de229347eb9627968a8434d55bb5f723 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Wed, 27 May 2020 13:10:28 +0800 Subject: [PATCH 277/305] Rename --- .../{Nep5AccountState.cs => AccountState.cs} | 2 +- .../SmartContract/Native/Tokens/GasToken.cs | 2 +- .../SmartContract/Native/Tokens/NeoToken.cs | 12 +++--- .../SmartContract/Native/Tokens/Nep5Token.cs | 2 +- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 4 +- .../Network/P2P/Payloads/UT_Transaction.cs | 36 ++++++++--------- .../Native/Tokens/UT_NeoToken.cs | 14 +++---- .../Native/Tokens/UT_Nep5Token.cs | 2 +- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 40 +++++++++---------- 9 files changed, 57 insertions(+), 57 deletions(-) rename src/neo/SmartContract/Native/Tokens/{Nep5AccountState.cs => AccountState.cs} (90%) diff --git a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs b/src/neo/SmartContract/Native/Tokens/AccountState.cs similarity index 90% rename from src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs rename to src/neo/SmartContract/Native/Tokens/AccountState.cs index cb46d0bd94..4cffcc406c 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5AccountState.cs +++ b/src/neo/SmartContract/Native/Tokens/AccountState.cs @@ -4,7 +4,7 @@ namespace Neo.SmartContract.Native.Tokens { - public class Nep5AccountState : IInteroperable + public class AccountState : IInteroperable { public BigInteger Balance; diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index 4813e63ffb..09c2ab1127 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -8,7 +8,7 @@ namespace Neo.SmartContract.Native.Tokens { - public sealed class GasToken : Nep5Token + public sealed class GasToken : Nep5Token { public override int Id => -2; public override string Name => "GAS"; diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 3793765c12..8005d054ba 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -15,7 +15,7 @@ namespace Neo.SmartContract.Native.Tokens { - public sealed class NeoToken : Nep5Token + public sealed class NeoToken : Nep5Token { public override int Id => -1; public override string Name => "NEO"; @@ -36,7 +36,7 @@ public override BigInteger TotalSupply(StoreView snapshot) return TotalAmount; } - protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 account, AccountState state, BigInteger amount) + protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 account, NeoAccountState state, BigInteger amount) { DistributeGas(engine, account, state); if (amount.IsZero) return; @@ -48,7 +48,7 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco } } - private void DistributeGas(ApplicationEngine engine, UInt160 account, AccountState state) + private void DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccountState state) { BigInteger gas = CalculateBonus(engine.Snapshot, state.Balance, state.BalanceHeight, engine.Snapshot.PersistingBlock.Index); state.BalanceHeight = engine.Snapshot.PersistingBlock.Index; @@ -127,7 +127,7 @@ public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) { StorageItem storage = snapshot.Storages.TryGet(CreateAccountKey(account)); if (storage is null) return BigInteger.Zero; - AccountState state = storage.GetInteroperable(); + NeoAccountState state = storage.GetInteroperable(); return CalculateBonus(snapshot, state.Balance, state.BalanceHeight, end); } @@ -185,7 +185,7 @@ private bool Vote(StoreView snapshot, UInt160 account, ECPoint voteTo) StorageKey key_account = CreateAccountKey(account); if (snapshot.Storages.TryGet(key_account) is null) return false; StorageItem storage_account = snapshot.Storages.GetAndChange(key_account); - AccountState state_account = storage_account.GetInteroperable(); + NeoAccountState state_account = storage_account.GetInteroperable(); if (state_account.VoteTo != null) { StorageKey key = CreateStorageKey(Prefix_Candidate, state_account.VoteTo.ToArray()); @@ -270,7 +270,7 @@ public ECPoint[] GetNextBlockValidators(StoreView snapshot) return storage.Value.AsSerializableArray(); } - public class AccountState : Nep5AccountState + public class NeoAccountState : AccountState { public uint BalanceHeight; public ECPoint VoteTo; diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index 2a6c54c310..045db4e4b5 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -14,7 +14,7 @@ namespace Neo.SmartContract.Native.Tokens { public abstract class Nep5Token : NativeContract - where TState : Nep5AccountState, new() + where TState : AccountState, new() { public override string[] SupportedStandards { get; } = { "NEP-5", "NEP-10" }; public abstract string Symbol { get; } diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index c200bec7c8..f672c2a707 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -116,9 +116,9 @@ public void TestValidTransaction() // Fake balance var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 100_000_000 * NativeContract.GAS.Factor; + entry.GetInteroperable().Balance = 100_000_000 * NativeContract.GAS.Factor; snapshot.Commit(); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 4267f8931e..0cb8b95b30 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -106,9 +106,9 @@ public void FeeIsMultiSigContract() // Fake balance var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -176,9 +176,9 @@ public void FeeIsSignatureContractDetailed() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -287,9 +287,9 @@ public void FeeIsSignatureContract_TestScope_Global() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -373,9 +373,9 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -460,9 +460,9 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -550,9 +550,9 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; // Make transaction // Manually creating script @@ -600,9 +600,9 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); @@ -692,9 +692,9 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; // Make transaction // Manually creating script @@ -938,9 +938,9 @@ public void FeeIsSignatureContract_TestScope_Global_Default() var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 6d26b5d9ef..37b5bc51e3 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -238,14 +238,14 @@ public void TestCalculateBonus() { var snapshot = Blockchain.Singleton.GetSnapshot(); StorageKey key = CreateStorageKey(20, UInt160.Zero.ToArray()); - snapshot.Storages.Add(key, new StorageItem(new AccountState + snapshot.Storages.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.Storages.GetAndChange(key, () => new StorageItem(new AccountState + snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoAccountState { Balance = 100 })); @@ -420,7 +420,7 @@ public void TestUnclaimedGas() { var snapshot = Blockchain.Singleton.GetSnapshot(); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); - snapshot.Storages.Add(CreateStorageKey(20, UInt160.Zero.ToArray()), new StorageItem(new AccountState())); + snapshot.Storages.Add(CreateStorageKey(20, UInt160.Zero.ToArray()), new StorageItem(new NeoAccountState())); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 10).Should().Be(new BigInteger(0)); } @@ -439,13 +439,13 @@ public void TestVote() ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); - snapshot.Storages.Add(keyAccount, new StorageItem(new AccountState())); + snapshot.Storages.Add(keyAccount, new StorageItem(new NeoAccountState())); ret = Check_Vote(snapshot, account.ToArray(), ECCurve.Secp256r1.G.ToArray(), true); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); snapshot.Storages.Delete(keyAccount); - snapshot.Storages.GetAndChange(keyAccount, () => new StorageItem(new AccountState + snapshot.Storages.GetAndChange(keyAccount, () => new StorageItem(new NeoAccountState { VoteTo = ECCurve.Secp256r1.G })); @@ -465,7 +465,7 @@ public void TestVote() UInt160 from = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot)[0]; if (addVotes) { - snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem(new AccountState + snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem(new NeoAccountState { VoteTo = ECCurve.Secp256r1.G, Balance = new BigInteger(1000) @@ -474,7 +474,7 @@ public void TestVote() } else { - snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem(new AccountState + snapshot.Storages.Add(CreateStorageKey(20, from.ToArray()), new StorageItem(new NeoAccountState { Balance = new BigInteger(1000) })); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs index cadd4f63a4..8c6d8eefef 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs @@ -78,7 +78,7 @@ public StorageKey CreateStorageKey(byte prefix, byte[] key = null) } } - public class TestNep5Token : Nep5Token + public class TestNep5Token : Nep5Token { public override int Id => 0x10000005; diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index e8cd43a46a..41069a5006 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -203,14 +203,14 @@ 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 Nep5AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + var entry = snapshot.Storages.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 Nep5AccountState())); - entry.GetInteroperable().Balance = 0; + entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + entry.GetInteroperable().Balance = 0; snapshot.Commit(); } @@ -225,15 +225,15 @@ 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 Nep5AccountState())); - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + var entry = snapshot.Storages.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 Nep5AccountState())); - entry.GetInteroperable().Balance = 0; + entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + entry.GetInteroperable().Balance = 0; snapshot.Commit(); } @@ -325,12 +325,12 @@ 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 Nep5AccountState())); - entry1.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; + var entry1 = snapshot.Storages.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.AccountState())); - entry2.GetInteroperable().Balance = 10000 * NativeContract.NEO.Factor; + var entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new NeoToken.NeoAccountState())); + entry2.GetInteroperable().Balance = 10000 * NativeContract.NEO.Factor; snapshot.Commit(); @@ -356,10 +356,10 @@ public void TestMakeTransaction1() }); tx.Should().NotBeNull(); - entry1 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry1.GetInteroperable().Balance = 0; - entry2.GetInteroperable().Balance = 0; + entry1 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + entry2 = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + entry1.GetInteroperable().Balance = 0; + entry2.GetInteroperable().Balance = 0; snapshot.Commit(); } @@ -377,8 +377,8 @@ 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 Nep5AccountState())); - entry.GetInteroperable().Balance = 1000000 * NativeContract.GAS.Factor; + var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + entry.GetInteroperable().Balance = 1000000 * NativeContract.GAS.Factor; snapshot.Commit(); var tx = wallet.MakeTransaction(new byte[] { }, account.ScriptHash, new TransactionAttribute[] { }); @@ -387,8 +387,8 @@ public void TestMakeTransaction2() tx = wallet.MakeTransaction(new byte[] { }, null, new TransactionAttribute[] { }); tx.Should().NotBeNull(); - entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new Nep5AccountState())); - entry.GetInteroperable().Balance = 0; + entry = snapshot.Storages.GetAndChange(key, () => new StorageItem(new AccountState())); + entry.GetInteroperable().Balance = 0; snapshot.Commit(); } From aa047c3e15e51b455979d9b14d248858397d2d7d Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 30 May 2020 19:39:50 +0800 Subject: [PATCH 278/305] Refactor InteropService (#1655) --- src/neo/Ledger/Blockchain.cs | 2 +- .../SmartContract/ApplicationEngine.Binary.cs | 20 + .../ApplicationEngine.Blockchain.cs | 103 ++++++ .../ApplicationEngine.Contract.cs | 181 +++++++++ .../SmartContract/ApplicationEngine.Crypto.cs | 99 +++++ .../ApplicationEngine.Enumerator.cs | 41 +++ .../ApplicationEngine.Iterator.cs | 48 +++ .../SmartContract/ApplicationEngine.Json.cs | 21 ++ .../SmartContract/ApplicationEngine.Native.cs | 34 ++ .../ApplicationEngine.Runtime.cs | 173 +++++++++ .../ApplicationEngine.Storage.cs | 127 +++++++ src/neo/SmartContract/ApplicationEngine.cs | 114 ++++-- src/neo/SmartContract/Contract.cs | 4 +- src/neo/SmartContract/Helper.cs | 4 +- src/neo/SmartContract/InteropDescriptor.cs | 14 +- .../InteropParameterDescriptor.cs | 59 +++ .../SmartContract/InteropService.Binary.cs | 30 -- .../InteropService.Blockchain.cs | 120 ------ .../SmartContract/InteropService.Contract.cs | 211 ----------- .../SmartContract/InteropService.Crypto.cs | 144 -------- .../InteropService.Enumerator.cs | 69 ---- .../SmartContract/InteropService.Iterator.cs | 84 ----- src/neo/SmartContract/InteropService.Json.cs | 31 -- .../SmartContract/InteropService.Native.cs | 39 -- .../SmartContract/InteropService.Runtime.cs | 222 ----------- .../SmartContract/InteropService.Storage.cs | 175 --------- src/neo/SmartContract/InteropService.cs | 43 --- .../SmartContract/Native/NativeContract.cs | 7 +- .../SmartContract/Native/PolicyContract.cs | 6 +- .../SmartContract/Native/Tokens/GasToken.cs | 6 +- .../SmartContract/Native/Tokens/NeoToken.cs | 11 +- .../SmartContract/Native/Tokens/Nep5Token.cs | 2 +- src/neo/SmartContract/NotifyEventArgs.cs | 15 +- src/neo/VM/Helper.cs | 6 +- src/neo/Wallets/Wallet.cs | 4 +- tests/neo.UnitTests/Consensus/UT_Consensus.cs | 6 +- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 6 +- .../Native/Tokens/UT_GasToken.cs | 1 - .../Native/Tokens/UT_NeoToken.cs | 34 +- .../SmartContract/Native/UT_NativeContract.cs | 7 +- .../SmartContract/Native/UT_PolicyContract.cs | 11 +- .../SmartContract/UT_ApplicationEngine.cs | 59 --- .../SmartContract/UT_Contract.cs | 12 +- .../UT_ContractParameterContext.cs | 10 +- .../SmartContract/UT_InteropDescriptor.cs | 26 -- .../SmartContract/UT_InteropPrices.cs | 22 +- .../SmartContract/UT_InteropService.NEO.cs | 220 +++-------- .../SmartContract/UT_InteropService.cs | 347 ++++-------------- .../SmartContract/UT_Syscalls.cs | 40 +- tests/neo.UnitTests/VM/UT_Helper.cs | 6 +- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 4 +- 51 files changed, 1220 insertions(+), 1860 deletions(-) create mode 100644 src/neo/SmartContract/ApplicationEngine.Binary.cs create mode 100644 src/neo/SmartContract/ApplicationEngine.Blockchain.cs create mode 100644 src/neo/SmartContract/ApplicationEngine.Contract.cs create mode 100644 src/neo/SmartContract/ApplicationEngine.Crypto.cs create mode 100644 src/neo/SmartContract/ApplicationEngine.Enumerator.cs create mode 100644 src/neo/SmartContract/ApplicationEngine.Iterator.cs create mode 100644 src/neo/SmartContract/ApplicationEngine.Json.cs create mode 100644 src/neo/SmartContract/ApplicationEngine.Native.cs create mode 100644 src/neo/SmartContract/ApplicationEngine.Runtime.cs create mode 100644 src/neo/SmartContract/ApplicationEngine.Storage.cs create mode 100644 src/neo/SmartContract/InteropParameterDescriptor.cs delete mode 100644 src/neo/SmartContract/InteropService.Binary.cs delete mode 100644 src/neo/SmartContract/InteropService.Blockchain.cs delete mode 100644 src/neo/SmartContract/InteropService.Contract.cs delete mode 100644 src/neo/SmartContract/InteropService.Crypto.cs delete mode 100644 src/neo/SmartContract/InteropService.Enumerator.cs delete mode 100644 src/neo/SmartContract/InteropService.Iterator.cs delete mode 100644 src/neo/SmartContract/InteropService.Json.cs delete mode 100644 src/neo/SmartContract/InteropService.Native.cs delete mode 100644 src/neo/SmartContract/InteropService.Runtime.cs delete mode 100644 src/neo/SmartContract/InteropService.Storage.cs delete mode 100644 src/neo/SmartContract/InteropService.cs delete mode 100644 tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 811db655a6..2d2fafab04 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -156,7 +156,7 @@ private static Transaction DeployNativeContracts() byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitSysCall(InteropService.Native.Deploy); + sb.EmitSysCall(ApplicationEngine.Neo_Native_Deploy); script = sb.ToArray(); } return new Transaction diff --git a/src/neo/SmartContract/ApplicationEngine.Binary.cs b/src/neo/SmartContract/ApplicationEngine.Binary.cs new file mode 100644 index 0000000000..d5f86feefb --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Binary.cs @@ -0,0 +1,20 @@ +using Neo.VM.Types; + +namespace Neo.SmartContract +{ + partial class ApplicationEngine + { + public static readonly InteropDescriptor System_Binary_Serialize = Register("System.Binary.Serialize", nameof(BinarySerialize), 0_00100000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Binary_Deserialize = Register("System.Binary.Deserialize", nameof(BinaryDeserialize), 0_00500000, TriggerType.All, CallFlags.None); + + internal byte[] BinarySerialize(StackItem item) + { + return BinarySerializer.Serialize(item, MaxItemSize); + } + + internal StackItem BinaryDeserialize(byte[] data) + { + return BinarySerializer.Deserialize(data, MaxStackSize, MaxItemSize, ReferenceCounter); + } + } +} diff --git a/src/neo/SmartContract/ApplicationEngine.Blockchain.cs b/src/neo/SmartContract/ApplicationEngine.Blockchain.cs new file mode 100644 index 0000000000..025fae972a --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Blockchain.cs @@ -0,0 +1,103 @@ +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using System; +using System.Numerics; + +namespace Neo.SmartContract +{ + partial class ApplicationEngine + { + public const uint MaxTraceableBlocks = Transaction.MaxValidUntilBlockIncrement; + + public static readonly InteropDescriptor System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", nameof(GetBlockchainHeight), 0_00000400, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", nameof(GetBlock), 0_02500000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", nameof(GetTransaction), 0_01000000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", nameof(GetTransactionHeight), 0_01000000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Blockchain_GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", nameof(GetTransactionFromBlock), 0_01000000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Blockchain_GetContract = Register("System.Blockchain.GetContract", nameof(GetContract), 0_01000000, TriggerType.Application, CallFlags.AllowStates); + + internal uint GetBlockchainHeight() + { + return Snapshot.Height; + } + + 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; + } + + internal Transaction GetTransaction(UInt256 hash) + { + TransactionState state = Snapshot.Transactions.TryGet(hash); + if (state != null && !IsTraceableBlock(Snapshot, state.BlockIndex)) state = null; + return state?.Transaction; + } + + 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; + } + + 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]); + } + + internal ContractState GetContract(UInt160 hash) + { + return Snapshot.Contracts.TryGet(hash); + } + + private static bool IsTraceableBlock(StoreView snapshot, uint index) + { + if (index > snapshot.Height) return false; + return index + MaxTraceableBlocks > snapshot.Height; + } + } +} diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs new file mode 100644 index 0000000000..31407a596c --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -0,0 +1,181 @@ +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.Ledger; +using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native; +using Neo.VM; +using System; +using System.Linq; +using Array = Neo.VM.Types.Array; + +namespace Neo.SmartContract +{ + partial class ApplicationEngine + { + public const int MaxContractLength = 1024 * 1024; + + public static readonly InteropDescriptor System_Contract_Create = Register("System.Contract.Create", nameof(CreateContract), 0, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor System_Contract_Update = Register("System.Contract.Update", nameof(UpdateContract), 0, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor System_Contract_Destroy = Register("System.Contract.Destroy", nameof(DestroyContract), 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor System_Contract_Call = Register("System.Contract.Call", nameof(CallContract), 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); + public static readonly InteropDescriptor System_Contract_CallEx = Register("System.Contract.CallEx", nameof(CallContractEx), 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); + public static readonly InteropDescriptor System_Contract_IsStandard = Register("System.Contract.IsStandard", nameof(IsStandardContract), 0_00030000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Contract_GetCallFlags = Register("System.Contract.GetCallFlags", nameof(GetCallFlags), 0_00030000, TriggerType.All, 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), 0_00010000, TriggerType.All, CallFlags.None); + + internal ContractState CreateContract(byte[] script, byte[] manifest) + { + if (script.Length == 0 || script.Length > MaxContractLength || manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) + throw new ArgumentException(); + + if (!AddGas(StoragePrice * (script.Length + manifest.Length))) + throw new InvalidOperationException(); + + UInt160 hash = script.ToScriptHash(); + ContractState contract = Snapshot.Contracts.TryGet(hash); + if (contract != null) throw new InvalidOperationException(); + contract = new ContractState + { + Id = Snapshot.ContractId.GetAndChange().NextId++, + Script = script.ToArray(), + Manifest = ContractManifest.Parse(manifest) + }; + + if (!contract.Manifest.IsValid(hash)) throw new InvalidOperationException(); + + Snapshot.Contracts.Add(hash, contract); + return contract; + } + + internal void UpdateContract(byte[] script, byte[] manifest) + { + if (!AddGas(StoragePrice * (script?.Length ?? 0 + manifest?.Length ?? 0))) + throw new InvalidOperationException(); + + var contract = Snapshot.Contracts.TryGet(CurrentScriptHash); + if (contract is null) throw new InvalidOperationException(); + + if (script != null) + { + if (script.Length == 0 || script.Length > MaxContractLength) + throw new ArgumentException(); + UInt160 hash_new = script.ToScriptHash(); + if (hash_new.Equals(CurrentScriptHash) || Snapshot.Contracts.TryGet(hash_new) != null) + throw new InvalidOperationException(); + contract = new ContractState + { + Id = contract.Id, + Script = script.ToArray(), + Manifest = contract.Manifest + }; + contract.Manifest.Abi.Hash = hash_new; + Snapshot.Contracts.Add(hash_new, contract); + Snapshot.Contracts.Delete(CurrentScriptHash); + } + if (manifest != null) + { + if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) + throw new ArgumentException(); + contract = Snapshot.Contracts.GetAndChange(contract.ScriptHash); + contract.Manifest = ContractManifest.Parse(manifest); + if (!contract.Manifest.IsValid(contract.ScriptHash)) + throw new InvalidOperationException(); + if (!contract.HasStorage && Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id)).Any()) + throw new InvalidOperationException(); + } + } + + internal void DestroyContract() + { + UInt160 hash = CurrentScriptHash; + ContractState contract = Snapshot.Contracts.TryGet(hash); + if (contract == null) return; + Snapshot.Contracts.Delete(hash); + if (contract.HasStorage) + foreach (var (key, _) in Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id))) + Snapshot.Storages.Delete(key); + } + + internal void CallContract(UInt160 contractHash, string method, Array args) + { + CallContractInternal(contractHash, method, args, CallFlags.All); + } + + internal void CallContractEx(UInt160 contractHash, string method, Array args, CallFlags callFlags) + { + if ((callFlags & ~CallFlags.All) != 0) + throw new ArgumentOutOfRangeException(nameof(callFlags)); + CallContractInternal(contractHash, method, args, callFlags); + } + + private void CallContractInternal(UInt160 contractHash, string method, Array args, CallFlags flags) + { + if (method.StartsWith('_')) throw new ArgumentException(); + + ContractState contract = Snapshot.Contracts.TryGet(contractHash); + if (contract is null) throw new InvalidOperationException(); + + ContractManifest currentManifest = Snapshot.Contracts.TryGet(CurrentScriptHash)?.Manifest; + + if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method)) + throw new InvalidOperationException(); + + if (invocationCounter.TryGetValue(contract.ScriptHash, out var counter)) + { + invocationCounter[contract.ScriptHash] = counter + 1; + } + else + { + invocationCounter[contract.ScriptHash] = 1; + } + + ExecutionContextState state = CurrentContext.GetState(); + UInt160 callingScriptHash = state.ScriptHash; + CallFlags callingFlags = state.CallFlags; + + ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); + if (md is null) throw new InvalidOperationException(); + int rvcount = md.ReturnType == ContractParameterType.Void ? 0 : 1; + ExecutionContext context_new = LoadScript(contract.Script, rvcount); + state = context_new.GetState(); + state.CallingScriptHash = callingScriptHash; + state.CallFlags = flags & callingFlags; + + if (NativeContract.IsNative(contractHash)) + { + context_new.EvaluationStack.Push(args); + context_new.EvaluationStack.Push(method); + } + else + { + for (int i = args.Count - 1; i >= 0; i--) + context_new.EvaluationStack.Push(args[i]); + context_new.InstructionPointer = md.Offset; + } + + md = contract.Manifest.Abi.GetMethod("_initialize"); + if (md != null) LoadClonedContext(md.Offset); + } + + internal bool IsStandardContract(UInt160 hash) + { + ContractState contract = Snapshot.Contracts.TryGet(hash); + return contract is null || contract.Script.IsStandardContract(); + } + + internal CallFlags GetCallFlags() + { + var state = CurrentContext.GetState(); + return state.CallFlags; + } + + internal UInt160 CreateStandardAccount(ECPoint pubKey) + { + return Contract.CreateSignatureRedeemScript(pubKey).ToScriptHash(); + } + } +} diff --git a/src/neo/SmartContract/ApplicationEngine.Crypto.cs b/src/neo/SmartContract/ApplicationEngine.Crypto.cs new file mode 100644 index 0000000000..9cb3a9abf4 --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Crypto.cs @@ -0,0 +1,99 @@ +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; +using Neo.VM; +using Neo.VM.Types; +using System; + +namespace Neo.SmartContract +{ + partial class ApplicationEngine + { + public const long ECDsaVerifyPrice = 0_01000000; + + public static readonly InteropDescriptor Neo_Crypto_SHA256 = Register("Neo.Crypto.SHA256", nameof(Sha256), 0_01000000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.VerifyWithECDsaSecp256r1", nameof(VerifyWithECDsaSecp256r1), ECDsaVerifyPrice, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.VerifyWithECDsaSecp256k1", nameof(VerifyWithECDsaSecp256k1), ECDsaVerifyPrice, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256r1", nameof(CheckMultisigWithECDsaSecp256r1), 0, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256k1", nameof(CheckMultisigWithECDsaSecp256k1), 0, TriggerType.All, CallFlags.None); + + internal byte[] Sha256(StackItem item) + { + ReadOnlySpan value = item switch + { + InteropInterface _interface => _interface.GetInterface().GetHashData(), + Null _ => ScriptContainer.GetHashData(), + _ => item.GetSpan() + }; + return value.Sha256(); + } + + internal bool VerifyWithECDsaSecp256r1(StackItem item, byte[] pubkey, byte[] signature) + { + return VerifyWithECDsa(item, pubkey, signature, ECCurve.Secp256r1); + } + + internal bool VerifyWithECDsaSecp256k1(StackItem item, byte[] pubkey, byte[] signature) + { + return VerifyWithECDsa(item, pubkey, signature, ECCurve.Secp256k1); + } + + private bool VerifyWithECDsa(StackItem item, byte[] pubkey, byte[] signature, ECCurve curve) + { + ReadOnlySpan message = item switch + { + InteropInterface _interface => _interface.GetInterface().GetHashData(), + Null _ => ScriptContainer.GetHashData(), + _ => item.GetSpan() + }; + try + { + return Crypto.VerifySignature(message, signature, pubkey, curve); + } + catch (ArgumentException) + { + return false; + } + } + + internal bool CheckMultisigWithECDsaSecp256r1(StackItem item, byte[][] pubkeys, byte[][] signatures) + { + return CheckMultiSigWithECDsa(item, pubkeys, signatures, ECCurve.Secp256r1); + } + + internal bool CheckMultisigWithECDsaSecp256k1(StackItem item, byte[][] pubkeys, byte[][] signatures) + { + return CheckMultiSigWithECDsa(item, pubkeys, signatures, ECCurve.Secp256k1); + } + + private bool CheckMultiSigWithECDsa(StackItem item0, byte[][] pubkeys, byte[][] signatures, ECCurve curve) + { + int m = signatures.Length, n = pubkeys.Length; + ReadOnlySpan message = item0 switch + { + InteropInterface _interface => _interface.GetInterface().GetHashData(), + Null _ => ScriptContainer.GetHashData(), + _ => item0.GetSpan() + }; + if (n == 0 || m == 0 || m > n) throw new ArgumentException(); + if (!AddGas(ECDsaVerifyPrice * n)) throw new InvalidOperationException(); + try + { + for (int i = 0, j = 0; i < m && j < n;) + { + if (Crypto.VerifySignature(message, signatures[i], pubkeys[j], curve)) + i++; + j++; + if (m - i > n - j) + return false; + } + } + catch (ArgumentException) + { + return false; + } + return true; + } + } +} diff --git a/src/neo/SmartContract/ApplicationEngine.Enumerator.cs b/src/neo/SmartContract/ApplicationEngine.Enumerator.cs new file mode 100644 index 0000000000..7fdbbd8bf8 --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Enumerator.cs @@ -0,0 +1,41 @@ +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), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Enumerator_Next = Register("System.Enumerator.Next", nameof(EnumeratorNext), 0_01000000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Enumerator_Value = Register("System.Enumerator.Value", nameof(EnumeratorValue), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Enumerator_Concat = Register("System.Enumerator.Concat", nameof(ConcatEnumerators), 0_00000400, TriggerType.All, CallFlags.None); + + internal IEnumerator CreateEnumerator(StackItem item) + { + return item switch + { + Array array => new ArrayWrapper(array), + PrimitiveType primitive => new ByteArrayWrapper(primitive), + _ => throw new ArgumentException() + }; + } + + internal bool EnumeratorNext(IEnumerator enumerator) + { + return enumerator.Next(); + } + + internal StackItem EnumeratorValue(IEnumerator enumerator) + { + return enumerator.Value(); + } + + 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 new file mode 100644 index 0000000000..be3bde386b --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Iterator.cs @@ -0,0 +1,48 @@ +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_Iterator_Create = Register("System.Iterator.Create", nameof(CreateIterator), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Iterator_Key = Register("System.Iterator.Key", nameof(IteratorKey), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Iterator_Keys = Register("System.Iterator.Keys", nameof(IteratorKeys), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Iterator_Values = Register("System.Iterator.Values", nameof(IteratorValues), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Iterator_Concat = Register("System.Iterator.Concat", nameof(ConcatIterators), 0_00000400, TriggerType.All, CallFlags.None); + + internal IIterator CreateIterator(StackItem item) + { + return item switch + { + Array array => new ArrayWrapper(array), + Map map => new MapWrapper(map), + PrimitiveType primitive => new ByteArrayWrapper(primitive), + _ => throw new ArgumentException() + }; + } + + internal PrimitiveType IteratorKey(IIterator iterator) + { + return iterator.Key(); + } + + internal IEnumerator IteratorKeys(IIterator iterator) + { + return new IteratorKeysWrapper(iterator); + } + + internal IEnumerator IteratorValues(IIterator iterator) + { + return new IteratorValuesWrapper(iterator); + } + + internal IIterator ConcatIterators(IIterator first, IIterator second) + { + return new ConcatenatedIterator(first, second); + } + } +} diff --git a/src/neo/SmartContract/ApplicationEngine.Json.cs b/src/neo/SmartContract/ApplicationEngine.Json.cs new file mode 100644 index 0000000000..d8f972c343 --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Json.cs @@ -0,0 +1,21 @@ +using Neo.IO.Json; +using Neo.VM.Types; + +namespace Neo.SmartContract +{ + partial class ApplicationEngine + { + public static readonly InteropDescriptor System_Json_Serialize = Register("System.Json.Serialize", nameof(JsonSerialize), 0_00100000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Json_Deserialize = Register("System.Json.Deserialize", nameof(JsonDeserialize), 0_00500000, TriggerType.All, CallFlags.None); + + internal byte[] JsonSerialize(StackItem item) + { + return JsonSerializer.SerializeToByteArray(item, MaxItemSize); + } + + internal StackItem JsonDeserialize(byte[] json) + { + return JsonSerializer.Deserialize(JObject.Parse(json, 10), ReferenceCounter); + } + } +} diff --git a/src/neo/SmartContract/ApplicationEngine.Native.cs b/src/neo/SmartContract/ApplicationEngine.Native.cs new file mode 100644 index 0000000000..e413df7b17 --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Native.cs @@ -0,0 +1,34 @@ +using Neo.Ledger; +using Neo.SmartContract.Native; +using System; + +namespace Neo.SmartContract +{ + partial class ApplicationEngine + { + public static readonly InteropDescriptor Neo_Native_Deploy = Register("Neo.Native.Deploy", nameof(DeployNativeContracts), 0, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor Neo_Native_Call = Register("Neo.Native.Call", nameof(CallNativeContract), 0, TriggerType.System | TriggerType.Application, CallFlags.None); + + internal void DeployNativeContracts() + { + if (Snapshot.PersistingBlock.Index != 0) + throw new InvalidOperationException(); + foreach (NativeContract contract in NativeContract.Contracts) + { + Snapshot.Contracts.Add(contract.Hash, new ContractState + { + Id = contract.Id, + Script = contract.Script, + Manifest = contract.Manifest + }); + contract.Initialize(this); + } + } + + internal void CallNativeContract(string name) + { + if (!NativeContract.GetContract(name).Invoke(this)) + throw new InvalidOperationException(); + } + } +} diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs new file mode 100644 index 0000000000..481e6fcfcb --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -0,0 +1,173 @@ +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.Network.P2P.Payloads; +using Neo.VM.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Array = Neo.VM.Types.Array; + +namespace Neo.SmartContract +{ + partial class ApplicationEngine + { + public const int MaxNotificationSize = 1024; + + public static readonly InteropDescriptor System_Runtime_Platform = Register("System.Runtime.Platform", nameof(GetPlatform), 0_00000250, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", nameof(Trigger), 0_00000250, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetTime = Register("System.Runtime.GetTime", nameof(GetTime), 0_00000250, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Runtime_GetScriptContainer = Register("System.Runtime.GetScriptContainer", nameof(GetScriptContainer), 0_00000250, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", nameof(CurrentScriptHash), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", nameof(CallingScriptHash), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", nameof(EntryScriptHash), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", nameof(CheckWitness), 0_00030000, TriggerType.All, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", nameof(GetInvocationCounter), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_Log = Register("System.Runtime.Log", nameof(RuntimeLog), 0_01000000, TriggerType.All, CallFlags.AllowNotify); + public static readonly InteropDescriptor System_Runtime_Notify = Register("System.Runtime.Notify", nameof(RuntimeNotify), 0_01000000, TriggerType.All, CallFlags.AllowNotify); + public static readonly InteropDescriptor System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", nameof(GetNotifications), 0_00010000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_GasLeft = Register("System.Runtime.GasLeft", nameof(GasLeft), 0_00000400, TriggerType.All, CallFlags.None); + + private static bool CheckItemForNotification(StackItem state) + { + int size = 0; + List items_checked = new List(); + Queue items_unchecked = new Queue(); + while (true) + { + switch (state) + { + case Struct array: + foreach (StackItem item in array) + items_unchecked.Enqueue(item); + break; + case Array array: + if (items_checked.All(p => !ReferenceEquals(p, array))) + { + items_checked.Add(array); + foreach (StackItem item in array) + items_unchecked.Enqueue(item); + } + break; + case PrimitiveType primitive: + size += primitive.Size; + break; + case Null _: + break; + case InteropInterface _: + return false; + case Map map: + if (items_checked.All(p => !ReferenceEquals(p, map))) + { + items_checked.Add(map); + foreach (var pair in map) + { + size += pair.Key.Size; + items_unchecked.Enqueue(pair.Value); + } + } + break; + } + if (size > MaxNotificationSize) return false; + if (items_unchecked.Count == 0) return true; + state = items_unchecked.Dequeue(); + } + } + + internal string GetPlatform() + { + return "NEO"; + } + + internal ulong GetTime() + { + return Snapshot.PersistingBlock.Timestamp; + } + + internal IInteroperable GetScriptContainer() + { + return ScriptContainer as IInteroperable; + } + + internal bool CheckWitness(byte[] hashOrPubkey) + { + UInt160 hash = hashOrPubkey.Length switch + { + 20 => new UInt160(hashOrPubkey), + 33 => Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(hashOrPubkey, ECCurve.Secp256r1)).ToScriptHash(), + _ => throw new ArgumentException() + }; + return CheckWitnessInternal(hash); + } + + internal bool CheckWitnessInternal(UInt160 hash) + { + if (ScriptContainer is Transaction tx) + { + Cosigner cosigner = tx.Cosigners.FirstOrDefault(p => p.Account.Equals(hash)); + if (cosigner is null) return false; + if (cosigner.Scopes == WitnessScope.Global) return true; + if (cosigner.Scopes.HasFlag(WitnessScope.CalledByEntry)) + { + if (CallingScriptHash == EntryScriptHash) + return true; + } + if (cosigner.Scopes.HasFlag(WitnessScope.CustomContracts)) + { + if (cosigner.AllowedContracts.Contains(CurrentScriptHash)) + return true; + } + if (cosigner.Scopes.HasFlag(WitnessScope.CustomGroups)) + { + var contract = Snapshot.Contracts[CallingScriptHash]; + // check if current group is the required one + if (contract.Manifest.Groups.Select(p => p.PubKey).Intersect(cosigner.AllowedGroups).Any()) + return true; + } + return false; + } + + // only for non-Transaction types (Block, etc) + + var hashes_for_verifying = ScriptContainer.GetScriptHashesForVerifying(Snapshot); + return hashes_for_verifying.Contains(hash); + } + + internal int GetInvocationCounter() + { + if (!invocationCounter.TryGetValue(CurrentScriptHash, out var counter)) + throw new InvalidOperationException(); + return counter; + } + + internal void RuntimeLog(byte[] state) + { + if (state.Length > MaxNotificationSize) throw new ArgumentException(); + string message = Encoding.UTF8.GetString(state); + Log?.Invoke(this, new LogEventArgs(ScriptContainer, CurrentScriptHash, message)); + } + + internal void RuntimeNotify(StackItem state) + { + if (!CheckItemForNotification(state)) throw new ArgumentException(); + SendNotification(CurrentScriptHash, state); + } + + internal void SendNotification(UInt160 script_hash, StackItem state) + { + NotifyEventArgs notification = new NotifyEventArgs(ScriptContainer, script_hash, state); + Notify?.Invoke(this, notification); + notifications.Add(notification); + } + + internal NotifyEventArgs[] GetNotifications(UInt160 hash) + { + IEnumerable notifications = Notifications; + if (hash != null) // must filter by scriptHash + notifications = notifications.Where(p => p.ScriptHash == hash); + NotifyEventArgs[] array = notifications.ToArray(); + if (array.Length > MaxStackSize) throw new InvalidOperationException(); + return array; + } + } +} diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs new file mode 100644 index 0000000000..b579c5f37f --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -0,0 +1,127 @@ +using Neo.Ledger; +using Neo.SmartContract.Iterators; +using System; +using System.Linq; + +namespace Neo.SmartContract +{ + partial class ApplicationEngine + { + public const long StoragePrice = 100000; + public const int MaxStorageKeySize = 64; + public const int MaxStorageValueSize = ushort.MaxValue; + + public static readonly InteropDescriptor System_Storage_GetContext = Register("System.Storage.GetContext", nameof(GetStorageContext), 0_00000400, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", nameof(GetReadOnlyContext), 0_00000400, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Storage_AsReadOnly = Register("System.Storage.AsReadOnly", nameof(AsReadOnly), 0_00000400, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Storage_Get = Register("System.Storage.Get", nameof(Get), 0_01000000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Storage_Find = Register("System.Storage.Find", nameof(Find), 0_01000000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 0, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor System_Storage_PutEx = Register("System.Storage.PutEx", nameof(PutEx), 0, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 1 * StoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); + + internal StorageContext GetStorageContext() + { + ContractState contract = Snapshot.Contracts.TryGet(CurrentScriptHash); + if (!contract.HasStorage) throw new InvalidOperationException(); + return new StorageContext + { + Id = contract.Id, + IsReadOnly = false + }; + } + + internal StorageContext GetReadOnlyContext() + { + ContractState contract = Snapshot.Contracts.TryGet(CurrentScriptHash); + if (!contract.HasStorage) throw new InvalidOperationException(); + return new StorageContext + { + Id = contract.Id, + IsReadOnly = true + }; + } + + internal StorageContext AsReadOnly(StorageContext context) + { + if (!context.IsReadOnly) + context = new StorageContext + { + Id = context.Id, + IsReadOnly = true + }; + return context; + } + + internal byte[] Get(StorageContext context, byte[] key) + { + return Snapshot.Storages.TryGet(new StorageKey + { + Id = context.Id, + Key = key.ToArray() + })?.Value; + } + + 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()); + disposables.Add(iterator); + return iterator; + } + + internal void Put(StorageContext context, byte[] key, byte[] value) + { + PutExInternal(context, key, value, StorageFlags.None); + } + + internal void PutEx(StorageContext context, byte[] key, byte[] value, StorageFlags flags) + { + PutExInternal(context, key, value, flags); + } + + private void PutExInternal(StorageContext context, byte[] key, byte[] value, StorageFlags flags) + { + if (key.Length > MaxStorageKeySize || value.Length > MaxStorageValueSize || context.IsReadOnly) + throw new ArgumentException(); + + int newDataSize; + StorageKey skey = new StorageKey + { + Id = context.Id, + Key = key + }; + StorageItem item = Snapshot.Storages.GetAndChange(skey); + if (item is null) + { + newDataSize = key.Length + value.Length; + Snapshot.Storages.Add(skey, item = new StorageItem()); + } + else + { + if (item.IsConstant) throw new InvalidOperationException(); + if (value.Length <= item.Value.Length) + newDataSize = 1; + else + newDataSize = value.Length - item.Value.Length; + } + if (!AddGas(newDataSize * StoragePrice)) throw new InvalidOperationException(); + + item.Value = value; + item.IsConstant = flags.HasFlag(StorageFlags.Constant); + } + + internal void Delete(StorageContext context, byte[] key) + { + if (context.IsReadOnly) throw new ArgumentException(); + StorageKey skey = new StorageKey + { + Id = context.Id, + Key = key + }; + if (Snapshot.Storages.TryGet(skey)?.IsConstant == true) + throw new InvalidOperationException(); + Snapshot.Storages.Delete(skey); + } + } +} diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index bfec85c8f8..11708d9609 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -1,3 +1,4 @@ +using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -5,8 +6,12 @@ using Neo.VM.Types; using System; using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Reflection; using System.Text; using Array = System.Array; +using VMArray = Neo.VM.Types.Array; namespace Neo.SmartContract { @@ -16,22 +21,24 @@ public partial class ApplicationEngine : ExecutionEngine public static event EventHandler Log; public const long GasFree = 0; + + private static Dictionary services; private readonly long gas_amount; private readonly bool testMode; private readonly List notifications = new List(); private readonly List disposables = new List(); + private readonly Dictionary invocationCounter = new Dictionary(); + public static IEnumerable Services => services.Values; public TriggerType Trigger { get; } public IVerifiable ScriptContainer { get; } public StoreView Snapshot { get; } public long GasConsumed { get; private set; } = 0; public long GasLeft => testMode ? -1 : gas_amount - GasConsumed; - public UInt160 CurrentScriptHash => CurrentContext?.GetState().ScriptHash; public UInt160 CallingScriptHash => CurrentContext?.GetState().CallingScriptHash; public UInt160 EntryScriptHash => EntryContext?.GetState().ScriptHash; public IReadOnlyList Notifications => notifications; - internal Dictionary InvocationCounter { get; } = new Dictionary(); public ApplicationEngine(TriggerType trigger, IVerifiable container, StoreView snapshot, long gas, bool testMode = false) { @@ -42,12 +49,6 @@ public ApplicationEngine(TriggerType trigger, IVerifiable container, StoreView s this.Snapshot = snapshot; } - internal T AddDisposable(T disposable) where T : IDisposable - { - disposables.Add(disposable); - return disposable; - } - internal bool AddGas(long gas) { GasConsumed = checked(GasConsumed + gas); @@ -70,6 +71,32 @@ public ExecutionContext LoadScript(Script script, CallFlags callFlags, int rvcou return context; } + private StackItem ConvertReturnValue(object value) + { + return value switch + { + null => StackItem.Null, + bool b => b, + sbyte i => i, + byte i => (BigInteger)i, + short i => i, + ushort i => (BigInteger)i, + int i => i, + uint i => i, + long i => i, + ulong i => i, + Enum e => ConvertReturnValue(Convert.ChangeType(e, e.GetTypeCode())), + byte[] data => data, + string s => s, + UInt160 i => i.ToArray(), + UInt256 i => i.ToArray(), + IInteroperable interoperable => interoperable.ToStackItem(ReferenceCounter), + IInteroperable[] array => new VMArray(ReferenceCounter, array.Select(p => p.ToStackItem(ReferenceCounter))), + StackItem item => item, + _ => StackItem.FromInterface(value) + }; + } + public override void Dispose() { foreach (IDisposable disposable in disposables) @@ -80,7 +107,53 @@ public override void Dispose() protected override bool OnSysCall(uint method) { - return InteropService.Invoke(this, method); + if (!services.TryGetValue(method, out InteropDescriptor descriptor)) + return false; + if (!descriptor.AllowedTriggers.HasFlag(Trigger)) + return false; + ExecutionContextState state = CurrentContext.GetState(); + if (!state.CallFlags.HasFlag(descriptor.RequiredCallFlags)) + return false; + if (!AddGas(descriptor.FixedPrice)) + return false; + List parameters = descriptor.Parameters.Length > 0 + ? new List() + : null; + foreach (var pd in descriptor.Parameters) + { + StackItem item = Pop(); + object value; + if (pd.IsArray) + { + Array av; + if (item is VMArray array) + { + av = Array.CreateInstance(pd.Type.GetElementType(), array.Count); + for (int i = 0; i < av.Length; i++) + av.SetValue(pd.Converter(array[i]), i); + } + else + { + av = Array.CreateInstance(pd.Type.GetElementType(), (int)item.GetBigInteger()); + for (int i = 0; i < av.Length; i++) + av.SetValue(pd.Converter(Pop()), i); + } + value = av; + } + else + { + value = pd.Converter(item); + if (pd.IsEnum) + value = Convert.ChangeType(value, pd.Type); + else if (pd.IsInterface) + value = ((InteropInterface)value).GetInterface(); + } + parameters.Add(value); + } + object returnValue = descriptor.Handler.Invoke(this, parameters?.ToArray()); + if (descriptor.Handler.ReturnType != typeof(void)) + Push(ConvertReturnValue(returnValue)); + return true; } protected override bool PreExecuteInstruction() @@ -111,6 +184,16 @@ private static Block CreateDummyBlock(StoreView snapshot) }; } + private static InteropDescriptor Register(string name, string handler, long fixedPrice, TriggerType allowedTriggers, 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, allowedTriggers, requiredCallFlags); + services ??= new Dictionary(); + services.Add(descriptor.Hash, descriptor); + return descriptor; + } + public static ApplicationEngine Run(byte[] script, StoreView snapshot, IVerifiable container = null, Block persistingBlock = null, int offset = 0, bool testMode = false, long extraGAS = default) { @@ -129,19 +212,6 @@ public static ApplicationEngine Run(byte[] script, IVerifiable container = null, } } - internal void SendLog(UInt160 script_hash, string message) - { - LogEventArgs log = new LogEventArgs(ScriptContainer, script_hash, message); - Log?.Invoke(this, log); - } - - internal void SendNotification(UInt160 script_hash, StackItem state) - { - NotifyEventArgs notification = new NotifyEventArgs(ScriptContainer, script_hash, state); - Notify?.Invoke(this, notification); - notifications.Add(notification); - } - public bool TryPop(out string s) { if (TryPop(out ReadOnlySpan b)) diff --git a/src/neo/SmartContract/Contract.cs b/src/neo/SmartContract/Contract.cs index 32f409eab8..ca5a27b945 100644 --- a/src/neo/SmartContract/Contract.cs +++ b/src/neo/SmartContract/Contract.cs @@ -82,7 +82,7 @@ public static byte[] CreateMultiSigRedeemScript(int m, params ECPoint[] publicKe } sb.EmitPush(publicKeys.Length); sb.Emit(OpCode.PUSHNULL); - sb.EmitSysCall(InteropService.Crypto.CheckMultisigWithECDsaSecp256r1); + sb.EmitSysCall(ApplicationEngine.Neo_Crypto_CheckMultisigWithECDsaSecp256r1); return sb.ToArray(); } } @@ -102,7 +102,7 @@ public static byte[] CreateSignatureRedeemScript(ECPoint publicKey) { sb.EmitPush(publicKey.EncodePoint(true)); sb.Emit(OpCode.PUSHNULL); - sb.EmitSysCall(InteropService.Crypto.VerifyWithECDsaSecp256r1); + sb.EmitSysCall(ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1); return sb.ToArray(); } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index c9ca603f68..fcab335b9d 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -91,7 +91,7 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List if (script[i++] != (byte)OpCode.PUSHNULL) return false; if (script[i++] != (byte)OpCode.SYSCALL) return false; if (script.Length != i + 4) return false; - if (BitConverter.ToUInt32(script, i) != InteropService.Crypto.CheckMultisigWithECDsaSecp256r1) + if (BitConverter.ToUInt32(script, i) != ApplicationEngine.Neo_Crypto_CheckMultisigWithECDsaSecp256r1) return false; return true; } @@ -103,7 +103,7 @@ public static bool IsSignatureContract(this byte[] script) || script[1] != 33 || script[35] != (byte)OpCode.PUSHNULL || script[36] != (byte)OpCode.SYSCALL - || BitConverter.ToUInt32(script, 37) != InteropService.Crypto.VerifyWithECDsaSecp256r1) + || BitConverter.ToUInt32(script, 37) != ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1) return false; return true; } diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index b5efb42edd..86e4c6ba87 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -1,23 +1,27 @@ using Neo.Cryptography; using System; +using System.Linq; +using System.Reflection; using System.Text; namespace Neo.SmartContract { public class InteropDescriptor { - public string Method { get; } + public string Name { get; } public uint Hash { get; } - internal Func Handler { get; } + internal MethodInfo Handler { get; } + internal InteropParameterDescriptor[] Parameters { get; } public long FixedPrice { get; } public TriggerType AllowedTriggers { get; } public CallFlags RequiredCallFlags { get; } - internal InteropDescriptor(string method, Func handler, long fixedPrice, TriggerType allowedTriggers, CallFlags requiredCallFlags) + internal InteropDescriptor(string name, MethodInfo handler, long fixedPrice, TriggerType allowedTriggers, CallFlags requiredCallFlags) { - this.Method = method; - this.Hash = BitConverter.ToUInt32(Encoding.ASCII.GetBytes(method).Sha256(), 0); + this.Name = name; + this.Hash = BitConverter.ToUInt32(Encoding.ASCII.GetBytes(name).Sha256(), 0); this.Handler = handler; + this.Parameters = handler.GetParameters().Select(p => new InteropParameterDescriptor(p)).ToArray(); this.FixedPrice = fixedPrice; this.AllowedTriggers = allowedTriggers; this.RequiredCallFlags = requiredCallFlags; diff --git a/src/neo/SmartContract/InteropParameterDescriptor.cs b/src/neo/SmartContract/InteropParameterDescriptor.cs new file mode 100644 index 0000000000..e7ee51cf28 --- /dev/null +++ b/src/neo/SmartContract/InteropParameterDescriptor.cs @@ -0,0 +1,59 @@ +using Neo.Cryptography.ECC; +using Neo.VM; +using Neo.VM.Types; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace Neo.SmartContract +{ + internal class InteropParameterDescriptor + { + public Type Type { get; } + public Func Converter { get; } + public bool IsEnum => Type.IsEnum; + public bool IsArray => Type.IsArray && Type.GetElementType() != typeof(byte); + public bool IsInterface { get; } + + private static readonly Dictionary> converters = new Dictionary> + { + [typeof(StackItem)] = p => p, + [typeof(VM.Types.Array)] = p => p, + [typeof(InteropInterface)] = p => p, + [typeof(sbyte)] = p => (sbyte)p.GetBigInteger(), + [typeof(byte)] = p => (byte)p.GetBigInteger(), + [typeof(short)] = p => (short)p.GetBigInteger(), + [typeof(ushort)] = p => (ushort)p.GetBigInteger(), + [typeof(int)] = p => (int)p.GetBigInteger(), + [typeof(uint)] = p => (uint)p.GetBigInteger(), + [typeof(long)] = p => (long)p.GetBigInteger(), + [typeof(ulong)] = p => (ulong)p.GetBigInteger(), + [typeof(byte[])] = p => p.IsNull ? null : p.GetSpan().ToArray(), + [typeof(string)] = p => p.IsNull ? null : p.GetString(), + [typeof(UInt160)] = p => p.IsNull ? null : new UInt160(p.GetSpan()), + [typeof(UInt256)] = p => p.IsNull ? null : new UInt256(p.GetSpan()), + [typeof(ECPoint)] = p => p.IsNull ? null : ECPoint.DecodePoint(p.GetSpan(), ECCurve.Secp256r1), + }; + + public InteropParameterDescriptor(ParameterInfo parameterInfo) + { + Type = parameterInfo.ParameterType; + if (IsEnum) + { + Converter = converters[Type.GetEnumUnderlyingType()]; + } + else if (IsArray) + { + Converter = converters[Type.GetElementType()]; + } + else + { + IsInterface = !converters.TryGetValue(Type, out var converter); + if (IsInterface) + Converter = converters[typeof(InteropInterface)]; + else + Converter = converter; + } + } + } +} diff --git a/src/neo/SmartContract/InteropService.Binary.cs b/src/neo/SmartContract/InteropService.Binary.cs deleted file mode 100644 index 50d5946dbd..0000000000 --- a/src/neo/SmartContract/InteropService.Binary.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Neo.VM.Types; -using System; - -namespace Neo.SmartContract -{ - partial class InteropService - { - public static class Binary - { - public static readonly InteropDescriptor Serialize = Register("System.Binary.Serialize", Binary_Serialize, 0_00100000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Deserialize = Register("System.Binary.Deserialize", Binary_Deserialize, 0_00500000, TriggerType.All, CallFlags.None); - - private static bool Binary_Serialize(ApplicationEngine engine) - { - if (!engine.TryPop(out StackItem item)) return false; - byte[] serialized = BinarySerializer.Serialize(item, engine.MaxItemSize); - engine.Push(serialized); - return true; - } - - private static bool Binary_Deserialize(ApplicationEngine engine) - { - if (!engine.TryPop(out ReadOnlySpan data)) return false; - StackItem item = BinarySerializer.Deserialize(data, engine.MaxStackSize, engine.MaxItemSize, engine.ReferenceCounter); - engine.CurrentContext.EvaluationStack.Push(item); - return true; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.Blockchain.cs b/src/neo/SmartContract/InteropService.Blockchain.cs deleted file mode 100644 index a8ada65f45..0000000000 --- a/src/neo/SmartContract/InteropService.Blockchain.cs +++ /dev/null @@ -1,120 +0,0 @@ -using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.Persistence; -using Neo.VM; -using Neo.VM.Types; -using System; -using System.Numerics; - -namespace Neo.SmartContract -{ - partial class InteropService - { - public static class Blockchain - { - public const uint MaxTraceableBlocks = Transaction.MaxValidUntilBlockIncrement; - - public static readonly InteropDescriptor GetHeight = Register("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor GetBlock = Register("System.Blockchain.GetBlock", Blockchain_GetBlock, 0_02500000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor GetTransaction = Register("System.Blockchain.GetTransaction", Blockchain_GetTransaction, 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", Blockchain_GetTransactionHeight, 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", Blockchain_GetTransactionFromBlock, 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor GetContract = Register("System.Blockchain.GetContract", Blockchain_GetContract, 0_01000000, TriggerType.Application, CallFlags.AllowStates); - - private static bool Blockchain_GetHeight(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.Height); - return true; - } - - private static bool Blockchain_GetBlock(ApplicationEngine engine) - { - UInt256 hash; - if (engine.TryPop(out uint height)) - { - hash = Ledger.Blockchain.Singleton.GetBlockHash(height); - } - else if (engine.TryPop(out ReadOnlySpan data)) - { - if (data.Length != 32) return false; - hash = new UInt256(data); - } - else - { - return false; - } - Block block = hash != null ? engine.Snapshot.GetBlock(hash) : null; - if (block != null && !IsTraceableBlock(engine.Snapshot, block.Index)) block = null; - engine.Push(block?.ToStackItem(engine.ReferenceCounter) ?? StackItem.Null); - return true; - } - - private static bool Blockchain_GetTransaction(ApplicationEngine engine) - { - if (!engine.TryPop(out ReadOnlySpan hash)) return false; - TransactionState state = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); - if (state != null && !IsTraceableBlock(engine.Snapshot, state.BlockIndex)) state = null; - engine.Push(state?.Transaction.ToStackItem(engine.ReferenceCounter) ?? StackItem.Null); - return true; - } - - private static bool Blockchain_GetTransactionHeight(ApplicationEngine engine) - { - if (!engine.TryPop(out ReadOnlySpan hash)) return false; - TransactionState state = engine.Snapshot.Transactions.TryGet(new UInt256(hash)); - if (state != null && !IsTraceableBlock(engine.Snapshot, state.BlockIndex)) state = null; - engine.Push(state?.BlockIndex ?? BigInteger.MinusOne); - return true; - } - - private static bool Blockchain_GetTransactionFromBlock(ApplicationEngine engine) - { - UInt256 hash; - if (engine.TryPop(out uint height)) - { - hash = Ledger.Blockchain.Singleton.GetBlockHash(height); - } - else if (engine.TryPop(out ReadOnlySpan data)) - { - if (data.Length != 32) return false; - hash = new UInt256(data); - } - else - { - return false; - } - TrimmedBlock block = hash != null ? engine.Snapshot.Blocks.TryGet(hash) : null; - if (block != null && !IsTraceableBlock(engine.Snapshot, block.Index)) block = null; - if (block is null) - { - engine.Push(StackItem.Null); - } - else - { - if (!engine.TryPop(out int index)) return false; - if (index < 0 || index >= block.Hashes.Length - 1) return false; - Transaction tx = engine.Snapshot.GetTransaction(block.Hashes[index + 1]); - engine.Push(tx?.ToStackItem(engine.ReferenceCounter) ?? StackItem.Null); - } - return true; - } - - private static bool Blockchain_GetContract(ApplicationEngine engine) - { - UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan()); - ContractState contract = engine.Snapshot.Contracts.TryGet(hash); - if (contract == null) - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - else - engine.CurrentContext.EvaluationStack.Push(contract.ToStackItem(engine.ReferenceCounter)); - return true; - } - - private static bool IsTraceableBlock(StoreView snapshot, uint index) - { - if (index > snapshot.Height) return false; - return index + MaxTraceableBlocks > snapshot.Height; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.Contract.cs b/src/neo/SmartContract/InteropService.Contract.cs deleted file mode 100644 index fdcb7607be..0000000000 --- a/src/neo/SmartContract/InteropService.Contract.cs +++ /dev/null @@ -1,211 +0,0 @@ -using Neo.Cryptography.ECC; -using Neo.IO; -using Neo.Ledger; -using Neo.SmartContract.Manifest; -using Neo.SmartContract.Native; -using Neo.VM; -using Neo.VM.Types; -using System; -using System.Linq; -using Array = Neo.VM.Types.Array; - -namespace Neo.SmartContract -{ - partial class InteropService - { - public static class Contract - { - public const int MaxLength = 1024 * 1024; - - public static readonly InteropDescriptor Create = Register("System.Contract.Create", Contract_Create, 0, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor Update = Register("System.Contract.Update", Contract_Update, 0, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor Destroy = Register("System.Contract.Destroy", Contract_Destroy, 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor Call = Register("System.Contract.Call", Contract_Call, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); - public static readonly InteropDescriptor CallEx = Register("System.Contract.CallEx", Contract_CallEx, 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); - public static readonly InteropDescriptor IsStandard = Register("System.Contract.IsStandard", Contract_IsStandard, 0_00030000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor GetCallFlags = Register("System.Contract.GetCallFlags", Contract_GetCallFlags, 0_00030000, TriggerType.All, 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 CreateStandardAccount = Register("System.Contract.CreateStandardAccount", Contract_CreateStandardAccount, 0_00010000, TriggerType.All, CallFlags.None); - - private static bool Contract_GetCallFlags(ApplicationEngine engine) - { - var state = engine.CurrentContext.GetState(); - engine.Push((int)state.CallFlags); - return true; - } - - private static bool Contract_Create(ApplicationEngine engine) - { - if (!engine.TryPop(out ReadOnlySpan script)) return false; - if (script.Length == 0 || script.Length > MaxLength) return false; - - if (!engine.TryPop(out ReadOnlySpan manifest)) return false; - if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) return false; - - if (!engine.AddGas(Storage.GasPerByte * (script.Length + manifest.Length))) return false; - - UInt160 hash = script.ToScriptHash(); - ContractState contract = engine.Snapshot.Contracts.TryGet(hash); - if (contract != null) return false; - contract = new ContractState - { - Id = engine.Snapshot.ContractId.GetAndChange().NextId++, - Script = script.ToArray(), - Manifest = ContractManifest.Parse(manifest) - }; - - if (!contract.Manifest.IsValid(hash)) return false; - - engine.Snapshot.Contracts.Add(hash, contract); - engine.Push(StackItem.FromInterface(contract)); - return true; - } - - private static bool Contract_Update(ApplicationEngine engine) - { - if (!engine.TryPop(out StackItem item0)) return false; - if (!engine.TryPop(out StackItem item1)) return false; - - if (!engine.AddGas(Storage.GasPerByte * (item0.GetByteLength() + item1.GetByteLength()))) return false; - - var contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash); - if (contract is null) return false; - - if (!item0.IsNull) - { - ReadOnlySpan script = item0.GetSpan(); - if (script.Length == 0 || script.Length > MaxLength) return false; - UInt160 hash_new = script.ToScriptHash(); - if (hash_new.Equals(engine.CurrentScriptHash)) return false; - if (engine.Snapshot.Contracts.TryGet(hash_new) != null) return false; - contract = new ContractState - { - Id = contract.Id, - Script = script.ToArray(), - Manifest = contract.Manifest - }; - contract.Manifest.Abi.Hash = hash_new; - engine.Snapshot.Contracts.Add(hash_new, contract); - engine.Snapshot.Contracts.Delete(engine.CurrentScriptHash); - } - if (!item1.IsNull) - { - ReadOnlySpan manifest = item1.GetSpan(); - if (manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) return false; - contract = engine.Snapshot.Contracts.GetAndChange(contract.ScriptHash); - contract.Manifest = ContractManifest.Parse(manifest); - if (!contract.Manifest.IsValid(contract.ScriptHash)) return false; - if (!contract.HasStorage && engine.Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id)).Any()) return false; - } - - return true; - } - - private static bool Contract_Destroy(ApplicationEngine engine) - { - UInt160 hash = engine.CurrentScriptHash; - ContractState contract = engine.Snapshot.Contracts.TryGet(hash); - if (contract == null) return true; - engine.Snapshot.Contracts.Delete(hash); - if (contract.HasStorage) - foreach (var (key, _) in engine.Snapshot.Storages.Find(BitConverter.GetBytes(contract.Id))) - engine.Snapshot.Storages.Delete(key); - return true; - } - - private static bool Contract_Call(ApplicationEngine engine) - { - if (!engine.TryPop(out ReadOnlySpan contractHash)) return false; - if (!engine.TryPop(out string method)) return false; - if (!engine.TryPop(out Array args)) return false; - - return Contract_CallEx(engine, new UInt160(contractHash), method, args, CallFlags.All); - } - - private static bool Contract_CallEx(ApplicationEngine engine) - { - if (!engine.TryPop(out ReadOnlySpan contractHash)) return false; - if (!engine.TryPop(out string method)) return false; - if (!engine.TryPop(out Array args)) return false; - if (!engine.TryPop(out int flagsValue)) return false; - - CallFlags flags = (CallFlags)flagsValue; - if ((flags & ~CallFlags.All) != 0) return false; - - return Contract_CallEx(engine, new UInt160(contractHash), method, args, flags); - } - - private static bool Contract_CallEx(ApplicationEngine engine, UInt160 contractHash, string method, Array args, CallFlags flags) - { - if (method.StartsWith('_')) return false; - - ContractState contract = engine.Snapshot.Contracts.TryGet(contractHash); - if (contract is null) return false; - - ContractManifest currentManifest = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash)?.Manifest; - - if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method)) - return false; - - if (engine.InvocationCounter.TryGetValue(contract.ScriptHash, out var counter)) - { - engine.InvocationCounter[contract.ScriptHash] = counter + 1; - } - else - { - engine.InvocationCounter[contract.ScriptHash] = 1; - } - - ExecutionContextState state = engine.CurrentContext.GetState(); - UInt160 callingScriptHash = state.ScriptHash; - CallFlags callingFlags = state.CallFlags; - - ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); - if (md is null) return false; - int rvcount = md.ReturnType == ContractParameterType.Void ? 0 : 1; - ExecutionContext context_new = engine.LoadScript(contract.Script, rvcount); - state = context_new.GetState(); - state.CallingScriptHash = callingScriptHash; - state.CallFlags = flags & callingFlags; - - if (NativeContract.IsNative(contractHash)) - { - context_new.EvaluationStack.Push(args); - context_new.EvaluationStack.Push(method); - } - else - { - for (int i = args.Count - 1; i >= 0; i--) - context_new.EvaluationStack.Push(args[i]); - context_new.InstructionPointer = md.Offset; - } - - md = contract.Manifest.Abi.GetMethod("_initialize"); - if (md != null) engine.LoadClonedContext(md.Offset); - - return true; - } - - private static bool Contract_IsStandard(ApplicationEngine engine) - { - UInt160 hash = new UInt160(engine.CurrentContext.EvaluationStack.Pop().GetSpan()); - ContractState contract = engine.Snapshot.Contracts.TryGet(hash); - bool isStandard = contract is null || contract.Script.IsStandardContract(); - engine.CurrentContext.EvaluationStack.Push(isStandard); - return true; - } - - private static bool Contract_CreateStandardAccount(ApplicationEngine engine) - { - if (!engine.TryPop(out ReadOnlySpan pubKey)) return false; - UInt160 scriptHash = SmartContract.Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubKey, ECCurve.Secp256r1)).ToScriptHash(); - engine.Push(scriptHash.ToArray()); - return true; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.Crypto.cs b/src/neo/SmartContract/InteropService.Crypto.cs deleted file mode 100644 index 73fb99c3d2..0000000000 --- a/src/neo/SmartContract/InteropService.Crypto.cs +++ /dev/null @@ -1,144 +0,0 @@ -using Neo.Cryptography; -using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; -using Neo.VM; -using Neo.VM.Types; -using System; -using System.Linq; -using Array = Neo.VM.Types.Array; - -namespace Neo.SmartContract -{ - partial class InteropService - { - public static class Crypto - { - public static readonly InteropDescriptor SHA256 = Register("Neo.Crypto.SHA256", Crypto_SHA256, 0_01000000, TriggerType.All, CallFlags.None); - - public static readonly InteropDescriptor VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.ECDsa.Secp256r1.Verify", Crypto_ECDsaSecp256r1Verify, 0_01000000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.ECDsa.Secp256k1.Verify", Crypto_ECDsaSecp256k1Verify, 0_01000000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.ECDsa.Secp256r1.CheckMultiSig", Crypto_ECDsaSecp256r1CheckMultiSig, 0, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.ECDsa.Secp256k1.CheckMultiSig", Crypto_ECDsaSecp256k1CheckMultiSig, 0, TriggerType.All, CallFlags.None); - - private static bool Crypto_SHA256(ApplicationEngine engine) - { - StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); - ReadOnlySpan value = item0 switch - { - InteropInterface _interface => _interface.GetInterface().GetHashData(), - Null _ => engine.ScriptContainer.GetHashData(), - _ => item0.GetSpan() - }; - - engine.CurrentContext.EvaluationStack.Push(value.ToArray().Sha256()); - return true; - } - - private static bool Crypto_ECDsaSecp256r1Verify(ApplicationEngine engine) - { - return Crypto_ECDsaVerify(engine, Cryptography.ECC.ECCurve.Secp256r1); - } - - private static bool Crypto_ECDsaSecp256k1Verify(ApplicationEngine engine) - { - return Crypto_ECDsaVerify(engine, Cryptography.ECC.ECCurve.Secp256k1); - } - - private static bool Crypto_ECDsaVerify(ApplicationEngine engine, Cryptography.ECC.ECCurve curve) - { - StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); - ReadOnlySpan message = item0 switch - { - InteropInterface _interface => _interface.GetInterface().GetHashData(), - Null _ => engine.ScriptContainer.GetHashData(), - _ => item0.GetSpan() - }; - ReadOnlySpan pubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - ReadOnlySpan signature = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - try - { - engine.CurrentContext.EvaluationStack.Push(Cryptography.Crypto.VerifySignature(message, signature, pubkey, curve)); - } - catch (ArgumentException) - { - engine.CurrentContext.EvaluationStack.Push(false); - } - return true; - } - - private static bool Crypto_ECDsaSecp256r1CheckMultiSig(ApplicationEngine engine) - { - return Crypto_ECDsaCheckMultiSig(engine, Cryptography.ECC.ECCurve.Secp256r1); - } - - private static bool Crypto_ECDsaSecp256k1CheckMultiSig(ApplicationEngine engine) - { - return Crypto_ECDsaCheckMultiSig(engine, Cryptography.ECC.ECCurve.Secp256k1); - } - - private static bool Crypto_ECDsaCheckMultiSig(ApplicationEngine engine, Cryptography.ECC.ECCurve curve) - { - StackItem item0 = engine.CurrentContext.EvaluationStack.Pop(); - ReadOnlySpan message = item0 switch - { - InteropInterface _interface => _interface.GetInterface().GetHashData(), - Null _ => engine.ScriptContainer.GetHashData(), - _ => item0.GetSpan() - }; - int n; - byte[][] pubkeys; - StackItem item = engine.CurrentContext.EvaluationStack.Pop(); - if (item is Array array1) - { - pubkeys = array1.Select(p => p.GetSpan().ToArray()).ToArray(); - n = pubkeys.Length; - if (n == 0) return false; - } - else - { - n = (int)item.GetBigInteger(); - if (n < 1 || n > engine.CurrentContext.EvaluationStack.Count) return false; - pubkeys = new byte[n][]; - for (int i = 0; i < n; i++) - pubkeys[i] = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - } - if (!engine.AddGas(VerifyWithECDsaSecp256r1.FixedPrice * n)) return false; - int m; - byte[][] signatures; - item = engine.CurrentContext.EvaluationStack.Pop(); - if (item is Array array2) - { - signatures = array2.Select(p => p.GetSpan().ToArray()).ToArray(); - m = signatures.Length; - if (m == 0 || m > n) return false; - } - else - { - m = (int)item.GetBigInteger(); - if (m < 1 || m > n || m > engine.CurrentContext.EvaluationStack.Count) return false; - signatures = new byte[m][]; - for (int i = 0; i < m; i++) - signatures[i] = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - } - bool fSuccess = true; - try - { - for (int i = 0, j = 0; fSuccess && i < m && j < n;) - { - if (Cryptography.Crypto.VerifySignature(message, signatures[i], pubkeys[j], curve)) - i++; - j++; - if (m - i > n - j) - fSuccess = false; - } - } - catch (ArgumentException) - { - fSuccess = false; - } - engine.CurrentContext.EvaluationStack.Push(fSuccess); - return true; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.Enumerator.cs b/src/neo/SmartContract/InteropService.Enumerator.cs deleted file mode 100644 index 444dda80a2..0000000000 --- a/src/neo/SmartContract/InteropService.Enumerator.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Neo.SmartContract.Enumerators; -using Neo.SmartContract.Iterators; -using Neo.VM.Types; -using Array = Neo.VM.Types.Array; - -namespace Neo.SmartContract -{ - partial class InteropService - { - public static class Enumerator - { - public static readonly InteropDescriptor Create = Register("System.Enumerator.Create", Enumerator_Create, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Next = Register("System.Enumerator.Next", Enumerator_Next, 0_01000000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Value = Register("System.Enumerator.Value", Enumerator_Value, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Concat = Register("System.Enumerator.Concat", Enumerator_Concat, 0_00000400, TriggerType.All, CallFlags.None); - - private static bool Enumerator_Create(ApplicationEngine engine) - { - IEnumerator enumerator; - switch (engine.CurrentContext.EvaluationStack.Pop()) - { - case Array array: - enumerator = new ArrayWrapper(array); - break; - case PrimitiveType primitive: - enumerator = new ByteArrayWrapper(primitive); - break; - default: - return false; - } - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(enumerator)); - return true; - } - - private static bool Enumerator_Next(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - IEnumerator enumerator = _interface.GetInterface(); - engine.CurrentContext.EvaluationStack.Push(enumerator.Next()); - return true; - } - return false; - } - - private static bool Enumerator_Value(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - IEnumerator enumerator = _interface.GetInterface(); - engine.CurrentContext.EvaluationStack.Push(enumerator.Value()); - return true; - } - return false; - } - - private static bool Enumerator_Concat(ApplicationEngine engine) - { - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface1)) return false; - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface2)) return false; - IEnumerator first = _interface1.GetInterface(); - IEnumerator second = _interface2.GetInterface(); - IEnumerator result = new ConcatenatedEnumerator(first, second); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(result)); - return true; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.Iterator.cs b/src/neo/SmartContract/InteropService.Iterator.cs deleted file mode 100644 index 590b298eb0..0000000000 --- a/src/neo/SmartContract/InteropService.Iterator.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Neo.SmartContract.Enumerators; -using Neo.SmartContract.Iterators; -using Neo.VM.Types; -using Array = Neo.VM.Types.Array; - -namespace Neo.SmartContract -{ - partial class InteropService - { - public static class Iterator - { - public static readonly InteropDescriptor Create = Register("System.Iterator.Create", Iterator_Create, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Key = Register("System.Iterator.Key", Iterator_Key, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Keys = Register("System.Iterator.Keys", Iterator_Keys, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Values = Register("System.Iterator.Values", Iterator_Values, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Concat = Register("System.Iterator.Concat", Iterator_Concat, 0_00000400, TriggerType.All, CallFlags.None); - - private static bool Iterator_Create(ApplicationEngine engine) - { - IIterator iterator; - switch (engine.CurrentContext.EvaluationStack.Pop()) - { - case Array array: - iterator = new ArrayWrapper(array); - break; - case Map map: - iterator = new MapWrapper(map); - break; - case PrimitiveType primitive: - iterator = new ByteArrayWrapper(primitive); - break; - default: - return false; - } - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator)); - return true; - } - - private static bool Iterator_Key(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - IIterator iterator = _interface.GetInterface(); - engine.CurrentContext.EvaluationStack.Push(iterator.Key()); - return true; - } - return false; - } - - private static bool Iterator_Keys(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - IIterator iterator = _interface.GetInterface(); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new IteratorKeysWrapper(iterator))); - return true; - } - return false; - } - - private static bool Iterator_Values(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - IIterator iterator = _interface.GetInterface(); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new IteratorValuesWrapper(iterator))); - return true; - } - return false; - } - - private static bool Iterator_Concat(ApplicationEngine engine) - { - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface1)) return false; - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface2)) return false; - IIterator first = _interface1.GetInterface(); - IIterator second = _interface2.GetInterface(); - IIterator result = new ConcatenatedIterator(first, second); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(result)); - return true; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.Json.cs b/src/neo/SmartContract/InteropService.Json.cs deleted file mode 100644 index 262fbe2e9a..0000000000 --- a/src/neo/SmartContract/InteropService.Json.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Neo.IO.Json; -using Neo.VM.Types; -using System; - -namespace Neo.SmartContract -{ - partial class InteropService - { - public static class Json - { - public static readonly InteropDescriptor Serialize = Register("System.Json.Serialize", Json_Serialize, 0_00100000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Deserialize = Register("System.Json.Deserialize", Json_Deserialize, 0_00500000, TriggerType.All, CallFlags.None); - - private static bool Json_Serialize(ApplicationEngine engine) - { - if (!engine.TryPop(out StackItem item)) return false; - byte[] json = JsonSerializer.SerializeToByteArray(item, engine.MaxItemSize); - engine.Push(json); - return true; - } - - private static bool Json_Deserialize(ApplicationEngine engine) - { - if (!engine.TryPop(out ReadOnlySpan json)) return false; - StackItem item = JsonSerializer.Deserialize(JObject.Parse(json, 10), engine.ReferenceCounter); - engine.Push(item); - return true; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.Native.cs b/src/neo/SmartContract/InteropService.Native.cs deleted file mode 100644 index 62c3970647..0000000000 --- a/src/neo/SmartContract/InteropService.Native.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Neo.Ledger; -using Neo.SmartContract.Native; - -namespace Neo.SmartContract -{ - partial class InteropService - { - internal static class Native - { - public static readonly InteropDescriptor Deploy = Register("Neo.Native.Deploy", Native_Deploy, 0, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor Call = Register("Neo.Native.Call", Native_Call, 0, TriggerType.System | TriggerType.Application, CallFlags.None); - - private static bool Native_Deploy(ApplicationEngine engine) - { - if (engine.Snapshot.PersistingBlock.Index != 0) return false; - foreach (NativeContract contract in NativeContract.Contracts) - { - engine.Snapshot.Contracts.Add(contract.Hash, new ContractState - { - Id = contract.Id, - Script = contract.Script, - Manifest = contract.Manifest - }); - contract.Initialize(engine); - } - return true; - } - - private static bool Native_Call(ApplicationEngine engine) - { - if (!engine.TryPop(out string name)) return false; - NativeContract contract = NativeContract.GetContract(name); - if (contract is null) return false; - contract.Invoke(engine); - return true; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.Runtime.cs b/src/neo/SmartContract/InteropService.Runtime.cs deleted file mode 100644 index ff58e0a897..0000000000 --- a/src/neo/SmartContract/InteropService.Runtime.cs +++ /dev/null @@ -1,222 +0,0 @@ -using Neo.Cryptography.ECC; -using Neo.IO; -using Neo.Network.P2P.Payloads; -using Neo.VM; -using Neo.VM.Types; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Array = Neo.VM.Types.Array; - -namespace Neo.SmartContract -{ - partial class InteropService - { - public static class Runtime - { - public const int MaxNotificationSize = 1024; - - public static readonly InteropDescriptor Platform = Register("System.Runtime.Platform", Runtime_Platform, 0_00000250, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor GetTrigger = Register("System.Runtime.GetTrigger", Runtime_GetTrigger, 0_00000250, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor GetTime = Register("System.Runtime.GetTime", Runtime_GetTime, 0_00000250, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor GetScriptContainer = Register("System.Runtime.GetScriptContainer", Runtime_GetScriptContainer, 0_00000250, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", Runtime_GetExecutingScriptHash, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", Runtime_GetCallingScriptHash, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", Runtime_GetEntryScriptHash, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor CheckWitness = Register("System.Runtime.CheckWitness", Runtime_CheckWitness, 0_00030000, TriggerType.All, CallFlags.AllowStates); - public static readonly InteropDescriptor GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", Runtime_GetInvocationCounter, 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Log = Register("System.Runtime.Log", Runtime_Log, 0_01000000, TriggerType.All, CallFlags.AllowNotify); - public static readonly InteropDescriptor Notify = Register("System.Runtime.Notify", Runtime_Notify, 0_01000000, TriggerType.All, CallFlags.AllowNotify); - public static readonly InteropDescriptor GetNotifications = Register("System.Runtime.GetNotifications", Runtime_GetNotifications, 0_00010000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor GasLeft = Register("System.Runtime.GasLeft", Runtime_GasLeft, 0_00000400, TriggerType.All, CallFlags.None); - - private static bool CheckItemForNotification(StackItem state) - { - int size = 0; - List items_checked = new List(); - Queue items_unchecked = new Queue(); - while (true) - { - switch (state) - { - case Struct array: - foreach (StackItem item in array) - items_unchecked.Enqueue(item); - break; - case Array array: - if (items_checked.All(p => !ReferenceEquals(p, array))) - { - items_checked.Add(array); - foreach (StackItem item in array) - items_unchecked.Enqueue(item); - } - break; - case PrimitiveType primitive: - size += primitive.Size; - break; - case Null _: - break; - case InteropInterface _: - return false; - case Map map: - if (items_checked.All(p => !ReferenceEquals(p, map))) - { - items_checked.Add(map); - foreach (var pair in map) - { - size += pair.Key.Size; - items_unchecked.Enqueue(pair.Value); - } - } - break; - } - if (size > MaxNotificationSize) return false; - if (items_unchecked.Count == 0) return true; - state = items_unchecked.Dequeue(); - } - } - - internal static bool CheckWitnessInternal(ApplicationEngine engine, UInt160 hash) - { - if (engine.ScriptContainer is Transaction tx) - { - Cosigner cosigner = tx.Cosigners.FirstOrDefault(p => p.Account.Equals(hash)); - if (cosigner is null) return false; - if (cosigner.Scopes == WitnessScope.Global) return true; - if (cosigner.Scopes.HasFlag(WitnessScope.CalledByEntry)) - { - if (engine.CallingScriptHash == engine.EntryScriptHash) - return true; - } - if (cosigner.Scopes.HasFlag(WitnessScope.CustomContracts)) - { - if (cosigner.AllowedContracts.Contains(engine.CurrentScriptHash)) - return true; - } - if (cosigner.Scopes.HasFlag(WitnessScope.CustomGroups)) - { - var contract = engine.Snapshot.Contracts[engine.CallingScriptHash]; - // check if current group is the required one - if (contract.Manifest.Groups.Select(p => p.PubKey).Intersect(cosigner.AllowedGroups).Any()) - return true; - } - return false; - } - - // only for non-Transaction types (Block, etc) - - var hashes_for_verifying = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); - return hashes_for_verifying.Contains(hash); - } - - private static bool Runtime_Platform(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(Encoding.ASCII.GetBytes("NEO")); - return true; - } - - private static bool Runtime_GetTrigger(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push((int)engine.Trigger); - return true; - } - - private static bool Runtime_GetTime(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.PersistingBlock.Timestamp); - return true; - } - - private static bool Runtime_GetScriptContainer(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push( - engine.ScriptContainer is IInteroperable value ? value.ToStackItem(engine.ReferenceCounter) : - StackItem.FromInterface(engine.ScriptContainer)); - return true; - } - - private static bool Runtime_GetExecutingScriptHash(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.CurrentScriptHash.ToArray()); - return true; - } - - private static bool Runtime_GetCallingScriptHash(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.CallingScriptHash?.ToArray() ?? StackItem.Null); - return true; - } - - private static bool Runtime_GetEntryScriptHash(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.EntryScriptHash.ToArray()); - return true; - } - - private static bool Runtime_CheckWitness(ApplicationEngine engine) - { - ReadOnlySpan hashOrPubkey = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - UInt160 hash = hashOrPubkey.Length switch - { - 20 => new UInt160(hashOrPubkey), - 33 => SmartContract.Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(hashOrPubkey, ECCurve.Secp256r1)).ToScriptHash(), - _ => null - }; - if (hash is null) return false; - engine.CurrentContext.EvaluationStack.Push(CheckWitnessInternal(engine, hash)); - return true; - } - - private static bool Runtime_GasLeft(ApplicationEngine engine) - { - engine.Push(engine.GasLeft); - return true; - } - - private static bool Runtime_GetInvocationCounter(ApplicationEngine engine) - { - if (!engine.InvocationCounter.TryGetValue(engine.CurrentScriptHash, out var counter)) - { - return false; - } - - engine.CurrentContext.EvaluationStack.Push(counter); - return true; - } - - private static bool Runtime_Log(ApplicationEngine engine) - { - ReadOnlySpan state = engine.CurrentContext.EvaluationStack.Pop().GetSpan(); - if (state.Length > MaxNotificationSize) return false; - string message = Encoding.UTF8.GetString(state); - engine.SendLog(engine.CurrentScriptHash, message); - return true; - } - - private static bool Runtime_Notify(ApplicationEngine engine) - { - StackItem state = engine.CurrentContext.EvaluationStack.Pop(); - if (!CheckItemForNotification(state)) return false; - engine.SendNotification(engine.CurrentScriptHash, state); - return true; - } - - private static bool Runtime_GetNotifications(ApplicationEngine engine) - { - StackItem item = engine.CurrentContext.EvaluationStack.Pop(); - - IEnumerable notifications = engine.Notifications; - if (!item.IsNull) // must filter by scriptHash - { - var hash = new UInt160(item.GetSpan()); - notifications = notifications.Where(p => p.ScriptHash == hash); - } - - if (notifications.Count() > engine.MaxStackSize) return false; - engine.Push(new Array(engine.ReferenceCounter, notifications.Select(u => new Array(engine.ReferenceCounter, new[] { u.ScriptHash.ToArray(), u.State })))); - return true; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.Storage.cs b/src/neo/SmartContract/InteropService.Storage.cs deleted file mode 100644 index 0525b9a774..0000000000 --- a/src/neo/SmartContract/InteropService.Storage.cs +++ /dev/null @@ -1,175 +0,0 @@ -using Neo.Ledger; -using Neo.SmartContract.Iterators; -using Neo.VM; -using Neo.VM.Types; -using System; -using System.Linq; - -namespace Neo.SmartContract -{ - partial class InteropService - { - public static class Storage - { - public const long GasPerByte = 100000; - public const int MaxKeySize = 64; - public const int MaxValueSize = ushort.MaxValue; - - public static readonly InteropDescriptor GetContext = Register("System.Storage.GetContext", Storage_GetContext, 0_00000400, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", Storage_GetReadOnlyContext, 0_00000400, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor AsReadOnly = Register("System.Storage.AsReadOnly", Storage_AsReadOnly, 0_00000400, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor Get = Register("System.Storage.Get", Storage_Get, 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor Find = Register("System.Storage.Find", Storage_Find, 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor Put = Register("System.Storage.Put", Storage_Put, 0, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor PutEx = Register("System.Storage.PutEx", Storage_PutEx, 0, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor Delete = Register("System.Storage.Delete", Storage_Delete, 1 * GasPerByte, TriggerType.Application, CallFlags.AllowModifyStates); - - private static bool PutExInternal(ApplicationEngine engine, StorageContext context, byte[] key, byte[] value, StorageFlags flags) - { - if (key.Length > MaxKeySize) return false; - if (value.Length > MaxValueSize) return false; - if (context.IsReadOnly) return false; - - int newDataSize; - StorageKey skey = new StorageKey - { - Id = context.Id, - Key = key - }; - StorageItem item = engine.Snapshot.Storages.GetAndChange(skey); - if (item is null) - { - newDataSize = key.Length + value.Length; - engine.Snapshot.Storages.Add(skey, item = new StorageItem()); - } - else - { - if (item.IsConstant) return false; - if (value.Length <= item.Value.Length) - newDataSize = 1; - else - newDataSize = value.Length - item.Value.Length; - } - if (!engine.AddGas(newDataSize * GasPerByte)) return false; - - item.Value = value; - item.IsConstant = flags.HasFlag(StorageFlags.Constant); - - return true; - } - - private static bool Storage_GetContext(ApplicationEngine engine) - { - ContractState contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash); - if (contract == null) return false; - if (!contract.HasStorage) return false; - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new StorageContext - { - Id = contract.Id, - IsReadOnly = false - })); - return true; - } - - private static bool Storage_GetReadOnlyContext(ApplicationEngine engine) - { - ContractState contract = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash); - if (contract == null) return false; - if (!contract.HasStorage) return false; - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(new StorageContext - { - Id = contract.Id, - IsReadOnly = true - })); - return true; - } - - private static bool Storage_AsReadOnly(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - StorageContext context = _interface.GetInterface(); - if (!context.IsReadOnly) - context = new StorageContext - { - Id = context.Id, - IsReadOnly = true - }; - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(context)); - return true; - } - return false; - } - - private static bool Storage_Get(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - StorageContext context = _interface.GetInterface(); - byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - StorageItem item = engine.Snapshot.Storages.TryGet(new StorageKey - { - Id = context.Id, - Key = key - }); - engine.CurrentContext.EvaluationStack.Push(item?.Value ?? StackItem.Null); - return true; - } - return false; - } - - private static bool Storage_Find(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - StorageContext context = _interface.GetInterface(); - byte[] prefix = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); - StorageIterator iterator = engine.AddDisposable(new StorageIterator(engine.Snapshot.Storages.Find(prefix_key).Where(p => p.Key.Key.AsSpan().StartsWith(prefix)).GetEnumerator())); - engine.CurrentContext.EvaluationStack.Push(StackItem.FromInterface(iterator)); - return true; - } - return false; - } - - private static bool Storage_Put(ApplicationEngine engine) - { - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)) - return false; - StorageContext context = _interface.GetInterface(); - byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - byte[] value = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - return PutExInternal(engine, context, key, value, StorageFlags.None); - } - - private static bool Storage_PutEx(ApplicationEngine engine) - { - if (!(engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface)) - return false; - StorageContext context = _interface.GetInterface(); - byte[] key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - byte[] value = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray(); - StorageFlags flags = (StorageFlags)(byte)engine.CurrentContext.EvaluationStack.Pop().GetBigInteger(); - return PutExInternal(engine, context, key, value, flags); - } - - private static bool Storage_Delete(ApplicationEngine engine) - { - if (engine.CurrentContext.EvaluationStack.Pop() is InteropInterface _interface) - { - StorageContext context = _interface.GetInterface(); - if (context.IsReadOnly) return false; - StorageKey key = new StorageKey - { - Id = context.Id, - Key = engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray() - }; - if (engine.Snapshot.Storages.TryGet(key)?.IsConstant == true) return false; - engine.Snapshot.Storages.Delete(key); - return true; - } - return false; - } - } - } -} diff --git a/src/neo/SmartContract/InteropService.cs b/src/neo/SmartContract/InteropService.cs deleted file mode 100644 index e4a399bf94..0000000000 --- a/src/neo/SmartContract/InteropService.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; - -namespace Neo.SmartContract -{ - public static partial class InteropService - { - private static readonly Dictionary methods = new Dictionary(); - - static InteropService() - { - foreach (Type t in typeof(InteropService).GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)) - t.GetFields()[0].GetValue(null); - } - - public static IEnumerable SupportedMethods() - { - return methods.Values; - } - - internal static bool Invoke(ApplicationEngine engine, uint method) - { - if (!methods.TryGetValue(method, out InteropDescriptor descriptor)) - return false; - if (!descriptor.AllowedTriggers.HasFlag(engine.Trigger)) - return false; - ExecutionContextState state = engine.CurrentContext.GetState(); - if (!state.CallFlags.HasFlag(descriptor.RequiredCallFlags)) - return false; - if (!engine.AddGas(descriptor.FixedPrice)) - return false; - return descriptor.Handler(engine); - } - - private static InteropDescriptor Register(string method, Func handler, long price, TriggerType allowedTriggers, CallFlags requiredCallFlags) - { - InteropDescriptor descriptor = new InteropDescriptor(method, handler, price, allowedTriggers, requiredCallFlags); - methods.Add(descriptor.Hash, descriptor); - return descriptor; - } - } -} diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 81d8b499ab..c42d7eb439 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -38,7 +38,7 @@ protected NativeContract() using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(Name); - sb.EmitSysCall(InteropService.Native.Call); + sb.EmitSysCall(ApplicationEngine.Neo_Native_Call); this.Script = sb.ToArray(); } this.Hash = Script.ToScriptHash(); @@ -135,11 +135,8 @@ public static bool IsNative(UInt160 hash) return contractsHashDictionary.ContainsKey(hash); } - internal virtual bool Initialize(ApplicationEngine engine) + internal virtual void Initialize(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.Application) - throw new InvalidOperationException(); - return true; } [ContractMethod(0, ContractParameterType.Boolean, CallFlags.AllowModifyStates)] diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 552227f7be..2bd2244a96 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -41,12 +41,11 @@ internal bool CheckPolicy(Transaction tx, StoreView snapshot) private bool CheckCommittees(ApplicationEngine engine) { UInt160 committeeMultiSigAddr = NEO.GetCommitteeAddress(engine.Snapshot); - return InteropService.Runtime.CheckWitnessInternal(engine, committeeMultiSigAddr); + return engine.CheckWitnessInternal(committeeMultiSigAddr); } - internal override bool Initialize(ApplicationEngine engine) + internal override void Initialize(ApplicationEngine engine) { - if (!base.Initialize(engine)) return false; engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MaxBlockSize), new StorageItem { Value = BitConverter.GetBytes(1024u * 256u) @@ -63,7 +62,6 @@ internal override bool Initialize(ApplicationEngine engine) { Value = new UInt160[0].ToByteArray() }); - return true; } [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index 09c2ab1127..4a8cd5e1bf 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -4,7 +4,6 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; using System.Linq; -using System.Numerics; namespace Neo.SmartContract.Native.Tokens { @@ -19,13 +18,10 @@ internal GasToken() { } - internal override bool Initialize(ApplicationEngine engine) + internal override void Initialize(ApplicationEngine engine) { - if (!base.Initialize(engine)) return false; - if (TotalSupply(engine.Snapshot) != BigInteger.Zero) return false; UInt160 account = Blockchain.GetConsensusAddress(Blockchain.StandbyValidators); Mint(engine, account, 30_000_000 * Factor); - return true; } protected override bool OnPersist(ApplicationEngine engine) diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 8005d054ba..1c63485b5c 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -87,10 +87,8 @@ private BigInteger CalculateBonus(StoreView snapshot, BigInteger value, uint sta return value * amount * GAS.Factor / TotalAmount; } - internal override bool Initialize(ApplicationEngine engine) + internal override void Initialize(ApplicationEngine engine) { - if (!base.Initialize(engine)) return false; - if (base.TotalSupply(engine.Snapshot) != BigInteger.Zero) return false; BigInteger amount = TotalAmount; for (int i = 0; i < Blockchain.StandbyCommittee.Length; i++) { @@ -104,7 +102,6 @@ internal override bool Initialize(ApplicationEngine engine) amount -= balance; } Mint(engine, Blockchain.GetConsensusAddress(Blockchain.StandbyValidators), amount); - return true; } protected override bool OnPersist(ApplicationEngine engine) @@ -135,7 +132,7 @@ public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) private StackItem RegisterCandidate(ApplicationEngine engine, Array args) { ECPoint pubkey = args[0].GetSpan().AsSerializable(); - if (!InteropService.Runtime.CheckWitnessInternal(engine, Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) + if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) return false; return RegisterCandidate(engine.Snapshot, pubkey); } @@ -153,7 +150,7 @@ private bool RegisterCandidate(StoreView snapshot, ECPoint pubkey) private StackItem UnregisterCandidate(ApplicationEngine engine, Array args) { ECPoint pubkey = args[0].GetSpan().AsSerializable(); - if (!InteropService.Runtime.CheckWitnessInternal(engine, Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) + if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) return false; return UnregisterCandidate(engine.Snapshot, pubkey); } @@ -176,7 +173,7 @@ private StackItem Vote(ApplicationEngine engine, Array args) { UInt160 account = new UInt160(args[0].GetSpan()); ECPoint voteTo = args[1].IsNull ? null : args[1].GetSpan().AsSerializable(); - if (!InteropService.Runtime.CheckWitnessInternal(engine, account)) return false; + if (!engine.CheckWitnessInternal(account)) return false; return Vote(engine.Snapshot, account, voteTo); } diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index 045db4e4b5..855d864c59 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -159,7 +159,7 @@ protected StackItem Transfer(ApplicationEngine engine, Array args) protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount) { if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); - if (!from.Equals(engine.CallingScriptHash) && !InteropService.Runtime.CheckWitnessInternal(engine, from)) + if (!from.Equals(engine.CallingScriptHash) && !engine.CheckWitnessInternal(from)) return false; ContractState contract_to = engine.Snapshot.Contracts.TryGet(to); if (contract_to?.Payable == false) return false; diff --git a/src/neo/SmartContract/NotifyEventArgs.cs b/src/neo/SmartContract/NotifyEventArgs.cs index e1d3fd8c1e..7b52c2a570 100644 --- a/src/neo/SmartContract/NotifyEventArgs.cs +++ b/src/neo/SmartContract/NotifyEventArgs.cs @@ -1,10 +1,13 @@ +using Neo.IO; using Neo.Network.P2P.Payloads; +using Neo.VM; using Neo.VM.Types; using System; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract { - public class NotifyEventArgs : EventArgs + public class NotifyEventArgs : EventArgs, IInteroperable { public IVerifiable ScriptContainer { get; } public UInt160 ScriptHash { get; } @@ -16,5 +19,15 @@ public NotifyEventArgs(IVerifiable container, UInt160 script_hash, StackItem sta this.ScriptHash = script_hash; this.State = state; } + + public void FromStackItem(StackItem stackItem) + { + throw new NotSupportedException(); + } + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Array(referenceCounter, new[] { ScriptHash.ToArray(), State }); + } } } diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index eaed72fb31..c99ad135f2 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -29,7 +29,7 @@ public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHas sb.Emit(OpCode.NEWARRAY); sb.EmitPush(operation); sb.EmitPush(scriptHash); - sb.EmitSysCall(InteropService.Contract.Call); + sb.EmitSysCall(ApplicationEngine.System_Contract_Call); return sb; } @@ -41,7 +41,7 @@ public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHas sb.Emit(OpCode.PACK); sb.EmitPush(operation); sb.EmitPush(scriptHash); - sb.EmitSysCall(InteropService.Contract.Call); + sb.EmitSysCall(ApplicationEngine.System_Contract_Call); return sb; } @@ -53,7 +53,7 @@ public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHas sb.Emit(OpCode.PACK); sb.EmitPush(operation); sb.EmitPush(scriptHash); - sb.EmitSysCall(InteropService.Contract.Call); + sb.EmitSysCall(ApplicationEngine.System_Contract_Call); return sb; } diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 2d4f6167cf..def6977959 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -356,7 +356,7 @@ public static long CalculateNetworkFee(byte[] witness_script, ref int size) if (witness_script.IsSignatureContract()) { size += 67 + witness_script.GetVarSize(); - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.Crypto.VerifyWithECDsaSecp256r1.FixedPrice; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + ApplicationEngine.ECDsaVerifyPrice; } else if (witness_script.IsMultiSigContract(out int m, out int n)) { @@ -368,7 +368,7 @@ public static long CalculateNetworkFee(byte[] witness_script, ref int size) networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * n; using (ScriptBuilder sb = new ScriptBuilder()) networkFee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; - networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + InteropService.Crypto.VerifyWithECDsaSecp256r1.FixedPrice * n; + networkFee += ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + ApplicationEngine.ECDsaVerifyPrice * n; } else { diff --git a/tests/neo.UnitTests/Consensus/UT_Consensus.cs b/tests/neo.UnitTests/Consensus/UT_Consensus.cs index ee26c3ca85..86eb6bc8c2 100644 --- a/tests/neo.UnitTests/Consensus/UT_Consensus.cs +++ b/tests/neo.UnitTests/Consensus/UT_Consensus.cs @@ -154,8 +154,8 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm 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("0x7ab841144dcdbf228ff57f7068f795e2afd1a3c1")); - mockContext.Object.Block.NextConsensus.Should().Be(UInt160.Parse("0x7ab841144dcdbf228ff57f7068f795e2afd1a3c1")); + 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!"); @@ -176,7 +176,7 @@ public void ConsensusService_SingleNodeActors_OnStart_PrepReq_PrepResponses_Comm Console.WriteLine("will create template MakePrepareRequest..."); mockContext.Object.PrevHeader.Timestamp = defaultTimestamp; - mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0x7ab841144dcdbf228ff57f7068f795e2afd1a3c1")); + mockContext.Object.PrevHeader.NextConsensus.Should().Be(UInt160.Parse("0xe239c7228fa6b46cc0cf43623b2f934301d0b4f7")); var prepReq = mockContext.Object.MakePrepareRequest(); var ppToSend = (PrepareRequest)prepReq.ConsensusMessage; // Forcing hashes to 0 because mempool is currently shared diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index f672c2a707..7e04dfe229 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -73,13 +73,13 @@ public void TestContainsTransaction() [TestMethod] public void TestGetCurrentBlockHash() { - Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0x557f5c9d0c865a211a749899681e5b4fbf745b3bcf0c395e6d6a7f1edb0d86f1")); + Blockchain.Singleton.CurrentBlockHash.Should().Be(UInt256.Parse("0x3843c6d0dd2082a801cf3da0fe0e847ba8d5571e0606c5018f9a35ce49c55e18")); } [TestMethod] public void TestGetCurrentHeaderHash() { - Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0x557f5c9d0c865a211a749899681e5b4fbf745b3bcf0c395e6d6a7f1edb0d86f1")); + Blockchain.Singleton.CurrentHeaderHash.Should().Be(UInt256.Parse("0x3843c6d0dd2082a801cf3da0fe0e847ba8d5571e0606c5018f9a35ce49c55e18")); } [TestMethod] @@ -91,7 +91,7 @@ public void TestGetBlock() [TestMethod] public void TestGetBlockHash() { - Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0x557f5c9d0c865a211a749899681e5b4fbf745b3bcf0c395e6d6a7f1edb0d86f1")); + Blockchain.Singleton.GetBlockHash(0).Should().Be(UInt256.Parse("0x3843c6d0dd2082a801cf3da0fe0e847ba8d5571e0606c5018f9a35ce49c55e18")); Blockchain.Singleton.GetBlockHash(10).Should().BeNull(); } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index aa478633c1..f7be52f9a6 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -46,7 +46,6 @@ public void Check_BalanceOfTransferAndBurn() var keyCount = snapshot.Storages.GetChangeSet().Count(); - NativeContract.NEO.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)); var supply = NativeContract.GAS.TotalSupply(snapshot); supply.Should().Be(3000000000000000); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 37b5bc51e3..12b025400b 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -203,22 +203,7 @@ public void Check_Initialize() // StandbyValidators - var validators = Check_GetValidators(snapshot); - - for (var x = 0; x < Blockchain.StandbyValidators.Length; x++) - { - validators[x].Equals(Blockchain.StandbyValidators[x]); - } - - // Check double call - - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); - - engine.LoadScript(NativeContract.NEO.Script); - - var result = NativeContract.NEO.Initialize(engine); - - result.Should().Be(false); + Check_GetValidators(snapshot); } [TestMethod] @@ -375,23 +360,6 @@ public void TestGetValidators2() result[6].ToArray().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); } - [TestMethod] - public void TestInitialize() - { - var snapshot = Blockchain.Singleton.GetSnapshot(); - var engine = new ApplicationEngine(TriggerType.System, null, snapshot, 0, true); - Action action = () => NativeContract.NEO.Initialize(engine); - action.Should().Throw(); - - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); - NativeContract.NEO.Initialize(engine).Should().BeFalse(); - - snapshot.Storages.Delete(CreateStorageKey(11)); - snapshot.PersistingBlock = Blockchain.GenesisBlock; - engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); - NativeContract.NEO.Initialize(engine).Should().BeTrue(); - } - [TestMethod] public void TestOnBalanceChanging() { diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index b3d5da5cd2..656332aabc 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -24,12 +24,7 @@ public void TestSetup() public void TestInitialize() { ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0); - - testNativeContract.Initialize(ae).Should().BeTrue(); - - ae = new ApplicationEngine(TriggerType.System, null, null, 0); - Action action = () => testNativeContract.Initialize(ae); - action.Should().Throw(); + testNativeContract.Initialize(ae); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 22442e08cf..1740495571 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -3,7 +3,6 @@ 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; @@ -30,7 +29,7 @@ public void Check_Initialize() var snapshot = Blockchain.Singleton.GetSnapshot(); var keyCount = snapshot.Storages.GetChangeSet().Count(); - NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); + NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)); (keyCount + 4).Should().Be(snapshot.Storages.GetChangeSet().Count()); @@ -63,7 +62,7 @@ public void Check_SetMaxBlockSize() UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); - NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); + NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)); // Without signature @@ -109,7 +108,7 @@ public void Check_SetMaxTransactionsPerBlock() snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); + NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)); // Without signature @@ -144,7 +143,7 @@ public void Check_SetFeePerByte() snapshot.PersistingBlock = new Block() { Index = 1000, PrevHash = UInt256.Zero }; snapshot.Blocks.Add(UInt256.Zero, new Neo.Ledger.TrimmedBlock() { NextConsensus = UInt160.Zero }); - NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); + NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)); // Without signature @@ -181,7 +180,7 @@ public void Check_Block_UnblockAccount() UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); - NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)).Should().BeTrue(); + NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)); // Block without signature diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index 71e8dfe759..a05a5e9f88 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -1,6 +1,5 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; using Neo.IO; using Neo.IO.Caching; using Neo.Ledger; @@ -8,7 +7,6 @@ using Neo.Persistence; using Neo.SmartContract; using Neo.VM.Types; -using System; namespace Neo.UnitTests.SmartContract { @@ -24,31 +22,6 @@ public void TestSetup() TestBlockchain.InitializeMockNeoSystem(); } - [TestMethod] - public void TestLog() - { - var snapshot = Blockchain.Singleton.GetSnapshot(); - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); - ApplicationEngine.Log += Test_Log1; - string logMessage = "TestMessage"; - - engine.SendLog(UInt160.Zero, logMessage); - message.Should().Be(logMessage); - - ApplicationEngine.Log += Test_Log2; - engine.SendLog(UInt160.Zero, logMessage); - message.Should().Be(null); - - message = logMessage; - ApplicationEngine.Log -= Test_Log1; - engine.SendLog(UInt160.Zero, logMessage); - message.Should().Be(null); - - ApplicationEngine.Log -= Test_Log2; - engine.SendLog(UInt160.Zero, logMessage); - message.Should().Be(null); - } - [TestMethod] public void TestNotify() { @@ -74,17 +47,6 @@ public void TestNotify() item.Should().Be(null); } - [TestMethod] - public void TestDisposable() - { - var snapshot = Blockchain.Singleton.GetSnapshot(); - var m = new Mock(); - var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); - engine.AddDisposable(m.Object).Should().Be(m.Object); - Action action = () => engine.Dispose(); - action.Should().NotThrow(); - } - private void Test_Log1(object sender, LogEventArgs e) { message = e.Message; @@ -115,27 +77,6 @@ public void TestCreateDummyBlock() snapshot.PersistingBlock.PrevHash.Should().Be(Blockchain.GenesisBlock.Hash); snapshot.PersistingBlock.MerkleRoot.Should().Be(new UInt256()); } - - [TestMethod] - public void TestOnSysCall() - { - InteropDescriptor descriptor = new InteropDescriptor("System.Blockchain.GetHeight", Blockchain_GetHeight, 0_00000400, TriggerType.Application, CallFlags.None); - TestApplicationEngine engine = new TestApplicationEngine(TriggerType.Application, null, null, 0); - byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; - engine.LoadScript(SyscallSystemRuntimeCheckWitnessHash); - engine.GetOnSysCall(descriptor.Hash).Should().BeFalse(); - - var snapshot = Blockchain.Singleton.GetSnapshot(); - engine = new TestApplicationEngine(TriggerType.Application, null, snapshot, 0, true); - engine.LoadScript(SyscallSystemRuntimeCheckWitnessHash); - engine.GetOnSysCall(descriptor.Hash).Should().BeTrue(); - } - - private static bool Blockchain_GetHeight(ApplicationEngine engine) - { - engine.CurrentContext.EvaluationStack.Push(engine.Snapshot.Height); - return true; - } } public class TestApplicationEngine : ApplicationEngine diff --git a/tests/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/neo.UnitTests/SmartContract/UT_Contract.cs index ee943c6477..249018f69b 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Contract.cs @@ -26,7 +26,7 @@ public void TestGetAddress() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); Assert.AreEqual(expectedArray.ToScriptHash().ToAddress(), contract.Address); } @@ -44,7 +44,7 @@ public void TestGetScriptHash() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); Assert.AreEqual(expectedArray.ToScriptHash(), contract.ScriptHash); } @@ -86,7 +86,7 @@ public void TestCreateMultiSigContract() expectedArray[71] = (byte)OpCode.PUSH2; expectedArray[72] = (byte)OpCode.PUSHNULL; expectedArray[73] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.CheckMultisigWithECDsaSecp256r1), 0, expectedArray, 74, 4); + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckMultisigWithECDsaSecp256r1), 0, expectedArray, 74, 4); CollectionAssert.AreEqual(expectedArray, contract.Script); Assert.AreEqual(2, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); @@ -122,7 +122,7 @@ public void TestCreateMultiSigRedeemScript() expectedArray[71] = (byte)OpCode.PUSH2; expectedArray[72] = (byte)OpCode.PUSHNULL; expectedArray[73] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.CheckMultisigWithECDsaSecp256r1), 0, expectedArray, 74, 4); + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckMultisigWithECDsaSecp256r1), 0, expectedArray, 74, 4); CollectionAssert.AreEqual(expectedArray, script); } @@ -140,7 +140,7 @@ public void TestCreateSignatureContract() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); CollectionAssert.AreEqual(expectedArray, contract.Script); Assert.AreEqual(1, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); @@ -160,7 +160,7 @@ public void TestCreateSignatureRedeemScript() Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); expectedArray[35] = (byte)OpCode.PUSHNULL; expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(InteropService.Crypto.VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); CollectionAssert.AreEqual(expectedArray, script); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index aaf6511997..23e474e4b8 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -72,7 +72,7 @@ public void TestAdd() var context1 = new ContractParametersContext(tx); context1.Add(contract, 0, new byte[] { 0x01 }).Should().BeFalse(); - tx.Sender = UInt160.Parse("0xcd4ced947d791e887559b3829c3bc08fe37b0a64"); + tx.Sender = UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0"); var context2 = new ContractParametersContext(tx); context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); //test repeatlly createItem @@ -83,7 +83,7 @@ public void TestAdd() public void TestGetParameter() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0xcd4ced947d791e887559b3829c3bc08fe37b0a64"); + tx.Sender = UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0"); var context = new ContractParametersContext(tx); context.GetParameter(tx.Sender, 0).Should().BeNull(); @@ -96,7 +96,7 @@ public void TestGetParameter() public void TestGetWitnesses() { Transaction tx = TestUtils.GetTransaction(); - tx.Sender = UInt160.Parse("0xcd4ced947d791e887559b3829c3bc08fe37b0a64"); + tx.Sender = UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0"); var context = new ContractParametersContext(tx); context.Add(contract, 0, new byte[] { 0x01 }); Witness[] witnesses = context.GetWitnesses(); @@ -109,7 +109,7 @@ public void TestGetWitnesses() public void TestAddSignature() { Transaction tx = TestUtils.GetTransaction(); - var singleSender = UInt160.Parse("0xcd4ced947d791e887559b3829c3bc08fe37b0a64"); + var singleSender = UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0"); tx.Sender = singleSender; //singleSign @@ -139,7 +139,7 @@ public void TestAddSignature() key.PublicKey, key2.PublicKey }); - var multiSender = UInt160.Parse("0x6bb1ea23cefb73dd959775c035a114018c2c1119"); + var multiSender = UInt160.Parse("0x3593816cc1085a6328fea2b899c24d78cd0ba372"); tx.Sender = multiSender; context = new ContractParametersContext(tx); context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs b/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs deleted file mode 100644 index 1d2d59d5a2..0000000000 --- a/tests/neo.UnitTests/SmartContract/UT_InteropDescriptor.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract; - -namespace Neo.UnitTests.SmartContract -{ - [TestClass] - public class UT_InteropDescriptor - { - [TestMethod] - public void TestGetMethod() - { - string method = @"System.ExecutionEngine.GetScriptContainer"; - long price = 0_00000250; - TriggerType allowedTriggers = TriggerType.All; - InteropDescriptor descriptor = new InteropDescriptor(method, TestHandler, price, allowedTriggers, CallFlags.None); - descriptor.Method.Should().Be(method); - descriptor.FixedPrice.Should().Be(price); - } - - private bool TestHandler(ApplicationEngine engine) - { - return true; - } - } -} diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index 5329463ffe..cf93e5abb3 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -24,7 +24,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemRuntimeCheckWitnessHash); - InteropService.Runtime.CheckWitness.FixedPrice.Should().Be(0_00030000L); + ApplicationEngine.System_Runtime_CheckWitness.FixedPrice.Should().Be(0_00030000L); } // System.Storage.GetContext: 9bf667ce (price is 1) @@ -32,7 +32,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemStorageGetContextHash); - InteropService.Storage.GetContext.FixedPrice.Should().Be(0_00000400L); + ApplicationEngine.System_Storage_GetContext.FixedPrice.Should().Be(0_00000400L); } // System.Storage.Get: 925de831 (price is 100) @@ -40,7 +40,7 @@ public void ApplicationEngineFixedPrices() using (ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, null, 0)) { ae.LoadScript(SyscallSystemStorageGetHash); - InteropService.Storage.Get.FixedPrice.Should().Be(0_01000000L); + ApplicationEngine.System_Storage_Get.FixedPrice.Should().Be(0_01000000L); } } @@ -74,7 +74,7 @@ public void ApplicationEngineRegularPut() debugger.StepInto(); var setupPrice = ae.GasConsumed; debugger.Execute(); - (ae.GasConsumed - setupPrice).Should().Be(InteropService.Storage.GasPerByte * value.Length); + (ae.GasConsumed - setupPrice).Should().Be(ApplicationEngine.StoragePrice * value.Length); } } @@ -108,7 +108,7 @@ public void ApplicationEngineReusedStorage_FullReuse() debugger.StepInto(); var setupPrice = applicationEngine.GasConsumed; debugger.Execute(); - (applicationEngine.GasConsumed - setupPrice).Should().Be(1 * InteropService.Storage.GasPerByte); + (applicationEngine.GasConsumed - setupPrice).Should().Be(1 * ApplicationEngine.StoragePrice); } } @@ -145,7 +145,7 @@ public void ApplicationEngineReusedStorage_PartialReuse() var setupPrice = ae.GasConsumed; debugger.StepInto(); debugger.StepInto(); - (ae.GasConsumed - setupPrice).Should().Be(1 * InteropService.Storage.GasPerByte); + (ae.GasConsumed - setupPrice).Should().Be(1 * ApplicationEngine.StoragePrice); } } @@ -185,7 +185,7 @@ public void ApplicationEngineReusedStorage_PartialReuseTwice() debugger.StepInto(); //syscall Storage.GetContext var setupPrice = ae.GasConsumed; debugger.StepInto(); //syscall Storage.Put - (ae.GasConsumed - setupPrice).Should().Be(1 * InteropService.Storage.GasPerByte); // = PUT basic fee + (ae.GasConsumed - setupPrice).Should().Be(1 * ApplicationEngine.StoragePrice); // = PUT basic fee } } @@ -197,8 +197,8 @@ private byte[] CreateMultiplePutScript(byte[] key, byte[] value, int times = 2) { scriptBuilder.EmitPush(value); scriptBuilder.EmitPush(key); - scriptBuilder.EmitSysCall(InteropService.Storage.GetContext); - scriptBuilder.EmitSysCall(InteropService.Storage.Put); + scriptBuilder.EmitSysCall(ApplicationEngine.System_Storage_GetContext); + scriptBuilder.EmitSysCall(ApplicationEngine.System_Storage_Put); } return scriptBuilder.ToArray(); @@ -209,8 +209,8 @@ private byte[] CreatePutScript(byte[] key, byte[] value) var scriptBuilder = new ScriptBuilder(); scriptBuilder.EmitPush(value); scriptBuilder.EmitPush(key); - scriptBuilder.EmitSysCall(InteropService.Storage.GetContext); - scriptBuilder.EmitSysCall(InteropService.Storage.Put); + scriptBuilder.EmitSysCall(ApplicationEngine.System_Storage_GetContext); + scriptBuilder.EmitSysCall(ApplicationEngine.System_Storage_Put); return scriptBuilder.ToArray(); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 99beb32046..b0a2547107 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -7,7 +7,6 @@ using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.SmartContract; -using Neo.SmartContract.Enumerators; using Neo.SmartContract.Iterators; using Neo.SmartContract.Manifest; using Neo.VM; @@ -15,6 +14,7 @@ using Neo.Wallets; using System; using System.Linq; +using System.Text; using VMArray = Neo.VM.Types.Array; namespace Neo.UnitTests.SmartContract @@ -32,17 +32,8 @@ public void TestCheckSig() KeyPair keyPair = new KeyPair(privateKey); ECPoint pubkey = keyPair.PublicKey; byte[] signature = Crypto.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); - engine.CurrentContext.EvaluationStack.Push(signature); - engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.VerifyWithECDsaSecp256r1).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); - - engine.CurrentContext.EvaluationStack.Push(signature); - engine.CurrentContext.EvaluationStack.Push(new byte[70]); - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.VerifyWithECDsaSecp256r1).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); + engine.VerifyWithECDsaSecp256r1(StackItem.Null, pubkey.EncodePoint(false), signature).Should().BeTrue(); + engine.VerifyWithECDsaSecp256r1(StackItem.Null, new byte[70], signature).Should().BeFalse(); } [TestMethod] @@ -64,70 +55,52 @@ public void TestCrypto_CheckMultiSig() ECPoint pubkey2 = key2.PublicKey; byte[] signature2 = Crypto.Sign(message, privkey2, pubkey2.EncodePoint(false).Skip(1).ToArray()); - var pubkeys = new VMArray + var pubkeys = new[] { pubkey1.EncodePoint(false), pubkey2.EncodePoint(false) }; - var signatures = new VMArray + var signatures = new[] { signature1, signature2 }; - engine.CurrentContext.EvaluationStack.Push(signatures); - engine.CurrentContext.EvaluationStack.Push(pubkeys); - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.CheckMultisigWithECDsaSecp256r1).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); - - pubkeys = new VMArray(); - engine.CurrentContext.EvaluationStack.Push(signatures); - engine.CurrentContext.EvaluationStack.Push(pubkeys); - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.CheckMultisigWithECDsaSecp256r1).Should().BeFalse(); - - pubkeys = new VMArray + engine.CheckMultisigWithECDsaSecp256r1(StackItem.Null, pubkeys, signatures).Should().BeTrue(); + + pubkeys = new byte[0][]; + Assert.ThrowsException(() => engine.CheckMultisigWithECDsaSecp256r1(StackItem.Null, pubkeys, signatures)); + + pubkeys = new[] { pubkey1.EncodePoint(false), pubkey2.EncodePoint(false) }; - signatures = new VMArray(); - engine.CurrentContext.EvaluationStack.Push(signatures); - engine.CurrentContext.EvaluationStack.Push(pubkeys); - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.CheckMultisigWithECDsaSecp256r1).Should().BeFalse(); + signatures = new byte[0][]; + Assert.ThrowsException(() => engine.CheckMultisigWithECDsaSecp256r1(StackItem.Null, pubkeys, signatures)); - pubkeys = new VMArray + pubkeys = new[] { pubkey1.EncodePoint(false), pubkey2.EncodePoint(false) }; - signatures = new VMArray + signatures = new[] { signature1, new byte[64] }; - engine.CurrentContext.EvaluationStack.Push(signatures); - engine.CurrentContext.EvaluationStack.Push(pubkeys); - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.CheckMultisigWithECDsaSecp256r1).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); + engine.CheckMultisigWithECDsaSecp256r1(StackItem.Null, pubkeys, signatures).Should().BeFalse(); - pubkeys = new VMArray + pubkeys = new[] { pubkey1.EncodePoint(false), new byte[70] }; - signatures = new VMArray + signatures = new[] { signature1, signature2 }; - engine.CurrentContext.EvaluationStack.Push(signatures); - engine.CurrentContext.EvaluationStack.Push(pubkeys); - engine.CurrentContext.EvaluationStack.Push(StackItem.Null); - InteropService.Invoke(engine, InteropService.Crypto.CheckMultisigWithECDsaSecp256r1).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); + engine.CheckMultisigWithECDsaSecp256r1(StackItem.Null, pubkeys, signatures).Should().BeFalse(); } [TestMethod] @@ -138,72 +111,43 @@ public void TestAccount_IsStandard() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; - engine.CurrentContext.EvaluationStack.Push(hash); - InteropService.Invoke(engine, InteropService.Contract.IsStandard).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); + engine.IsStandardContract(new UInt160(hash)).Should().BeTrue(); var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); snapshot.Contracts.Add(state.ScriptHash, state); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.Contract.IsStandard).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); + engine.IsStandardContract(state.ScriptHash).Should().BeFalse(); } [TestMethod] public void TestContract_Create() { var engine = GetEngine(false, true); - var script = new byte[1024 * 1024 + 1]; - engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeFalse(); - - string manifestStr = new string(new char[ContractManifest.MaxLength + 1]); - script = new byte[] { 0x01 }; - engine.CurrentContext.EvaluationStack.Push(manifestStr); - engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeFalse(); + var script = new byte[] { 0x01 }; + Assert.ThrowsException(() => engine.CreateContract(script, new byte[ContractManifest.MaxLength + 1])); var manifest = TestUtils.CreateDefaultManifest(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01")); - engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); - engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeFalse(); + Assert.ThrowsException(() => engine.CreateContract(script, manifest.ToJson().ToByteArray(false))); manifest.Abi.Hash = script.ToScriptHash(); - engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); - engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeTrue(); + engine.CreateContract(script, manifest.ToJson().ToByteArray(false)); var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); snapshot.Contracts.Add(state.ScriptHash, state); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); engine.LoadScript(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); - engine.CurrentContext.EvaluationStack.Push(state.Script); - InteropService.Invoke(engine, InteropService.Contract.Create).Should().BeFalse(); + Assert.ThrowsException(() => engine.CreateContract(state.Script, manifest.ToJson().ToByteArray(false))); } [TestMethod] public void TestContract_Update() { var engine = GetEngine(false, true); - var script = new byte[1024 * 1024 + 1]; - engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeFalse(); - - string manifestStr = new string(new char[ContractManifest.MaxLength + 1]); - script = new byte[] { 0x01 }; - engine.CurrentContext.EvaluationStack.Push(manifestStr); - engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeFalse(); - - manifestStr = ""; - engine.CurrentContext.EvaluationStack.Push(manifestStr); - engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeFalse(); + var script = new byte[] { 0x01 }; + Assert.ThrowsException(() => engine.UpdateContract(script, new byte[0])); var manifest = TestUtils.CreateDefaultManifest(script.ToScriptHash()); byte[] privkey = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, @@ -238,9 +182,7 @@ public void TestContract_Update() snapshot.Storages.Add(storageKey, storageItem); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(state.Script); - engine.CurrentContext.EvaluationStack.Push(manifest.ToString()); - engine.CurrentContext.EvaluationStack.Push(script); - InteropService.Invoke(engine, InteropService.Contract.Update).Should().BeTrue(); + engine.UpdateContract(script, manifest.ToJson().ToByteArray(false)); engine.Snapshot.Storages.Find(BitConverter.GetBytes(state.Id)).ToList().Count().Should().Be(1); } @@ -266,20 +208,14 @@ public void TestStorage_Find() var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext + var iterator = engine.Find(new StorageContext { Id = state.Id, IsReadOnly = false - })); - InteropService.Invoke(engine, InteropService.Storage.Find).Should().BeTrue(); - var iterator = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + }, new byte[] { 0x01 }); iterator.Next(); var ele = iterator.Value(); ele.GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); - - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Storage.Find).Should().BeFalse(); } [TestMethod] @@ -290,15 +226,9 @@ public void TestEnumerator_Create() new byte[]{ 0x01 }, new byte[]{ 0x02 } }; - engine.CurrentContext.EvaluationStack.Push(arr); - InteropService.Invoke(engine, InteropService.Enumerator.Create).Should().BeTrue(); - var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); - ret.GetInterface().Next(); - ret.GetInterface().Value().GetSpan().ToHexString() - .Should().Be(new byte[] { 0x01 }.ToHexString()); - - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Enumerator.Create).Should().BeTrue(); + var ret = engine.CreateEnumerator(arr); + ret.Next(); + ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } [TestMethod] @@ -309,12 +239,7 @@ public void TestEnumerator_Next() new byte[]{ 0x01 }, new byte[]{ 0x02 } }; - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new ArrayWrapper(arr))); - InteropService.Invoke(engine, InteropService.Enumerator.Next).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); - - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Enumerator.Next).Should().BeFalse(); + engine.EnumeratorNext(new ArrayWrapper(arr)).Should().BeTrue(); } [TestMethod] @@ -327,12 +252,7 @@ public void TestEnumerator_Value() }; var wrapper = new ArrayWrapper(arr); wrapper.Next(); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); - InteropService.Invoke(engine, InteropService.Enumerator.Value).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); - - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Enumerator.Value).Should().BeFalse(); + engine.EnumeratorValue(wrapper).GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } [TestMethod] @@ -349,10 +269,7 @@ public void TestEnumerator_Concat() }; var wrapper1 = new ArrayWrapper(arr1); var wrapper2 = new ArrayWrapper(arr2); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper2)); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper1)); - InteropService.Invoke(engine, InteropService.Enumerator.Concat).Should().BeTrue(); - var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + var ret = engine.ConcatEnumerators(wrapper1, wrapper2); ret.Next().Should().BeTrue(); ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } @@ -365,31 +282,22 @@ public void TestIterator_Create() new byte[]{ 0x01 }, new byte[]{ 0x02 } }; - engine.CurrentContext.EvaluationStack.Push(arr); - InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeTrue(); - var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); - ret.GetInterface().Next(); - ret.GetInterface().Value().GetSpan().ToHexString() - .Should().Be(new byte[] { 0x01 }.ToHexString()); + var ret = engine.CreateIterator(arr); + ret.Next(); + ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); var interop = new InteropInterface(1); - engine.CurrentContext.EvaluationStack.Push(interop); - InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeFalse(); + Assert.ThrowsException(() => engine.CreateIterator(interop)); var map = new Map { [1] = 2, [3] = 4 }; - engine.CurrentContext.EvaluationStack.Push(map); - InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeTrue(); - ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); - ret.GetInterface().Next(); - ret.GetInterface().Key().GetBigInteger().Should().Be(1); - ret.GetInterface().Value().GetBigInteger().Should().Be(2); - - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Iterator.Create).Should().BeTrue(); + ret = engine.CreateIterator(map); + ret.Next(); + ret.Key().GetBigInteger().Should().Be(1); + ret.Value().GetBigInteger().Should().Be(2); } [TestMethod] @@ -402,12 +310,7 @@ public void TestIterator_Key() }; var wrapper = new ArrayWrapper(arr); wrapper.Next(); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); - InteropService.Invoke(engine, InteropService.Iterator.Key).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(0); - - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Iterator.Key).Should().BeFalse(); + engine.IteratorKey(wrapper).GetBigInteger().Should().Be(0); } [TestMethod] @@ -419,14 +322,9 @@ public void TestIterator_Keys() new byte[]{ 0x02 } }; var wrapper = new ArrayWrapper(arr); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); - InteropService.Invoke(engine, InteropService.Iterator.Keys).Should().BeTrue(); - var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + var ret = engine.IteratorKeys(wrapper); ret.Next(); ret.Value().GetBigInteger().Should().Be(0); - - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Iterator.Keys).Should().BeFalse(); } [TestMethod] @@ -438,14 +336,9 @@ public void TestIterator_Values() new byte[]{ 0x02 } }; var wrapper = new ArrayWrapper(arr); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper)); - InteropService.Invoke(engine, InteropService.Iterator.Values).Should().BeTrue(); - var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + var ret = engine.IteratorValues(wrapper); ret.Next(); ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); - - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Iterator.Values).Should().BeFalse(); } [TestMethod] @@ -462,10 +355,7 @@ public void TestIterator_Concat() }; var wrapper1 = new ArrayWrapper(arr1); var wrapper2 = new ArrayWrapper(arr2); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper2)); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(wrapper1)); - InteropService.Invoke(engine, InteropService.Iterator.Concat).Should().BeTrue(); - var ret = ((InteropInterface)engine.CurrentContext.EvaluationStack.Pop()).GetInterface(); + var ret = engine.ConcatIterators(wrapper1, wrapper2); ret.Next().Should().BeTrue(); ret.Value().GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } @@ -473,21 +363,13 @@ public void TestIterator_Concat() [TestMethod] public void TestJson_Deserialize() { - var engine = GetEngine(); - engine.CurrentContext.EvaluationStack.Push("1"); - InteropService.Invoke(engine, InteropService.Json.Deserialize).Should().BeTrue(); - var ret = engine.CurrentContext.EvaluationStack.Pop(); - ret.GetBigInteger().Should().Be(1); + GetEngine().JsonDeserialize(new byte[] { (byte)'1' }).GetBigInteger().Should().Be(1); } [TestMethod] public void TestJson_Serialize() { - var engine = GetEngine(); - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Json.Serialize).Should().BeTrue(); - var ret = engine.CurrentContext.EvaluationStack.Pop(); - ret.GetString().Should().Be("1"); + Encoding.UTF8.GetString(GetEngine().JsonSerialize(1)).Should().Be("1"); } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 1fa5689e7a..399f063ec7 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -40,7 +40,7 @@ public void Runtime_GetNotifications_Test() // Notify method - script.EmitSysCall(InteropService.Runtime.Notify); + script.EmitSysCall(ApplicationEngine.System_Runtime_Notify); // Add return @@ -67,7 +67,7 @@ public void Runtime_GetNotifications_Test() // Retrive script.EmitPush(1); - script.EmitSysCall(InteropService.Runtime.GetNotifications); + script.EmitSysCall(ApplicationEngine.System_Runtime_GetNotifications); // Execute @@ -84,7 +84,7 @@ public void Runtime_GetNotifications_Test() // Notification 1 -> 13 script.EmitPush(13); - script.EmitSysCall(InteropService.Runtime.Notify); + script.EmitSysCall(ApplicationEngine.System_Runtime_Notify); // Call script @@ -97,7 +97,7 @@ public void Runtime_GetNotifications_Test() // Receive all notifications script.Emit(OpCode.PUSHNULL); - script.EmitSysCall(InteropService.Runtime.GetNotifications); + script.EmitSysCall(ApplicationEngine.System_Runtime_GetNotifications); // Execute @@ -134,7 +134,7 @@ public void Runtime_GetNotifications_Test() // Notification 1 -> 13 script.EmitPush(13); - script.EmitSysCall(InteropService.Runtime.Notify); + script.EmitSysCall(ApplicationEngine.System_Runtime_Notify); // Call script @@ -147,7 +147,7 @@ public void Runtime_GetNotifications_Test() // Receive all notifications script.EmitPush(scriptHash2.ToArray()); - script.EmitSysCall(InteropService.Runtime.GetNotifications); + script.EmitSysCall(ApplicationEngine.System_Runtime_GetNotifications); // Execute @@ -203,20 +203,7 @@ private void AssertNotification(StackItem stackItem, UInt160 scriptHash, int not [TestMethod] public void TestExecutionEngine_GetScriptContainer() { - var engine = GetEngine(true); - InteropService.Invoke(engine, InteropService.Runtime.GetScriptContainer).Should().BeTrue(); - var stackItem = ((VM.Types.Array)engine.CurrentContext.EvaluationStack.Pop()).ToArray(); - stackItem.Length.Should().Be(8); - stackItem[0].GetSpan().ToHexString().Should().Be(TestUtils.GetTransaction().Hash.ToArray().ToHexString()); - } - - [TestMethod] - public void TestExecutionEngine_GetExecutingScriptHash() - { - var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.Runtime.GetExecutingScriptHash).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() - .Should().Be(engine.CurrentScriptHash.ToArray().ToHexString()); + GetEngine(true).GetScriptContainer().Should().BeOfType(); } [TestMethod] @@ -225,15 +212,14 @@ public void TestExecutionEngine_GetCallingScriptHash() // Test without var engine = GetEngine(true); - InteropService.Invoke(engine, InteropService.Runtime.GetCallingScriptHash).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); + engine.CallingScriptHash.Should().BeNull(); // Test real using ScriptBuilder scriptA = new ScriptBuilder(); scriptA.Emit(OpCode.DROP); // Drop arguments scriptA.Emit(OpCode.DROP); // Drop method - scriptA.EmitSysCall(InteropService.Runtime.GetCallingScriptHash); + scriptA.EmitSysCall(ApplicationEngine.System_Runtime_GetCallingScriptHash); var contract = new ContractState() { @@ -252,40 +238,16 @@ public void TestExecutionEngine_GetCallingScriptHash() engine.ResultStack.Pop().GetSpan().ToHexString().Should().Be(scriptB.ToArray().ToScriptHash().ToArray().ToHexString()); } - [TestMethod] - public void TestExecutionEngine_GetEntryScriptHash() - { - var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.Runtime.GetEntryScriptHash).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() - .Should().Be(engine.EntryScriptHash.ToArray().ToHexString()); - } - [TestMethod] public void TestContract_GetCallFlags() { - var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.Contract.GetCallFlags).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBigInteger() - .Should().Be(((int)CallFlags.All)); + GetEngine().GetCallFlags().Should().Be(CallFlags.All); } [TestMethod] public void TestRuntime_Platform() { - var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.Runtime.Platform).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() - .Should().Be(Encoding.ASCII.GetBytes("NEO").ToHexString()); - } - - [TestMethod] - public void TestRuntime_GetTrigger() - { - var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.Runtime.GetTrigger).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBigInteger() - .Should().Be((int)engine.Trigger); + GetEngine().GetPlatform().Should().Be("NEO"); } [TestMethod] @@ -299,18 +261,11 @@ public void TestRuntime_CheckWitness() var engine = GetEngine(true); ((Transaction)engine.ScriptContainer).Sender = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); - engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(true)); - InteropService.Invoke(engine, InteropService.Runtime.CheckWitness).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Peek().GetType().Should().Be(typeof(Neo.VM.Types.Boolean)); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().Be(false); + engine.CheckWitness(pubkey.EncodePoint(true)).Should().BeFalse(); + engine.CheckWitness(((Transaction)engine.ScriptContainer).Sender.ToArray()).Should().BeFalse(); - engine.CurrentContext.EvaluationStack.Push(((Transaction)engine.ScriptContainer).Sender.ToArray()); - InteropService.Invoke(engine, InteropService.Runtime.CheckWitness).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Peek().GetType().Should().Be(typeof(Neo.VM.Types.Boolean)); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().Be(false); - - engine.CurrentContext.EvaluationStack.Push(new byte[0]); - InteropService.Invoke(engine, InteropService.Runtime.CheckWitness).Should().BeFalse(); + Action action = () => engine.CheckWitness(new byte[0]); + action.Should().Throw(); } [TestMethod] @@ -318,9 +273,8 @@ public void TestRuntime_Log() { var engine = GetEngine(true); string message = "hello"; - engine.CurrentContext.EvaluationStack.Push(Encoding.UTF8.GetBytes(message)); ApplicationEngine.Log += LogEvent; - InteropService.Invoke(engine, InteropService.Runtime.Log).Should().BeTrue(); + engine.RuntimeLog(Encoding.UTF8.GetBytes(message)); ((Transaction)engine.ScriptContainer).Script.ToHexString().Should().Be(new byte[] { 0x01, 0x02, 0x03 }.ToHexString()); ApplicationEngine.Log -= LogEvent; } @@ -329,51 +283,40 @@ public void TestRuntime_Log() public void TestRuntime_GetTime() { Block block = new Block(); - TestUtils.SetupBlockWithValues(block, UInt256.Zero, out var merkRootVal, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 0); + TestUtils.SetupBlockWithValues(block, UInt256.Zero, out _, out _, out _, out _, out _, out _, 0); var engine = GetEngine(true, true); engine.Snapshot.PersistingBlock = block; - - InteropService.Invoke(engine, InteropService.Runtime.GetTime).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(block.Timestamp); + engine.GetTime().Should().Be(block.Timestamp); } [TestMethod] public void TestRuntime_Serialize() { var engine = GetEngine(); - engine.CurrentContext.EvaluationStack.Push(100); - InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString() - .Should().Be(new byte[] { 0x21, 0x01, 0x64 }.ToHexString()); + engine.BinarySerialize(100).ToHexString().Should().Be(new byte[] { 0x21, 0x01, 0x64 }.ToHexString()); - engine.CurrentContext.EvaluationStack.Push(new byte[1024 * 1024 * 2]); //Larger than MaxItemSize - Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Binary.Serialize)); + //Larger than MaxItemSize + Assert.ThrowsException(() => engine.BinarySerialize(new byte[1024 * 1024 * 2])); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new object())); //NotSupportedException - Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Binary.Serialize)); + //NotSupportedException + Assert.ThrowsException(() => engine.BinarySerialize(new InteropInterface(new object()))); } [TestMethod] public void TestRuntime_Deserialize() { var engine = GetEngine(); - engine.CurrentContext.EvaluationStack.Push(100); - InteropService.Invoke(engine, InteropService.Binary.Serialize).Should().BeTrue(); - InteropService.Invoke(engine, InteropService.Binary.Deserialize).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(100); + engine.BinaryDeserialize(engine.BinarySerialize(100)).GetBigInteger().Should().Be(100); - engine.CurrentContext.EvaluationStack.Push(new byte[] { 0xfa, 0x01 }); //FormatException - Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Binary.Deserialize)); + //FormatException + Assert.ThrowsException(() => engine.BinaryDeserialize(new byte[] { 0xfa, 0x01 })); } [TestMethod] public void TestRuntime_GetInvocationCounter() { var engine = GetEngine(); - InteropService.Invoke(engine, InteropService.Runtime.GetInvocationCounter).Should().BeFalse(); - engine.InvocationCounter.TryAdd(engine.CurrentScriptHash, 10); - InteropService.Invoke(engine, InteropService.Runtime.GetInvocationCounter).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(10); + Assert.ThrowsException(() => engine.GetInvocationCounter()); } [TestMethod] @@ -387,28 +330,17 @@ public void TestCrypto_Verify() KeyPair keyPair = new KeyPair(privateKey); ECPoint pubkey = keyPair.PublicKey; byte[] signature = Crypto.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); - - engine.CurrentContext.EvaluationStack.Push(signature); - engine.CurrentContext.EvaluationStack.Push(pubkey.EncodePoint(false)); - engine.CurrentContext.EvaluationStack.Push(message); - InteropService.Invoke(engine, InteropService.Crypto.VerifyWithECDsaSecp256r1).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeTrue(); + engine.VerifyWithECDsaSecp256r1(message, pubkey.EncodePoint(false), signature).Should().BeTrue(); byte[] wrongkey = pubkey.EncodePoint(false); wrongkey[0] = 5; - engine.CurrentContext.EvaluationStack.Push(signature); - engine.CurrentContext.EvaluationStack.Push(wrongkey); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(engine.ScriptContainer)); - InteropService.Invoke(engine, InteropService.Crypto.VerifyWithECDsaSecp256r1).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Peek().ToBoolean().Should().BeFalse(); + engine.VerifyWithECDsaSecp256r1(new InteropInterface(engine.ScriptContainer), wrongkey, signature).Should().BeFalse(); } [TestMethod] public void TestBlockchain_GetHeight() { - var engine = GetEngine(true, true); - InteropService.Invoke(engine, InteropService.Blockchain.GetHeight).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(0); + GetEngine(true, true).GetBlockchainHeight().Should().Be(0); } [TestMethod] @@ -416,21 +348,16 @@ public void TestBlockchain_GetBlock() { var engine = GetEngine(true, true); - engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); - InteropService.Invoke(engine, InteropService.Blockchain.GetBlock).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); + engine.GetBlock(new byte[] { 0x01 }).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.CurrentContext.EvaluationStack.Push(data1); - InteropService.Invoke(engine, InteropService.Blockchain.GetBlock).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); + engine.GetBlock(data1).Should().BeNull(); byte[] data2 = new byte[] { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; - engine.CurrentContext.EvaluationStack.Push(data2); - InteropService.Invoke(engine, InteropService.Blockchain.GetBlock).Should().BeFalse(); + Assert.ThrowsException(() => engine.GetBlock(data2)); } [TestMethod] @@ -441,9 +368,7 @@ 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.CurrentContext.EvaluationStack.Push(data1); - InteropService.Invoke(engine, InteropService.Blockchain.GetTransaction).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().ToBoolean().Should().BeFalse(); + engine.GetTransaction(new UInt256(data1)).Should().BeNull(); } [TestMethod] @@ -454,9 +379,7 @@ public void TestBlockchain_GetTransactionHeight() 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.CurrentContext.EvaluationStack.Push(data1); - InteropService.Invoke(engine, InteropService.Blockchain.GetTransactionHeight).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetBigInteger().Should().Be(-1); + engine.GetTransactionHeight(new UInt256(data1)).Should().Be(-1); } [TestMethod] @@ -467,23 +390,14 @@ public void TestBlockchain_GetContract() 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; - engine.CurrentContext.EvaluationStack.Push(data1); - InteropService.Invoke(engine, InteropService.Blockchain.GetContract).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().Should().Be(StackItem.Null); + engine.GetContract(new UInt160(data1)).Should().BeNull(); var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); snapshot.Contracts.Add(state.ScriptHash, state); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.Blockchain.GetContract).Should().BeTrue(); - var stackItems = ((VM.Types.Array)engine.CurrentContext.EvaluationStack.Pop()).ToArray(); - stackItems.Length.Should().Be(3); - stackItems[0].GetType().Should().Be(typeof(ByteString)); - stackItems[0].GetSpan().ToHexString().Should().Be(state.Script.ToHexString()); - stackItems[1].ToBoolean().Should().BeFalse(); - stackItems[2].ToBoolean().Should().BeFalse(); + engine.GetContract(state.ScriptHash).Should().BeSameAs(state); } [TestMethod] @@ -494,9 +408,7 @@ public void TestStorage_GetContext() state.Manifest.Features = ContractFeatures.HasStorage; engine.Snapshot.Contracts.Add(state.ScriptHash, state); engine.LoadScript(state.Script); - InteropService.Invoke(engine, InteropService.Storage.GetContext).Should().BeTrue(); - var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); - ret.GetInterface().IsReadOnly.Should().BeFalse(); + engine.GetStorageContext().IsReadOnly.Should().BeFalse(); } [TestMethod] @@ -507,9 +419,7 @@ public void TestStorage_GetReadOnlyContext() state.Manifest.Features = ContractFeatures.HasStorage; engine.Snapshot.Contracts.Add(state.ScriptHash, state); engine.LoadScript(state.Script); - InteropService.Invoke(engine, InteropService.Storage.GetReadOnlyContext).Should().BeTrue(); - var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); - ret.GetInterface().IsReadOnly.Should().BeTrue(); + engine.GetReadOnlyContext().IsReadOnly.Should().BeTrue(); } [TestMethod] @@ -535,61 +445,44 @@ public void TestStorage_Get() var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(new StorageContext + engine.Get(new StorageContext { Id = state.Id, IsReadOnly = false - })); - InteropService.Invoke(engine, InteropService.Storage.Get).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToHexString().Should().Be(storageItem.Value.ToHexString()); + }, new byte[] { 0x01 }).ToHexString().Should().Be(storageItem.Value.ToHexString()); } [TestMethod] public void TestStorage_Put() { var engine = GetEngine(false, true); - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); //CheckStorageContext fail var key = new byte[] { 0x01 }; var value = new byte[] { 0x02 }; - engine.CurrentContext.EvaluationStack.Push(value); - engine.CurrentContext.EvaluationStack.Push(key); var state = TestUtils.GetContract(); var storageContext = new StorageContext { Id = state.Id, IsReadOnly = false }; - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeTrue(); + engine.Put(storageContext, key, value); //key.Length > MaxStorageKeySize - key = new byte[InteropService.Storage.MaxKeySize + 1]; + key = new byte[ApplicationEngine.MaxStorageKeySize + 1]; value = new byte[] { 0x02 }; - engine.CurrentContext.EvaluationStack.Push(value); - engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); + Assert.ThrowsException(() => engine.Put(storageContext, key, value)); //value.Length > MaxStorageValueSize key = new byte[] { 0x01 }; value = new byte[ushort.MaxValue + 1]; - engine.CurrentContext.EvaluationStack.Push(value); - engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); + Assert.ThrowsException(() => engine.Put(storageContext, key, value)); //context.IsReadOnly key = new byte[] { 0x01 }; value = new byte[] { 0x02 }; storageContext.IsReadOnly = true; - engine.CurrentContext.EvaluationStack.Push(value); - engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); + Assert.ThrowsException(() => engine.Put(storageContext, key, value)); //storage value is constant var snapshot = Blockchain.Singleton.GetSnapshot(); @@ -612,34 +505,22 @@ public void TestStorage_Put() key = new byte[] { 0x01 }; value = new byte[] { 0x02 }; storageContext.IsReadOnly = false; - engine.CurrentContext.EvaluationStack.Push(value); - engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeFalse(); + Assert.ThrowsException(() => engine.Put(storageContext, key, value)); //success storageItem.IsConstant = false; - engine.CurrentContext.EvaluationStack.Push(value); - engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeTrue(); + engine.Put(storageContext, key, value); //value length == 0 key = new byte[] { 0x01 }; value = new byte[0]; - engine.CurrentContext.EvaluationStack.Push(value); - engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Put).Should().BeTrue(); + engine.Put(storageContext, key, value); } [TestMethod] public void TestStorage_PutEx() { var engine = GetEngine(false, true); - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Storage.PutEx).Should().BeFalse(); - var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); state.Manifest.Features = ContractFeatures.HasStorage; @@ -664,21 +545,13 @@ public void TestStorage_PutEx() Id = state.Id, IsReadOnly = false }; - engine.CurrentContext.EvaluationStack.Push((int)StorageFlags.None); - engine.CurrentContext.EvaluationStack.Push(value); - engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.PutEx).Should().BeTrue(); + engine.PutEx(storageContext, key, value, StorageFlags.None); } [TestMethod] public void TestStorage_Delete() { var engine = GetEngine(false, true); - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeFalse(); - - var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); state.Manifest.Features = ContractFeatures.HasStorage; @@ -703,42 +576,24 @@ public void TestStorage_Delete() Id = state.Id, IsReadOnly = false }; - engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeTrue(); + engine.Delete(storageContext, key); //context is readonly storageContext.IsReadOnly = true; - engine.CurrentContext.EvaluationStack.Push(key); - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.Delete).Should().BeFalse(); + Assert.ThrowsException(() => engine.Delete(storageContext, key)); } [TestMethod] public void TestStorageContext_AsReadOnly() { var engine = GetEngine(); - engine.CurrentContext.EvaluationStack.Push(1); - InteropService.Invoke(engine, InteropService.Storage.AsReadOnly).Should().BeFalse(); - var state = TestUtils.GetContract(); var storageContext = new StorageContext { Id = state.Id, IsReadOnly = false }; - engine.CurrentContext.EvaluationStack.Push(new InteropInterface(storageContext)); - InteropService.Invoke(engine, InteropService.Storage.AsReadOnly).Should().BeTrue(); - var ret = (InteropInterface)engine.CurrentContext.EvaluationStack.Pop(); - ret.GetInterface().IsReadOnly.Should().Be(true); - } - - [TestMethod] - public void TestInvoke() - { - var engine = new ApplicationEngine(TriggerType.Verification, null, null, 0); - InteropService.Invoke(engine, 10000).Should().BeFalse(); - InteropService.Invoke(engine, InteropService.Storage.AsReadOnly).Should().BeFalse(); + engine.AsReadOnly(storageContext).IsReadOnly.Should().BeTrue(); } [TestMethod] @@ -747,36 +602,24 @@ public void TestContract_Call() var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract("method"); state.Manifest.Features = ContractFeatures.HasStorage; - byte[] method = Encoding.UTF8.GetBytes("method"); + string method = "method"; var args = new VM.Types.Array { 0, 1 }; snapshot.Contracts.Add(state.ScriptHash, state); var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push(args); - engine.CurrentContext.EvaluationStack.Push(method); - engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.Contract.Call).Should().BeTrue(); + engine.CallContract(state.ScriptHash, method, 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.CurrentContext.EvaluationStack.Push(args); - engine.CurrentContext.EvaluationStack.Push(method); - engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.Contract.Call).Should().BeFalse(); - state.Manifest.Permissions[0].Methods = WildcardContainer.CreateWildcard(); + Assert.ThrowsException(() => engine.CallContract(state.ScriptHash, method, args)); - engine.CurrentContext.EvaluationStack.Push(args); - engine.CurrentContext.EvaluationStack.Push(method); - engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.Contract.Call).Should().BeTrue(); + state.Manifest.Permissions[0].Methods = WildcardContainer.CreateWildcard(); + engine.CallContract(state.ScriptHash, method, args); - engine.CurrentContext.EvaluationStack.Push(args); - engine.CurrentContext.EvaluationStack.Push(method); - engine.CurrentContext.EvaluationStack.Push(UInt160.Zero.ToArray()); - InteropService.Invoke(engine, InteropService.Contract.Call).Should().BeFalse(); + Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, method, args)); } [TestMethod] @@ -788,7 +631,7 @@ public void TestContract_CallEx() state.Manifest.Features = ContractFeatures.HasStorage; snapshot.Contracts.Add(state.ScriptHash, state); - byte[] method = Encoding.UTF8.GetBytes("method"); + string method = "method"; var args = new VM.Types.Array { 0, 1 }; foreach (var flags in new CallFlags[] { CallFlags.None, CallFlags.AllowCall, CallFlags.AllowModifyStates, CallFlags.All }) @@ -796,39 +639,17 @@ public void TestContract_CallEx() var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); - engine.CurrentContext.EvaluationStack.Push((int)CallFlags.All); - engine.CurrentContext.EvaluationStack.Push(args); - engine.CurrentContext.EvaluationStack.Push(method); - engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.Contract.CallEx).Should().BeTrue(); + engine.CallContractEx(state.ScriptHash, method, args, CallFlags.All); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); // Contract doesn't exists - - engine.CurrentContext.EvaluationStack.Push((int)CallFlags.All); - engine.CurrentContext.EvaluationStack.Push(args); - engine.CurrentContext.EvaluationStack.Push(method); - engine.CurrentContext.EvaluationStack.Push(UInt160.Zero.ToArray()); - InteropService.Invoke(engine, InteropService.Contract.CallEx).Should().BeFalse(); + Assert.ThrowsException(() => engine.CallContractEx(UInt160.Zero, method, args, CallFlags.All)); // Call with rights - - engine.CurrentContext.EvaluationStack.Push((int)flags); - engine.CurrentContext.EvaluationStack.Push(args); - engine.CurrentContext.EvaluationStack.Push(method); - engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.Contract.CallEx).Should().BeTrue(); + engine.CallContractEx(state.ScriptHash, method, args, flags); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); - - // Check rights - - engine.CurrentContext.EvaluationStack.Push((int)CallFlags.All); - engine.CurrentContext.EvaluationStack.Push(args); - engine.CurrentContext.EvaluationStack.Push(method); - engine.CurrentContext.EvaluationStack.Push(state.ScriptHash.ToArray()); - InteropService.Invoke(engine, InteropService.Contract.CallEx).Should().Be(flags.HasFlag(CallFlags.AllowCall)); } } @@ -836,7 +657,7 @@ public void TestContract_CallEx() public void TestContract_Destroy() { var engine = GetEngine(false, true); - InteropService.Invoke(engine, InteropService.Contract.Destroy).Should().BeTrue(); + engine.DestroyContract(); var snapshot = Blockchain.Singleton.GetSnapshot(); var state = TestUtils.GetContract(); @@ -857,7 +678,7 @@ public void TestContract_Destroy() snapshot.Storages.Add(storageKey, storageItem); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[0]); - InteropService.Invoke(engine, InteropService.Contract.Destroy).Should().BeTrue(); + engine.DestroyContract(); engine.Snapshot.Storages.Find(BitConverter.GetBytes(0x43000000)).Any().Should().BeFalse(); //storages are removed @@ -866,48 +687,16 @@ public void TestContract_Destroy() snapshot.Contracts.Add(scriptHash, state); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[0]); - InteropService.Invoke(engine, InteropService.Contract.Destroy).Should().BeTrue(); + engine.DestroyContract(); engine.Snapshot.Storages.Find(BitConverter.GetBytes(0x43000000)).Any().Should().BeFalse(); - } [TestMethod] public void TestContract_CreateStandardAccount() { var engine = GetEngine(true, true); - byte[] data = "024b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e".HexToBytes(); - - engine.CurrentContext.EvaluationStack.Push(data); - InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount).Should().BeTrue(); - engine.CurrentContext.EvaluationStack.Pop().GetSpan().ToArray().ToHexString().Should().Be("68f96a15748750cccd548feb71be766e8a2c2733"); - - data = "064b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e".HexToBytes(); - engine.CurrentContext.EvaluationStack.Push(data); - Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("Invalid point encoding 6"); - - data = "024b817ef37f2fc3d4a33fe36687e599f30fe24b3e28187dc8f12b3b3b2b839e".HexToBytes(); - engine.CurrentContext.EvaluationStack.Push(data); - Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("Incorrect length for compressed encoding"); - - data = "02ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".HexToBytes(); - engine.CurrentContext.EvaluationStack.Push(data); - Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("x value too large in field element"); - - data = "020fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".HexToBytes(); - engine.CurrentContext.EvaluationStack.Push(data); - Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("Invalid point compression"); - - data = "044b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e".HexToBytes(); - engine.CurrentContext.EvaluationStack.Push(data); - Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("Incorrect length for uncompressed/hybrid encoding"); - - data = "04ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".HexToBytes(); - engine.CurrentContext.EvaluationStack.Push(data); - Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("x value too large in field element"); - - data = "040fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".HexToBytes(); - engine.CurrentContext.EvaluationStack.Push(data); - Assert.ThrowsException(() => InteropService.Invoke(engine, InteropService.Contract.CreateStandardAccount)).Message.Should().BeEquivalentTo("x value too large in field element"); + ECPoint pubkey = ECPoint.Parse("024b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e", ECCurve.Secp256r1); + engine.CreateStandardAccount(pubkey).ToArray().ToHexString().Should().Be("a17e91aff4bb5e0ad54d7ce8de8472e17ce88bf1"); } public static void LogEvent(object sender, LogEventArgs args) diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 0fc198f3d5..848079aef3 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -57,7 +57,7 @@ public void System_Blockchain_GetBlock() using (var script = new ScriptBuilder()) { script.EmitPush(block.Hash.ToArray()); - script.EmitSysCall(InteropService.Blockchain.GetBlock); + script.EmitSysCall(ApplicationEngine.System_Blockchain_GetBlock); // Without block @@ -89,7 +89,7 @@ public void System_Blockchain_GetBlock() height.Index = block.Index; - script.EmitSysCall(InteropService.Json.Serialize); + script.EmitSysCall(ApplicationEngine.System_Json_Serialize); engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(script.ToArray()); @@ -115,9 +115,9 @@ public void Json_Deserialize() using (var script = new ScriptBuilder()) { script.EmitPush("123"); - script.EmitSysCall(InteropService.Json.Deserialize); + script.EmitSysCall(ApplicationEngine.System_Json_Deserialize); script.EmitPush("null"); - script.EmitSysCall(InteropService.Json.Deserialize); + script.EmitSysCall(ApplicationEngine.System_Json_Deserialize); using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) { @@ -136,7 +136,7 @@ public void Json_Deserialize() using (var script = new ScriptBuilder()) { script.EmitPush("***"); - script.EmitSysCall(InteropService.Json.Deserialize); + script.EmitSysCall(ApplicationEngine.System_Json_Deserialize); using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) { @@ -152,7 +152,7 @@ public void Json_Deserialize() using (var script = new ScriptBuilder()) { script.EmitPush("123.45"); - script.EmitSysCall(InteropService.Json.Deserialize); + script.EmitSysCall(ApplicationEngine.System_Json_Deserialize); using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) { @@ -172,20 +172,20 @@ public void Json_Serialize() using (var script = new ScriptBuilder()) { script.EmitPush(5); - script.EmitSysCall(InteropService.Json.Serialize); + script.EmitSysCall(ApplicationEngine.System_Json_Serialize); script.Emit(OpCode.PUSH0); script.Emit(OpCode.NOT); - script.EmitSysCall(InteropService.Json.Serialize); + script.EmitSysCall(ApplicationEngine.System_Json_Serialize); script.EmitPush("test"); - script.EmitSysCall(InteropService.Json.Serialize); + script.EmitSysCall(ApplicationEngine.System_Json_Serialize); script.Emit(OpCode.PUSHNULL); - script.EmitSysCall(InteropService.Json.Serialize); + script.EmitSysCall(ApplicationEngine.System_Json_Serialize); script.Emit(OpCode.NEWMAP); script.Emit(OpCode.DUP); script.EmitPush("key"); script.EmitPush("value"); script.Emit(OpCode.SETITEM); - script.EmitSysCall(InteropService.Json.Serialize); + script.EmitSysCall(ApplicationEngine.System_Json_Serialize); using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) { @@ -206,8 +206,8 @@ public void Json_Serialize() using (var script = new ScriptBuilder()) { - script.EmitSysCall(InteropService.Storage.GetContext); - script.EmitSysCall(InteropService.Json.Serialize); + script.EmitSysCall(ApplicationEngine.System_Storage_GetContext); + script.EmitSysCall(ApplicationEngine.System_Json_Serialize); using (var engine = new ApplicationEngine(TriggerType.Application, null, null, 0, true)) { @@ -225,7 +225,7 @@ public void System_ExecutionEngine_GetScriptContainer() var snapshot = Blockchain.Singleton.GetSnapshot(); using (var script = new ScriptBuilder()) { - script.EmitSysCall(InteropService.Runtime.GetScriptContainer); + script.EmitSysCall(ApplicationEngine.System_Runtime_GetScriptContainer); // Without tx @@ -238,7 +238,7 @@ public void System_ExecutionEngine_GetScriptContainer() // With tx - script.EmitSysCall(InteropService.Json.Serialize); + script.EmitSysCall(ApplicationEngine.System_Json_Serialize); var tx = new Transaction() { @@ -273,13 +273,13 @@ public void System_Runtime_GasLeft() using (var script = new ScriptBuilder()) { script.Emit(OpCode.NOP); - script.EmitSysCall(InteropService.Runtime.GasLeft); + script.EmitSysCall(ApplicationEngine.System_Runtime_GasLeft); script.Emit(OpCode.NOP); - script.EmitSysCall(InteropService.Runtime.GasLeft); + script.EmitSysCall(ApplicationEngine.System_Runtime_GasLeft); script.Emit(OpCode.NOP); script.Emit(OpCode.NOP); script.Emit(OpCode.NOP); - script.EmitSysCall(InteropService.Runtime.GasLeft); + script.EmitSysCall(ApplicationEngine.System_Runtime_GasLeft); // Execute @@ -300,7 +300,7 @@ public void System_Runtime_GasLeft() using (var script = new ScriptBuilder()) { - script.EmitSysCall(InteropService.Runtime.GasLeft); + script.EmitSysCall(ApplicationEngine.System_Runtime_GasLeft); // Execute @@ -327,7 +327,7 @@ public void System_Runtime_GetInvocationCounter() using (var script = new ScriptBuilder()) { - script.EmitSysCall(InteropService.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() }; diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 428855d0ae..4c89fe09a5 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -62,7 +62,7 @@ public void TestEmitAppCall1() tempArray[9] = (byte)OpCode.PUSHDATA1; tempArray[10] = 0x14;//scriptHash.Length Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 11, 20);//operation.data - uint api = InteropService.Contract.Call; + uint api = ApplicationEngine.System_Contract_Call; tempArray[31] = (byte)OpCode.SYSCALL; Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 32, 4);//api.data CollectionAssert.AreEqual(tempArray, sb.ToArray()); @@ -84,7 +84,7 @@ public void TestEmitAppCall2() tempArray[10] = (byte)OpCode.PUSHDATA1; tempArray[11] = 0x14;//scriptHash.Length Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 12, 20);//operation.data - uint api = InteropService.Contract.Call; + 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()); @@ -106,7 +106,7 @@ public void TestEmitAppCall3() tempArray[10] = (byte)OpCode.PUSHDATA1; tempArray[11] = 0x14;//scriptHash.Length Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 12, 20);//operation.data - uint api = InteropService.Contract.Call; + 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()); diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 41069a5006..e63145ff5d 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -171,9 +171,9 @@ public void TestGetVersion() public void TestGetAccount1() { MyWallet wallet = new MyWallet(); - wallet.CreateAccount(UInt160.Parse("0xb3f1526d9f9670df1a21a5953d5296c3a9c9173c")); + wallet.CreateAccount(UInt160.Parse("0xf55f6873ae944cf4ec9626e8855b8554e798a7d1")); WalletAccount account = wallet.GetAccount(ECCurve.Secp256r1.G); - account.ScriptHash.Should().Be(UInt160.Parse("0xb3f1526d9f9670df1a21a5953d5296c3a9c9173c")); + account.ScriptHash.Should().Be(UInt160.Parse("0xf55f6873ae944cf4ec9626e8855b8554e798a7d1")); } [TestMethod] From dfeb53e303319aecde22216734eef5ae60b2a432 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 30 May 2020 19:46:50 +0800 Subject: [PATCH 279/305] Optimize TransactionAttribute (#1674) --- src/neo/Network/P2P/Payloads/Cosigner.cs | 6 +--- src/neo/Network/P2P/Payloads/Transaction.cs | 33 ++++++++++++++----- .../P2P/Payloads/TransactionAttribute.cs | 1 + .../ApplicationEngine.Runtime.cs | 3 +- tests/neo.UnitTests/Ledger/UT_PoolItem.cs | 2 +- .../Network/P2P/Payloads/UT_Transaction.cs | 9 ++--- 6 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Cosigner.cs b/src/neo/Network/P2P/Payloads/Cosigner.cs index 7a045967fa..6677c688ea 100644 --- a/src/neo/Network/P2P/Payloads/Cosigner.cs +++ b/src/neo/Network/P2P/Payloads/Cosigner.cs @@ -17,11 +17,7 @@ public class Cosigner : TransactionAttribute public ECPoint[] AllowedGroups; public override TransactionAttributeType Type => TransactionAttributeType.Cosigner; - - public Cosigner() - { - this.Scopes = WitnessScope.Global; - } + public override bool AllowMultiple => true; public override int Size => base.Size + /*Account*/ UInt160.Length + diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 1d8cfe539f..48fe5902f9 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -50,8 +50,8 @@ public TransactionAttribute[] Attributes set { attributes = value; _cosigners = null; _hash = null; _size = 0; } } - private Cosigner[] _cosigners; - public Cosigner[] Cosigners => _cosigners ??= attributes.OfType().ToArray(); + private Dictionary _cosigners; + public IReadOnlyDictionary Cosigners => _cosigners ??= attributes.OfType().ToDictionary(p => p.Account); /// /// The NetworkFee for the transaction divided by its Size. @@ -155,6 +155,19 @@ void ISerializable.Deserialize(BinaryReader reader) _size = (int)reader.BaseStream.Position - startPosition; } + private static IEnumerable DeserializeAttributes(BinaryReader reader) + { + int count = (int)reader.ReadVarInt(MaxTransactionAttributes); + HashSet hashset = new HashSet(); + while (count-- > 0) + { + TransactionAttribute attribute = TransactionAttribute.DeserializeFrom(reader); + if (!attribute.AllowMultiple && !hashset.Add(attribute.Type)) + throw new FormatException(); + yield return attribute; + } + } + public void DeserializeUnsigned(BinaryReader reader) { Version = reader.ReadByte(); @@ -167,10 +180,15 @@ public void DeserializeUnsigned(BinaryReader reader) if (NetworkFee < 0) throw new FormatException(); if (SystemFee + NetworkFee < SystemFee) throw new FormatException(); ValidUntilBlock = reader.ReadUInt32(); - Attributes = new TransactionAttribute[reader.ReadVarInt(MaxTransactionAttributes)]; - for (int i = 0; i < Attributes.Length; i++) - Attributes[i] = TransactionAttribute.DeserializeFrom(reader); - if (Cosigners.Select(u => u.Account).Distinct().Count() != Cosigners.Length) throw new FormatException(); + Attributes = DeserializeAttributes(reader).ToArray(); + try + { + _ = Cosigners; + } + catch (ArgumentException) + { + throw new FormatException(); + } Script = reader.ReadVarBytes(ushort.MaxValue); if (Script.Length == 0) throw new FormatException(); } @@ -199,8 +217,7 @@ public override int GetHashCode() public UInt160[] GetScriptHashesForVerifying(StoreView snapshot) { - var hashes = new HashSet { Sender }; - hashes.UnionWith(Cosigners.Select(p => p.Account)); + var hashes = new HashSet(Cosigners.Keys) { Sender }; return hashes.OrderBy(p => p).ToArray(); } diff --git a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs index 344c23f052..1e94cdaddd 100644 --- a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -9,6 +9,7 @@ namespace Neo.Network.P2P.Payloads public abstract class TransactionAttribute : ISerializable { public abstract TransactionAttributeType Type { get; } + public abstract bool AllowMultiple { get; } public virtual int Size => sizeof(TransactionAttributeType); public void Deserialize(BinaryReader reader) diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index 481e6fcfcb..9610222a2d 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -104,8 +104,7 @@ internal bool CheckWitnessInternal(UInt160 hash) { if (ScriptContainer is Transaction tx) { - Cosigner cosigner = tx.Cosigners.FirstOrDefault(p => p.Account.Equals(hash)); - if (cosigner is null) return false; + if (!tx.Cosigners.TryGetValue(hash, out Cosigner cosigner)) return false; if (cosigner.Scopes == WitnessScope.Global) return true; if (cosigner.Scopes.HasFlag(WitnessScope.CalledByEntry)) { diff --git a/tests/neo.UnitTests/Ledger/UT_PoolItem.cs b/tests/neo.UnitTests/Ledger/UT_PoolItem.cs index a3c55712b8..09b6e71fba 100644 --- a/tests/neo.UnitTests/Ledger/UT_PoolItem.cs +++ b/tests/neo.UnitTests/Ledger/UT_PoolItem.cs @@ -132,7 +132,7 @@ public static Transaction GenerateTx(long networkFee, int size, byte[] overrideS }; tx.Attributes.Length.Should().Be(0); - tx.Cosigners.Length.Should().Be(0); + tx.Cosigners.Count.Should().Be(0); int diff = size - tx.Size; if (diff < 0) throw new ArgumentException(); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 0cb8b95b30..7e025ad4cd 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -11,6 +11,7 @@ using Neo.VM; using Neo.Wallets; using System; +using System.Linq; using System.Numerics; namespace Neo.UnitTests.Network.P2P.Payloads @@ -250,10 +251,10 @@ public void FeeIsSignatureContractDetailed() // Part II Assert.AreEqual(23, tx.Attributes.GetVarSize()); Assert.AreEqual(1, tx.Attributes.Length); - Assert.AreEqual(1, tx.Cosigners.Length); - Assert.AreEqual(23, tx.Cosigners.GetVarSize()); + Assert.AreEqual(1, tx.Cosigners.Count); + Assert.AreEqual(23, tx.Cosigners.Values.ToArray().GetVarSize()); // Note that Data size and Usage size are different (because of first byte on GetVarSize()) - Assert.AreEqual(22, tx.Cosigners[0].Size); + Assert.AreEqual(22, tx.Cosigners.Values.First().Size); // Part III Assert.AreEqual(86, tx.Script.GetVarSize()); // Part IV @@ -649,7 +650,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() // no attributes must exist tx.Attributes.Length.Should().Be(1); // one cosigner must exist - tx.Cosigners.Length.Should().Be(1); + tx.Cosigners.Count.Should().Be(1); // Fast check Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); From 0f047a1021f765302099ab6482cc931291239741 Mon Sep 17 00:00:00 2001 From: zhangtao Date: Mon, 1 Jun 2020 12:41:29 +0800 Subject: [PATCH 280/305] Add MPT (#1442) --- src/neo/Cryptography/MPT/BranchNode.cs | 35 ++ src/neo/Cryptography/MPT/ExtensionNode.cs | 30 ++ src/neo/Cryptography/MPT/HashNode.cs | 41 +++ src/neo/Cryptography/MPT/LeafNode.cs | 36 ++ src/neo/Cryptography/MPT/MPTNode.cs | 61 ++++ src/neo/Cryptography/MPT/MPTNodeType.cs | 16 + src/neo/Cryptography/MPT/MPTTrie.Delete.cs | 120 +++++++ src/neo/Cryptography/MPT/MPTTrie.Find.cs | 110 ++++++ src/neo/Cryptography/MPT/MPTTrie.Get.cs | 61 ++++ src/neo/Cryptography/MPT/MPTTrie.Proof.cs | 72 ++++ src/neo/Cryptography/MPT/MPTTrie.Put.cs | 158 +++++++++ src/neo/Cryptography/MPT/MPTTrie.cs | 58 ++++ src/neo/Helper.cs | 8 + .../Cryptography/MPT/UT_MPTNode.cs | 30 ++ .../Cryptography/MPT/UT_MPTTrie.cs | 317 ++++++++++++++++++ 15 files changed, 1153 insertions(+) create mode 100644 src/neo/Cryptography/MPT/BranchNode.cs create mode 100644 src/neo/Cryptography/MPT/ExtensionNode.cs create mode 100644 src/neo/Cryptography/MPT/HashNode.cs create mode 100644 src/neo/Cryptography/MPT/LeafNode.cs create mode 100644 src/neo/Cryptography/MPT/MPTNode.cs create mode 100644 src/neo/Cryptography/MPT/MPTNodeType.cs create mode 100644 src/neo/Cryptography/MPT/MPTTrie.Delete.cs create mode 100644 src/neo/Cryptography/MPT/MPTTrie.Find.cs create mode 100644 src/neo/Cryptography/MPT/MPTTrie.Get.cs create mode 100644 src/neo/Cryptography/MPT/MPTTrie.Proof.cs create mode 100644 src/neo/Cryptography/MPT/MPTTrie.Put.cs create mode 100644 src/neo/Cryptography/MPT/MPTTrie.cs create mode 100644 tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs create mode 100644 tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs diff --git a/src/neo/Cryptography/MPT/BranchNode.cs b/src/neo/Cryptography/MPT/BranchNode.cs new file mode 100644 index 0000000000..e69f2130ab --- /dev/null +++ b/src/neo/Cryptography/MPT/BranchNode.cs @@ -0,0 +1,35 @@ +using System.IO; + +namespace Neo.Cryptography.MPT +{ + public class BranchNode : MPTNode + { + public const int ChildCount = 17; + public readonly MPTNode[] Children = new MPTNode[ChildCount]; + + protected override NodeType Type => NodeType.BranchNode; + + public BranchNode() + { + for (int i = 0; i < ChildCount; i++) + { + Children[i] = HashNode.EmptyNode; + } + } + + internal override void EncodeSpecific(BinaryWriter writer) + { + for (int i = 0; i < ChildCount; i++) + WriteHash(writer, Children[i].Hash); + } + + internal override void DecodeSpecific(BinaryReader reader) + { + for (int i = 0; i < ChildCount; i++) + { + Children[i] = new HashNode(); + Children[i].DecodeSpecific(reader); + } + } + } +} diff --git a/src/neo/Cryptography/MPT/ExtensionNode.cs b/src/neo/Cryptography/MPT/ExtensionNode.cs new file mode 100644 index 0000000000..9c575915cf --- /dev/null +++ b/src/neo/Cryptography/MPT/ExtensionNode.cs @@ -0,0 +1,30 @@ +using Neo.IO; +using Neo.SmartContract; +using System.IO; + +namespace Neo.Cryptography.MPT +{ + public class ExtensionNode : MPTNode + { + //max lenght when store StorageKey + public const int MaxKeyLength = (ApplicationEngine.MaxStorageValueSize + sizeof(int)) * 2; + + public byte[] Key; + public MPTNode Next; + + protected override NodeType Type => NodeType.ExtensionNode; + + internal override void EncodeSpecific(BinaryWriter writer) + { + writer.WriteVarBytes(Key); + WriteHash(writer, Next.Hash); + } + + internal override void DecodeSpecific(BinaryReader reader) + { + Key = reader.ReadVarBytes(MaxKeyLength); + Next = new HashNode(); + Next.DecodeSpecific(reader); + } + } +} diff --git a/src/neo/Cryptography/MPT/HashNode.cs b/src/neo/Cryptography/MPT/HashNode.cs new file mode 100644 index 0000000000..b304833d66 --- /dev/null +++ b/src/neo/Cryptography/MPT/HashNode.cs @@ -0,0 +1,41 @@ +using Neo.IO; +using System; +using System.IO; + +namespace Neo.Cryptography.MPT +{ + public class HashNode : MPTNode + { + private UInt256 hash; + + public override UInt256 Hash => hash; + protected override NodeType Type => NodeType.HashNode; + public bool IsEmpty => Hash is null; + public static HashNode EmptyNode { get; } = new HashNode(); + + public HashNode() + { + } + + public HashNode(UInt256 hash) + { + this.hash = hash; + } + + internal override void EncodeSpecific(BinaryWriter writer) + { + WriteHash(writer, hash); + } + + internal override void DecodeSpecific(BinaryReader reader) + { + byte[] buffer = reader.ReadVarBytes(UInt256.Length); + hash = buffer.Length switch + { + 0 => null, + UInt256.Length => new UInt256(buffer), + _ => throw new FormatException() + }; + } + } +} diff --git a/src/neo/Cryptography/MPT/LeafNode.cs b/src/neo/Cryptography/MPT/LeafNode.cs new file mode 100644 index 0000000000..cf95a5bc63 --- /dev/null +++ b/src/neo/Cryptography/MPT/LeafNode.cs @@ -0,0 +1,36 @@ +using Neo.IO; +using Neo.SmartContract; +using System; +using System.IO; + +namespace Neo.Cryptography.MPT +{ + public class LeafNode : MPTNode + { + //the max size when store StorageItem + public const int MaxValueLength = 3 + ApplicationEngine.MaxStorageValueSize + sizeof(bool); + + public byte[] Value; + + protected override NodeType Type => NodeType.LeafNode; + + public LeafNode() + { + } + + public LeafNode(ReadOnlySpan value) + { + Value = value.ToArray(); + } + + internal override void EncodeSpecific(BinaryWriter writer) + { + writer.WriteVarBytes(Value); + } + + internal override void DecodeSpecific(BinaryReader reader) + { + Value = reader.ReadVarBytes(MaxValueLength); + } + } +} diff --git a/src/neo/Cryptography/MPT/MPTNode.cs b/src/neo/Cryptography/MPT/MPTNode.cs new file mode 100644 index 0000000000..85955c625f --- /dev/null +++ b/src/neo/Cryptography/MPT/MPTNode.cs @@ -0,0 +1,61 @@ +using Neo.IO; +using Neo.IO.Caching; +using System; +using System.IO; + +namespace Neo.Cryptography.MPT +{ + public abstract class MPTNode + { + private UInt256 hash; + + public virtual UInt256 Hash => hash ??= new UInt256(Crypto.Hash256(Encode())); + protected abstract NodeType Type { get; } + + public void SetDirty() + { + hash = null; + } + + public byte[] Encode() + { + using MemoryStream ms = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(ms); + + writer.Write((byte)Type); + EncodeSpecific(writer); + writer.Flush(); + + return ms.ToArray(); + } + + internal abstract void EncodeSpecific(BinaryWriter writer); + + public static unsafe MPTNode Decode(ReadOnlySpan data) + { + if (data.IsEmpty) return null; + + fixed (byte* pointer = data) + { + using UnmanagedMemoryStream stream = new UnmanagedMemoryStream(pointer, data.Length); + using BinaryReader reader = new BinaryReader(stream); + + MPTNode n = (MPTNode)ReflectionCache.CreateInstance((NodeType)reader.ReadByte()); + if (n is null) throw new InvalidOperationException(); + + n.DecodeSpecific(reader); + return n; + } + } + + internal abstract void DecodeSpecific(BinaryReader reader); + + protected void WriteHash(BinaryWriter writer, UInt256 hash) + { + if (hash is null) + writer.Write((byte)0); + else + writer.WriteVarBytes(hash.ToArray()); + } + } +} diff --git a/src/neo/Cryptography/MPT/MPTNodeType.cs b/src/neo/Cryptography/MPT/MPTNodeType.cs new file mode 100644 index 0000000000..0194fa9664 --- /dev/null +++ b/src/neo/Cryptography/MPT/MPTNodeType.cs @@ -0,0 +1,16 @@ +using Neo.IO.Caching; + +namespace Neo.Cryptography.MPT +{ + public enum NodeType : byte + { + [ReflectionCache(typeof(BranchNode))] + BranchNode = 0x00, + [ReflectionCache(typeof(ExtensionNode))] + ExtensionNode = 0x01, + [ReflectionCache(typeof(HashNode))] + HashNode = 0x02, + [ReflectionCache(typeof(LeafNode))] + LeafNode = 0x03, + } +} diff --git a/src/neo/Cryptography/MPT/MPTTrie.Delete.cs b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs new file mode 100644 index 0000000000..4996738b92 --- /dev/null +++ b/src/neo/Cryptography/MPT/MPTTrie.Delete.cs @@ -0,0 +1,120 @@ +using Neo.IO; +using System; +using System.Collections.Generic; +using static Neo.Helper; + +namespace Neo.Cryptography.MPT +{ + partial class MPTTrie + { + public bool Delete(TKey key) + { + var path = ToNibbles(key.ToArray()); + if (path.Length == 0) return false; + return TryDelete(ref root, path); + } + + private bool TryDelete(ref MPTNode node, ReadOnlySpan path) + { + switch (node) + { + case LeafNode _: + { + if (path.IsEmpty) + { + node = HashNode.EmptyNode; + return true; + } + return false; + } + case ExtensionNode extensionNode: + { + if (path.StartsWith(extensionNode.Key)) + { + var result = TryDelete(ref extensionNode.Next, path[extensionNode.Key.Length..]); + if (!result) return false; + if (extensionNode.Next is HashNode hashNode && hashNode.IsEmpty) + { + node = extensionNode.Next; + return true; + } + if (extensionNode.Next is ExtensionNode sn) + { + extensionNode.Key = Concat(extensionNode.Key, sn.Key); + extensionNode.Next = sn.Next; + } + extensionNode.SetDirty(); + PutToStore(extensionNode); + return true; + } + return false; + } + case BranchNode branchNode: + { + bool result; + if (path.IsEmpty) + { + result = TryDelete(ref branchNode.Children[BranchNode.ChildCount - 1], path); + } + else + { + result = TryDelete(ref branchNode.Children[path[0]], path[1..]); + } + if (!result) return false; + List childrenIndexes = new List(BranchNode.ChildCount); + for (int i = 0; i < BranchNode.ChildCount; i++) + { + if (branchNode.Children[i] is HashNode hn && hn.IsEmpty) continue; + childrenIndexes.Add((byte)i); + } + if (childrenIndexes.Count > 1) + { + branchNode.SetDirty(); + PutToStore(branchNode); + return true; + } + var lastChildIndex = childrenIndexes[0]; + var lastChild = branchNode.Children[lastChildIndex]; + if (lastChildIndex == BranchNode.ChildCount - 1) + { + node = lastChild; + return true; + } + if (lastChild is HashNode hashNode) + { + lastChild = Resolve(hashNode); + if (lastChild is null) return false; + } + if (lastChild is ExtensionNode exNode) + { + exNode.Key = Concat(childrenIndexes.ToArray(), exNode.Key); + exNode.SetDirty(); + PutToStore(exNode); + node = exNode; + return true; + } + node = new ExtensionNode() + { + Key = childrenIndexes.ToArray(), + Next = lastChild, + }; + PutToStore(node); + return true; + } + case HashNode hashNode: + { + if (hashNode.IsEmpty) + { + return true; + } + var newNode = Resolve(hashNode); + if (newNode is null) return false; + node = newNode; + return TryDelete(ref node, path); + } + default: + return false; + } + } + } +} diff --git a/src/neo/Cryptography/MPT/MPTTrie.Find.cs b/src/neo/Cryptography/MPT/MPTTrie.Find.cs new file mode 100644 index 0000000000..5f4153758b --- /dev/null +++ b/src/neo/Cryptography/MPT/MPTTrie.Find.cs @@ -0,0 +1,110 @@ +using Neo.IO; +using System; +using System.Collections.Generic; +using System.Linq; +using static Neo.Helper; + +namespace Neo.Cryptography.MPT +{ + partial class MPTTrie + { + private ReadOnlySpan Seek(ref MPTNode node, ReadOnlySpan path, out MPTNode start) + { + switch (node) + { + case LeafNode leafNode: + { + if (path.IsEmpty) + { + start = leafNode; + return ReadOnlySpan.Empty; + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmpty) break; + var newNode = Resolve(hashNode); + if (newNode is null) break; + node = newNode; + return Seek(ref node, path, out start); + } + case BranchNode branchNode: + { + if (path.IsEmpty) + { + start = branchNode; + return ReadOnlySpan.Empty; + } + return Concat(path[..1], Seek(ref branchNode.Children[path[0]], path[1..], out start)); + } + case ExtensionNode extensionNode: + { + if (path.IsEmpty) + { + start = extensionNode.Next; + return extensionNode.Key; + } + if (path.StartsWith(extensionNode.Key)) + { + return Concat(extensionNode.Key, Seek(ref extensionNode.Next, path[extensionNode.Key.Length..], out start)); + } + if (extensionNode.Key.AsSpan().StartsWith(path)) + { + start = extensionNode.Next; + return extensionNode.Key; + } + break; + } + } + start = null; + return ReadOnlySpan.Empty; + } + + public IEnumerable<(TKey Key, TValue Value)> Find(ReadOnlySpan prefix) + { + var path = ToNibbles(prefix); + path = Seek(ref root, path, out MPTNode start).ToArray(); + return Travers(start, path) + .Select(p => (FromNibbles(p.Key).AsSerializable(), p.Value.AsSerializable())); + } + + private IEnumerable<(byte[] Key, byte[] Value)> Travers(MPTNode node, byte[] path) + { + if (node is null) yield break; + switch (node) + { + case LeafNode leafNode: + { + yield return (path, (byte[])leafNode.Value.Clone()); + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmpty) break; + var newNode = Resolve(hashNode); + if (newNode is null) break; + node = newNode; + foreach (var item in Travers(node, path)) + yield return item; + break; + } + case BranchNode branchNode: + { + for (int i = 0; i < BranchNode.ChildCount; i++) + { + foreach (var item in Travers(branchNode.Children[i], i == BranchNode.ChildCount - 1 ? path : Concat(path, new byte[] { (byte)i }))) + yield return item; + } + break; + } + case ExtensionNode extensionNode: + { + foreach (var item in Travers(extensionNode.Next, Concat(path, extensionNode.Key))) + yield return item; + break; + } + } + } + } +} diff --git a/src/neo/Cryptography/MPT/MPTTrie.Get.cs b/src/neo/Cryptography/MPT/MPTTrie.Get.cs new file mode 100644 index 0000000000..367f2a61ce --- /dev/null +++ b/src/neo/Cryptography/MPT/MPTTrie.Get.cs @@ -0,0 +1,61 @@ +using Neo.IO; +using System; + +namespace Neo.Cryptography.MPT +{ + partial class MPTTrie + { + public TValue this[TKey key] + { + get + { + var path = ToNibbles(key.ToArray()); + if (path.Length == 0) return null; + var result = TryGet(ref root, path, out var value); + return result ? value.AsSerializable() : null; + } + } + + private bool TryGet(ref MPTNode node, ReadOnlySpan path, out ReadOnlySpan value) + { + switch (node) + { + case LeafNode leafNode: + { + if (path.IsEmpty) + { + value = leafNode.Value; + return true; + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmpty) break; + var newNode = Resolve(hashNode); + if (newNode is null) break; + node = newNode; + return TryGet(ref node, path, out value); + } + case BranchNode branchNode: + { + if (path.IsEmpty) + { + return TryGet(ref branchNode.Children[BranchNode.ChildCount - 1], path, out value); + } + return TryGet(ref branchNode.Children[path[0]], path[1..], out value); + } + case ExtensionNode extensionNode: + { + if (path.StartsWith(extensionNode.Key)) + { + return TryGet(ref extensionNode.Next, path[extensionNode.Key.Length..], out value); + } + break; + } + } + value = default; + return false; + } + } +} diff --git a/src/neo/Cryptography/MPT/MPTTrie.Proof.cs b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs new file mode 100644 index 0000000000..7b4be120ab --- /dev/null +++ b/src/neo/Cryptography/MPT/MPTTrie.Proof.cs @@ -0,0 +1,72 @@ +using Neo.IO; +using Neo.Persistence; +using System; +using System.Collections.Generic; + +namespace Neo.Cryptography.MPT +{ + partial class MPTTrie + { + public HashSet GetProof(TKey key) + { + var path = ToNibbles(key.ToArray()); + if (path.Length == 0) return null; + HashSet set = new HashSet(ByteArrayEqualityComparer.Default); + if (!GetProof(ref root, path, set)) return null; + return set; + } + + private bool GetProof(ref MPTNode node, ReadOnlySpan path, HashSet set) + { + switch (node) + { + case LeafNode leafNode: + { + if (path.IsEmpty) + { + set.Add(leafNode.Encode()); + return true; + } + break; + } + case HashNode hashNode: + { + if (hashNode.IsEmpty) break; + var newNode = Resolve(hashNode); + if (newNode is null) break; + node = newNode; + return GetProof(ref node, path, set); + } + case BranchNode branchNode: + { + set.Add(branchNode.Encode()); + if (path.IsEmpty) + { + return GetProof(ref branchNode.Children[BranchNode.ChildCount - 1], path, set); + } + return GetProof(ref branchNode.Children[path[0]], path[1..], set); + } + case ExtensionNode extensionNode: + { + if (path.StartsWith(extensionNode.Key)) + { + set.Add(extensionNode.Encode()); + return GetProof(ref extensionNode.Next, path[extensionNode.Key.Length..], set); + } + break; + } + } + return false; + } + + public static TValue VerifyProof(UInt256 root, TKey key, HashSet proof) + { + using var memoryStore = new MemoryStore(); + foreach (byte[] data in proof) + memoryStore.Put(Prefix, Crypto.Hash256(data), data); + using ISnapshot snapshot = memoryStore.GetSnapshot(); + var trie = new MPTTrie(snapshot, root); + return trie[key]; + } + } +} diff --git a/src/neo/Cryptography/MPT/MPTTrie.Put.cs b/src/neo/Cryptography/MPT/MPTTrie.Put.cs new file mode 100644 index 0000000000..491213916f --- /dev/null +++ b/src/neo/Cryptography/MPT/MPTTrie.Put.cs @@ -0,0 +1,158 @@ +using Neo.IO; +using System; + +namespace Neo.Cryptography.MPT +{ + partial class MPTTrie + { + private static ReadOnlySpan CommonPrefix(ReadOnlySpan a, ReadOnlySpan b) + { + var minLen = a.Length <= b.Length ? a.Length : b.Length; + int i = 0; + if (a.Length != 0 && b.Length != 0) + { + for (i = 0; i < minLen; i++) + { + if (a[i] != b[i]) break; + } + } + return a[..i]; + } + + public bool Put(TKey key, TValue value) + { + var path = ToNibbles(key.ToArray()); + var val = value.ToArray(); + if (path.Length == 0 || path.Length > ExtensionNode.MaxKeyLength) + return false; + if (val.Length > LeafNode.MaxValueLength) + return false; + if (val.Length == 0) + return TryDelete(ref root, path); + var n = new LeafNode(val); + return Put(ref root, path, n); + } + + private bool Put(ref MPTNode node, ReadOnlySpan path, MPTNode val) + { + switch (node) + { + case LeafNode leafNode: + { + if (val is LeafNode v) + { + if (path.IsEmpty) + { + node = v; + PutToStore(node); + return true; + } + var branch = new BranchNode(); + branch.Children[BranchNode.ChildCount - 1] = leafNode; + Put(ref branch.Children[path[0]], path[1..], v); + PutToStore(branch); + node = branch; + return true; + } + return false; + } + case ExtensionNode extensionNode: + { + if (path.StartsWith(extensionNode.Key)) + { + var result = Put(ref extensionNode.Next, path[extensionNode.Key.Length..], val); + if (result) + { + extensionNode.SetDirty(); + PutToStore(extensionNode); + } + return result; + } + var prefix = CommonPrefix(extensionNode.Key, path); + var pathRemain = path[prefix.Length..]; + var keyRemain = extensionNode.Key.AsSpan(prefix.Length); + var son = new BranchNode(); + MPTNode grandSon1 = HashNode.EmptyNode; + MPTNode grandSon2 = HashNode.EmptyNode; + + Put(ref grandSon1, keyRemain[1..], extensionNode.Next); + son.Children[keyRemain[0]] = grandSon1; + + if (pathRemain.IsEmpty) + { + Put(ref grandSon2, pathRemain, val); + son.Children[BranchNode.ChildCount - 1] = grandSon2; + } + else + { + Put(ref grandSon2, pathRemain[1..], val); + son.Children[pathRemain[0]] = grandSon2; + } + PutToStore(son); + if (prefix.Length > 0) + { + var exNode = new ExtensionNode() + { + Key = prefix.ToArray(), + Next = son, + }; + PutToStore(exNode); + node = exNode; + } + else + { + node = son; + } + return true; + } + case BranchNode branchNode: + { + bool result; + if (path.IsEmpty) + { + result = Put(ref branchNode.Children[BranchNode.ChildCount - 1], path, val); + } + else + { + result = Put(ref branchNode.Children[path[0]], path[1..], val); + } + if (result) + { + branchNode.SetDirty(); + PutToStore(branchNode); + } + return result; + } + case HashNode hashNode: + { + MPTNode newNode; + if (hashNode.IsEmpty) + { + if (path.IsEmpty) + { + newNode = val; + } + else + { + newNode = new ExtensionNode() + { + Key = path.ToArray(), + Next = val, + }; + PutToStore(newNode); + } + node = newNode; + if (val is LeafNode) PutToStore(val); + return true; + } + newNode = Resolve(hashNode); + if (newNode is null) return false; + node = newNode; + return Put(ref node, path, val); + } + default: + return false; + } + } + } +} diff --git a/src/neo/Cryptography/MPT/MPTTrie.cs b/src/neo/Cryptography/MPT/MPTTrie.cs new file mode 100644 index 0000000000..df97b54f9c --- /dev/null +++ b/src/neo/Cryptography/MPT/MPTTrie.cs @@ -0,0 +1,58 @@ +using Neo.IO; +using Neo.Persistence; +using System; + +namespace Neo.Cryptography.MPT +{ + public partial class MPTTrie + where TKey : notnull, ISerializable, new() + where TValue : class, ISerializable, new() + { + private const byte Prefix = 0xf0; + + private readonly ISnapshot store; + private MPTNode root; + + public MPTNode Root => root; + + public MPTTrie(ISnapshot store, UInt256 root) + { + this.store = store ?? throw new ArgumentNullException(); + this.root = root is null ? HashNode.EmptyNode : new HashNode(root); + } + + private MPTNode Resolve(HashNode n) + { + var data = store.TryGet(Prefix, n.Hash.ToArray()); + return MPTNode.Decode(data); + } + + private static byte[] ToNibbles(ReadOnlySpan path) + { + var result = new byte[path.Length * 2]; + for (int i = 0; i < path.Length; i++) + { + result[i * 2] = (byte)(path[i] >> 4); + result[i * 2 + 1] = (byte)(path[i] & 0x0F); + } + return result; + } + + private static byte[] FromNibbles(ReadOnlySpan path) + { + if (path.Length % 2 != 0) throw new FormatException($"MPTTrie.FromNibbles invalid path."); + var key = new byte[path.Length / 2]; + for (int i = 0; i < key.Length; i++) + { + key[i] = (byte)(path[i * 2] << 4); + key[i] |= path[i * 2 + 1]; + } + return key; + } + + private void PutToStore(MPTNode node) + { + store.Put(Prefix, node.Hash.ToArray(), node.Encode()); + } + } +} diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index b8f185e2c9..dafeba7643 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -50,6 +50,14 @@ public static byte[] Concat(params byte[][] buffers) return dst; } + public static byte[] Concat(ReadOnlySpan a, ReadOnlySpan b) + { + byte[] buffer = new byte[a.Length + b.Length]; + a.CopyTo(buffer); + b.CopyTo(buffer.AsSpan(a.Length)); + return buffer; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static int GetBitLength(this BigInteger i) { diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs new file mode 100644 index 0000000000..6ddc00f4be --- /dev/null +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTNode.cs @@ -0,0 +1,30 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.MPT; +using System.Text; + +namespace Neo.UnitTests.Cryptography.MPT +{ + [TestClass] + public class UT_MPTNode + { + [TestMethod] + public void TestDecode() + { + var n = new LeafNode + { + Value = Encoding.ASCII.GetBytes("hello") + }; + var code = n.Encode(); + var m = MPTNode.Decode(code); + Assert.IsInstanceOfType(m, n.GetType()); + } + + [TestMethod] + public void TestHashNode() + { + var hn = new HashNode(null); + var data = hn.Encode(); + Assert.AreEqual("0200", data.ToHexString()); + } + } +} diff --git a/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs new file mode 100644 index 0000000000..014a10e6d2 --- /dev/null +++ b/tests/neo.UnitTests/Cryptography/MPT/UT_MPTTrie.cs @@ -0,0 +1,317 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.MPT; +using Neo.IO; +using Neo.Persistence; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Neo.UnitTests.Cryptography.MPT +{ + public class TestKey : ISerializable + { + private byte[] key; + + public int Size => key.Length; + + public TestKey() + { + this.key = Array.Empty(); + } + + public TestKey(byte[] key) + { + this.key = key; + } + public void Serialize(BinaryWriter writer) + { + writer.Write(key); + } + + public void Deserialize(BinaryReader reader) + { + key = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); + } + + public override string ToString() + { + return key.ToHexString(); + } + + public static implicit operator TestKey(byte[] key) + { + return new TestKey(key); + } + } + + public class TestValue : ISerializable + { + private byte[] value; + + public int Size => value.Length; + + public TestValue() + { + this.value = Array.Empty(); + } + + public TestValue(byte[] value) + { + this.value = value; + } + + public void Serialize(BinaryWriter writer) + { + writer.Write(value); + } + + public void Deserialize(BinaryReader reader) + { + value = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position)); + } + + public override string ToString() + { + return value.ToHexString(); + } + + public static implicit operator TestValue(byte[] value) + { + return new TestValue(value); + } + } + + [TestClass] + public class UT_MPTTrie + { + private MPTNode root; + private IStore mptdb; + + private void PutToStore(MPTNode node) + { + mptdb.Put(0xf0, node.Hash.ToArray(), node.Encode()); + } + + [TestInitialize] + public void TestInit() + { + var b = new BranchNode(); + var r = new ExtensionNode { Key = "0a0c".HexToBytes(), Next = b }; + var v1 = new LeafNode { Value = "abcd".HexToBytes() }; + var v2 = new LeafNode { Value = "2222".HexToBytes() }; + var v3 = new LeafNode { Value = Encoding.ASCII.GetBytes("hello") }; + var h1 = new HashNode(v3.Hash); + var l1 = new ExtensionNode { Key = new byte[] { 0x01 }, Next = v1 }; + var l2 = new ExtensionNode { Key = new byte[] { 0x09 }, Next = v2 }; + var l3 = new ExtensionNode { Key = "0e".HexToBytes(), Next = h1 }; + b.Children[0] = l1; + b.Children[9] = l2; + b.Children[10] = l3; + this.root = r; + this.mptdb = new MemoryStore(); + PutToStore(r); + PutToStore(b); + PutToStore(l1); + PutToStore(l2); + PutToStore(l3); + PutToStore(v1); + PutToStore(v2); + PutToStore(v3); + } + + [TestMethod] + public void TestTryGet() + { + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); + Assert.AreEqual("abcd", mpt["ac01".HexToBytes()].ToString()); + Assert.AreEqual("2222", mpt["ac99".HexToBytes()].ToString()); + Assert.IsNull(mpt["ab99".HexToBytes()]); + Assert.IsNull(mpt["ac39".HexToBytes()]); + Assert.IsNull(mpt["ac02".HexToBytes()]); + Assert.IsNull(mpt["ac9910".HexToBytes()]); + } + + [TestMethod] + public void TestTryGetResolve() + { + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); + Assert.AreEqual(Encoding.ASCII.GetBytes("hello").ToHexString(), mpt["acae".HexToBytes()].ToString()); + } + + [TestMethod] + public void TestTryPut() + { + var store = new MemoryStore(); + var mpt = new MPTTrie(store.GetSnapshot(), null); + var result = mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes()); + Assert.IsTrue(result); + result = mpt.Put("ac99".HexToBytes(), "2222".HexToBytes()); + Assert.IsTrue(result); + result = mpt.Put("acae".HexToBytes(), Encoding.ASCII.GetBytes("hello")); + Assert.IsTrue(result); + Assert.AreEqual(root.Hash.ToString(), mpt.Root.Hash.ToString()); + } + + [TestMethod] + public void TestTryDelete() + { + var b = new BranchNode(); + var r = new ExtensionNode { Key = "0a0c".HexToBytes(), Next = b }; + var v1 = new LeafNode { Value = "abcd".HexToBytes() }; + var v2 = new LeafNode { Value = "2222".HexToBytes() }; + var r1 = new ExtensionNode { Key = "0a0c0001".HexToBytes(), Next = v1 }; + var l1 = new ExtensionNode { Key = new byte[] { 0x01 }, Next = v1 }; + var l2 = new ExtensionNode { Key = new byte[] { 0x09 }, Next = v2 }; + b.Children[0] = l1; + b.Children[9] = l2; + + Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", r1.Hash.ToString()); + Assert.AreEqual("0x93e8e1ffe2f83dd92fca67330e273bcc811bf64b8f8d9d1b25d5e7366b47d60d", r.Hash.ToString()); + + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); + Assert.IsNotNull(mpt["ac99".HexToBytes()]); + bool result = mpt.Delete("ac99".HexToBytes()); + Assert.IsTrue(result); + result = mpt.Delete("acae".HexToBytes()); + Assert.IsTrue(result); + Assert.AreEqual("0xdea3ab46e9461e885ed7091c1e533e0a8030b248d39cbc638962394eaca0fbb3", mpt.Root.Hash.ToString()); + } + + [TestMethod] + public void TestDeleteSameValue() + { + var store = new MemoryStore(); + var snapshot = store.GetSnapshot(); + var mpt = new MPTTrie(snapshot, null); + Assert.IsTrue(mpt.Put("ac01".HexToBytes(), "abcd".HexToBytes())); + Assert.IsTrue(mpt.Put("ac02".HexToBytes(), "abcd".HexToBytes())); + Assert.IsNotNull(mpt["ac01".HexToBytes()]); + Assert.IsNotNull(mpt["ac02".HexToBytes()]); + mpt.Delete("ac01".HexToBytes()); + Assert.IsNotNull(mpt["ac02".HexToBytes()]); + snapshot.Commit(); + + var mpt0 = new MPTTrie(store.GetSnapshot(), mpt.Root.Hash); + Assert.IsNotNull(mpt0["ac02".HexToBytes()]); + } + + [TestMethod] + public void TestBranchNodeRemainValue() + { + var store = new MemoryStore(); + var mpt = new MPTTrie(store.GetSnapshot(), null); + Assert.IsTrue(mpt.Put("ac11".HexToBytes(), "ac11".HexToBytes())); + Assert.IsTrue(mpt.Put("ac22".HexToBytes(), "ac22".HexToBytes())); + Assert.IsTrue(mpt.Put("ac".HexToBytes(), "ac".HexToBytes())); + Assert.IsTrue(mpt.Delete("ac11".HexToBytes())); + mpt.Delete("ac22".HexToBytes()); + Assert.IsNotNull(mpt["ac".HexToBytes()]); + } + + [TestMethod] + public void TestGetProof() + { + var b = new BranchNode(); + var r = new ExtensionNode { Key = "0a0c".HexToBytes(), Next = b }; + var v1 = new LeafNode { Value = "abcd".HexToBytes() }; + var v2 = new LeafNode { Value = "2222".HexToBytes() }; + var v3 = new LeafNode { Value = Encoding.ASCII.GetBytes("hello") }; + var h1 = new HashNode(v3.Hash); + var l1 = new ExtensionNode { Key = new byte[] { 0x01 }, Next = v1 }; + var l2 = new ExtensionNode { Key = new byte[] { 0x09 }, Next = v2 }; + var l3 = new ExtensionNode { Key = "0e".HexToBytes(), Next = h1 }; + b.Children[0] = l1; + b.Children[9] = l2; + b.Children[10] = l3; + + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); + Assert.AreEqual(r.Hash.ToString(), mpt.Root.Hash.ToString()); + HashSet proof = mpt.GetProof("ac01".HexToBytes()); + Assert.AreEqual(4, proof.Count); + Assert.IsTrue(proof.Contains(b.Encode())); + Assert.IsTrue(proof.Contains(r.Encode())); + Assert.IsTrue(proof.Contains(l1.Encode())); + Assert.IsTrue(proof.Contains(v1.Encode())); + } + + [TestMethod] + public void TestVerifyProof() + { + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); + HashSet proof = mpt.GetProof("ac01".HexToBytes()); + TestValue value = MPTTrie.VerifyProof(root.Hash, "ac01".HexToBytes(), proof); + Assert.IsNotNull(value); + Assert.AreEqual(value.ToString(), "abcd"); + } + + [TestMethod] + public void TestAddLongerKey() + { + var store = new MemoryStore(); + var snapshot = store.GetSnapshot(); + var mpt = new MPTTrie(snapshot, null); + var result = mpt.Put(new byte[] { 0xab }, new byte[] { 0x01 }); + Assert.IsTrue(result); + result = mpt.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x02 }); + Assert.IsTrue(result); + } + + [TestMethod] + public void TestSplitKey() + { + var store = new MemoryStore(); + var snapshot = store.GetSnapshot(); + var mpt1 = new MPTTrie(snapshot, null); + Assert.IsTrue(mpt1.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); + Assert.IsTrue(mpt1.Put(new byte[] { 0xab }, new byte[] { 0x02 })); + HashSet set1 = mpt1.GetProof(new byte[] { 0xab, 0xcd }); + Assert.AreEqual(4, set1.Count); + var mpt2 = new MPTTrie(snapshot, null); + Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x02 })); + Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd }, new byte[] { 0x01 })); + HashSet set2 = mpt2.GetProof(new byte[] { 0xab, 0xcd }); + Assert.AreEqual(4, set2.Count); + Assert.AreEqual(mpt1.Root.Hash, mpt2.Root.Hash); + } + + [TestMethod] + public void TestFind() + { + var store = new MemoryStore(); + var snapshot = store.GetSnapshot(); + var mpt1 = new MPTTrie(snapshot, null); + var results = mpt1.Find(ReadOnlySpan.Empty).ToArray(); + Assert.AreEqual(0, results.Count()); + var mpt2 = new MPTTrie(snapshot, null); + Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd, 0xef }, new byte[] { 0x01 })); + Assert.IsTrue(mpt2.Put(new byte[] { 0xab, 0xcd, 0xe1 }, new byte[] { 0x02 })); + Assert.IsTrue(mpt2.Put(new byte[] { 0xab }, new byte[] { 0x03 })); + results = mpt2.Find(ReadOnlySpan.Empty).ToArray(); + Assert.AreEqual(3, results.Count()); + results = mpt2.Find(new byte[] { 0xab }).ToArray(); + Assert.AreEqual(3, results.Count()); + results = mpt2.Find(new byte[] { 0xab, 0xcd }).ToArray(); + Assert.AreEqual(2, results.Count()); + results = mpt2.Find(new byte[] { 0xac }).ToArray(); + Assert.AreEqual(0, results.Count()); + } + + [TestMethod] + public void TestFindLeadNode() + { + // r.Key = 0x0a0c + // b.Key = 0x00 + // l1.Key = 0x01 + var mpt = new MPTTrie(mptdb.GetSnapshot(), root.Hash); + var prefix = new byte[] { 0xac, 0x01 }; // = FromNibbles(path = { 0x0a, 0x0c, 0x00, 0x01 }); + var results = mpt.Find(prefix).ToArray(); + Assert.AreEqual(1, results.Count()); + + prefix = new byte[] { 0xac }; // = FromNibbles(path = { 0x0a, 0x0c }); + results = mpt.Find(prefix).ToArray(); + Assert.AreEqual(3, results.Count()); + } + } +} From 50684b8af58e03aab2f42ac2adf78b9184ed721d Mon Sep 17 00:00:00 2001 From: Luchuan Date: Mon, 1 Jun 2020 14:47:35 +0800 Subject: [PATCH 281/305] Fix DataCache.Find (#1673) --- src/neo/IO/Caching/DataCache.cs | 4 ++- .../neo.UnitTests/IO/Caching/UT_DataCache.cs | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/neo/IO/Caching/DataCache.cs b/src/neo/IO/Caching/DataCache.cs index 687aae8f71..4b39b5b7cc 100644 --- a/src/neo/IO/Caching/DataCache.cs +++ b/src/neo/IO/Caching/DataCache.cs @@ -152,6 +152,7 @@ public void Delete(TKey key) public IEnumerable<(TKey Key, TValue Value)> Find(byte[] key_prefix = null) { IEnumerable<(byte[], TKey, TValue)> cached; + HashSet cachedKeySet; lock (dictionary) { cached = dictionary @@ -164,9 +165,10 @@ public IEnumerable<(TKey Key, TValue Value)> Find(byte[] key_prefix = null) )) .OrderBy(p => p.KeyBytes, ByteArrayComparer.Default) .ToArray(); + cachedKeySet = new HashSet(dictionary.Keys); } var uncached = FindInternal(key_prefix ?? Array.Empty()) - .Where(p => !dictionary.ContainsKey(p.Key)) + .Where(p => !cachedKeySet.Contains(p.Key)) .Select(p => ( KeyBytes: p.Key.ToArray(), diff --git a/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs b/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs index e3cf329d43..2b07c8fa0c 100644 --- a/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs +++ b/tests/neo.UnitTests/IO/Caching/UT_DataCache.cs @@ -326,5 +326,30 @@ public void TestTryGet() myDataCache.TryGet(new MyKey("key2")).Should().Be(new MyValue("value2")); myDataCache.TryGet(new MyKey("key3")).Should().BeNull(); } + + [TestMethod] + public void TestFindInvalid() + { + var myDataCache = new MyDataCache(); + myDataCache.Add(new MyKey("key1"), new MyValue("value1")); + + myDataCache.InnerDict.Add(new MyKey("key2"), new MyValue("value2")); + myDataCache.InnerDict.Add(new MyKey("key3"), new MyValue("value3")); + myDataCache.InnerDict.Add(new MyKey("key4"), new MyValue("value3")); + + var items = myDataCache.Find().GetEnumerator(); + items.MoveNext().Should().Be(true); + items.Current.Key.Should().Be(new MyKey("key1")); + + myDataCache.TryGet(new MyKey("key3")); // GETLINE + + items.MoveNext().Should().Be(true); + items.Current.Key.Should().Be(new MyKey("key2")); + items.MoveNext().Should().Be(true); + items.Current.Key.Should().Be(new MyKey("key3")); + items.MoveNext().Should().Be(true); + items.Current.Key.Should().Be(new MyKey("key4")); + items.MoveNext().Should().Be(false); + } } } From 80ec37fb1513d8d98829058a9b3427659cc2fb4b Mon Sep 17 00:00:00 2001 From: ShawnYun <42930111+ShawnYun@users.noreply.github.com> Date: Tue, 2 Jun 2020 12:23:02 +0800 Subject: [PATCH 282/305] GetBlocks by block index (#1397) --- src/neo/Ledger/Blockchain.cs | 108 ++------- src/neo/Network/P2P/MessageCommand.cs | 7 +- ...taPayload.cs => GetBlockByIndexPayload.cs} | 16 +- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 45 ++-- src/neo/Network/P2P/TaskManager.cs | 215 ++++++++++-------- src/neo/Network/P2P/TaskSession.cs | 19 +- 6 files changed, 176 insertions(+), 234 deletions(-) rename src/neo/Network/P2P/Payloads/{GetBlockDataPayload.cs => GetBlockByIndexPayload.cs} (54%) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 2d2fafab04..b10a60e870 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -254,12 +254,19 @@ private void OnImport(IEnumerable blocks, bool verify) private void AddUnverifiedBlockToCache(Block block) { + // Check if any block proposal for height `block.Index` exists if (!block_cache_unverified.TryGetValue(block.Index, out LinkedList blocks)) { + // There are no blocks, a new LinkedList is created and, consequently, the current block is added to the list blocks = new LinkedList(); block_cache_unverified.Add(block.Index, blocks); } - + // Check if any block with the hash being added already exists on possible candidates to be processed + foreach (var unverifiedBlock in blocks) + { + if (block.Hash == unverifiedBlock.Hash) + return; + } blocks.AddLast(block); } @@ -310,54 +317,21 @@ private VerifyResult OnNewBlock(Block block) { if (block.Index <= Height) return VerifyResult.AlreadyExists; - if (block_cache.ContainsKey(block.Hash)) - return VerifyResult.AlreadyExists; - if (block.Index - 1 >= header_index.Count) + if (block.Index - 1 > Height) { AddUnverifiedBlockToCache(block); return VerifyResult.UnableToVerify; } - if (block.Index == header_index.Count) + if (block.Index == Height + 1) { if (!block.Verify(currentSnapshot)) return VerifyResult.Invalid; - } - else - { - if (!block.Hash.Equals(header_index[(int)block.Index])) - return VerifyResult.Invalid; - } - block_cache.TryAdd(block.Hash, block); - if (block.Index == Height + 1) - { - Block block_persist = block; - List blocksToPersistList = new List(); - while (true) - { - blocksToPersistList.Add(block_persist); - if (block_persist.Index + 1 >= header_index.Count) break; - UInt256 hash = header_index[(int)block_persist.Index + 1]; - if (!block_cache.TryGetValue(hash, out block_persist)) break; - } - - int blocksPersisted = 0; - foreach (Block blockToPersist in blocksToPersistList) - { - block_cache_unverified.Remove(blockToPersist.Index); - Persist(blockToPersist); - - // 15000 is the default among of seconds per block, while MilliSecondsPerBlock is the current - uint extraBlocks = (15000 - MillisecondsPerBlock) / 1000; - - 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 + 100 >= header_index.Count) - system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = blockToPersist }); - } + block_cache.TryAdd(block.Hash, block); + block_cache_unverified.Remove(block.Index); + // We can store the new block in block_cache and tell the new height to other nodes before Persist(). + system.LocalNode.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(Singleton.Height + 1))); + Persist(block); SaveHeaderHashList(); - if (block_cache_unverified.TryGetValue(Height + 1, out LinkedList unverifiedBlocks)) { foreach (var unverifiedBlock in unverifiedBlocks) @@ -365,47 +339,9 @@ private VerifyResult OnNewBlock(Block block) block_cache_unverified.Remove(Height + 1); } } - else - { - if (block.Index + 100 >= header_index.Count) - system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = block }); - if (block.Index == header_index.Count) - { - header_index.Add(block.Hash); - using (SnapshotView snapshot = GetSnapshot()) - { - snapshot.Blocks.Add(block.Hash, block.Header.Trim()); - snapshot.HeaderHashIndex.GetAndChange().Set(block); - SaveHeaderHashList(snapshot); - snapshot.Commit(); - } - UpdateCurrentSnapshot(); - } - } return VerifyResult.Succeed; } - private void OnNewHeaders(Header[] headers) - { - using (SnapshotView snapshot = GetSnapshot()) - { - foreach (Header header in headers) - { - if (header.Index - 1 >= header_index.Count) break; - if (header.Index < header_index.Count) continue; - if (!header.Verify(snapshot)) break; - header_index.Add(header.Hash); - snapshot.Blocks.Add(header.Hash, header.Trim()); - snapshot.HeaderHashIndex.GetAndChange().Hash = header.Hash; - snapshot.HeaderHashIndex.GetAndChange().Index = header.Index; - } - SaveHeaderHashList(snapshot); - snapshot.Commit(); - } - UpdateCurrentSnapshot(); - system.TaskManager.Tell(new TaskManager.HeaderTaskCompleted(), Sender); - } - private VerifyResult OnNewInventory(IInventory inventory) { if (!inventory.Verify(currentSnapshot)) return VerifyResult.Invalid; @@ -433,9 +369,6 @@ 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; @@ -459,6 +392,11 @@ private void Persist(Block block) { using (SnapshotView snapshot = GetSnapshot()) { + if (block.Index == header_index.Count) + { + header_index.Add(block.Hash); + snapshot.HeaderHashIndex.GetAndChange().Set(block); + } List all_application_executed = new List(); snapshot.PersistingBlock = block; if (block.Index > 0) @@ -504,11 +442,6 @@ private void Persist(Block block) } } snapshot.BlockHashIndex.GetAndChange().Set(block); - if (block.Index == header_index.Count) - { - header_index.Add(block.Hash); - snapshot.HeaderHashIndex.GetAndChange().Set(block); - } foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) plugin.OnPersist(snapshot, all_application_executed); snapshot.Commit(); @@ -590,7 +523,6 @@ internal protected override bool IsHighPriority(object message) { switch (message) { - case Header[] _: case Block _: case ConsensusPayload _: case Terminated _: diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index 771a98a459..89c21597cf 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -20,7 +20,7 @@ public enum MessageCommand : byte Pong = 0x19, //synchronization - [ReflectionCache(typeof(GetBlocksPayload))] + [ReflectionCache(typeof(GetBlockByIndexPayload))] GetHeaders = 0x20, [ReflectionCache(typeof(HeadersPayload))] Headers = 0x21, @@ -31,9 +31,8 @@ public enum MessageCommand : byte Inv = 0x27, [ReflectionCache(typeof(InvPayload))] GetData = 0x28, - [ReflectionCache(typeof(GetBlockDataPayload))] - GetBlockData = 0x29, - [ReflectionCache(typeof(InvPayload))] + [ReflectionCache(typeof(GetBlockByIndexPayload))] + GetBlockByIndex = 0x29, NotFound = 0x2a, [ReflectionCache(typeof(Transaction))] Transaction = 0x2b, diff --git a/src/neo/Network/P2P/Payloads/GetBlockDataPayload.cs b/src/neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs similarity index 54% rename from src/neo/Network/P2P/Payloads/GetBlockDataPayload.cs rename to src/neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs index 9ccd534f34..c07a8a8c0a 100644 --- a/src/neo/Network/P2P/Payloads/GetBlockDataPayload.cs +++ b/src/neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs @@ -4,17 +4,16 @@ namespace Neo.Network.P2P.Payloads { - public class GetBlockDataPayload : ISerializable + public class GetBlockByIndexPayload : ISerializable { - private const ushort MaxBlocksCount = 500; public uint IndexStart; - public ushort Count; + public short Count; - public int Size => sizeof(uint) + sizeof(ushort); + public int Size => sizeof(uint) + sizeof(short); - public static GetBlockDataPayload Create(uint index_start, ushort count) + public static GetBlockByIndexPayload Create(uint index_start, short count = -1) { - return new GetBlockDataPayload + return new GetBlockByIndexPayload { IndexStart = index_start, Count = count @@ -24,8 +23,9 @@ public static GetBlockDataPayload Create(uint index_start, ushort count) void ISerializable.Deserialize(BinaryReader reader) { IndexStart = reader.ReadUInt32(); - Count = reader.ReadUInt16(); - if (Count == 0 || Count > MaxBlocksCount) throw new FormatException(); + Count = reader.ReadInt16(); + if (Count < -1 || Count == 0 || Count > HeadersPayload.MaxHeadersCount) + 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 bdf235bdc0..3a55d84347 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -82,17 +82,14 @@ private void OnMessage(Message msg) case MessageCommand.GetBlocks: OnGetBlocksMessageReceived((GetBlocksPayload)msg.Payload); break; - case MessageCommand.GetBlockData: - OnGetBlockDataMessageReceived((GetBlockDataPayload)msg.Payload); + case MessageCommand.GetBlockByIndex: + OnGetBlockByIndexMessageReceived((GetBlockByIndexPayload)msg.Payload); break; case MessageCommand.GetData: OnGetDataMessageReceived((InvPayload)msg.Payload); break; case MessageCommand.GetHeaders: - OnGetHeadersMessageReceived((GetBlocksPayload)msg.Payload); - break; - case MessageCommand.Headers: - OnHeadersMessageReceived((HeadersPayload)msg.Payload); + OnGetHeadersMessageReceived((GetBlockByIndexPayload)msg.Payload); break; case MessageCommand.Inv: OnInvMessageReceived((InvPayload)msg.Payload); @@ -114,6 +111,7 @@ 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: @@ -189,9 +187,10 @@ private void OnGetBlocksMessageReceived(GetBlocksPayload payload) EnqueueMessage(Message.Create(MessageCommand.Inv, InvPayload.Create(InventoryType.Block, hashes.ToArray()))); } - private void OnGetBlockDataMessageReceived(GetBlockDataPayload payload) + private void OnGetBlockByIndexMessageReceived(GetBlockByIndexPayload payload) { - for (uint i = payload.IndexStart, max = payload.IndexStart + payload.Count; i < max; i++) + 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); if (block == null) @@ -264,24 +263,20 @@ private void OnGetDataMessageReceived(InvPayload payload) /// /// Will be triggered when a MessageCommand.GetHeaders message is received. - /// Tell the specified number of blocks' headers starting with the requested HashStart to RemoteNode actor. + /// 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 Hash and number of blocks' headers requested. - private void OnGetHeadersMessageReceived(GetBlocksPayload payload) + /// A GetBlocksPayload including start block index and number of blocks' headers requested. + private void OnGetHeadersMessageReceived(GetBlockByIndexPayload payload) { - UInt256 hash = payload.HashStart; - int count = payload.Count < 0 || payload.Count > HeadersPayload.MaxHeadersCount ? HeadersPayload.MaxHeadersCount : payload.Count; - DataCache cache = Blockchain.Singleton.View.Blocks; - TrimmedBlock state = cache.TryGet(hash); - if (state == null) return; + uint index = payload.IndexStart; + uint count = payload.Count == -1 ? HeadersPayload.MaxHeadersCount : (uint)payload.Count; + if (index > Blockchain.Singleton.HeaderHeight) + return; List
headers = new List
(); - for (uint i = 1; i <= count; i++) + for (uint i = 0; i < count; i++) { - uint index = state.Index + i; - hash = Blockchain.Singleton.GetBlockHash(index); - if (hash == null) break; - Header header = cache.TryGet(hash)?.Header; + var header = Blockchain.Singleton.GetHeader(index + i); if (header == null) break; headers.Add(header); } @@ -289,15 +284,9 @@ private void OnGetHeadersMessageReceived(GetBlocksPayload payload) EnqueueMessage(Message.Create(MessageCommand.Headers, HeadersPayload.Create(headers.ToArray()))); } - private void OnHeadersMessageReceived(HeadersPayload payload) - { - if (payload.Headers.Length == 0) return; - system.Blockchain.Tell(payload.Headers); - } - private void OnInventoryReceived(IInventory inventory) { - system.TaskManager.Tell(new TaskManager.TaskCompleted { Hash = inventory.Hash }); + system.TaskManager.Tell(inventory); if (inventory is Transaction transaction) system.Consensus?.Tell(transaction); system.Blockchain.Tell(inventory, ActorRefs.NoSender); diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 442efc8a68..3eab30ba2e 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -17,43 +17,75 @@ internal class TaskManager : UntypedActor public class Register { public VersionPayload Version; } public class Update { public uint LastBlockIndex; } public class NewTasks { public InvPayload Payload; } - public class TaskCompleted { public UInt256 Hash; } - public class HeaderTaskCompleted { } 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 HeaderTaskHash = UInt256.Zero; private static readonly UInt256 MemPoolTaskHash = UInt256.Parse("0x0000000000000000000000000000000000000000000000000000000000000001"); - private readonly NeoSystem system; 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 sessions = new Dictionary(); private readonly ICancelable timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimerInterval, TimerInterval, Context.Self, new Timer(), ActorRefs.NoSender); - - private bool HasHeaderTask => globalTasks.ContainsKey(HeaderTaskHash); + private uint lastTaskIndex = 0; public TaskManager(NeoSystem system) { this.system = system; this.knownHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); + this.lastTaskIndex = Blockchain.Singleton.Height; + Context.System.EventStream.Subscribe(Self, typeof(Blockchain.PersistCompleted)); } - private void OnHeaderTaskCompleted() + private bool AssignSyncTask(uint index, TaskSession filterSession = null) { - if (!sessions.TryGetValue(Sender, out TaskSession session)) - return; - session.Tasks.Remove(HeaderTaskHash); - DecrementGlobalTask(HeaderTaskHash); - RequestTasks(session); + if (index <= Blockchain.Singleton.Height || 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) + { + 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(); + } + + 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); } private void OnNewTasks(InvPayload payload) @@ -61,37 +93,33 @@ 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 < Blockchain.Singleton.HeaderHeight) - { - RequestTasks(session); + if (payload.Type == InventoryType.TX && Blockchain.Singleton.Height < 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 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 => globalTasks.ContainsKey(p))); // Remove those that are already in process by other sessions hashes.Remove(globalTasks); if (hashes.Count == 0) - { - RequestTasks(session); return; - } // Update globalTasks with the ones that will be requested within this current session foreach (UInt256 hash in hashes) { IncrementGlobalTask(hash); - session.Tasks[hash] = DateTime.UtcNow; + session.InvTasks[hash] = DateTime.UtcNow; } foreach (InvPayload group in InvPayload.CreateGroup(payload.Type, hashes.ToArray())) Sender.Tell(Message.Create(MessageCommand.GetData, group)); } + private void OnPersistCompleted(Block block) + { + receivedBlockIndex.Remove(block.Index); + } + protected override void OnReceive(object message) { switch (message) @@ -105,15 +133,22 @@ protected override void OnReceive(object message) case NewTasks tasks: OnNewTasks(tasks.Payload); break; - case TaskCompleted completed: - OnTaskCompleted(completed.Hash); - break; - case HeaderTaskCompleted _: - OnHeaderTaskCompleted(); - break; case RestartTasks restart: OnRestartTasks(restart.Payload); break; + case Block block: + OnBlock(block); + break; + case IInventory inventory: + OnTaskCompleted(inventory.Hash); + break; + case Blockchain.PersistCompleted pc: + OnPersistCompleted(pc.Block); + break; + case Blockchain.RelayResult rr: + if (rr.Inventory is Block invalidBlock && rr.Result == VerifyResult.Invalid) + OnInvalidBlock(invalidBlock); + break; case Timer _: OnTimer(); break; @@ -126,11 +161,11 @@ protected override void OnReceive(object message) private void OnRegister(VersionPayload version) { Context.Watch(Sender); - TaskSession session = new TaskSession(Sender, version); + TaskSession session = new TaskSession(version); if (session.IsFullNode) - session.AvailableTasks.Add(TaskManager.MemPoolTaskHash); - sessions.Add(Sender, session); - RequestTasks(session); + session.InvTasks.TryAdd(MemPoolTaskHash, TimeProvider.Current.UtcNow); + sessions.TryAdd(Sender, session); + RequestTasks(); } private void OnUpdate(uint lastBlockIndex) @@ -138,6 +173,7 @@ private void OnUpdate(uint lastBlockIndex) if (!sessions.TryGetValue(Sender, out TaskSession session)) return; session.LastBlockIndex = lastBlockIndex; + RequestTasks(); } private void OnRestartTasks(InvPayload payload) @@ -153,13 +189,8 @@ private void OnTaskCompleted(UInt256 hash) { knownHashes.Add(hash); globalTasks.Remove(hash); - foreach (TaskSession ms in sessions.Values) - ms.AvailableTasks.Remove(hash); if (sessions.TryGetValue(Sender, out TaskSession session)) - { - session.Tasks.Remove(hash); - RequestTasks(session); - } + session.InvTasks.Remove(hash); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -193,22 +224,38 @@ private void OnTerminated(IActorRef actor) { if (!sessions.TryGetValue(actor, out TaskSession session)) return; - sessions.Remove(actor); - foreach (UInt256 hash in session.Tasks.Keys) + foreach (uint index in session.IndexTasks.Keys) + AssignSyncTask(index, session); + + foreach (UInt256 hash in session.InvTasks.Keys) DecrementGlobalTask(hash); + sessions.Remove(actor); } private void OnTimer() { foreach (TaskSession session in sessions.Values) - foreach (var task in session.Tasks.ToArray()) + { + foreach (KeyValuePair kvp in session.IndexTasks) + { + if (TimeProvider.Current.UtcNow - kvp.Value > TaskTimeout) + { + session.IndexTasks.Remove(kvp.Key); + session.TimeoutTimes++; + AssignSyncTask(kvp.Key, session); + } + } + + foreach (var task in session.InvTasks.ToArray()) + { if (DateTime.UtcNow - task.Value > TaskTimeout) { - if (session.Tasks.Remove(task.Key)) + if (session.InvTasks.Remove(task.Key)) DecrementGlobalTask(task.Key); } - foreach (TaskSession session in sessions.Values) - RequestTasks(session); + } + } + RequestTasks(); } protected override void PostStop() @@ -222,64 +269,47 @@ public static Props Props(NeoSystem system) return Akka.Actor.Props.Create(() => new TaskManager(system)).WithMailbox("task-manager-mailbox"); } - private void RequestTasks(TaskSession session) + private void RequestTasks() { - if (session.HasTask) return; - // If there are pending tasks of InventoryType.Block we should process them - if (session.AvailableTasks.Count > 0) + if (sessions.Count() == 0) return; + + SendPingMessage(); + + while (failedSyncTasks.Count() > 0) { - 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 => Blockchain.Singleton.ContainsBlock(p)); - HashSet hashes = new HashSet(session.AvailableTasks); - hashes.Remove(MemPoolTaskHash); - if (hashes.Count > 0) + var failedTask = failedSyncTasks.First(); + if (failedTask <= Blockchain.Singleton.Height) { - foreach (UInt256 hash in hashes.ToArray()) - { - if (!IncrementGlobalTask(hash)) - hashes.Remove(hash); - } - session.AvailableTasks.Remove(hashes); - foreach (UInt256 hash in hashes) - session.Tasks[hash] = DateTime.UtcNow; - foreach (InvPayload group in InvPayload.CreateGroup(InventoryType.Block, hashes.ToArray())) - session.RemoteNode.Tell(Message.Create(MessageCommand.GetData, group)); - return; + failedSyncTasks.Remove(failedTask); + continue; } + if (!AssignSyncTask(failedTask)) return; } - // 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 || globalTasks[HeaderTaskHash] < MaxConncurrentTasks) && Blockchain.Singleton.HeaderHeight < session.LastBlockIndex) + int taskCounts = sessions.Values.Sum(p => p.IndexTasks.Count); + var highestBlockIndex = sessions.Values.Max(p => p.LastBlockIndex); + for (; taskCounts < MaxSyncTasksCount; taskCounts++) { - session.Tasks[HeaderTaskHash] = DateTime.UtcNow; - IncrementGlobalTask(HeaderTaskHash); - session.RemoteNode.Tell(Message.Create(MessageCommand.GetHeaders, GetBlocksPayload.Create(Blockchain.Singleton.CurrentHeaderHash))); + if (lastTaskIndex >= highestBlockIndex) break; + if (!AssignSyncTask(++lastTaskIndex)) break; } - else if (Blockchain.Singleton.Height < session.LastBlockIndex) + } + + private void SendPingMessage() + { + foreach (KeyValuePair item in sessions) { - UInt256 hash = Blockchain.Singleton.CurrentBlockHash; - for (uint i = Blockchain.Singleton.Height + 1; i <= Blockchain.Singleton.HeaderHeight; i++) + var node = item.Key; + var session = item.Value; + if (Blockchain.Singleton.Height >= session.LastBlockIndex + && TimeProvider.Current.UtcNow.ToTimestampMS() - PingCoolingOffPeriod >= Blockchain.Singleton.GetBlock(Blockchain.Singleton.CurrentBlockHash)?.Timestamp) { - hash = Blockchain.Singleton.GetBlockHash(i); - if (!globalTasks.ContainsKey(hash)) + if (session.InvTasks.Remove(MemPoolTaskHash)) { - hash = Blockchain.Singleton.GetBlockHash(i - 1); - break; + node.Tell(Message.Create(MessageCommand.Mempool)); } + node.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(Blockchain.Singleton.Height))); } - session.RemoteNode.Tell(Message.Create(MessageCommand.GetBlocks, GetBlocksPayload.Create(hash))); - } - else if (Blockchain.Singleton.HeaderHeight >= session.LastBlockIndex - && TimeProvider.Current.UtcNow.ToTimestampMS() - PingCoolingOffPeriod >= Blockchain.Singleton.GetBlock(Blockchain.Singleton.CurrentHeaderHash)?.Timestamp) - { - if (session.AvailableTasks.Remove(MemPoolTaskHash)) - { - session.RemoteNode.Tell(Message.Create(MessageCommand.Mempool)); - } - - session.RemoteNode.Tell(Message.Create(MessageCommand.Ping, PingPayload.Create(Blockchain.Singleton.Height))); } } } @@ -296,7 +326,6 @@ 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 d13c7c82f5..859f282d35 100644 --- a/src/neo/Network/P2P/TaskSession.cs +++ b/src/neo/Network/P2P/TaskSession.cs @@ -1,4 +1,3 @@ -using Akka.Actor; using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; using System; @@ -9,25 +8,19 @@ namespace Neo.Network.P2P { internal class TaskSession { - public readonly IActorRef RemoteNode; - public readonly VersionPayload Version; - public readonly Dictionary Tasks = new Dictionary(); - public readonly HashSet AvailableTasks = new HashSet(); + public readonly Dictionary InvTasks = new Dictionary(); + public readonly Dictionary IndexTasks = new Dictionary(); - public bool HasTask => Tasks.Count > 0; - public uint StartHeight { get; } public bool IsFullNode { get; } public uint LastBlockIndex { get; set; } + public uint TimeoutTimes = 0; + public uint InvalidBlockCount = 0; - public TaskSession(IActorRef node, VersionPayload version) + public TaskSession(VersionPayload version) { var fullNode = version.Capabilities.OfType().FirstOrDefault(); - this.IsFullNode = fullNode != null; - this.RemoteNode = node; - this.Version = version; - this.StartHeight = fullNode?.StartHeight ?? 0; - this.LastBlockIndex = this.StartHeight; + this.LastBlockIndex = fullNode.StartHeight; } } } From e9ea256b09c4267cfa9518ed80a38985124040c0 Mon Sep 17 00:00:00 2001 From: Luchuan Date: Wed, 3 Jun 2020 16:45:46 +0800 Subject: [PATCH 283/305] add description field (#1678) --- src/neo/Plugins/Plugin.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs index b2f6fb99d2..d98d1703a6 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -24,6 +24,7 @@ public abstract class Plugin : IDisposable public virtual string ConfigFile => Combine(PluginsDirectory, GetType().Assembly.GetName().Name, "config.json"); public virtual string Name => GetType().Name; + public virtual string Description => ""; public virtual string Path => Combine(PluginsDirectory, GetType().Assembly.ManifestModule.ScopeName); protected static NeoSystem System { get; private set; } public virtual Version Version => GetType().Assembly.GetName().Version; From 3f46aeb37372e294824c25c806df7a8a701d09d2 Mon Sep 17 00:00:00 2001 From: Luchuan Date: Fri, 5 Jun 2020 23:46:09 +0800 Subject: [PATCH 284/305] Fix duplicate connection (#1685) * fix duplicate connection * fix comments * Rename to AllowNewConnection * fix * Fix comment * fix * Rename * Remove useless check * Update * Update LocalNode.cs Co-authored-by: Shargon Co-authored-by: erikzhang --- src/neo/Network/P2P/LocalNode.cs | 22 +++++++++++++++++++ .../Network/P2P/RemoteNode.ProtocolHandler.cs | 7 +----- .../Network/P2P/UT_RemoteNode.cs | 3 ++- 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index c1e7046725..c8bc937001 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -127,6 +127,28 @@ internal static IPEndPoint GetIpEndPoint(string hostAndPort) return null; } + /// + /// Check the new connection
+ /// If it is equal to the Nonce of local or any remote node, it'll return false, else we'll return true and update the Listener address of the connected remote node. + ///
+ /// Remote node actor + /// Remote node + public bool AllowNewConnection(IActorRef actor, RemoteNode node) + { + if (node.Version.Magic != ProtocolSettings.Default.Magic) return false; + if (node.Version.Nonce == Nonce) return false; + + // filter duplicate connections + foreach (var other in RemoteNodes.Values) + if (other != node && other.Remote.Address.Equals(node.Remote.Address) && other.Version?.Nonce == node.Version.Nonce) + return false; + + if (node.Remote.Port != node.ListenerTcpPort && node.ListenerTcpPort != 0) + ConnectedPeers.TryUpdate(actor, node.Listener, node.Remote); + + return true; + } + public IEnumerable GetRemoteNodes() { return RemoteNodes.Values; diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 3a55d84347..9e73809cc1 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -356,12 +356,7 @@ private void OnVersionMessageReceived(VersionPayload payload) break; } } - if (payload.Nonce == LocalNode.Nonce || payload.Magic != ProtocolSettings.Default.Magic) - { - Disconnect(true); - return; - } - if (LocalNode.Singleton.RemoteNodes.Values.Where(p => p != this).Any(p => p.Remote.Address.Equals(Remote.Address) && p.Version?.Nonce == payload.Nonce)) + if (!LocalNode.Singleton.AllowNewConnection(Self, this)) { Disconnect(true); return; diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs index 61702115f3..8f5b9c8000 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs @@ -6,6 +6,7 @@ using Neo.Network.P2P; using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; +using System.Net; namespace Neo.UnitTests.Network.P2P { @@ -54,7 +55,7 @@ public void RemoteNode_Test_Abort_DifferentMagic() public void RemoteNode_Test_Accept_IfSameMagic() { var connectionTestProbe = CreateTestProbe(); - var remoteNodeActor = ActorOfAsTestActorRef(() => new RemoteNode(testBlockchain, connectionTestProbe, null, null)); + var remoteNodeActor = ActorOfAsTestActorRef(() => new RemoteNode(testBlockchain, connectionTestProbe, new IPEndPoint(IPAddress.Parse("192.168.1.2"), 8080), new IPEndPoint(IPAddress.Parse("192.168.1.1"), 8080))); var msg = Message.Create(MessageCommand.Version, new VersionPayload() { From 64d67c923461ca6026687afe8bcc948cad8cc2b3 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 11 Jun 2020 18:44:05 +0800 Subject: [PATCH 285/305] Check the parameters count (#1695) --- .../ApplicationEngine.Contract.cs | 1 + .../SmartContract/UT_InteropService.cs | 14 ++++---- .../SmartContract/UT_SmartContractHelper.cs | 2 +- .../SmartContract/UT_Syscalls.cs | 6 ++-- tests/neo.UnitTests/TestUtils.cs | 35 ++++++++++++------- 5 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 31407a596c..ed2557f356 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -139,6 +139,7 @@ private void CallContractInternal(UInt160 contractHash, string method, Array arg ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); if (md is null) throw new InvalidOperationException(); + if (args.Count != md.Parameters.Length) throw new InvalidOperationException(); int rvcount = md.ReturnType == ContractParameterType.Void ? 0 : 1; ExecutionContext context_new = LoadScript(contract.Script, rvcount); state = context_new.GetState(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 399f063ec7..5b83133e37 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -55,7 +55,7 @@ public void Runtime_GetNotifications_Test() snapshot.Contracts.Add(scriptHash2, new ContractState() { Script = script.ToArray(), - Manifest = TestUtils.CreateDefaultManifest(scriptHash2, "test"), + Manifest = TestUtils.CreateManifest(scriptHash2, "test", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer), }); } @@ -223,7 +223,7 @@ public void TestExecutionEngine_GetCallingScriptHash() var contract = new ContractState() { - Manifest = TestUtils.CreateDefaultManifest(scriptA.ToArray().ToScriptHash(), "test"), + Manifest = TestUtils.CreateManifest(scriptA.ToArray().ToScriptHash(), "test", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer), Script = scriptA.ToArray() }; engine = GetEngine(true, true, false); @@ -600,10 +600,10 @@ public void TestStorageContext_AsReadOnly() public void TestContract_Call() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var state = TestUtils.GetContract("method"); - state.Manifest.Features = ContractFeatures.HasStorage; string method = "method"; var args = new VM.Types.Array { 0, 1 }; + var state = TestUtils.GetContract(method, args.Count); + state.Manifest.Features = ContractFeatures.HasStorage; snapshot.Contracts.Add(state.ScriptHash, state); var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); @@ -627,12 +627,12 @@ public void TestContract_CallEx() { var snapshot = Blockchain.Singleton.GetSnapshot(); - var state = TestUtils.GetContract("method"); + string method = "method"; + var args = new VM.Types.Array { 0, 1 }; + var state = TestUtils.GetContract(method, args.Count); state.Manifest.Features = ContractFeatures.HasStorage; snapshot.Contracts.Add(state.ScriptHash, state); - string method = "method"; - var args = new VM.Types.Array { 0, 1 }; foreach (var flags in new CallFlags[] { CallFlags.None, CallFlags.AllowCall, CallFlags.AllowModifyStates, CallFlags.All }) { diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 3677041aa2..0f4e9f5f74 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -139,7 +139,7 @@ public void TestVerifyWitnesses() Header header3 = new Header() { PrevHash = index3, Witness = new Witness { VerificationScript = new byte[0] } }; snapshot3.Contracts.Add(UInt160.Zero, new ContractState() { - Manifest = TestUtils.CreateDefaultManifest(UInt160.Zero, "verify"), + Manifest = TestUtils.CreateManifest(UInt160.Zero, "verify", ContractParameterType.Boolean, ContractParameterType.Signature), }); Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header3, snapshot3, 100)); } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 848079aef3..96b2ac0345 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -339,9 +339,9 @@ public void System_Runtime_GetInvocationCounter() contracts.Delete(contractA.ScriptHash); contracts.Delete(contractB.ScriptHash); contracts.Delete(contractC.ScriptHash); - contractA.Manifest = TestUtils.CreateDefaultManifest(contractA.ScriptHash, "dummyMain"); - contractB.Manifest = TestUtils.CreateDefaultManifest(contractA.ScriptHash, "dummyMain"); - contractC.Manifest = TestUtils.CreateDefaultManifest(contractA.ScriptHash, "dummyMain"); + contractA.Manifest = TestUtils.CreateManifest(contractA.ScriptHash, "dummyMain", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer); + contractB.Manifest = TestUtils.CreateManifest(contractA.ScriptHash, "dummyMain", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer); + contractC.Manifest = TestUtils.CreateManifest(contractA.ScriptHash, "dummyMain", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer); contracts.Add(contractA.ScriptHash, contractA); contracts.Add(contractB.ScriptHash, contractB); contracts.Add(contractC.ScriptHash, contractC); diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index fd8e6fd620..ef380908dd 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -17,7 +17,7 @@ public static class TestUtils { public static readonly Random TestRandom = new Random(1337); // use fixed seed for guaranteed determinism - public static ContractManifest CreateDefaultManifest(UInt160 hash, string method = null) + public static ContractManifest CreateDefaultManifest(UInt160 hash) { return new ContractManifest() { @@ -26,15 +26,7 @@ public static ContractManifest CreateDefaultManifest(UInt160 hash, string method { Hash = hash, Events = new ContractEventDescriptor[0], - Methods = method == null ? new ContractMethodDescriptor[0] : new ContractMethodDescriptor[] - { - new ContractMethodDescriptor() - { - Name = method, - Parameters = new ContractParameterDefinition[0], - ReturnType = ContractParameterType.Integer - } - } + Methods = new ContractMethodDescriptor[0] }, Features = ContractFeatures.NoProperty, Groups = new ContractGroup[0], @@ -44,6 +36,25 @@ public static ContractManifest CreateDefaultManifest(UInt160 hash, string method }; } + public static ContractManifest CreateManifest(UInt160 hash, string method, ContractParameterType returnType, params ContractParameterType[] parameterTypes) + { + ContractManifest manifest = CreateDefaultManifest(hash); + manifest.Abi.Methods = new ContractMethodDescriptor[] + { + new ContractMethodDescriptor() + { + Name = method, + Parameters = parameterTypes.Select((p, i) => new ContractParameterDefinition + { + Name = $"p{i}", + Type = p + }).ToArray(), + ReturnType = returnType + } + }; + return manifest; + } + public static byte[] GetByteArray(int length, byte firstByte) { byte[] array = new byte[length]; @@ -82,13 +93,13 @@ public static Transaction GetTransaction() }; } - internal static ContractState GetContract(string method = null) + internal static ContractState GetContract(string method = "test", int parametersCount = 0) { return new ContractState { Id = 0x43000000, Script = new byte[] { 0x01, 0x01, 0x01, 0x01 }, - Manifest = CreateDefaultManifest(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), method) + Manifest = CreateManifest(UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), method, ContractParameterType.Any, Enumerable.Repeat(ContractParameterType.Any, parametersCount).ToArray()) }; } From 358cf1150474ec651751bda3de677ae2f6847d63 Mon Sep 17 00:00:00 2001 From: lichen <48947753+Lichen9618@users.noreply.github.com> Date: Thu, 11 Jun 2020 23:03:05 +0800 Subject: [PATCH 286/305] Add RIPMED60 to syscall in ApplicationEngine.Crypto (#1694) * Add Hash160 and Hash256 to syscall * remove unnecessary reference * format fix * provide RIPMED160 instead of Hash256 and Hash160 Add RIPMED160 UT Co-authored-by: Erik Zhang --- src/neo/Cryptography/Helper.cs | 6 ++++++ src/neo/SmartContract/ApplicationEngine.Crypto.cs | 12 ++++++++++++ .../Cryptography/UT_Cryptography_Helper.cs | 9 +++++++++ 3 files changed, 27 insertions(+) diff --git a/src/neo/Cryptography/Helper.cs b/src/neo/Cryptography/Helper.cs index 3cedb53236..1f0c460a94 100644 --- a/src/neo/Cryptography/Helper.cs +++ b/src/neo/Cryptography/Helper.cs @@ -75,6 +75,12 @@ public static byte[] RIPEMD160(this byte[] value) return ripemd160.ComputeHash(value); } + public static byte[] RIPEMD160(this ReadOnlySpan value) + { + byte[] source = value.ToArray(); + return source.RIPEMD160(); + } + public static uint Murmur32(this byte[] value, uint seed) { using (Murmur3 murmur = new Murmur3(seed)) diff --git a/src/neo/SmartContract/ApplicationEngine.Crypto.cs b/src/neo/SmartContract/ApplicationEngine.Crypto.cs index 9cb3a9abf4..70533d900c 100644 --- a/src/neo/SmartContract/ApplicationEngine.Crypto.cs +++ b/src/neo/SmartContract/ApplicationEngine.Crypto.cs @@ -12,12 +12,24 @@ partial class ApplicationEngine { public const long ECDsaVerifyPrice = 0_01000000; + public static readonly InteropDescriptor Neo_Crypto_RIPEMD160 = Register("Neo.Crypto.RIPEMD160", nameof(RIPEMD160), 0_01000000, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor Neo_Crypto_SHA256 = Register("Neo.Crypto.SHA256", nameof(Sha256), 0_01000000, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.VerifyWithECDsaSecp256r1", nameof(VerifyWithECDsaSecp256r1), ECDsaVerifyPrice, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.VerifyWithECDsaSecp256k1", nameof(VerifyWithECDsaSecp256k1), ECDsaVerifyPrice, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256r1", nameof(CheckMultisigWithECDsaSecp256r1), 0, TriggerType.All, CallFlags.None); public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256k1", nameof(CheckMultisigWithECDsaSecp256k1), 0, TriggerType.All, CallFlags.None); + internal byte[] RIPEMD160(StackItem item) + { + ReadOnlySpan value = item switch + { + InteropInterface _interface => _interface.GetInterface().GetHashData(), + Null _ => ScriptContainer.GetHashData(), + _ => item.GetSpan() + }; + return value.RIPEMD160(); + } + internal byte[] Sha256(StackItem item) { ReadOnlySpan value = item switch diff --git a/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index 0e17d0913c..b2c5775903 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -133,6 +133,15 @@ public void TestSha256() resultStr.Should().Be("b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"); } + [TestMethod] + public void TestRIPEMD160() + { + ReadOnlySpan value = Encoding.ASCII.GetBytes("hello world"); + byte[] result = value.RIPEMD160(); + string resultStr = result.ToHexString(); + resultStr.Should().Be("98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f"); + } + [TestMethod] public void TestTest() { From 552ea0c16bbc0e1d6d80196ae1688c56c59dbdce Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 12 Jun 2020 12:54:10 +0800 Subject: [PATCH 287/305] Refactor NativeContract (#1693) --- src/neo/SmartContract/ApplicationEngine.cs | 76 ++++++------ .../InteropParameterDescriptor.cs | 4 + .../Native/ContractMethodAttribute.cs | 8 +- .../Native/ContractMethodMetadata.cs | 38 +++++- .../SmartContract/Native/NativeContract.cs | 108 +++++++++++------- .../SmartContract/Native/PolicyContract.cs | 57 +++------ .../SmartContract/Native/Tokens/GasToken.cs | 7 +- .../SmartContract/Native/Tokens/NeoToken.cs | 94 +++++---------- .../SmartContract/Native/Tokens/Nep5Token.cs | 47 +------- .../Native/Tokens/UT_Nep5Token.cs | 16 +-- .../SmartContract/Native/UT_NativeContract.cs | 15 +-- 11 files changed, 202 insertions(+), 268 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 11708d9609..711a9c8dc9 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -71,7 +71,7 @@ public ExecutionContext LoadScript(Script script, CallFlags callFlags, int rvcou return context; } - private StackItem ConvertReturnValue(object value) + internal StackItem Convert(object value) { return value switch { @@ -85,18 +85,51 @@ private StackItem ConvertReturnValue(object value) uint i => i, long i => i, ulong i => i, - Enum e => ConvertReturnValue(Convert.ChangeType(e, e.GetTypeCode())), + Enum e => Convert(System.Convert.ChangeType(e, e.GetTypeCode())), byte[] data => data, string s => s, - UInt160 i => i.ToArray(), - UInt256 i => i.ToArray(), + BigInteger i => i, IInteroperable interoperable => interoperable.ToStackItem(ReferenceCounter), - IInteroperable[] array => new VMArray(ReferenceCounter, array.Select(p => p.ToStackItem(ReferenceCounter))), + ISerializable i => i.ToArray(), StackItem item => item, + (object a, object b) => new Struct(ReferenceCounter) { Convert(a), Convert(b) }, + Array array => new VMArray(ReferenceCounter, array.OfType().Select(p => Convert(p))), _ => StackItem.FromInterface(value) }; } + internal object Convert(StackItem item, InteropParameterDescriptor descriptor) + { + if (descriptor.IsArray) + { + Array av; + if (item is VMArray array) + { + av = Array.CreateInstance(descriptor.Type.GetElementType(), array.Count); + for (int i = 0; i < av.Length; i++) + av.SetValue(descriptor.Converter(array[i]), i); + } + else + { + int count = (int)item.GetBigInteger(); + if (count > MaxStackSize) throw new InvalidOperationException(); + av = Array.CreateInstance(descriptor.Type.GetElementType(), count); + for (int i = 0; i < av.Length; i++) + av.SetValue(descriptor.Converter(Pop()), i); + } + return av; + } + else + { + object value = descriptor.Converter(item); + if (descriptor.IsEnum) + value = System.Convert.ChangeType(value, descriptor.Type); + else if (descriptor.IsInterface) + value = ((InteropInterface)value).GetInterface(); + return value; + } + } + public override void Dispose() { foreach (IDisposable disposable in disposables) @@ -120,39 +153,10 @@ protected override bool OnSysCall(uint method) ? new List() : null; foreach (var pd in descriptor.Parameters) - { - StackItem item = Pop(); - object value; - if (pd.IsArray) - { - Array av; - if (item is VMArray array) - { - av = Array.CreateInstance(pd.Type.GetElementType(), array.Count); - for (int i = 0; i < av.Length; i++) - av.SetValue(pd.Converter(array[i]), i); - } - else - { - av = Array.CreateInstance(pd.Type.GetElementType(), (int)item.GetBigInteger()); - for (int i = 0; i < av.Length; i++) - av.SetValue(pd.Converter(Pop()), i); - } - value = av; - } - else - { - value = pd.Converter(item); - if (pd.IsEnum) - value = Convert.ChangeType(value, pd.Type); - else if (pd.IsInterface) - value = ((InteropInterface)value).GetInterface(); - } - parameters.Add(value); - } + parameters.Add(Convert(Pop(), pd)); object returnValue = descriptor.Handler.Invoke(this, parameters?.ToArray()); if (descriptor.Handler.ReturnType != typeof(void)) - Push(ConvertReturnValue(returnValue)); + Push(Convert(returnValue)); return true; } diff --git a/src/neo/SmartContract/InteropParameterDescriptor.cs b/src/neo/SmartContract/InteropParameterDescriptor.cs index e7ee51cf28..124f1e27e4 100644 --- a/src/neo/SmartContract/InteropParameterDescriptor.cs +++ b/src/neo/SmartContract/InteropParameterDescriptor.cs @@ -3,12 +3,14 @@ using Neo.VM.Types; using System; using System.Collections.Generic; +using System.Numerics; using System.Reflection; namespace Neo.SmartContract { internal class InteropParameterDescriptor { + public string Name { get; } public Type Type { get; } public Func Converter { get; } public bool IsEnum => Type.IsEnum; @@ -28,6 +30,7 @@ internal class InteropParameterDescriptor [typeof(uint)] = p => (uint)p.GetBigInteger(), [typeof(long)] = p => (long)p.GetBigInteger(), [typeof(ulong)] = p => (ulong)p.GetBigInteger(), + [typeof(BigInteger)] = p => p.GetBigInteger(), [typeof(byte[])] = p => p.IsNull ? null : p.GetSpan().ToArray(), [typeof(string)] = p => p.IsNull ? null : p.GetString(), [typeof(UInt160)] = p => p.IsNull ? null : new UInt160(p.GetSpan()), @@ -37,6 +40,7 @@ internal class InteropParameterDescriptor public InteropParameterDescriptor(ParameterInfo parameterInfo) { + Name = parameterInfo.Name; Type = parameterInfo.ParameterType; if (IsEnum) { diff --git a/src/neo/SmartContract/Native/ContractMethodAttribute.cs b/src/neo/SmartContract/Native/ContractMethodAttribute.cs index 6cb745fee5..4952fc2781 100644 --- a/src/neo/SmartContract/Native/ContractMethodAttribute.cs +++ b/src/neo/SmartContract/Native/ContractMethodAttribute.cs @@ -2,20 +2,16 @@ namespace Neo.SmartContract.Native { - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)] internal class ContractMethodAttribute : Attribute { public string Name { get; set; } public long Price { get; } - public ContractParameterType ReturnType { get; } - public ContractParameterType[] ParameterTypes { get; set; } = Array.Empty(); - public string[] ParameterNames { get; set; } = Array.Empty(); public CallFlags RequiredCallFlags { get; } - public ContractMethodAttribute(long price, ContractParameterType returnType, CallFlags requiredCallFlags) + public ContractMethodAttribute(long price, CallFlags requiredCallFlags) { this.Price = price; - this.ReturnType = returnType; this.RequiredCallFlags = requiredCallFlags; } } diff --git a/src/neo/SmartContract/Native/ContractMethodMetadata.cs b/src/neo/SmartContract/Native/ContractMethodMetadata.cs index 228252f093..563040721e 100644 --- a/src/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -1,13 +1,41 @@ -using Neo.VM.Types; +using Neo.Persistence; using System; -using VMArray = Neo.VM.Types.Array; +using System.Linq; +using System.Reflection; namespace Neo.SmartContract.Native { internal class ContractMethodMetadata { - public Func Delegate; - public long Price; - public CallFlags RequiredCallFlags; + public string Name { get; } + public MethodInfo Handler { get; } + public InteropParameterDescriptor[] Parameters { get; } + public bool NeedApplicationEngine { get; } + public bool NeedSnapshot { get; } + public long Price { get; } + public CallFlags RequiredCallFlags { get; } + + public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribute) + { + this.Name = attribute.Name ?? member.Name.ToLower()[0] + member.Name[1..]; + this.Handler = member switch + { + MethodInfo m => m, + PropertyInfo p => p.GetMethod, + _ => throw new ArgumentException(nameof(member)) + }; + ParameterInfo[] parameterInfos = this.Handler.GetParameters(); + if (parameterInfos.Length > 0) + { + NeedApplicationEngine = parameterInfos[0].ParameterType.IsAssignableFrom(typeof(ApplicationEngine)); + NeedSnapshot = parameterInfos[0].ParameterType.IsAssignableFrom(typeof(StoreView)); + } + if (NeedApplicationEngine || NeedSnapshot) + this.Parameters = parameterInfos.Skip(1).Select(p => new InteropParameterDescriptor(p)).ToArray(); + else + this.Parameters = parameterInfos.Select(p => new InteropParameterDescriptor(p)).ToArray(); + this.Price = attribute.Price; + this.RequiredCallFlags = attribute.RequiredCallFlags; + } } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index c42d7eb439..a18b26abd7 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -1,5 +1,3 @@ -#pragma warning disable IDE0060 - using Neo.IO; using Neo.Ledger; using Neo.SmartContract.Manifest; @@ -9,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using System.Reflection; using Array = Neo.VM.Types.Array; @@ -26,11 +25,13 @@ public abstract class NativeContract public static GasToken GAS { get; } = new GasToken(); public static PolicyContract Policy { get; } = new PolicyContract(); + [ContractMethod(0, CallFlags.None)] public abstract string Name { get; } public byte[] Script { get; } public UInt160 Hash { get; } public abstract int Id { get; } public ContractManifest Manifest { get; } + [ContractMethod(0, CallFlags.None)] public virtual string[] SupportedStandards { get; } = { "NEP-10" }; protected NativeContract() @@ -44,24 +45,19 @@ protected NativeContract() this.Hash = Script.ToScriptHash(); List descriptors = new List(); List safeMethods = new List(); - foreach (MethodInfo method in GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) + foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { - ContractMethodAttribute attribute = method.GetCustomAttribute(); + ContractMethodAttribute attribute = member.GetCustomAttribute(); if (attribute is null) continue; - string name = attribute.Name ?? (method.Name.ToLower()[0] + method.Name.Substring(1)); + ContractMethodMetadata metadata = new ContractMethodMetadata(member, attribute); descriptors.Add(new ContractMethodDescriptor { - Name = name, - ReturnType = attribute.ReturnType, - Parameters = attribute.ParameterTypes.Zip(attribute.ParameterNames, (t, n) => new ContractParameterDefinition { Type = t, Name = n }).ToArray() - }); - if (!attribute.RequiredCallFlags.HasFlag(CallFlags.AllowModifyStates)) safeMethods.Add(name); - methods.Add(name, new ContractMethodMetadata - { - Delegate = (Func)method.CreateDelegate(typeof(Func), this), - Price = attribute.Price, - RequiredCallFlags = attribute.RequiredCallFlags + Name = metadata.Name, + ReturnType = ToParameterType(metadata.Handler.ReturnType), + Parameters = metadata.Parameters.Select(p => new ContractParameterDefinition { Type = ToParameterType(p.Type), Name = p.Name }).ToArray() }); + if (!attribute.RequiredCallFlags.HasFlag(CallFlags.AllowModifyStates)) safeMethods.Add(metadata.Name); + methods.Add(metadata.Name, metadata); } this.Manifest = new ContractManifest { @@ -69,11 +65,11 @@ protected NativeContract() Abi = new ContractAbi() { Hash = Hash, - Events = new ContractEventDescriptor[0], + Events = System.Array.Empty(), Methods = descriptors.ToArray() }, Features = ContractFeatures.NoProperty, - Groups = new ContractGroup[0], + Groups = System.Array.Empty(), SafeMethods = WildcardContainer.Create(safeMethods.ToArray()), Trusts = WildcardContainer.Create(), Extra = null, @@ -114,19 +110,24 @@ public static NativeContract GetContract(string name) internal bool Invoke(ApplicationEngine engine) { - if (!engine.CurrentScriptHash.Equals(Hash)) - return false; - string operation = engine.CurrentContext.EvaluationStack.Pop().GetString(); - Array args = (Array)engine.CurrentContext.EvaluationStack.Pop(); - if (!methods.TryGetValue(operation, out ContractMethodMetadata method)) - return false; + if (!engine.CurrentScriptHash.Equals(Hash)) return false; + if (!engine.TryPop(out string operation)) return false; + if (!engine.TryPop(out Array args)) return false; + if (!methods.TryGetValue(operation, out ContractMethodMetadata method)) return false; ExecutionContextState state = engine.CurrentContext.GetState(); - if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) - return false; - if (!engine.AddGas(method.Price)) - return false; - StackItem result = method.Delegate(engine, args); - engine.CurrentContext.EvaluationStack.Push(result); + if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) return false; + if (!engine.AddGas(method.Price)) return false; + List parameters = new List(); + 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])); + } + object returnValue = method.Handler.Invoke(this, parameters.ToArray()); + if (method.Handler.ReturnType != typeof(void)) + engine.Push(engine.Convert(returnValue)); return true; } @@ -139,22 +140,11 @@ internal virtual void Initialize(ApplicationEngine engine) { } - [ContractMethod(0, ContractParameterType.Boolean, CallFlags.AllowModifyStates)] - protected StackItem OnPersist(ApplicationEngine engine, Array args) + [ContractMethod(0, CallFlags.AllowModifyStates)] + protected virtual void OnPersist(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.System) return false; - return OnPersist(engine); - } - - protected virtual bool OnPersist(ApplicationEngine engine) - { - return true; - } - - [ContractMethod(0, ContractParameterType.Array, CallFlags.None, Name = "supportedStandards")] - protected StackItem SupportedStandardsMethod(ApplicationEngine engine, Array args) - { - return new Array(engine.ReferenceCounter, SupportedStandards.Select(p => (StackItem)p)); + if (engine.Trigger != TriggerType.System) + throw new InvalidOperationException(); } public ApplicationEngine TestCall(string operation, params object[] args) @@ -165,5 +155,35 @@ public ApplicationEngine TestCall(string operation, params object[] args) return ApplicationEngine.Run(sb.ToArray(), testMode: true); } } + + private static ContractParameterType ToParameterType(Type type) + { + if (type == typeof(void)) return ContractParameterType.Void; + if (type == typeof(bool)) return ContractParameterType.Boolean; + if (type == typeof(sbyte)) return ContractParameterType.Integer; + if (type == typeof(byte)) return ContractParameterType.Integer; + if (type == typeof(short)) return ContractParameterType.Integer; + if (type == typeof(ushort)) return ContractParameterType.Integer; + if (type == typeof(int)) return ContractParameterType.Integer; + if (type == typeof(uint)) return ContractParameterType.Integer; + if (type == typeof(long)) return ContractParameterType.Integer; + if (type == typeof(ulong)) return ContractParameterType.Integer; + if (type == typeof(BigInteger)) return ContractParameterType.Integer; + if (type == typeof(byte[])) return ContractParameterType.ByteArray; + if (type == typeof(string)) return ContractParameterType.ByteArray; + if (type == typeof(VM.Types.Boolean)) return ContractParameterType.Boolean; + if (type == typeof(Integer)) return ContractParameterType.Integer; + if (type == typeof(ByteString)) return ContractParameterType.ByteArray; + if (type == typeof(VM.Types.Buffer)) return ContractParameterType.ByteArray; + if (type == typeof(Array)) return ContractParameterType.Array; + if (type == typeof(Struct)) return ContractParameterType.Array; + if (type == typeof(Map)) return ContractParameterType.Map; + if (type == typeof(StackItem)) return ContractParameterType.Any; + if (typeof(IInteroperable).IsAssignableFrom(type)) return ContractParameterType.Array; + if (typeof(ISerializable).IsAssignableFrom(type)) return ContractParameterType.ByteArray; + if (type.IsArray) return ContractParameterType.Array; + if (type.IsEnum) return ContractParameterType.Integer; + return ContractParameterType.Any; + } } } diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 2bd2244a96..2cef384306 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -1,17 +1,13 @@ #pragma warning disable IDE0051 -#pragma warning disable IDE0060 using Neo.IO; using Neo.Ledger; 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; using System.Linq; -using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native { @@ -64,86 +60,62 @@ internal override void Initialize(ApplicationEngine engine) }); } - [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] - private StackItem GetMaxTransactionsPerBlock(ApplicationEngine engine, Array args) - { - return GetMaxTransactionsPerBlock(engine.Snapshot); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public uint GetMaxTransactionsPerBlock(StoreView snapshot) { return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxTransactionsPerBlock)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] - private StackItem GetMaxBlockSize(ApplicationEngine engine, Array args) - { - return GetMaxBlockSize(engine.Snapshot); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public uint GetMaxBlockSize(StoreView snapshot) { return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxBlockSize)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] - private StackItem GetFeePerByte(ApplicationEngine engine, Array args) - { - return GetFeePerByte(engine.Snapshot); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public long GetFeePerByte(StoreView snapshot) { return BitConverter.ToInt64(snapshot.Storages[CreateStorageKey(Prefix_FeePerByte)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Array, CallFlags.AllowStates)] - private StackItem GetBlockedAccounts(ApplicationEngine engine, Array args) - { - return new Array(engine.ReferenceCounter, GetBlockedAccounts(engine.Snapshot).Select(p => (StackItem)p.ToArray())); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public UInt160[] GetBlockedAccounts(StoreView snapshot) { return snapshot.Storages[CreateStorageKey(Prefix_BlockedAccounts)].Value.AsSerializableArray(); } - [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] - private StackItem SetMaxBlockSize(ApplicationEngine engine, Array args) + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool SetMaxBlockSize(ApplicationEngine engine, uint value) { if (!CheckCommittees(engine)) return false; - uint value = (uint)args[0].GetBigInteger(); if (Network.P2P.Message.PayloadMaxSize <= value) return false; StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize)); storage.Value = BitConverter.GetBytes(value); return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] - private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, Array args) + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool SetMaxTransactionsPerBlock(ApplicationEngine engine, uint value) { if (!CheckCommittees(engine)) return false; - uint value = (uint)args[0].GetBigInteger(); StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock)); storage.Value = BitConverter.GetBytes(value); return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] - private StackItem SetFeePerByte(ApplicationEngine engine, Array args) + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool SetFeePerByte(ApplicationEngine engine, long value) { if (!CheckCommittees(engine)) return false; - long value = (long)args[0].GetBigInteger(); StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_FeePerByte)); storage.Value = BitConverter.GetBytes(value); return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] - private StackItem BlockAccount(ApplicationEngine engine, Array args) + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool BlockAccount(ApplicationEngine engine, UInt160 account) { if (!CheckCommittees(engine)) return false; - UInt160 account = new UInt160(args[0].GetSpan()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; SortedSet accounts = new SortedSet(storage.Value.AsSerializableArray()); @@ -153,11 +125,10 @@ private StackItem BlockAccount(ApplicationEngine engine, Array args) return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] - private StackItem UnblockAccount(ApplicationEngine engine, Array args) + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool UnblockAccount(ApplicationEngine engine, UInt160 account) { if (!CheckCommittees(engine)) return false; - UInt160 account = new UInt160(args[0].GetSpan()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; SortedSet accounts = new SortedSet(storage.Value.AsSerializableArray()); diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index 4a8cd5e1bf..37a5098f04 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -1,5 +1,3 @@ -#pragma warning disable IDE0051 - using Neo.Cryptography.ECC; using Neo.Ledger; using Neo.Network.P2P.Payloads; @@ -24,15 +22,14 @@ internal override void Initialize(ApplicationEngine engine) Mint(engine, account, 30_000_000 * Factor); } - protected override bool OnPersist(ApplicationEngine engine) + protected override void OnPersist(ApplicationEngine engine) { - if (!base.OnPersist(engine)) return false; + base.OnPersist(engine); foreach (Transaction tx in engine.Snapshot.PersistingBlock.Transactions) Burn(engine, tx.Sender, tx.SystemFee + tx.NetworkFee); ECPoint[] validators = NEO.GetNextBlockValidators(engine.Snapshot); UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.Snapshot.PersistingBlock.ConsensusData.PrimaryIndex]).ToScriptHash(); Mint(engine, primary, engine.Snapshot.PersistingBlock.Transactions.Sum(p => p.NetworkFee)); - return true; } } } diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 1c63485b5c..494bf4afc6 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -1,5 +1,4 @@ #pragma warning disable IDE0051 -#pragma warning disable IDE0060 using Neo.Cryptography.ECC; using Neo.IO; @@ -11,7 +10,6 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native.Tokens { @@ -50,12 +48,12 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco private void DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccountState state) { - BigInteger gas = CalculateBonus(engine.Snapshot, state.Balance, state.BalanceHeight, engine.Snapshot.PersistingBlock.Index); + BigInteger gas = CalculateBonus(state.Balance, state.BalanceHeight, engine.Snapshot.PersistingBlock.Index); state.BalanceHeight = engine.Snapshot.PersistingBlock.Index; GAS.Mint(engine, account, gas); } - private BigInteger CalculateBonus(StoreView snapshot, BigInteger value, uint start, uint end) + private BigInteger CalculateBonus(BigInteger value, uint start, uint end) { if (value.IsZero || start >= end) return BigInteger.Zero; if (value.Sign < 0) throw new ArgumentOutOfRangeException(nameof(value)); @@ -93,91 +91,74 @@ internal override void Initialize(ApplicationEngine engine) for (int i = 0; i < Blockchain.StandbyCommittee.Length; i++) { ECPoint pubkey = Blockchain.StandbyCommittee[i]; - RegisterCandidate(engine.Snapshot, pubkey); + RegisterCandidateInternal(engine.Snapshot, pubkey); BigInteger balance = TotalAmount / 2 / (Blockchain.StandbyValidators.Length * 2 + (Blockchain.StandbyCommittee.Length - Blockchain.StandbyValidators.Length)); if (i < Blockchain.StandbyValidators.Length) balance *= 2; UInt160 account = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); Mint(engine, account, balance); - Vote(engine.Snapshot, account, pubkey); + VoteInternal(engine.Snapshot, account, pubkey); amount -= balance; } Mint(engine, Blockchain.GetConsensusAddress(Blockchain.StandbyValidators), amount); } - protected override bool OnPersist(ApplicationEngine engine) + protected override void OnPersist(ApplicationEngine engine) { - if (!base.OnPersist(engine)) return false; + base.OnPersist(engine); StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_NextValidators), () => new StorageItem()); storage.Value = GetValidators(engine.Snapshot).ToByteArray(); - return true; - } - - [ContractMethod(0_03000000, ContractParameterType.Integer, CallFlags.AllowStates, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "account", "end" })] - private StackItem UnclaimedGas(ApplicationEngine engine, Array args) - { - UInt160 account = new UInt160(args[0].GetSpan()); - uint end = (uint)args[1].GetBigInteger(); - return UnclaimedGas(engine.Snapshot, account, end); } + [ContractMethod(0_03000000, CallFlags.AllowStates)] public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) { StorageItem storage = snapshot.Storages.TryGet(CreateAccountKey(account)); if (storage is null) return BigInteger.Zero; NeoAccountState state = storage.GetInteroperable(); - return CalculateBonus(snapshot, state.Balance, state.BalanceHeight, end); + return CalculateBonus(state.Balance, state.BalanceHeight, end); } - [ContractMethod(0_05000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] - private StackItem RegisterCandidate(ApplicationEngine engine, Array args) + [ContractMethod(0_05000000, CallFlags.AllowModifyStates)] + private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) { - ECPoint pubkey = args[0].GetSpan().AsSerializable(); if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) return false; - return RegisterCandidate(engine.Snapshot, pubkey); + RegisterCandidateInternal(engine.Snapshot, pubkey); + return true; } - private bool RegisterCandidate(StoreView snapshot, ECPoint pubkey) + private void RegisterCandidateInternal(StoreView snapshot, ECPoint pubkey) { StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey); StorageItem item = snapshot.Storages.GetAndChange(key, () => new StorageItem(new CandidateState())); CandidateState state = item.GetInteroperable(); state.Registered = true; - return true; } - [ContractMethod(0_05000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] - private StackItem UnregisterCandidate(ApplicationEngine engine, Array args) + [ContractMethod(0_05000000, CallFlags.AllowModifyStates)] + private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) { - ECPoint pubkey = args[0].GetSpan().AsSerializable(); if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) return false; - return UnregisterCandidate(engine.Snapshot, pubkey); - } - - private bool UnregisterCandidate(StoreView snapshot, ECPoint pubkey) - { StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey); - if (snapshot.Storages.TryGet(key) is null) return true; - StorageItem item = snapshot.Storages.GetAndChange(key); + if (engine.Snapshot.Storages.TryGet(key) is null) return true; + StorageItem item = engine.Snapshot.Storages.GetAndChange(key); CandidateState state = item.GetInteroperable(); if (state.Votes.IsZero) - snapshot.Storages.Delete(key); + engine.Snapshot.Storages.Delete(key); else state.Registered = false; return true; } - [ContractMethod(5_00000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" })] - private StackItem Vote(ApplicationEngine engine, Array args) + [ContractMethod(5_00000000, CallFlags.AllowModifyStates)] + private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) { - UInt160 account = new UInt160(args[0].GetSpan()); - ECPoint voteTo = args[1].IsNull ? null : args[1].GetSpan().AsSerializable(); if (!engine.CheckWitnessInternal(account)) return false; - return Vote(engine.Snapshot, account, voteTo); + return VoteInternal(engine.Snapshot, account, voteTo); } - private bool Vote(StoreView snapshot, UInt160 account, ECPoint voteTo) + private bool VoteInternal(StoreView snapshot, UInt160 account, ECPoint voteTo) { StorageKey key_account = CreateAccountKey(account); if (snapshot.Storages.TryGet(key_account) is null) return false; @@ -205,13 +186,13 @@ private bool Vote(StoreView snapshot, UInt160 account, ECPoint voteTo) return true; } - [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] - private StackItem GetCandidates(ApplicationEngine engine, Array args) + [ContractMethod(1_00000000, CallFlags.AllowStates)] + public (ECPoint PublicKey, BigInteger Votes)[] GetCandidates(StoreView snapshot) { - return new Array(engine.ReferenceCounter, GetCandidates(engine.Snapshot).Select(p => new Struct(engine.ReferenceCounter, new StackItem[] { p.PublicKey.ToArray(), p.Votes }))); + return GetCandidatesInternal(snapshot).ToArray(); } - public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetCandidates(StoreView snapshot) + private IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetCandidatesInternal(StoreView snapshot) { byte[] prefix_key = StorageKey.CreateSearchPrefix(Id, new[] { Prefix_Candidate }); return snapshot.Storages.Find(prefix_key).Select(p => @@ -221,23 +202,13 @@ public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetCandidates(StoreVie )).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes)); } - [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] - private StackItem GetValidators(ApplicationEngine engine, Array args) - { - return new Array(engine.ReferenceCounter, GetValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray())); - } - + [ContractMethod(1_00000000, CallFlags.AllowStates)] public ECPoint[] GetValidators(StoreView snapshot) { return GetCommitteeMembers(snapshot, ProtocolSettings.Default.MaxValidatorsCount).OrderBy(p => p).ToArray(); } - [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] - private StackItem GetCommittee(ApplicationEngine engine, Array args) - { - return new Array(engine.ReferenceCounter, GetCommittee(engine.Snapshot).Select(p => (StackItem)p.ToArray())); - } - + [ContractMethod(1_00000000, CallFlags.AllowStates)] public ECPoint[] GetCommittee(StoreView snapshot) { return GetCommitteeMembers(snapshot, ProtocolSettings.Default.MaxCommitteeMembersCount).OrderBy(p => p).ToArray(); @@ -251,15 +222,10 @@ public UInt160 GetCommitteeAddress(StoreView snapshot) private IEnumerable GetCommitteeMembers(StoreView snapshot, int count) { - return GetCandidates(snapshot).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count); - } - - [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] - private StackItem GetNextBlockValidators(ApplicationEngine engine, Array args) - { - return new Array(engine.ReferenceCounter, GetNextBlockValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray())); + return GetCandidatesInternal(snapshot).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count); } + [ContractMethod(1_00000000, CallFlags.AllowStates)] public ECPoint[] GetNextBlockValidators(StoreView snapshot) { StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_NextValidators)); diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index 855d864c59..f8b190ef92 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -1,10 +1,7 @@ -#pragma warning disable IDE0060 - using Neo.IO; using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract.Manifest; -using Neo.VM; using Neo.VM.Types; using System; using System.Collections.Generic; @@ -17,7 +14,9 @@ public abstract class Nep5Token : NativeContract where TState : AccountState, new() { public override string[] SupportedStandards { get; } = { "NEP-5", "NEP-10" }; + [ContractMethod(0, CallFlags.None)] public abstract string Symbol { get; } + [ContractMethod(0, CallFlags.None)] public abstract byte Decimals { get; } public BigInteger Factor { get; } @@ -103,30 +102,7 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, engine.SendNotification(Hash, new Array(new StackItem[] { "Transfer", account.ToArray(), StackItem.Null, amount })); } - [ContractMethod(0, ContractParameterType.String, CallFlags.None, Name = "name")] - protected StackItem NameMethod(ApplicationEngine engine, Array args) - { - return Name; - } - - [ContractMethod(0, ContractParameterType.String, CallFlags.None, Name = "symbol")] - protected StackItem SymbolMethod(ApplicationEngine engine, Array args) - { - return Symbol; - } - - [ContractMethod(0, ContractParameterType.Integer, CallFlags.None, Name = "decimals")] - protected StackItem DecimalsMethod(ApplicationEngine engine, Array args) - { - return (uint)Decimals; - } - - [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] - protected StackItem TotalSupply(ApplicationEngine engine, Array args) - { - return TotalSupply(engine.Snapshot); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public virtual BigInteger TotalSupply(StoreView snapshot) { StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_TotalSupply)); @@ -134,12 +110,7 @@ public virtual BigInteger TotalSupply(StoreView snapshot) return new BigInteger(storage.Value); } - [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] - protected StackItem BalanceOf(ApplicationEngine engine, Array args) - { - return BalanceOf(engine.Snapshot, new UInt160(args[0].GetSpan())); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) { StorageItem storage = snapshot.Storages.TryGet(CreateAccountKey(account)); @@ -147,15 +118,7 @@ public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) return storage.GetInteroperable().Balance; } - [ContractMethod(0_08000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" })] - protected StackItem Transfer(ApplicationEngine engine, Array args) - { - UInt160 from = new UInt160(args[0].GetSpan()); - UInt160 to = new UInt160(args[1].GetSpan()); - BigInteger amount = args[2].GetBigInteger(); - return Transfer(engine, from, to, amount); - } - + [ContractMethod(0_08000000, CallFlags.AllowModifyStates)] protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount) { if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs index 8c6d8eefef..57b2a25491 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs @@ -2,10 +2,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; -using Neo.SmartContract; using Neo.SmartContract.Native.Tokens; -using Neo.VM; -using Neo.VM.Types; using System; using System.Numerics; @@ -37,9 +34,7 @@ public void TestTotalSupply() key.Id = test.Id; snapshot.Storages.Add(key, item); - ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); - StackItem stackItem = test.TotalSupply(ae, null); - stackItem.GetBigInteger().Should().Be(1); + test.TotalSupply(snapshot).Should().Be(1); } [TestMethod] @@ -60,9 +55,7 @@ public void TestTotalSupplyDecimal() snapshot.Storages.Add(key, item); - ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); - StackItem stackItem = test.TotalSupply(ae, null); - stackItem.GetBigInteger().Should().Be(10_000_000_000_000_000); + test.TotalSupply(snapshot).Should().Be(10_000_000_000_000_000); } public StorageKey CreateStorageKey(byte prefix, byte[] key = null) @@ -87,10 +80,5 @@ public class TestNep5Token : Nep5Token public override string Symbol => throw new NotImplementedException(); public override byte Decimals => 8; - - public new StackItem TotalSupply(ApplicationEngine engine, VM.Types.Array args) - { - return base.TotalSupply(engine, args); - } } } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 656332aabc..aa58ba157c 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -31,7 +31,7 @@ public void TestInitialize() public void TestInvoke() { var snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + ApplicationEngine engine = new ApplicationEngine(TriggerType.System, null, snapshot, 0); engine.LoadScript(testNativeContract.Script); ByteString method1 = new ByteString(System.Text.Encoding.Default.GetBytes("wrongMethod")); @@ -51,15 +51,12 @@ public void TestInvoke() public void TestOnPersistWithArgs() { var snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); - VMArray args = new VMArray(); - VM.Types.Boolean result1 = new VM.Types.Boolean(false); - testNativeContract.TestOnPersist(engine1, args).Should().Be(result1); + ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + Assert.ThrowsException(() => testNativeContract.TestOnPersist(engine1)); ApplicationEngine engine2 = new ApplicationEngine(TriggerType.System, null, snapshot, 0); - VM.Types.Boolean result2 = new VM.Types.Boolean(true); - testNativeContract.TestOnPersist(engine2, args).Should().Be(result2); + testNativeContract.TestOnPersist(engine2); } [TestMethod] @@ -76,9 +73,9 @@ public class TestNativeContract : NativeContract public override int Id => 0x10000006; - public StackItem TestOnPersist(ApplicationEngine engine, VMArray args) + public void TestOnPersist(ApplicationEngine engine) { - return OnPersist(engine, args); + OnPersist(engine); } } } From 684b6596c6890cb77a8232fe82e48fad2c6863f3 Mon Sep 17 00:00:00 2001 From: erikzhang Date: Fri, 12 Jun 2020 13:06:24 +0800 Subject: [PATCH 288/305] Close #1696 --- src/neo/SmartContract/Helper.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index fcab335b9d..b8fe4450a3 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -14,6 +14,11 @@ namespace Neo.SmartContract { public static class Helper { + public static UInt160 GetScriptHash(this ExecutionContext context) + { + return context.GetState().ScriptHash; + } + public static bool IsMultiSigContract(this byte[] script) { return IsMultiSigContract(script, out _, out _, null); From d89ed15bada24bd224608c55627a04a17077d400 Mon Sep 17 00:00:00 2001 From: Luchuan Date: Sat, 13 Jun 2020 18:44:14 +0800 Subject: [PATCH 289/305] Add MaxBlockSystemFee (#1689) * add max block system fee * fix tx * fix and add more ut * format * optimze code * Reset maxblocksystem = 9000/GAS * Update src/neo/SmartContract/Native/PolicyContract.cs Co-authored-by: HaoqiangZhang * Remove new line in log * Update ConsensusService.cs * up Prefix_MaxBlockSize as skiptable.insert Co-authored-by: Shargon Co-authored-by: HaoqiangZhang --- src/neo/Consensus/ConsensusContext.cs | 20 ++++++- src/neo/Consensus/ConsensusService.cs | 9 ++- src/neo/Network/P2P/Payloads/Transaction.cs | 2 + .../SmartContract/Native/PolicyContract.cs | 23 +++++++- .../Consensus/UT_ConsensusContext.cs | 53 ++++++++++++++++-- .../SmartContract/Native/UT_PolicyContract.cs | 55 ++++++++++++++++++- 6 files changed, 151 insertions(+), 11 deletions(-) diff --git a/src/neo/Consensus/ConsensusContext.cs b/src/neo/Consensus/ConsensusContext.cs index ee9b3dfe39..3d3a4fcb5d 100644 --- a/src/neo/Consensus/ConsensusContext.cs +++ b/src/neo/Consensus/ConsensusContext.cs @@ -219,6 +219,14 @@ internal int GetExpectedBlockSize() 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 /// @@ -248,9 +256,10 @@ internal int GetExpectedBlockSizeWithoutTransactions(int expectedTransactions) /// Prevent that block exceed the max size /// /// Ordered transactions - internal void EnsureMaxBlockSize(IEnumerable txs) + 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 @@ -261,14 +270,19 @@ internal void EnsureMaxBlockSize(IEnumerable txs) // Expected block size var blockSize = GetExpectedBlockSizeWithoutTransactions(txs.Count()); + var blockSystemFee = 0L; - // Iterate transaction until reach the size + // 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); SendersFeeMonitor.AddSenderFee(tx); @@ -283,7 +297,7 @@ public ConsensusPayload MakePrepareRequest() Span buffer = stackalloc byte[sizeof(ulong)]; random.NextBytes(buffer); Block.ConsensusData.Nonce = BitConverter.ToUInt64(buffer); - EnsureMaxBlockSize(Blockchain.Singleton.MemPool.GetSortedVerifiedTransactions()); + EnsureMaxBlockLimitation(Blockchain.Singleton.MemPool.GetSortedVerifiedTransactions()); Block.Timestamp = Math.Max(TimeProvider.Current.UtcNow.ToTimestampMS(), PrevHeader.Timestamp + 1); return PreparationPayloads[MyIndex] = MakeSignedPayload(new PrepareRequest diff --git a/src/neo/Consensus/ConsensusService.cs b/src/neo/Consensus/ConsensusService.cs index 4199bb13a0..26648c4429 100644 --- a/src/neo/Consensus/ConsensusService.cs +++ b/src/neo/Consensus/ConsensusService.cs @@ -92,7 +92,14 @@ private bool CheckPrepareResponse() // Check maximum block size via Native Contract policy if (context.GetExpectedBlockSize() > NativeContract.Policy.GetMaxBlockSize(context.Snapshot)) { - Log($"rejected block: {context.Block.Index}{Environment.NewLine} The size exceed the policy", LogLevel.Warning); + 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; } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 48fe5902f9..10591659ca 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -268,6 +268,8 @@ public virtual VerifyResult VerifyForEachBlock(StoreView snapshot, BigInteger to UInt160[] hashes = GetScriptHashesForVerifying(snapshot); if (NativeContract.Policy.GetBlockedAccounts(snapshot).Intersect(hashes).Any()) return VerifyResult.PolicyFail; + if (NativeContract.Policy.GetMaxBlockSystemFee(snapshot) < SystemFee) + return VerifyResult.PolicyFail; BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, Sender); BigInteger fee = SystemFee + NetworkFee + totalSenderFeeFromPool; if (balance < fee) return VerifyResult.InsufficientFunds; diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 2cef384306..6fd173b409 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -19,7 +19,8 @@ public sealed class PolicyContract : NativeContract private const byte Prefix_MaxTransactionsPerBlock = 23; private const byte Prefix_FeePerByte = 10; private const byte Prefix_BlockedAccounts = 15; - private const byte Prefix_MaxBlockSize = 16; + private const byte Prefix_MaxBlockSize = 12; + private const byte Prefix_MaxBlockSystemFee = 17; public PolicyContract() { @@ -50,6 +51,10 @@ internal override void Initialize(ApplicationEngine engine) { Value = BitConverter.GetBytes(512u) }); + engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_MaxBlockSystemFee), new StorageItem + { + Value = BitConverter.GetBytes(9000 * (long)GAS.Factor) // For the transfer method of NEP5, the maximum persisting time is about three seconds. + }); engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem { Value = BitConverter.GetBytes(1000L) @@ -72,6 +77,12 @@ public uint GetMaxBlockSize(StoreView snapshot) return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxBlockSize)].Value, 0); } + [ContractMethod(0_01000000, CallFlags.AllowStates)] + public long GetMaxBlockSystemFee(StoreView snapshot) + { + return BitConverter.ToInt64(snapshot.Storages[CreateStorageKey(Prefix_MaxBlockSystemFee)].Value, 0); + } + [ContractMethod(0_01000000, CallFlags.AllowStates)] public long GetFeePerByte(StoreView snapshot) { @@ -103,6 +114,16 @@ private bool SetMaxTransactionsPerBlock(ApplicationEngine engine, uint value) return true; } + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool SetMaxBlockSystemFee(ApplicationEngine engine, long value) + { + if (!CheckCommittees(engine)) return false; + if (value <= 4007600) return false; + StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxBlockSystemFee)); + storage.Value = BitConverter.GetBytes(value); + return true; + } + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] private bool SetFeePerByte(ApplicationEngine engine, long value) { diff --git a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs index 5fab8d80d2..2f32b4b7ff 100644 --- a/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs +++ b/tests/neo.UnitTests/Consensus/UT_ConsensusContext.cs @@ -6,6 +6,7 @@ using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; +using Neo.VM.Types; using Neo.Wallets; using System; using System.Linq; @@ -58,7 +59,7 @@ public void TestMaxBlockSize_Good() // Only one tx, is included var tx1 = CreateTransactionWithSize(200); - _context.EnsureMaxBlockSize(new Transaction[] { tx1 }); + _context.EnsureMaxBlockLimitation(new Transaction[] { tx1 }); EnsureContext(_context, tx1); // All txs included @@ -68,7 +69,7 @@ public void TestMaxBlockSize_Good() for (int x = 0; x < max; x++) txs[x] = CreateTransactionWithSize(100); - _context.EnsureMaxBlockSize(txs); + _context.EnsureMaxBlockLimitation(txs); EnsureContext(_context, txs); } @@ -79,7 +80,7 @@ public void TestMaxBlockSize_Exceed() var tx1 = CreateTransactionWithSize(200); var tx2 = CreateTransactionWithSize(256 * 1024); - _context.EnsureMaxBlockSize(new Transaction[] { tx1, tx2 }); + _context.EnsureMaxBlockLimitation(new Transaction[] { tx1, tx2 }); EnsureContext(_context, tx1); // Exceed txs number, just MaxTransactionsPerBlock included @@ -89,16 +90,41 @@ public void TestMaxBlockSize_Exceed() for (int x = 0; x < max; x++) txs[x] = CreateTransactionWithSize(100); - _context.EnsureMaxBlockSize(txs); + _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 = Array.Empty(), + Attributes = System.Array.Empty(), NetworkFee = 0, Nonce = (uint)Environment.TickCount, Script = new byte[0], @@ -114,6 +140,23 @@ private Transaction CreateTransactionWithSize(int v) return tx; } + private Transaction CreateTransactionWithSytemFee(long fee) + { + var tx = new Transaction() + { + Attributes = System.Array.Empty(), + NetworkFee = 0, + Nonce = (uint)Environment.TickCount, + Script = new byte[0], + Sender = UInt160.Zero, + SystemFee = fee, + ValidUntilBlock = int.MaxValue, + Version = 0, + Witnesses = new Witness[0], + }; + return tx; + } + private Block SignBlock(ConsensusContext context) { context.Block.MerkleRoot = null; diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 1740495571..5695f3d105 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -5,6 +5,7 @@ using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Native; +using Neo.SmartContract.Native.Tokens; using Neo.UnitTests.Extensions; using Neo.VM; using System.Linq; @@ -31,7 +32,7 @@ public void Check_Initialize() NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)); - (keyCount + 4).Should().Be(snapshot.Storages.GetChangeSet().Count()); + (keyCount + 5).Should().Be(snapshot.Storages.GetChangeSet().Count()); var ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); ret.Should().BeOfType(); @@ -41,6 +42,10 @@ public void Check_Initialize() ret.Should().BeOfType(); ret.GetBigInteger().Should().Be(1024 * 256); + ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); + ret.Should().BeOfType(); + ret.GetBigInteger().Should().Be(9000 * 100000000L); + ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); ret.GetBigInteger().Should().Be(1000); @@ -98,6 +103,54 @@ public void Check_SetMaxBlockSize() ret.GetBigInteger().Should().Be(1024); } + [TestMethod] + public void Check_SetMaxBlockSystemFee() + { + var snapshot = Blockchain.Singleton.GetSnapshot(); + + // Fake blockchain + + snapshot.PersistingBlock = 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); + + NativeContract.Policy.Initialize(new ApplicationEngine(TriggerType.Application, null, snapshot, 0)); + + // Without signature + + var ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(null), + "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = 1024 * (long)NativeContract.GAS.Factor }); + ret.Should().BeOfType(); + ret.ToBoolean().Should().BeFalse(); + + ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); + ret.Should().BeOfType(); + ret.GetBigInteger().Should().Be(9000 * (long)NativeContract.GAS.Factor); + + // Less than expected + + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = -1000 }); + ret.Should().BeOfType(); + ret.ToBoolean().Should().BeFalse(); + + ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); + ret.Should().BeOfType(); + ret.GetBigInteger().Should().Be(9000 * (long)NativeContract.GAS.Factor); + + // With signature + + ret = NativeContract.Policy.Call(snapshot, new Nep5NativeContractExtensions.ManualWitness(committeeMultiSigAddr), + "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = 1024 * (long)NativeContract.GAS.Factor }); + ret.Should().BeOfType(); + ret.ToBoolean().Should().BeTrue(); + + ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); + ret.Should().BeOfType(); + ret.GetBigInteger().Should().Be(1024 * (long)NativeContract.GAS.Factor); + } + [TestMethod] public void Check_SetMaxTransactionsPerBlock() { From 6cbb77ae14d2a6760b86d0dec63944010191725d Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 15 Jun 2020 20:12:15 +0800 Subject: [PATCH 290/305] Add event name to System.Runtime.Notify() (#1701) --- .../ApplicationEngine.Runtime.cs | 10 ++-- .../SmartContract/Native/Tokens/Nep5Token.cs | 6 +-- src/neo/SmartContract/NotifyEventArgs.cs | 8 ++-- .../SmartContract/UT_ApplicationEngine.cs | 27 +++++------ .../SmartContract/UT_InteropService.cs | 47 ++++++++----------- .../SmartContract/UT_NotifyEventArgs.cs | 2 +- 6 files changed, 47 insertions(+), 53 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index 9610222a2d..92bab2ad36 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -12,6 +12,7 @@ namespace Neo.SmartContract { 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), 0_00000250, TriggerType.All, CallFlags.None); @@ -146,15 +147,16 @@ internal void RuntimeLog(byte[] state) Log?.Invoke(this, new LogEventArgs(ScriptContainer, CurrentScriptHash, message)); } - internal void RuntimeNotify(StackItem state) + internal void RuntimeNotify(byte[] eventName, Array state) { + if (eventName.Length > MaxEventName) throw new ArgumentException(); if (!CheckItemForNotification(state)) throw new ArgumentException(); - SendNotification(CurrentScriptHash, state); + SendNotification(CurrentScriptHash, Encoding.UTF8.GetString(eventName), state); } - internal void SendNotification(UInt160 script_hash, StackItem state) + internal void SendNotification(UInt160 hash, string eventName, Array state) { - NotifyEventArgs notification = new NotifyEventArgs(ScriptContainer, script_hash, state); + NotifyEventArgs notification = new NotifyEventArgs(ScriptContainer, hash, eventName, state); Notify?.Invoke(this, notification); notifications.Add(notification); } diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index f8b190ef92..852f1f00da 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -79,7 +79,7 @@ internal protected virtual void Mint(ApplicationEngine engine, UInt160 account, BigInteger totalSupply = new BigInteger(storage.Value); totalSupply += amount; storage.Value = totalSupply.ToByteArrayStandard(); - engine.SendNotification(Hash, new Array(new StackItem[] { "Transfer", StackItem.Null, account.ToArray(), amount })); + engine.SendNotification(Hash, "Transfer", new Array { StackItem.Null, account.ToArray(), amount }); } internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, BigInteger amount) @@ -99,7 +99,7 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, BigInteger totalSupply = new BigInteger(storage.Value); totalSupply -= amount; storage.Value = totalSupply.ToByteArrayStandard(); - engine.SendNotification(Hash, new Array(new StackItem[] { "Transfer", account.ToArray(), StackItem.Null, amount })); + engine.SendNotification(Hash, "Transfer", new Array { account.ToArray(), StackItem.Null, amount }); } [ContractMethod(0_01000000, CallFlags.AllowStates)] @@ -159,7 +159,7 @@ protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 state_to.Balance += amount; } } - engine.SendNotification(Hash, new Array(new StackItem[] { "Transfer", from.ToArray(), to.ToArray(), amount })); + engine.SendNotification(Hash, "Transfer", new Array { from.ToArray(), to.ToArray(), amount }); return true; } diff --git a/src/neo/SmartContract/NotifyEventArgs.cs b/src/neo/SmartContract/NotifyEventArgs.cs index 7b52c2a570..d2a0d94605 100644 --- a/src/neo/SmartContract/NotifyEventArgs.cs +++ b/src/neo/SmartContract/NotifyEventArgs.cs @@ -11,12 +11,14 @@ public class NotifyEventArgs : EventArgs, IInteroperable { public IVerifiable ScriptContainer { get; } public UInt160 ScriptHash { get; } - public StackItem State { get; } + public string EventName { get; } + public Array State { get; } - public NotifyEventArgs(IVerifiable container, UInt160 script_hash, StackItem state) + public NotifyEventArgs(IVerifiable container, UInt160 script_hash, string eventName, Array state) { this.ScriptContainer = container; this.ScriptHash = script_hash; + this.EventName = eventName; this.State = state; } @@ -27,7 +29,7 @@ public void FromStackItem(StackItem stackItem) public StackItem ToStackItem(ReferenceCounter referenceCounter) { - return new Array(referenceCounter, new[] { ScriptHash.ToArray(), State }); + return new Array(referenceCounter) { ScriptHash.ToArray(), EventName, State }; } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index a05a5e9f88..447c5b9f4f 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -6,7 +6,6 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; -using Neo.VM.Types; namespace Neo.UnitTests.SmartContract { @@ -14,7 +13,7 @@ namespace Neo.UnitTests.SmartContract public class UT_ApplicationEngine { private string message = null; - private StackItem item = null; + private string eventName = null; [TestInitialize] public void TestSetup() @@ -28,23 +27,23 @@ public void TestNotify() var snapshot = Blockchain.Singleton.GetSnapshot(); var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); ApplicationEngine.Notify += Test_Notify1; - StackItem notifyItem = "TestItem"; + const string notifyEvent = "TestEvent"; - engine.SendNotification(UInt160.Zero, notifyItem); - item.Should().Be(notifyItem); + engine.SendNotification(UInt160.Zero, notifyEvent, null); + eventName.Should().Be(notifyEvent); ApplicationEngine.Notify += Test_Notify2; - engine.SendNotification(UInt160.Zero, notifyItem); - item.Should().Be(null); + engine.SendNotification(UInt160.Zero, notifyEvent, null); + eventName.Should().Be(null); - item = notifyItem; + eventName = notifyEvent; ApplicationEngine.Notify -= Test_Notify1; - engine.SendNotification(UInt160.Zero, notifyItem); - item.Should().Be(null); + engine.SendNotification(UInt160.Zero, notifyEvent, null); + eventName.Should().Be(null); ApplicationEngine.Notify -= Test_Notify2; - engine.SendNotification(UInt160.Zero, notifyItem); - item.Should().Be(null); + engine.SendNotification(UInt160.Zero, notifyEvent, null); + eventName.Should().Be(null); } private void Test_Log1(object sender, LogEventArgs e) @@ -59,12 +58,12 @@ private void Test_Log2(object sender, LogEventArgs e) private void Test_Notify1(object sender, NotifyEventArgs e) { - item = e.State; + eventName = e.EventName; } private void Test_Notify2(object sender, NotifyEventArgs e) { - item = null; + eventName = null; } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 5b83133e37..a72fb896c3 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -34,12 +34,9 @@ public void Runtime_GetNotifications_Test() using (var script = new ScriptBuilder()) { - // Drop arguments - - script.Emit(OpCode.NIP); - // Notify method + script.Emit(OpCode.SWAP, OpCode.NEWARRAY, OpCode.SWAP); script.EmitSysCall(ApplicationEngine.System_Runtime_Notify); // Add return @@ -81,14 +78,16 @@ public void Runtime_GetNotifications_Test() using (var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true)) using (var script = new ScriptBuilder()) { - // Notification 1 -> 13 + // Notification - script.EmitPush(13); + script.EmitPush(0); + script.Emit(OpCode.NEWARRAY); + script.EmitPush("testEvent1"); script.EmitSysCall(ApplicationEngine.System_Runtime_Notify); // Call script - script.EmitAppCall(scriptHash2, "test", 2, 1); + script.EmitAppCall(scriptHash2, "test", "testEvent2", 1); // Drop return @@ -114,16 +113,16 @@ public void Runtime_GetNotifications_Test() // Check syscall result - AssertNotification(array[1], scriptHash2, 2); - AssertNotification(array[0], currentScriptHash, 13); + AssertNotification(array[1], scriptHash2, "testEvent2"); + AssertNotification(array[0], currentScriptHash, "testEvent1"); // Check notifications Assert.AreEqual(scriptHash2, engine.Notifications[1].ScriptHash); - Assert.AreEqual(2, engine.Notifications[1].State.GetBigInteger()); + Assert.AreEqual("testEvent2", engine.Notifications[1].EventName); Assert.AreEqual(currentScriptHash, engine.Notifications[0].ScriptHash); - Assert.AreEqual(13, engine.Notifications[0].State.GetBigInteger()); + Assert.AreEqual("testEvent1", engine.Notifications[0].EventName); } // Script notifications @@ -131,14 +130,16 @@ public void Runtime_GetNotifications_Test() using (var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true)) using (var script = new ScriptBuilder()) { - // Notification 1 -> 13 + // Notification - script.EmitPush(13); + script.EmitPush(0); + script.Emit(OpCode.NEWARRAY); + script.EmitPush("testEvent1"); script.EmitSysCall(ApplicationEngine.System_Runtime_Notify); // Call script - script.EmitAppCall(scriptHash2, "test", 2, 1); + script.EmitAppCall(scriptHash2, "test", "testEvent2", 1); // Drop return @@ -164,15 +165,15 @@ public void Runtime_GetNotifications_Test() // Check syscall result - AssertNotification(array[0], scriptHash2, 2); + AssertNotification(array[0], scriptHash2, "testEvent2"); // Check notifications Assert.AreEqual(scriptHash2, engine.Notifications[1].ScriptHash); - Assert.AreEqual(2, engine.Notifications[1].State.GetBigInteger()); + Assert.AreEqual("testEvent2", engine.Notifications[1].EventName); Assert.AreEqual(currentScriptHash, engine.Notifications[0].ScriptHash); - Assert.AreEqual(13, engine.Notifications[0].State.GetBigInteger()); + Assert.AreEqual("testEvent1", engine.Notifications[0].EventName); } // Clean storage @@ -185,21 +186,11 @@ private void AssertNotification(StackItem stackItem, UInt160 scriptHash, string Assert.IsInstanceOfType(stackItem, typeof(VM.Types.Array)); var array = (VM.Types.Array)stackItem; - Assert.AreEqual(2, array.Count); + Assert.AreEqual(3, array.Count); CollectionAssert.AreEqual(scriptHash.ToArray(), array[0].GetSpan().ToArray()); Assert.AreEqual(notification, array[1].GetString()); } - private void AssertNotification(StackItem stackItem, UInt160 scriptHash, int notification) - { - Assert.IsInstanceOfType(stackItem, typeof(VM.Types.Array)); - - var array = (VM.Types.Array)stackItem; - Assert.AreEqual(2, array.Count); - CollectionAssert.AreEqual(scriptHash.ToArray(), array[0].GetSpan().ToArray()); - Assert.AreEqual(notification, array[1].GetBigInteger()); - } - [TestMethod] public void TestExecutionEngine_GetScriptContainer() { diff --git a/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs b/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs index d86a9d208e..e83f8fd7fc 100644 --- a/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs +++ b/tests/neo.UnitTests/SmartContract/UT_NotifyEventArgs.cs @@ -13,7 +13,7 @@ public void TestGetScriptContainer() { IVerifiable container = new TestVerifiable(); UInt160 script_hash = new byte[] { 0x00 }.ToScriptHash(); - NotifyEventArgs args = new NotifyEventArgs(container, script_hash, 0); + NotifyEventArgs args = new NotifyEventArgs(container, script_hash, "Test", null); args.ScriptContainer.Should().Be(container); } } From 1146cf68036e5a7b9bda293f5aee7f98a42a0c5f Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 15 Jun 2020 20:18:41 +0800 Subject: [PATCH 291/305] Check return value of contracts (#1680) --- src/neo/Ledger/Blockchain.cs | 2 +- .../ApplicationEngine.Contract.cs | 18 ++++--- src/neo/SmartContract/ApplicationEngine.cs | 15 ++++-- .../SmartContract/ContractParameterType.cs | 25 +++++----- .../SmartContract/ExecutionContextState.cs | 2 + .../SmartContract/Native/NativeContract.cs | 4 +- src/neo/VM/Helper.cs | 15 +++--- src/neo/Wallets/AssetDescriptor.cs | 4 +- src/neo/Wallets/Wallet.cs | 8 +-- src/neo/neo.csproj | 2 +- .../Network/P2P/Payloads/UT_Transaction.cs | 40 +++++++-------- .../Native/Tokens/UT_NeoToken.cs | 2 +- .../SmartContract/UT_InteropService.cs | 20 ++++---- .../SmartContract/UT_Syscalls.cs | 8 +-- tests/neo.UnitTests/VM/UT_Helper.cs | 49 ++++++++++--------- .../Wallets/SQLite/UT_VerificationContract.cs | 2 +- 16 files changed, 119 insertions(+), 97 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index b10a60e870..b937d8c0a4 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -91,7 +91,7 @@ static Blockchain() using (ScriptBuilder sb = new ScriptBuilder()) { foreach (NativeContract contract in contracts) - sb.EmitAppCall(contract.Hash, "onPersist"); + sb.EmitAppCall(contract.Hash, ContractParameterType.Boolean, "onPersist"); onPersistNativeContractScript = sb.ToArray(); } diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index ed2557f356..ab0e1ac8f4 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -100,19 +100,19 @@ internal void DestroyContract() Snapshot.Storages.Delete(key); } - internal void CallContract(UInt160 contractHash, string method, Array args) + internal void CallContract(UInt160 contractHash, ContractParameterType returnType, string method, Array args) { - CallContractInternal(contractHash, method, args, CallFlags.All); + CallContractInternal(contractHash, returnType, method, args, CallFlags.All); } - internal void CallContractEx(UInt160 contractHash, string method, Array args, CallFlags callFlags) + internal void CallContractEx(UInt160 contractHash, ContractParameterType returnType, string method, Array args, CallFlags callFlags) { if ((callFlags & ~CallFlags.All) != 0) throw new ArgumentOutOfRangeException(nameof(callFlags)); - CallContractInternal(contractHash, method, args, callFlags); + CallContractInternal(contractHash, returnType, method, args, callFlags); } - private void CallContractInternal(UInt160 contractHash, string method, Array args, CallFlags flags) + private void CallContractInternal(UInt160 contractHash, ContractParameterType returnType, string method, Array args, CallFlags flags) { if (method.StartsWith('_')) throw new ArgumentException(); @@ -140,11 +140,15 @@ private void CallContractInternal(UInt160 contractHash, string method, Array arg ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); if (md is null) throw new InvalidOperationException(); if (args.Count != md.Parameters.Length) throw new InvalidOperationException(); - int rvcount = md.ReturnType == ContractParameterType.Void ? 0 : 1; - ExecutionContext context_new = LoadScript(contract.Script, rvcount); + if (returnType == ContractParameterType.Void && md.ReturnType != ContractParameterType.Void) + throw new InvalidOperationException(); + if (returnType != ContractParameterType.Any && md.ReturnType != returnType) + throw new InvalidOperationException(); + ExecutionContext context_new = LoadScript(contract.Script); state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; state.CallFlags = flags & callingFlags; + state.RVCount = returnType == ContractParameterType.Void ? 0 : 1; if (NativeContract.IsNative(contractHash)) { diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 711a9c8dc9..cc3ceb36a8 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -55,6 +55,15 @@ internal bool AddGas(long gas) return testMode || GasConsumed <= gas_amount; } + protected override void ContextUnloaded(ExecutionContext context) + { + base.ContextUnloaded(context); + if (context.EvaluationStack == CurrentContext?.EvaluationStack) return; + int rvcount = context.GetState().RVCount; + if (rvcount != -1 && rvcount != context.EvaluationStack.Count) + throw new InvalidOperationException(); + } + protected override void LoadContext(ExecutionContext context) { // Set default execution context state @@ -64,9 +73,9 @@ protected override void LoadContext(ExecutionContext context) base.LoadContext(context); } - public ExecutionContext LoadScript(Script script, CallFlags callFlags, int rvcount = -1) + public ExecutionContext LoadScript(Script script, CallFlags callFlags) { - ExecutionContext context = LoadScript(script, rvcount); + ExecutionContext context = LoadScript(script); context.GetState().CallFlags = callFlags; return context; } @@ -123,7 +132,7 @@ internal object Convert(StackItem item, InteropParameterDescriptor descriptor) { object value = descriptor.Converter(item); if (descriptor.IsEnum) - value = System.Convert.ChangeType(value, descriptor.Type); + value = Enum.ToObject(descriptor.Type, value); else if (descriptor.IsInterface) value = ((InteropInterface)value).GetInterface(); return value; diff --git a/src/neo/SmartContract/ContractParameterType.cs b/src/neo/SmartContract/ContractParameterType.cs index 748ff86654..f7592ba7d1 100644 --- a/src/neo/SmartContract/ContractParameterType.cs +++ b/src/neo/SmartContract/ContractParameterType.cs @@ -2,21 +2,22 @@ namespace Neo.SmartContract { public enum ContractParameterType : byte { - Signature = 0x00, - Boolean = 0x01, - Integer = 0x02, - Hash160 = 0x03, - Hash256 = 0x04, - ByteArray = 0x05, - PublicKey = 0x06, - String = 0x07, + Any = 0x00, - Array = 0x10, - Map = 0x12, + Boolean = 0x10, + Integer = 0x11, + ByteArray = 0x12, + String = 0x13, + Hash160 = 0x14, + Hash256 = 0x15, + PublicKey = 0x16, + Signature = 0x17, - InteropInterface = 0xf0, + Array = 0x20, + Map = 0x22, + + InteropInterface = 0x30, - Any = 0xfe, Void = 0xff } } diff --git a/src/neo/SmartContract/ExecutionContextState.cs b/src/neo/SmartContract/ExecutionContextState.cs index 7c5aca4231..d31b411720 100644 --- a/src/neo/SmartContract/ExecutionContextState.cs +++ b/src/neo/SmartContract/ExecutionContextState.cs @@ -16,5 +16,7 @@ internal class ExecutionContextState /// Execution context rights /// public CallFlags CallFlags { get; set; } = CallFlags.All; + + public int RVCount { get; set; } = -1; } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index a18b26abd7..5deed39568 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -151,7 +151,7 @@ public ApplicationEngine TestCall(string operation, params object[] args) { using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitAppCall(Hash, operation, args); + sb.EmitAppCall(Hash, ContractParameterType.Any, operation, args); return ApplicationEngine.Run(sb.ToArray(), testMode: true); } } @@ -170,7 +170,7 @@ private static ContractParameterType ToParameterType(Type type) if (type == typeof(ulong)) return ContractParameterType.Integer; if (type == typeof(BigInteger)) return ContractParameterType.Integer; if (type == typeof(byte[])) return ContractParameterType.ByteArray; - if (type == typeof(string)) return ContractParameterType.ByteArray; + if (type == typeof(string)) return ContractParameterType.String; if (type == typeof(VM.Types.Boolean)) return ContractParameterType.Boolean; if (type == typeof(Integer)) return ContractParameterType.Integer; if (type == typeof(ByteString)) return ContractParameterType.ByteArray; diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index c99ad135f2..22a36b0345 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -23,35 +23,38 @@ 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 EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, ContractParameterType returnType, string operation) { sb.EmitPush(0); sb.Emit(OpCode.NEWARRAY); sb.EmitPush(operation); + sb.EmitPush(returnType); sb.EmitPush(scriptHash); 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 EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, ContractParameterType returnType, string operation, params ContractParameter[] args) { for (int i = args.Length - 1; i >= 0; i--) sb.EmitPush(args[i]); sb.EmitPush(args.Length); sb.Emit(OpCode.PACK); sb.EmitPush(operation); + sb.EmitPush(returnType); sb.EmitPush(scriptHash); 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 EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, ContractParameterType returnType, string operation, params object[] args) { for (int i = args.Length - 1; i >= 0; i--) sb.EmitPush(args[i]); sb.EmitPush(args.Length); sb.Emit(OpCode.PACK); sb.EmitPush(operation); + sb.EmitPush(returnType); sb.EmitPush(scriptHash); sb.EmitSysCall(ApplicationEngine.System_Contract_Call); return sb; @@ -208,14 +211,14 @@ public static string GetString(this StackItem item) /// contract operation /// operation arguments /// - public static byte[] MakeScript(this UInt160 scriptHash, string operation, params object[] args) + public static byte[] MakeScript(this UInt160 scriptHash, ContractParameterType returnType, string operation, params object[] args) { using (ScriptBuilder sb = new ScriptBuilder()) { if (args.Length > 0) - sb.EmitAppCall(scriptHash, operation, args); + sb.EmitAppCall(scriptHash, returnType, operation, args); else - sb.EmitAppCall(scriptHash, operation); + sb.EmitAppCall(scriptHash, returnType, operation); return sb.ToArray(); } } diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index 46a80a0f5c..a12cd280bd 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -15,8 +15,8 @@ public AssetDescriptor(UInt160 asset_id) byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitAppCall(asset_id, "decimals"); - sb.EmitAppCall(asset_id, "name"); + sb.EmitAppCall(asset_id, ContractParameterType.Integer, "decimals"); + sb.EmitAppCall(asset_id, ContractParameterType.String, "name"); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, extraGAS: 3_000_000); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index def6977959..1a2cdefa0e 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -137,10 +137,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.EmitAppCall(asset_id, ContractParameterType.Integer, "balanceOf", account); sb.Emit(OpCode.ADD); } - sb.EmitAppCall(asset_id, "decimals"); + sb.EmitAppCall(asset_id, ContractParameterType.Integer, "decimals"); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, extraGAS: 20000000L * accounts.Length); @@ -246,7 +246,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.EmitAppCall(assetId, ContractParameterType.Integer, "balanceOf", account); using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, testMode: true)) { if (engine.State.HasFlag(VMState.FAULT)) @@ -265,7 +265,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null cosignerList.UnionWith(balances_used.Select(p => p.Account)); foreach (var (account, value) in balances_used) { - sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); + sb.EmitAppCall(output.AssetId, ContractParameterType.Boolean, "transfer", account, output.ScriptHash, value); sb.Emit(OpCode.ASSERT); } } diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index ae25d748cb..978c6af877 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 7e025ad4cd..169aba50d2 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -158,8 +158,8 @@ public void FeeIsMultiSigContract() var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); Assert.AreEqual(2000810, verificationGas); - Assert.AreEqual(367000, sizeGas); - Assert.AreEqual(2367810, tx.NetworkFee); + Assert.AreEqual(368000, sizeGas); + Assert.AreEqual(2368810, tx.NetworkFee); } } @@ -200,7 +200,7 @@ public void FeeIsSignatureContractDetailed() Assert.IsNull(tx.Witnesses); // check pre-computed network fee (already guessing signature sizes) - tx.NetworkFee.Should().Be(1264390); + tx.NetworkFee.Should().Be(1265390); // ---- // Sign @@ -242,7 +242,7 @@ public void FeeIsSignatureContractDetailed() // ------------------ // check tx_size cost // ------------------ - Assert.AreEqual(264, tx.Size); + Assert.AreEqual(265, tx.Size); // will verify tx size, step by step @@ -256,18 +256,18 @@ public void FeeIsSignatureContractDetailed() // Note that Data size and Usage size are different (because of first byte on GetVarSize()) Assert.AreEqual(22, tx.Cosigners.Values.First().Size); // Part III - Assert.AreEqual(86, tx.Script.GetVarSize()); + Assert.AreEqual(87, tx.Script.GetVarSize()); // Part IV Assert.AreEqual(110, tx.Witnesses.GetVarSize()); // I + II + III + IV - Assert.AreEqual(45 + 23 + 86 + 110, tx.Size); + Assert.AreEqual(45 + 23 + 87 + 110, tx.Size); Assert.AreEqual(1000, NativeContract.Policy.GetFeePerByte(snapshot)); var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); - Assert.AreEqual(264000, sizeGas); + Assert.AreEqual(265000, sizeGas); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1264390, verificationGas + sizeGas); + Assert.AreEqual(1265390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -302,7 +302,7 @@ public void FeeIsSignatureContract_TestScope_Global() { // 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.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -353,7 +353,7 @@ public void FeeIsSignatureContract_TestScope_Global() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1264390, verificationGas + sizeGas); + Assert.AreEqual(1265390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -388,7 +388,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_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); + sb.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -440,7 +440,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1285390, verificationGas + sizeGas); + Assert.AreEqual(1286390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -475,7 +475,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); + sb.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -530,7 +530,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1285390, verificationGas + sizeGas); + Assert.AreEqual(1286390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -563,7 +563,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.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -615,7 +615,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_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); + sb.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -672,7 +672,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1305390, verificationGas + sizeGas); + Assert.AreEqual(1306390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -705,7 +705,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() { // 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.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -953,7 +953,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() { // 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.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -1003,7 +1003,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1264390, verificationGas + sizeGas); + Assert.AreEqual(1265390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 12b025400b..591cca7abe 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -448,7 +448,7 @@ public void TestVote() })); } - sb.EmitAppCall(NativeContract.NEO.Hash, "transfer", from, UInt160.Zero, amount); + sb.EmitAppCall(NativeContract.NEO.Hash, ContractParameterType.Boolean, "transfer", from, UInt160.Zero, amount); 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 a72fb896c3..38037c5223 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -87,7 +87,7 @@ public void Runtime_GetNotifications_Test() // Call script - script.EmitAppCall(scriptHash2, "test", "testEvent2", 1); + script.EmitAppCall(scriptHash2, ContractParameterType.Any, "test", "testEvent2", 1); // Drop return @@ -139,7 +139,7 @@ public void Runtime_GetNotifications_Test() // Call script - script.EmitAppCall(scriptHash2, "test", "testEvent2", 1); + script.EmitAppCall(scriptHash2, ContractParameterType.Any, "test", "testEvent2", 1); // Drop return @@ -221,7 +221,7 @@ public void TestExecutionEngine_GetCallingScriptHash() engine.Snapshot.Contracts.Add(contract.ScriptHash, contract); using ScriptBuilder scriptB = new ScriptBuilder(); - scriptB.EmitAppCall(contract.ScriptHash, "test", 0, 1); + scriptB.EmitAppCall(contract.ScriptHash, ContractParameterType.Any, "test", 0, 1); engine.LoadScript(scriptB.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -600,17 +600,17 @@ public void TestContract_Call() var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); - engine.CallContract(state.ScriptHash, method, args); + engine.CallContract(state.ScriptHash, ContractParameterType.Any, method, 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"); - Assert.ThrowsException(() => engine.CallContract(state.ScriptHash, method, args)); + Assert.ThrowsException(() => engine.CallContract(state.ScriptHash, ContractParameterType.Any, method, args)); state.Manifest.Permissions[0].Methods = WildcardContainer.CreateWildcard(); - engine.CallContract(state.ScriptHash, method, args); + engine.CallContract(state.ScriptHash, ContractParameterType.Any, method, args); - Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, method, args)); + Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, ContractParameterType.Any, method, args)); } [TestMethod] @@ -630,15 +630,15 @@ public void TestContract_CallEx() var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); - engine.CallContractEx(state.ScriptHash, method, args, CallFlags.All); + engine.CallContractEx(state.ScriptHash, ContractParameterType.Any, 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)); + Assert.ThrowsException(() => engine.CallContractEx(UInt160.Zero, ContractParameterType.Any, method, args, CallFlags.All)); // Call with rights - engine.CallContractEx(state.ScriptHash, method, args, flags); + engine.CallContractEx(state.ScriptHash, ContractParameterType.Any, method, args, flags); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 96b2ac0345..3c583d73ef 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -351,10 +351,10 @@ public void System_Runtime_GetInvocationCounter() using (var script = new ScriptBuilder()) { - script.EmitAppCall(contractA.ScriptHash, "dummyMain", 0, 1); - script.EmitAppCall(contractB.ScriptHash, "dummyMain", 0, 1); - script.EmitAppCall(contractB.ScriptHash, "dummyMain", 0, 1); - script.EmitAppCall(contractC.ScriptHash, "dummyMain", 0, 1); + script.EmitAppCall(contractA.ScriptHash, ContractParameterType.Any, "dummyMain", 0, 1); + script.EmitAppCall(contractB.ScriptHash, ContractParameterType.Any, "dummyMain", 0, 1); + script.EmitAppCall(contractB.ScriptHash, ContractParameterType.Any, "dummyMain", 0, 1); + script.EmitAppCall(contractC.ScriptHash, ContractParameterType.Any, "dummyMain", 0, 1); // Execute diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index 4c89fe09a5..e63fb9dd44 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -52,19 +52,20 @@ 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"); - byte[] tempArray = new byte[36]; + sb.EmitAppCall(UInt160.Zero, ContractParameterType.Any, "AAAAA"); + byte[] tempArray = new byte[37]; tempArray[0] = (byte)OpCode.PUSH0; tempArray[1] = (byte)OpCode.NEWARRAY; 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 + tempArray[9] = (byte)OpCode.PUSH0; + 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_Call; - tempArray[31] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 32, 4);//api.data + tempArray[32] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data CollectionAssert.AreEqual(tempArray, sb.ToArray()); } @@ -73,20 +74,21 @@ 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) }); - byte[] tempArray = new byte[37]; + sb.EmitAppCall(UInt160.Zero, ContractParameterType.Any, "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.PACK; tempArray[3] = (byte)OpCode.PUSHDATA1; tempArray[4] = 0x05;//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 + tempArray[10] = (byte)OpCode.PUSH0; + 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_Call; - tempArray[32] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data + tempArray[33] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 34, 4);//api.data CollectionAssert.AreEqual(tempArray, sb.ToArray()); } @@ -95,29 +97,30 @@ 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); - byte[] tempArray = new byte[37]; + sb.EmitAppCall(UInt160.Zero, ContractParameterType.Any, "AAAAA", true); + byte[] tempArray = new byte[38]; tempArray[0] = (byte)OpCode.PUSH1;//arg tempArray[1] = (byte)OpCode.PUSH1;//args.Length tempArray[2] = (byte)OpCode.PACK; tempArray[3] = (byte)OpCode.PUSHDATA1; tempArray[4] = 0x05;//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 + tempArray[10] = (byte)OpCode.PUSH0; + 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_Call; - tempArray[32] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data + tempArray[33] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 34, 4);//api.data CollectionAssert.AreEqual(tempArray, sb.ToArray()); } [TestMethod] public void TestMakeScript() { - byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); + byte[] testScript = NativeContract.GAS.Hash.MakeScript(ContractParameterType.Integer, "balanceOf", UInt160.Zero); - Assert.AreEqual("0c14000000000000000000000000000000000000000011c00c0962616c616e63654f660c14bcaf41d684c7d4ad6ee0d99da9707b9d1f0c8e6641627d5b52", + Assert.AreEqual("0c14000000000000000000000000000000000000000011c00c0962616c616e63654f6600110c14bcaf41d684c7d4ad6ee0d99da9707b9d1f0c8e6641627d5b52", testScript.ToHexString()); } diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs index 19eaa434dc..75d2ef7a5d 100644 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs +++ b/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs @@ -122,7 +122,7 @@ public void TestSerialize() byte[] script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey); byte[] result = new byte[64]; result[20] = 0x01; - result[21] = 0x00; + result[21] = (byte)ContractParameterType.Signature; result[22] = 0x29; Array.Copy(script, 0, result, 23, 41); CollectionAssert.AreEqual(result, byteArray); From 4a7e36e645ce6a170ac0778618b7b6871dba3df6 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 17 Jun 2020 07:58:38 +0200 Subject: [PATCH 292/305] Check Json.Serialize map keys (#1705) --- src/neo/SmartContract/JsonSerializer.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/neo/SmartContract/JsonSerializer.cs b/src/neo/SmartContract/JsonSerializer.cs index 375709a13d..5ed1369c8c 100644 --- a/src/neo/SmartContract/JsonSerializer.cs +++ b/src/neo/SmartContract/JsonSerializer.cs @@ -48,6 +48,8 @@ public static JObject Serialize(StackItem item) foreach (var entry in map) { + if (!(entry.Key is ByteString)) throw new FormatException(); + var key = entry.Key.GetString(); var value = Serialize(entry.Value); @@ -106,6 +108,7 @@ public static byte[] SerializeToByteArray(StackItem item, uint maxSize) stack.Push(JsonTokenType.EndObject); foreach (var pair in map.Reverse()) { + if (!(pair.Key is ByteString)) throw new FormatException(); stack.Push(pair.Value); stack.Push(pair.Key); stack.Push(JsonTokenType.PropertyName); @@ -115,7 +118,7 @@ public static byte[] SerializeToByteArray(StackItem item, uint maxSize) writer.WriteEndObject(); break; case JsonTokenType.PropertyName: - writer.WritePropertyName(((PrimitiveType)stack.Pop()).GetSpan()); + writer.WritePropertyName(((ByteString)stack.Pop()).GetSpan()); break; case Null _: writer.WriteNullValue(); From 33e93b05da75a1ec3bef72a340c6946176797a78 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 18 Jun 2020 15:29:29 +0800 Subject: [PATCH 293/305] Always return a value for public methods (#1706) --- src/neo/Ledger/Blockchain.cs | 5 +- .../ApplicationEngine.Contract.cs | 15 ++---- src/neo/SmartContract/ApplicationEngine.cs | 7 ++- .../SmartContract/ExecutionContextState.cs | 2 - .../SmartContract/Native/NativeContract.cs | 2 +- src/neo/VM/Helper.cs | 15 +++--- src/neo/Wallets/AssetDescriptor.cs | 4 +- src/neo/Wallets/Wallet.cs | 8 +-- .../Network/P2P/Payloads/UT_Transaction.cs | 40 +++++++-------- .../Native/Tokens/UT_NeoToken.cs | 2 +- .../SmartContract/UT_InteropService.cs | 20 ++++---- .../SmartContract/UT_Syscalls.cs | 8 +-- tests/neo.UnitTests/VM/UT_Helper.cs | 49 +++++++++---------- 13 files changed, 83 insertions(+), 94 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index b937d8c0a4..50cee9509d 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -91,7 +91,10 @@ static Blockchain() using (ScriptBuilder sb = new ScriptBuilder()) { foreach (NativeContract contract in contracts) - sb.EmitAppCall(contract.Hash, ContractParameterType.Boolean, "onPersist"); + { + sb.EmitAppCall(contract.Hash, "onPersist"); + sb.Emit(OpCode.DROP); + } onPersistNativeContractScript = sb.ToArray(); } diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index ab0e1ac8f4..3696ce2c13 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -100,19 +100,19 @@ internal void DestroyContract() Snapshot.Storages.Delete(key); } - internal void CallContract(UInt160 contractHash, ContractParameterType returnType, string method, Array args) + internal void CallContract(UInt160 contractHash, string method, Array args) { - CallContractInternal(contractHash, returnType, method, args, CallFlags.All); + CallContractInternal(contractHash, method, args, CallFlags.All); } - internal void CallContractEx(UInt160 contractHash, ContractParameterType returnType, string method, Array args, CallFlags callFlags) + internal void CallContractEx(UInt160 contractHash, string method, Array args, CallFlags callFlags) { if ((callFlags & ~CallFlags.All) != 0) throw new ArgumentOutOfRangeException(nameof(callFlags)); - CallContractInternal(contractHash, returnType, method, args, callFlags); + CallContractInternal(contractHash, method, args, callFlags); } - private void CallContractInternal(UInt160 contractHash, ContractParameterType returnType, string method, Array args, CallFlags flags) + private void CallContractInternal(UInt160 contractHash, string method, Array args, CallFlags flags) { if (method.StartsWith('_')) throw new ArgumentException(); @@ -140,15 +140,10 @@ private void CallContractInternal(UInt160 contractHash, ContractParameterType re ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod(method); if (md is null) throw new InvalidOperationException(); if (args.Count != md.Parameters.Length) throw new InvalidOperationException(); - if (returnType == ContractParameterType.Void && md.ReturnType != ContractParameterType.Void) - throw new InvalidOperationException(); - if (returnType != ContractParameterType.Any && md.ReturnType != returnType) - throw new InvalidOperationException(); ExecutionContext context_new = LoadScript(contract.Script); state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; state.CallFlags = flags & callingFlags; - state.RVCount = returnType == ContractParameterType.Void ? 0 : 1; if (NativeContract.IsNative(contractHash)) { diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index cc3ceb36a8..0270a90df8 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -58,10 +58,9 @@ internal bool AddGas(long gas) protected override void ContextUnloaded(ExecutionContext context) { base.ContextUnloaded(context); - if (context.EvaluationStack == CurrentContext?.EvaluationStack) return; - int rvcount = context.GetState().RVCount; - if (rvcount != -1 && rvcount != context.EvaluationStack.Count) - throw new InvalidOperationException(); + if (CurrentContext != null && context.EvaluationStack != CurrentContext.EvaluationStack) + if (context.EvaluationStack.Count == 0) + Push(StackItem.Null); } protected override void LoadContext(ExecutionContext context) diff --git a/src/neo/SmartContract/ExecutionContextState.cs b/src/neo/SmartContract/ExecutionContextState.cs index d31b411720..7c5aca4231 100644 --- a/src/neo/SmartContract/ExecutionContextState.cs +++ b/src/neo/SmartContract/ExecutionContextState.cs @@ -16,7 +16,5 @@ internal class ExecutionContextState /// Execution context rights /// public CallFlags CallFlags { get; set; } = CallFlags.All; - - public int RVCount { get; set; } = -1; } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 5deed39568..729af8ec51 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -151,7 +151,7 @@ public ApplicationEngine TestCall(string operation, params object[] args) { using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitAppCall(Hash, ContractParameterType.Any, operation, args); + sb.EmitAppCall(Hash, operation, args); return ApplicationEngine.Run(sb.ToArray(), testMode: true); } } diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 22a36b0345..c99ad135f2 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -23,38 +23,35 @@ public static ScriptBuilder Emit(this ScriptBuilder sb, params OpCode[] ops) return sb; } - public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, ContractParameterType returnType, string operation) + public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, string operation) { sb.EmitPush(0); sb.Emit(OpCode.NEWARRAY); sb.EmitPush(operation); - sb.EmitPush(returnType); sb.EmitPush(scriptHash); sb.EmitSysCall(ApplicationEngine.System_Contract_Call); return sb; } - public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, ContractParameterType returnType, string operation, params ContractParameter[] args) + public static ScriptBuilder EmitAppCall(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.Emit(OpCode.PACK); sb.EmitPush(operation); - sb.EmitPush(returnType); sb.EmitPush(scriptHash); sb.EmitSysCall(ApplicationEngine.System_Contract_Call); return sb; } - public static ScriptBuilder EmitAppCall(this ScriptBuilder sb, UInt160 scriptHash, ContractParameterType returnType, string operation, params object[] args) + public static ScriptBuilder EmitAppCall(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.Emit(OpCode.PACK); sb.EmitPush(operation); - sb.EmitPush(returnType); sb.EmitPush(scriptHash); sb.EmitSysCall(ApplicationEngine.System_Contract_Call); return sb; @@ -211,14 +208,14 @@ public static string GetString(this StackItem item) /// contract operation /// operation arguments /// - public static byte[] MakeScript(this UInt160 scriptHash, ContractParameterType returnType, string operation, params object[] args) + public static byte[] MakeScript(this UInt160 scriptHash, string operation, params object[] args) { using (ScriptBuilder sb = new ScriptBuilder()) { if (args.Length > 0) - sb.EmitAppCall(scriptHash, returnType, operation, args); + sb.EmitAppCall(scriptHash, operation, args); else - sb.EmitAppCall(scriptHash, returnType, operation); + sb.EmitAppCall(scriptHash, operation); return sb.ToArray(); } } diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index a12cd280bd..46a80a0f5c 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -15,8 +15,8 @@ public AssetDescriptor(UInt160 asset_id) byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitAppCall(asset_id, ContractParameterType.Integer, "decimals"); - sb.EmitAppCall(asset_id, ContractParameterType.String, "name"); + sb.EmitAppCall(asset_id, "decimals"); + sb.EmitAppCall(asset_id, "name"); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, extraGAS: 3_000_000); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 1a2cdefa0e..def6977959 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -137,10 +137,10 @@ public BigDecimal GetBalance(UInt160 asset_id, params UInt160[] accounts) sb.EmitPush(0); foreach (UInt160 account in accounts) { - sb.EmitAppCall(asset_id, ContractParameterType.Integer, "balanceOf", account); + sb.EmitAppCall(asset_id, "balanceOf", account); sb.Emit(OpCode.ADD); } - sb.EmitAppCall(asset_id, ContractParameterType.Integer, "decimals"); + sb.EmitAppCall(asset_id, "decimals"); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, extraGAS: 20000000L * accounts.Length); @@ -246,7 +246,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null foreach (UInt160 account in accounts) using (ScriptBuilder sb2 = new ScriptBuilder()) { - sb2.EmitAppCall(assetId, ContractParameterType.Integer, "balanceOf", account); + sb2.EmitAppCall(assetId, "balanceOf", account); using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, testMode: true)) { if (engine.State.HasFlag(VMState.FAULT)) @@ -265,7 +265,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null cosignerList.UnionWith(balances_used.Select(p => p.Account)); foreach (var (account, value) in balances_used) { - sb.EmitAppCall(output.AssetId, ContractParameterType.Boolean, "transfer", account, output.ScriptHash, value); + sb.EmitAppCall(output.AssetId, "transfer", account, output.ScriptHash, value); sb.Emit(OpCode.ASSERT); } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 169aba50d2..7e025ad4cd 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -158,8 +158,8 @@ public void FeeIsMultiSigContract() var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); Assert.AreEqual(2000810, verificationGas); - Assert.AreEqual(368000, sizeGas); - Assert.AreEqual(2368810, tx.NetworkFee); + Assert.AreEqual(367000, sizeGas); + Assert.AreEqual(2367810, tx.NetworkFee); } } @@ -200,7 +200,7 @@ public void FeeIsSignatureContractDetailed() Assert.IsNull(tx.Witnesses); // check pre-computed network fee (already guessing signature sizes) - tx.NetworkFee.Should().Be(1265390); + tx.NetworkFee.Should().Be(1264390); // ---- // Sign @@ -242,7 +242,7 @@ public void FeeIsSignatureContractDetailed() // ------------------ // check tx_size cost // ------------------ - Assert.AreEqual(265, tx.Size); + Assert.AreEqual(264, tx.Size); // will verify tx size, step by step @@ -256,18 +256,18 @@ public void FeeIsSignatureContractDetailed() // Note that Data size and Usage size are different (because of first byte on GetVarSize()) Assert.AreEqual(22, tx.Cosigners.Values.First().Size); // Part III - Assert.AreEqual(87, tx.Script.GetVarSize()); + Assert.AreEqual(86, tx.Script.GetVarSize()); // Part IV Assert.AreEqual(110, tx.Witnesses.GetVarSize()); // I + II + III + IV - Assert.AreEqual(45 + 23 + 87 + 110, tx.Size); + Assert.AreEqual(45 + 23 + 86 + 110, tx.Size); Assert.AreEqual(1000, NativeContract.Policy.GetFeePerByte(snapshot)); var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); - Assert.AreEqual(265000, sizeGas); + Assert.AreEqual(264000, sizeGas); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1265390, verificationGas + sizeGas); + Assert.AreEqual(1264390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -302,7 +302,7 @@ public void FeeIsSignatureContract_TestScope_Global() { // self-transfer of 1e-8 GAS System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); + sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -353,7 +353,7 @@ public void FeeIsSignatureContract_TestScope_Global() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1265390, verificationGas + sizeGas); + Assert.AreEqual(1264390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -388,7 +388,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() { // self-transfer of 1e-8 GAS System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); + sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -440,7 +440,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1286390, verificationGas + sizeGas); + Assert.AreEqual(1285390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -475,7 +475,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, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); + sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -530,7 +530,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1286390, verificationGas + sizeGas); + Assert.AreEqual(1285390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -563,7 +563,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, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); + sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -615,7 +615,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() { // self-transfer of 1e-8 GAS System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); + sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -672,7 +672,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1306390, verificationGas + sizeGas); + Assert.AreEqual(1305390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -705,7 +705,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() { // self-transfer of 1e-8 GAS System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); + sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -953,7 +953,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() { // self-transfer of 1e-8 GAS System.Numerics.BigInteger value = (new BigDecimal(1, 8)).Value; - sb.EmitAppCall(NativeContract.GAS.Hash, ContractParameterType.Boolean, "transfer", acc.ScriptHash, acc.ScriptHash, value); + sb.EmitAppCall(NativeContract.GAS.Hash, "transfer", acc.ScriptHash, acc.ScriptHash, value); sb.Emit(OpCode.ASSERT); script = sb.ToArray(); } @@ -1003,7 +1003,7 @@ public void FeeIsSignatureContract_TestScope_Global_Default() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1265390, verificationGas + sizeGas); + Assert.AreEqual(1264390, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 591cca7abe..12b025400b 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -448,7 +448,7 @@ public void TestVote() })); } - sb.EmitAppCall(NativeContract.NEO.Hash, ContractParameterType.Boolean, "transfer", from, UInt160.Zero, amount); + sb.EmitAppCall(NativeContract.NEO.Hash, "transfer", from, UInt160.Zero, amount); 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 38037c5223..a72fb896c3 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -87,7 +87,7 @@ public void Runtime_GetNotifications_Test() // Call script - script.EmitAppCall(scriptHash2, ContractParameterType.Any, "test", "testEvent2", 1); + script.EmitAppCall(scriptHash2, "test", "testEvent2", 1); // Drop return @@ -139,7 +139,7 @@ public void Runtime_GetNotifications_Test() // Call script - script.EmitAppCall(scriptHash2, ContractParameterType.Any, "test", "testEvent2", 1); + script.EmitAppCall(scriptHash2, "test", "testEvent2", 1); // Drop return @@ -221,7 +221,7 @@ public void TestExecutionEngine_GetCallingScriptHash() engine.Snapshot.Contracts.Add(contract.ScriptHash, contract); using ScriptBuilder scriptB = new ScriptBuilder(); - scriptB.EmitAppCall(contract.ScriptHash, ContractParameterType.Any, "test", 0, 1); + scriptB.EmitAppCall(contract.ScriptHash, "test", 0, 1); engine.LoadScript(scriptB.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); @@ -600,17 +600,17 @@ public void TestContract_Call() var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); - engine.CallContract(state.ScriptHash, ContractParameterType.Any, method, args); + engine.CallContract(state.ScriptHash, method, 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"); - Assert.ThrowsException(() => engine.CallContract(state.ScriptHash, ContractParameterType.Any, method, args)); + Assert.ThrowsException(() => engine.CallContract(state.ScriptHash, method, args)); state.Manifest.Permissions[0].Methods = WildcardContainer.CreateWildcard(); - engine.CallContract(state.ScriptHash, ContractParameterType.Any, method, args); + engine.CallContract(state.ScriptHash, method, args); - Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, ContractParameterType.Any, method, args)); + Assert.ThrowsException(() => engine.CallContract(UInt160.Zero, method, args)); } [TestMethod] @@ -630,15 +630,15 @@ public void TestContract_CallEx() var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); engine.LoadScript(new byte[] { 0x01 }); - engine.CallContractEx(state.ScriptHash, ContractParameterType.Any, method, args, CallFlags.All); + engine.CallContractEx(state.ScriptHash, 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, ContractParameterType.Any, method, args, CallFlags.All)); + Assert.ThrowsException(() => engine.CallContractEx(UInt160.Zero, method, args, CallFlags.All)); // Call with rights - engine.CallContractEx(state.ScriptHash, ContractParameterType.Any, method, args, flags); + engine.CallContractEx(state.ScriptHash, method, args, flags); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[0]); engine.CurrentContext.EvaluationStack.Pop().Should().Be(args[1]); } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 3c583d73ef..96b2ac0345 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -351,10 +351,10 @@ public void System_Runtime_GetInvocationCounter() using (var script = new ScriptBuilder()) { - script.EmitAppCall(contractA.ScriptHash, ContractParameterType.Any, "dummyMain", 0, 1); - script.EmitAppCall(contractB.ScriptHash, ContractParameterType.Any, "dummyMain", 0, 1); - script.EmitAppCall(contractB.ScriptHash, ContractParameterType.Any, "dummyMain", 0, 1); - script.EmitAppCall(contractC.ScriptHash, ContractParameterType.Any, "dummyMain", 0, 1); + script.EmitAppCall(contractA.ScriptHash, "dummyMain", 0, 1); + script.EmitAppCall(contractB.ScriptHash, "dummyMain", 0, 1); + script.EmitAppCall(contractB.ScriptHash, "dummyMain", 0, 1); + script.EmitAppCall(contractC.ScriptHash, "dummyMain", 0, 1); // Execute diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index e63fb9dd44..4c89fe09a5 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -52,20 +52,19 @@ 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, ContractParameterType.Any, "AAAAA"); - byte[] tempArray = new byte[37]; + sb.EmitAppCall(UInt160.Zero, "AAAAA"); + byte[] tempArray = new byte[36]; tempArray[0] = (byte)OpCode.PUSH0; tempArray[1] = (byte)OpCode.NEWARRAY; 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.PUSH0; - tempArray[10] = (byte)OpCode.PUSHDATA1; - tempArray[11] = 0x14;//scriptHash.Length - Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 12, 20);//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 CollectionAssert.AreEqual(tempArray, sb.ToArray()); } @@ -74,21 +73,20 @@ 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, ContractParameterType.Any, "AAAAA", new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) }); - byte[] tempArray = new byte[38]; + sb.EmitAppCall(UInt160.Zero, "AAAAA", new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) }); + byte[] tempArray = new byte[37]; tempArray[0] = (byte)OpCode.PUSH0; tempArray[1] = (byte)OpCode.PUSH1; tempArray[2] = (byte)OpCode.PACK; tempArray[3] = (byte)OpCode.PUSHDATA1; tempArray[4] = 0x05;//operation.Length Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 5, 5);//operation.data - tempArray[10] = (byte)OpCode.PUSH0; - tempArray[11] = (byte)OpCode.PUSHDATA1; - tempArray[12] = 0x14;//scriptHash.Length - Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 13, 20);//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_Call; - tempArray[33] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 34, 4);//api.data + tempArray[32] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data CollectionAssert.AreEqual(tempArray, sb.ToArray()); } @@ -97,30 +95,29 @@ 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, ContractParameterType.Any, "AAAAA", true); - byte[] tempArray = new byte[38]; + sb.EmitAppCall(UInt160.Zero, "AAAAA", true); + byte[] tempArray = new byte[37]; tempArray[0] = (byte)OpCode.PUSH1;//arg tempArray[1] = (byte)OpCode.PUSH1;//args.Length tempArray[2] = (byte)OpCode.PACK; tempArray[3] = (byte)OpCode.PUSHDATA1; tempArray[4] = 0x05;//operation.Length Array.Copy(Encoding.UTF8.GetBytes("AAAAA"), 0, tempArray, 5, 5);//operation.data - tempArray[10] = (byte)OpCode.PUSH0; - tempArray[11] = (byte)OpCode.PUSHDATA1; - tempArray[12] = 0x14;//scriptHash.Length - Array.Copy(UInt160.Zero.ToArray(), 0, tempArray, 13, 20);//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_Call; - tempArray[33] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 34, 4);//api.data + tempArray[32] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(api), 0, tempArray, 33, 4);//api.data CollectionAssert.AreEqual(tempArray, sb.ToArray()); } [TestMethod] public void TestMakeScript() { - byte[] testScript = NativeContract.GAS.Hash.MakeScript(ContractParameterType.Integer, "balanceOf", UInt160.Zero); + byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - Assert.AreEqual("0c14000000000000000000000000000000000000000011c00c0962616c616e63654f6600110c14bcaf41d684c7d4ad6ee0d99da9707b9d1f0c8e6641627d5b52", + Assert.AreEqual("0c14000000000000000000000000000000000000000011c00c0962616c616e63654f660c14bcaf41d684c7d4ad6ee0d99da9707b9d1f0c8e6641627d5b52", testScript.ToHexString()); } From d49b446fbc97f4b04938abc7855e34e55efa5716 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 18 Jun 2020 20:34:32 +0800 Subject: [PATCH 294/305] Update to Neo.VM.3.0.0-CI00226 (#1713) --- .../ApplicationEngine.Contract.cs | 6 +-- .../SmartContract/ApplicationEngine.Crypto.cs | 2 +- .../SmartContract/ApplicationEngine.Native.cs | 3 +- .../ApplicationEngine.Storage.cs | 2 +- src/neo/SmartContract/ApplicationEngine.cs | 40 +++++-------------- src/neo/SmartContract/ContainerPlaceholder.cs | 2 +- src/neo/SmartContract/Helper.cs | 3 +- .../SmartContract/Native/NativeContract.cs | 17 ++++---- src/neo/neo.csproj | 2 +- .../Native/Tokens/UT_GasToken.cs | 2 +- .../Native/Tokens/UT_NeoToken.cs | 4 +- .../SmartContract/Native/UT_NativeContract.cs | 5 ++- .../SmartContract/UT_ApplicationEngine.cs | 14 ------- .../SmartContract/UT_Syscalls.cs | 14 +++---- 14 files changed, 41 insertions(+), 75 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 3696ce2c13..aa460bbf6e 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -32,8 +32,7 @@ internal ContractState CreateContract(byte[] script, byte[] manifest) if (script.Length == 0 || script.Length > MaxContractLength || manifest.Length == 0 || manifest.Length > ContractManifest.MaxLength) throw new ArgumentException(); - if (!AddGas(StoragePrice * (script.Length + manifest.Length))) - throw new InvalidOperationException(); + AddGas(StoragePrice * (script.Length + manifest.Length)); UInt160 hash = script.ToScriptHash(); ContractState contract = Snapshot.Contracts.TryGet(hash); @@ -53,8 +52,7 @@ internal ContractState CreateContract(byte[] script, byte[] manifest) internal void UpdateContract(byte[] script, byte[] manifest) { - if (!AddGas(StoragePrice * (script?.Length ?? 0 + manifest?.Length ?? 0))) - throw new InvalidOperationException(); + AddGas(StoragePrice * (script?.Length ?? 0 + manifest?.Length ?? 0)); var contract = Snapshot.Contracts.TryGet(CurrentScriptHash); if (contract is null) throw new InvalidOperationException(); diff --git a/src/neo/SmartContract/ApplicationEngine.Crypto.cs b/src/neo/SmartContract/ApplicationEngine.Crypto.cs index 70533d900c..7ce7933884 100644 --- a/src/neo/SmartContract/ApplicationEngine.Crypto.cs +++ b/src/neo/SmartContract/ApplicationEngine.Crypto.cs @@ -89,7 +89,7 @@ private bool CheckMultiSigWithECDsa(StackItem item0, byte[][] pubkeys, byte[][] _ => item0.GetSpan() }; if (n == 0 || m == 0 || m > n) throw new ArgumentException(); - if (!AddGas(ECDsaVerifyPrice * n)) throw new InvalidOperationException(); + AddGas(ECDsaVerifyPrice * n); try { for (int i = 0, j = 0; i < m && j < n;) diff --git a/src/neo/SmartContract/ApplicationEngine.Native.cs b/src/neo/SmartContract/ApplicationEngine.Native.cs index e413df7b17..074b9b8dd2 100644 --- a/src/neo/SmartContract/ApplicationEngine.Native.cs +++ b/src/neo/SmartContract/ApplicationEngine.Native.cs @@ -27,8 +27,7 @@ internal void DeployNativeContracts() internal void CallNativeContract(string name) { - if (!NativeContract.GetContract(name).Invoke(this)) - throw new InvalidOperationException(); + NativeContract.GetContract(name).Invoke(this); } } } diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index b579c5f37f..7d1934aba7 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -105,7 +105,7 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto else newDataSize = value.Length - item.Value.Length; } - if (!AddGas(newDataSize * StoragePrice)) throw new InvalidOperationException(); + AddGas(newDataSize * StoragePrice); item.Value = value; item.IsConstant = flags.HasFlag(StorageFlags.Constant); diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 0270a90df8..8b37ba10d2 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Numerics; using System.Reflection; -using System.Text; using Array = System.Array; using VMArray = Neo.VM.Types.Array; @@ -49,10 +48,11 @@ public ApplicationEngine(TriggerType trigger, IVerifiable container, StoreView s this.Snapshot = snapshot; } - internal bool AddGas(long gas) + internal void AddGas(long gas) { GasConsumed = checked(GasConsumed + gas); - return testMode || GasConsumed <= gas_amount; + if (!testMode && GasConsumed > gas_amount) + throw new InvalidOperationException("Insufficient GAS."); } protected override void ContextUnloaded(ExecutionContext context) @@ -146,17 +146,15 @@ public override void Dispose() base.Dispose(); } - protected override bool OnSysCall(uint method) + protected override void OnSysCall(uint method) { - if (!services.TryGetValue(method, out InteropDescriptor descriptor)) - return false; + InteropDescriptor descriptor = services[method]; if (!descriptor.AllowedTriggers.HasFlag(Trigger)) - return false; + throw new InvalidOperationException($"Cannot call this SYSCALL with the trigger {Trigger}."); ExecutionContextState state = CurrentContext.GetState(); if (!state.CallFlags.HasFlag(descriptor.RequiredCallFlags)) - return false; - if (!AddGas(descriptor.FixedPrice)) - return false; + throw new InvalidOperationException($"Cannot call this SYSCALL with the flag {state.CallFlags}."); + AddGas(descriptor.FixedPrice); List parameters = descriptor.Parameters.Length > 0 ? new List() : null; @@ -165,14 +163,12 @@ protected override bool OnSysCall(uint method) object returnValue = descriptor.Handler.Invoke(this, parameters?.ToArray()); if (descriptor.Handler.ReturnType != typeof(void)) Push(Convert(returnValue)); - return true; } - protected override bool PreExecuteInstruction() + protected override void PreExecuteInstruction() { - if (CurrentContext.InstructionPointer >= CurrentContext.Script.Length) - return true; - return AddGas(OpCodePrices[CurrentContext.CurrentInstruction.OpCode]); + if (CurrentContext.InstructionPointer < CurrentContext.Script.Length) + AddGas(OpCodePrices[CurrentContext.CurrentInstruction.OpCode]); } private static Block CreateDummyBlock(StoreView snapshot) @@ -223,19 +219,5 @@ public static ApplicationEngine Run(byte[] script, IVerifiable container = null, return Run(script, snapshot, container, persistingBlock, offset, testMode, extraGAS); } } - - public bool TryPop(out string s) - { - if (TryPop(out ReadOnlySpan b)) - { - s = Encoding.UTF8.GetString(b); - return true; - } - else - { - s = default; - return false; - } - } } } diff --git a/src/neo/SmartContract/ContainerPlaceholder.cs b/src/neo/SmartContract/ContainerPlaceholder.cs index 72eb68e48d..757d77ccbc 100644 --- a/src/neo/SmartContract/ContainerPlaceholder.cs +++ b/src/neo/SmartContract/ContainerPlaceholder.cs @@ -14,7 +14,7 @@ public ContainerPlaceholder(StackItemType type, int count) ElementCount = count; } - public override bool Equals(object obj) => throw new NotSupportedException(); + public override bool Equals(StackItem other) => throw new NotSupportedException(); public override int GetHashCode() => throw new NotSupportedException(); diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index b8fe4450a3..d9299e30f3 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -5,7 +5,6 @@ using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.VM; -using Neo.VM.Types; using System; using System.Buffers.Binary; using System.Collections.Generic; @@ -165,7 +164,7 @@ internal static bool VerifyWitnesses(this IVerifiable verifiable, StoreView snap engine.LoadScript(verification, CallFlags.ReadOnly).InstructionPointer = offset; engine.LoadScript(verifiable.Witnesses[i].InvocationScript, CallFlags.None); if (engine.Execute() == VMState.FAULT) return false; - if (!engine.ResultStack.TryPop(out StackItem result) || !result.ToBoolean()) return false; + if (engine.ResultStack.Count != 1 || !engine.ResultStack.Pop().ToBoolean()) return false; gas -= engine.GasConsumed; } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 729af8ec51..21c9b429a1 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -108,15 +108,17 @@ public static NativeContract GetContract(string name) return contract; } - internal bool Invoke(ApplicationEngine engine) + internal void Invoke(ApplicationEngine engine) { - if (!engine.CurrentScriptHash.Equals(Hash)) return false; - if (!engine.TryPop(out string operation)) return false; - if (!engine.TryPop(out Array args)) return false; - if (!methods.TryGetValue(operation, out ContractMethodMetadata method)) return false; + if (!engine.CurrentScriptHash.Equals(Hash)) + throw new InvalidOperationException("It is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used."); + string operation = engine.PopString(); + Array args = engine.Pop(); + ContractMethodMetadata method = methods[operation]; ExecutionContextState state = engine.CurrentContext.GetState(); - if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) return false; - if (!engine.AddGas(method.Price)) return false; + if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) + throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}."); + engine.AddGas(method.Price); List parameters = new List(); if (method.NeedApplicationEngine) parameters.Add(engine); if (method.NeedSnapshot) parameters.Add(engine.Snapshot); @@ -128,7 +130,6 @@ internal bool Invoke(ApplicationEngine engine) object returnValue = method.Handler.Invoke(this, parameters.ToArray()); if (method.Handler.ReturnType != typeof(void)) engine.Push(engine.Convert(returnValue)); - return true; } public static bool IsNative(UInt160 hash) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 978c6af877..edc2301bf1 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs index f7be52f9a6..e2910ae1e5 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_GasToken.cs @@ -133,7 +133,7 @@ public void Check_BadScript() script.Emit(OpCode.NOP); engine.LoadScript(script.ToArray()); - NativeContract.GAS.Invoke(engine).Should().BeFalse(); + Assert.ThrowsException(() => NativeContract.GAS.Invoke(engine)); } } } diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs index 12b025400b..14b6536ccc 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_NeoToken.cs @@ -215,7 +215,7 @@ public void Check_BadScript() script.Emit(OpCode.NOP); engine.LoadScript(script.ToArray()); - NativeContract.NEO.Invoke(engine).Should().BeFalse(); + Assert.ThrowsException(() => NativeContract.NEO.Invoke(engine)); } [TestMethod] @@ -283,7 +283,7 @@ public void TestGetRegisteredValidators1() { using (ApplicationEngine engine = NativeContract.NEO.TestCall("getCandidates")) { - engine.ResultStack.TryPop(out VM.Types.Array array).Should().BeTrue(); + var array = engine.ResultStack.Pop(); array.Count.Should().Be(21); ((VM.Types.Struct)array[0])[0].GetSpan().ToHexString().Should().Be("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639"); ((VM.Types.Struct)array[0])[1].GetBigInteger().Should().Be(new BigInteger(1785714)); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index aa58ba157c..90970a506b 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -5,6 +5,7 @@ using Neo.SmartContract.Native; using Neo.VM.Types; using System; +using System.Collections.Generic; using VMArray = Neo.VM.Types.Array; namespace Neo.UnitTests.SmartContract.Native @@ -38,13 +39,13 @@ public void TestInvoke() VMArray args1 = new VMArray(); engine.CurrentContext.EvaluationStack.Push(args1); engine.CurrentContext.EvaluationStack.Push(method1); - testNativeContract.Invoke(engine).Should().BeFalse(); + Assert.ThrowsException(() => testNativeContract.Invoke(engine)); ByteString method2 = new ByteString(System.Text.Encoding.Default.GetBytes("onPersist")); VMArray args2 = new VMArray(); engine.CurrentContext.EvaluationStack.Push(args2); engine.CurrentContext.EvaluationStack.Push(method2); - testNativeContract.Invoke(engine).Should().BeTrue(); + testNativeContract.Invoke(engine); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index 447c5b9f4f..187ba61024 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -3,8 +3,6 @@ using Neo.IO; using Neo.IO.Caching; using Neo.Ledger; -using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract; namespace Neo.UnitTests.SmartContract @@ -78,18 +76,6 @@ public void TestCreateDummyBlock() } } - public class TestApplicationEngine : ApplicationEngine - { - public TestApplicationEngine(TriggerType trigger, IVerifiable container, StoreView snapshot, long gas, bool testMode = false) : base(trigger, container, snapshot, gas, testMode) - { - } - - public bool GetOnSysCall(uint method) - { - return OnSysCall(method); - } - } - public class TestMetaDataCache : MetaDataCache where T : class, ICloneable, ISerializable, new() { public TestMetaDataCache() diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 96b2ac0345..3768e6b8f7 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -126,8 +126,8 @@ public void Json_Deserialize() Assert.AreEqual(engine.Execute(), VMState.HALT); Assert.AreEqual(2, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.TryPop(out _)); - Assert.IsTrue(engine.ResultStack.TryPop(out var i) && i.GetBigInteger() == 123); + engine.ResultStack.Pop(); + Assert.IsTrue(engine.ResultStack.Pop().GetBigInteger() == 123); } } @@ -194,11 +194,11 @@ public void Json_Serialize() Assert.AreEqual(engine.Execute(), VMState.HALT); Assert.AreEqual(5, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.TryPop(out var m) && m.GetString() == "{\"key\":\"dmFsdWU=\"}"); - Assert.IsTrue(engine.ResultStack.TryPop(out var n) && n.GetString() == "null"); - Assert.IsTrue(engine.ResultStack.TryPop(out var s) && s.GetString() == "\"dGVzdA==\""); - Assert.IsTrue(engine.ResultStack.TryPop(out var b) && b.GetString() == "true"); - Assert.IsTrue(engine.ResultStack.TryPop(out var i) && i.GetString() == "5"); + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "{\"key\":\"dmFsdWU=\"}"); + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "null"); + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "\"dGVzdA==\""); + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "true"); + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "5"); } } From 678523c2afdc3e5c0b230303da07d24869bcdf9e Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 19 Jun 2020 09:34:59 +0200 Subject: [PATCH 295/305] Create callback (#1629) * Merge * Invoke callback * dotnet-format * Change UT for two args * callbacks * Syscall callback UT * rename * Rename * Clean * Clean again :P * Rename * Fix CurrentContext scope * Rename * abstract class * Erik's feedback * change ut * fix ut * Move to new namespace * Move callback logic to ApplicationEngine * Add TODO * We don't need to check the CallFlags * Remove RaiseLoadContext() and RaiseOnSysCall() * Fix UT * Add MethodCallback * Update neo.csproj * Fix compile * Don't need to check the return value any more. * Add AllowCallback to InteropDescriptor * Modify the prices * Fix Co-authored-by: erikzhang --- .../SmartContract/ApplicationEngine.Binary.cs | 4 +- .../ApplicationEngine.Blockchain.cs | 12 +-- .../ApplicationEngine.Callback.cs | 39 ++++++++ .../ApplicationEngine.Contract.cs | 16 ++-- .../SmartContract/ApplicationEngine.Crypto.cs | 12 +-- .../ApplicationEngine.Enumerator.cs | 8 +- .../ApplicationEngine.Iterator.cs | 10 +- .../SmartContract/ApplicationEngine.Json.cs | 4 +- .../SmartContract/ApplicationEngine.Native.cs | 4 +- .../ApplicationEngine.Runtime.cs | 26 +++--- .../ApplicationEngine.Storage.cs | 16 ++-- src/neo/SmartContract/ApplicationEngine.cs | 13 ++- .../SmartContract/Callbacks/CallbackBase.cs | 11 +++ .../SmartContract/Callbacks/MethodCallback.cs | 35 +++++++ .../Callbacks/PointerCallback.cs | 27 ++++++ .../Callbacks/SyscallCallback.cs | 24 +++++ src/neo/SmartContract/InteropDescriptor.cs | 4 +- .../InteropParameterDescriptor.cs | 1 + .../SmartContract/UT_Syscalls.Callback.cs | 93 +++++++++++++++++++ .../SmartContract/UT_Syscalls.cs | 2 +- 20 files changed, 299 insertions(+), 62 deletions(-) create mode 100644 src/neo/SmartContract/ApplicationEngine.Callback.cs create mode 100644 src/neo/SmartContract/Callbacks/CallbackBase.cs create mode 100644 src/neo/SmartContract/Callbacks/MethodCallback.cs create mode 100644 src/neo/SmartContract/Callbacks/PointerCallback.cs create mode 100644 src/neo/SmartContract/Callbacks/SyscallCallback.cs create 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 d5f86feefb..17a41f1072 100644 --- a/src/neo/SmartContract/ApplicationEngine.Binary.cs +++ b/src/neo/SmartContract/ApplicationEngine.Binary.cs @@ -4,8 +4,8 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Binary_Serialize = Register("System.Binary.Serialize", nameof(BinarySerialize), 0_00100000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Binary_Deserialize = Register("System.Binary.Deserialize", nameof(BinaryDeserialize), 0_00500000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Binary_Serialize = Register("System.Binary.Serialize", nameof(BinarySerialize), 0_00100000, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Binary_Deserialize = Register("System.Binary.Deserialize", nameof(BinaryDeserialize), 0_00500000, TriggerType.All, CallFlags.None, true); internal byte[] BinarySerialize(StackItem item) { diff --git a/src/neo/SmartContract/ApplicationEngine.Blockchain.cs b/src/neo/SmartContract/ApplicationEngine.Blockchain.cs index 025fae972a..a4d5cd8bd3 100644 --- a/src/neo/SmartContract/ApplicationEngine.Blockchain.cs +++ b/src/neo/SmartContract/ApplicationEngine.Blockchain.cs @@ -10,12 +10,12 @@ partial class ApplicationEngine { public const uint MaxTraceableBlocks = Transaction.MaxValidUntilBlockIncrement; - public static readonly InteropDescriptor System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", nameof(GetBlockchainHeight), 0_00000400, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", nameof(GetBlock), 0_02500000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", nameof(GetTransaction), 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", nameof(GetTransactionHeight), 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Blockchain_GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", nameof(GetTransactionFromBlock), 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Blockchain_GetContract = Register("System.Blockchain.GetContract", nameof(GetContract), 0_01000000, TriggerType.Application, CallFlags.AllowStates); + public static readonly InteropDescriptor System_Blockchain_GetHeight = Register("System.Blockchain.GetHeight", nameof(GetBlockchainHeight), 0_00000400, TriggerType.Application, CallFlags.AllowStates, true); + public static readonly InteropDescriptor System_Blockchain_GetBlock = Register("System.Blockchain.GetBlock", nameof(GetBlock), 0_02500000, TriggerType.Application, CallFlags.AllowStates, true); + public static readonly InteropDescriptor System_Blockchain_GetTransaction = Register("System.Blockchain.GetTransaction", nameof(GetTransaction), 0_01000000, TriggerType.Application, CallFlags.AllowStates, true); + public static readonly InteropDescriptor System_Blockchain_GetTransactionHeight = Register("System.Blockchain.GetTransactionHeight", nameof(GetTransactionHeight), 0_01000000, TriggerType.Application, CallFlags.AllowStates, true); + public static readonly InteropDescriptor System_Blockchain_GetTransactionFromBlock = Register("System.Blockchain.GetTransactionFromBlock", nameof(GetTransactionFromBlock), 0_01000000, TriggerType.Application, CallFlags.AllowStates, true); + public static readonly InteropDescriptor System_Blockchain_GetContract = Register("System.Blockchain.GetContract", nameof(GetContract), 0_01000000, TriggerType.Application, CallFlags.AllowStates, true); internal uint GetBlockchainHeight() { diff --git a/src/neo/SmartContract/ApplicationEngine.Callback.cs b/src/neo/SmartContract/ApplicationEngine.Callback.cs new file mode 100644 index 0000000000..633a6c6fe2 --- /dev/null +++ b/src/neo/SmartContract/ApplicationEngine.Callback.cs @@ -0,0 +1,39 @@ +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), 0_00000400, TriggerType.All, CallFlags.None, false); + public static readonly InteropDescriptor System_Callback_CreateFromMethod = Register("System.Callback.CreateFromMethod", nameof(CreateCallbackFromMethod), 0_01000000, TriggerType.All, CallFlags.None, false); + public static readonly InteropDescriptor System_Callback_CreateFromSyscall = Register("System.Callback.CreateFromSyscall", nameof(CreateCallbackFromSyscall), 0_00000400, TriggerType.All, CallFlags.None, false); + public static readonly InteropDescriptor System_Callback_Invoke = Register("System.Callback.Invoke", nameof(InvokeCallback), 0_01000000, TriggerType.All, CallFlags.None, false); + + 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); + } + + internal PointerCallback CreateCallback(Pointer pointer, int parcount) + { + return new PointerCallback(CurrentContext, pointer, parcount); + } + + internal MethodCallback CreateCallbackFromMethod(UInt160 hash, string method) + { + return new MethodCallback(this, hash, method); + } + + 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 aa460bbf6e..7ba4f0a33e 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -14,18 +14,18 @@ partial class ApplicationEngine { public const int MaxContractLength = 1024 * 1024; - public static readonly InteropDescriptor System_Contract_Create = Register("System.Contract.Create", nameof(CreateContract), 0, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor System_Contract_Update = Register("System.Contract.Update", nameof(UpdateContract), 0, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor System_Contract_Destroy = Register("System.Contract.Destroy", nameof(DestroyContract), 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor System_Contract_Call = Register("System.Contract.Call", nameof(CallContract), 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); - public static readonly InteropDescriptor System_Contract_CallEx = Register("System.Contract.CallEx", nameof(CallContractEx), 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall); - public static readonly InteropDescriptor System_Contract_IsStandard = Register("System.Contract.IsStandard", nameof(IsStandardContract), 0_00030000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Contract_GetCallFlags = Register("System.Contract.GetCallFlags", nameof(GetCallFlags), 0_00030000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Contract_Create = Register("System.Contract.Create", nameof(CreateContract), 0, TriggerType.Application, CallFlags.AllowModifyStates, false); + public static readonly InteropDescriptor System_Contract_Update = Register("System.Contract.Update", nameof(UpdateContract), 0, TriggerType.Application, CallFlags.AllowModifyStates, false); + public static readonly InteropDescriptor System_Contract_Destroy = Register("System.Contract.Destroy", nameof(DestroyContract), 0_01000000, TriggerType.Application, CallFlags.AllowModifyStates, false); + public static readonly InteropDescriptor System_Contract_Call = Register("System.Contract.Call", nameof(CallContract), 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall, false); + public static readonly InteropDescriptor System_Contract_CallEx = Register("System.Contract.CallEx", nameof(CallContractEx), 0_01000000, TriggerType.System | TriggerType.Application, CallFlags.AllowCall, false); + public static readonly InteropDescriptor System_Contract_IsStandard = Register("System.Contract.IsStandard", nameof(IsStandardContract), 0_00030000, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Contract_GetCallFlags = Register("System.Contract.GetCallFlags", nameof(GetCallFlags), 0_00030000, TriggerType.All, CallFlags.None, false); /// /// 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), 0_00010000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Contract_CreateStandardAccount = Register("System.Contract.CreateStandardAccount", nameof(CreateStandardAccount), 0_00010000, TriggerType.All, CallFlags.None, true); internal ContractState CreateContract(byte[] script, byte[] manifest) { diff --git a/src/neo/SmartContract/ApplicationEngine.Crypto.cs b/src/neo/SmartContract/ApplicationEngine.Crypto.cs index 7ce7933884..13a8edab9e 100644 --- a/src/neo/SmartContract/ApplicationEngine.Crypto.cs +++ b/src/neo/SmartContract/ApplicationEngine.Crypto.cs @@ -12,12 +12,12 @@ partial class ApplicationEngine { public const long ECDsaVerifyPrice = 0_01000000; - public static readonly InteropDescriptor Neo_Crypto_RIPEMD160 = Register("Neo.Crypto.RIPEMD160", nameof(RIPEMD160), 0_01000000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Neo_Crypto_SHA256 = Register("Neo.Crypto.SHA256", nameof(Sha256), 0_01000000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.VerifyWithECDsaSecp256r1", nameof(VerifyWithECDsaSecp256r1), ECDsaVerifyPrice, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.VerifyWithECDsaSecp256k1", nameof(VerifyWithECDsaSecp256k1), ECDsaVerifyPrice, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256r1", nameof(CheckMultisigWithECDsaSecp256r1), 0, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256k1", nameof(CheckMultisigWithECDsaSecp256k1), 0, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_RIPEMD160 = Register("Neo.Crypto.RIPEMD160", nameof(RIPEMD160), 0_01000000, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor Neo_Crypto_SHA256 = Register("Neo.Crypto.SHA256", nameof(Sha256), 0_01000000, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.VerifyWithECDsaSecp256r1", nameof(VerifyWithECDsaSecp256r1), ECDsaVerifyPrice, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.VerifyWithECDsaSecp256k1", nameof(VerifyWithECDsaSecp256k1), ECDsaVerifyPrice, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256r1", nameof(CheckMultisigWithECDsaSecp256r1), 0, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256k1", nameof(CheckMultisigWithECDsaSecp256k1), 0, TriggerType.All, CallFlags.None, true); internal byte[] RIPEMD160(StackItem item) { diff --git a/src/neo/SmartContract/ApplicationEngine.Enumerator.cs b/src/neo/SmartContract/ApplicationEngine.Enumerator.cs index 7fdbbd8bf8..5bfc2ae48b 100644 --- a/src/neo/SmartContract/ApplicationEngine.Enumerator.cs +++ b/src/neo/SmartContract/ApplicationEngine.Enumerator.cs @@ -8,10 +8,10 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Enumerator_Create = Register("System.Enumerator.Create", nameof(CreateEnumerator), 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Enumerator_Next = Register("System.Enumerator.Next", nameof(EnumeratorNext), 0_01000000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Enumerator_Value = Register("System.Enumerator.Value", nameof(EnumeratorValue), 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Enumerator_Concat = Register("System.Enumerator.Concat", nameof(ConcatEnumerators), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Enumerator_Create = Register("System.Enumerator.Create", nameof(CreateEnumerator), 0_00000400, TriggerType.All, CallFlags.None, false); + public static readonly InteropDescriptor System_Enumerator_Next = Register("System.Enumerator.Next", nameof(EnumeratorNext), 0_01000000, TriggerType.All, CallFlags.None, false); + public static readonly InteropDescriptor System_Enumerator_Value = Register("System.Enumerator.Value", nameof(EnumeratorValue), 0_00000400, TriggerType.All, CallFlags.None, false); + public static readonly InteropDescriptor System_Enumerator_Concat = Register("System.Enumerator.Concat", nameof(ConcatEnumerators), 0_00000400, TriggerType.All, CallFlags.None, false); internal IEnumerator CreateEnumerator(StackItem item) { diff --git a/src/neo/SmartContract/ApplicationEngine.Iterator.cs b/src/neo/SmartContract/ApplicationEngine.Iterator.cs index be3bde386b..26ea78d695 100644 --- a/src/neo/SmartContract/ApplicationEngine.Iterator.cs +++ b/src/neo/SmartContract/ApplicationEngine.Iterator.cs @@ -8,11 +8,11 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Iterator_Create = Register("System.Iterator.Create", nameof(CreateIterator), 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Iterator_Key = Register("System.Iterator.Key", nameof(IteratorKey), 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Iterator_Keys = Register("System.Iterator.Keys", nameof(IteratorKeys), 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Iterator_Values = Register("System.Iterator.Values", nameof(IteratorValues), 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Iterator_Concat = Register("System.Iterator.Concat", nameof(ConcatIterators), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Iterator_Create = Register("System.Iterator.Create", nameof(CreateIterator), 0_00000400, TriggerType.All, CallFlags.None, false); + public static readonly InteropDescriptor System_Iterator_Key = Register("System.Iterator.Key", nameof(IteratorKey), 0_00000400, TriggerType.All, CallFlags.None, false); + public static readonly InteropDescriptor System_Iterator_Keys = Register("System.Iterator.Keys", nameof(IteratorKeys), 0_00000400, TriggerType.All, CallFlags.None, false); + public static readonly InteropDescriptor System_Iterator_Values = Register("System.Iterator.Values", nameof(IteratorValues), 0_00000400, TriggerType.All, CallFlags.None, false); + public static readonly InteropDescriptor System_Iterator_Concat = Register("System.Iterator.Concat", nameof(ConcatIterators), 0_00000400, TriggerType.All, CallFlags.None, false); internal IIterator CreateIterator(StackItem item) { diff --git a/src/neo/SmartContract/ApplicationEngine.Json.cs b/src/neo/SmartContract/ApplicationEngine.Json.cs index d8f972c343..047ff2920f 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), 0_00100000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Json_Deserialize = Register("System.Json.Deserialize", nameof(JsonDeserialize), 0_00500000, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Json_Serialize = Register("System.Json.Serialize", nameof(JsonSerialize), 0_00100000, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Json_Deserialize = Register("System.Json.Deserialize", nameof(JsonDeserialize), 0_00500000, TriggerType.All, CallFlags.None, true); internal byte[] JsonSerialize(StackItem item) { diff --git a/src/neo/SmartContract/ApplicationEngine.Native.cs b/src/neo/SmartContract/ApplicationEngine.Native.cs index 074b9b8dd2..7ea7736745 100644 --- a/src/neo/SmartContract/ApplicationEngine.Native.cs +++ b/src/neo/SmartContract/ApplicationEngine.Native.cs @@ -6,8 +6,8 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor Neo_Native_Deploy = Register("Neo.Native.Deploy", nameof(DeployNativeContracts), 0, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor Neo_Native_Call = Register("Neo.Native.Call", nameof(CallNativeContract), 0, TriggerType.System | TriggerType.Application, CallFlags.None); + public static readonly InteropDescriptor Neo_Native_Deploy = Register("Neo.Native.Deploy", nameof(DeployNativeContracts), 0, TriggerType.Application, CallFlags.AllowModifyStates, false); + public static readonly InteropDescriptor Neo_Native_Call = Register("Neo.Native.Call", nameof(CallNativeContract), 0, TriggerType.System | TriggerType.Application, CallFlags.None, false); internal void DeployNativeContracts() { diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index 92bab2ad36..fd71966113 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), 0_00000250, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", nameof(Trigger), 0_00000250, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Runtime_GetTime = Register("System.Runtime.GetTime", nameof(GetTime), 0_00000250, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Runtime_GetScriptContainer = Register("System.Runtime.GetScriptContainer", nameof(GetScriptContainer), 0_00000250, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Runtime_GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", nameof(CurrentScriptHash), 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Runtime_GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", nameof(CallingScriptHash), 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Runtime_GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", nameof(EntryScriptHash), 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", nameof(CheckWitness), 0_00030000, TriggerType.All, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", nameof(GetInvocationCounter), 0_00000400, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Runtime_Log = Register("System.Runtime.Log", nameof(RuntimeLog), 0_01000000, TriggerType.All, CallFlags.AllowNotify); - public static readonly InteropDescriptor System_Runtime_Notify = Register("System.Runtime.Notify", nameof(RuntimeNotify), 0_01000000, TriggerType.All, CallFlags.AllowNotify); - public static readonly InteropDescriptor System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", nameof(GetNotifications), 0_00010000, TriggerType.All, CallFlags.None); - public static readonly InteropDescriptor System_Runtime_GasLeft = Register("System.Runtime.GasLeft", nameof(GasLeft), 0_00000400, TriggerType.All, CallFlags.None); + public static readonly InteropDescriptor System_Runtime_Platform = Register("System.Runtime.Platform", nameof(GetPlatform), 0_00000250, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", nameof(Trigger), 0_00000250, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Runtime_GetTime = Register("System.Runtime.GetTime", nameof(GetTime), 0_00000250, TriggerType.Application, CallFlags.AllowStates, true); + public static readonly InteropDescriptor System_Runtime_GetScriptContainer = Register("System.Runtime.GetScriptContainer", nameof(GetScriptContainer), 0_00000250, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Runtime_GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", nameof(CurrentScriptHash), 0_00000400, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Runtime_GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", nameof(CallingScriptHash), 0_00000400, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Runtime_GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", nameof(EntryScriptHash), 0_00000400, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", nameof(CheckWitness), 0_00030000, TriggerType.All, CallFlags.AllowStates, true); + public static readonly InteropDescriptor System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", nameof(GetInvocationCounter), 0_00000400, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Runtime_Log = Register("System.Runtime.Log", nameof(RuntimeLog), 0_01000000, TriggerType.All, CallFlags.AllowNotify, false); + public static readonly InteropDescriptor System_Runtime_Notify = Register("System.Runtime.Notify", nameof(RuntimeNotify), 0_01000000, TriggerType.All, CallFlags.AllowNotify, false); + public static readonly InteropDescriptor System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", nameof(GetNotifications), 0_00010000, TriggerType.All, CallFlags.None, true); + public static readonly InteropDescriptor System_Runtime_GasLeft = Register("System.Runtime.GasLeft", nameof(GasLeft), 0_00000400, TriggerType.All, CallFlags.None, true); private static bool CheckItemForNotification(StackItem state) { diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index 7d1934aba7..73aace9c37 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -11,14 +11,14 @@ 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), 0_00000400, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", nameof(GetReadOnlyContext), 0_00000400, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Storage_AsReadOnly = Register("System.Storage.AsReadOnly", nameof(AsReadOnly), 0_00000400, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Storage_Get = Register("System.Storage.Get", nameof(Get), 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Storage_Find = Register("System.Storage.Find", nameof(Find), 0_01000000, TriggerType.Application, CallFlags.AllowStates); - public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 0, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor System_Storage_PutEx = Register("System.Storage.PutEx", nameof(PutEx), 0, TriggerType.Application, CallFlags.AllowModifyStates); - public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 1 * StoragePrice, TriggerType.Application, CallFlags.AllowModifyStates); + public static readonly InteropDescriptor System_Storage_GetContext = Register("System.Storage.GetContext", nameof(GetStorageContext), 0_00000400, TriggerType.Application, CallFlags.AllowStates, false); + public static readonly InteropDescriptor System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", nameof(GetReadOnlyContext), 0_00000400, TriggerType.Application, CallFlags.AllowStates, false); + public static readonly InteropDescriptor System_Storage_AsReadOnly = Register("System.Storage.AsReadOnly", nameof(AsReadOnly), 0_00000400, TriggerType.Application, CallFlags.AllowStates, false); + public static readonly InteropDescriptor System_Storage_Get = Register("System.Storage.Get", nameof(Get), 0_01000000, TriggerType.Application, CallFlags.AllowStates, false); + public static readonly InteropDescriptor System_Storage_Find = Register("System.Storage.Find", nameof(Find), 0_01000000, TriggerType.Application, CallFlags.AllowStates, false); + public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 0, TriggerType.Application, CallFlags.AllowModifyStates, false); + public static readonly InteropDescriptor System_Storage_PutEx = Register("System.Storage.PutEx", nameof(PutEx), 0, TriggerType.Application, CallFlags.AllowModifyStates, false); + public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 1 * StoragePrice, TriggerType.Application, CallFlags.AllowModifyStates, false); internal StorageContext GetStorageContext() { diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 8b37ba10d2..c1dedd9178 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -28,7 +28,7 @@ public partial class ApplicationEngine : ExecutionEngine private readonly List disposables = new List(); private readonly Dictionary invocationCounter = new Dictionary(); - public static IEnumerable Services => services.Values; + public static IReadOnlyDictionary Services => services; public TriggerType Trigger { get; } public IVerifiable ScriptContainer { get; } public StoreView Snapshot { get; } @@ -68,10 +68,15 @@ protected override void LoadContext(ExecutionContext context) // Set default execution context state context.GetState().ScriptHash ??= ((byte[])context.Script).ToScriptHash(); - base.LoadContext(context); } + internal void LoadContext(ExecutionContext context, int initialPosition) + { + context.InstructionPointer = initialPosition; + LoadContext(context); + } + public ExecutionContext LoadScript(Script script, CallFlags callFlags) { ExecutionContext context = LoadScript(script); @@ -192,11 +197,11 @@ private static Block CreateDummyBlock(StoreView snapshot) }; } - private static InteropDescriptor Register(string name, string handler, long fixedPrice, TriggerType allowedTriggers, CallFlags requiredCallFlags) + private static InteropDescriptor Register(string name, string handler, long fixedPrice, TriggerType allowedTriggers, CallFlags requiredCallFlags, bool allowCallback) { 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, allowedTriggers, requiredCallFlags); + InteropDescriptor descriptor = new InteropDescriptor(name, method, fixedPrice, allowedTriggers, requiredCallFlags, allowCallback); 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 new file mode 100644 index 0000000000..ccbd0db07b --- /dev/null +++ b/src/neo/SmartContract/Callbacks/CallbackBase.cs @@ -0,0 +1,11 @@ +using Neo.VM.Types; + +namespace Neo.SmartContract.Callbacks +{ + internal 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 new file mode 100644 index 0000000000..37c5809196 --- /dev/null +++ b/src/neo/SmartContract/Callbacks/MethodCallback.cs @@ -0,0 +1,35 @@ +using Neo.IO; +using Neo.Ledger; +using Neo.SmartContract.Manifest; +using System; +using System.Linq; +using Array = Neo.VM.Types.Array; + +namespace Neo.SmartContract.Callbacks +{ + internal 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_Call) + { + if (method.StartsWith('_')) throw new ArgumentException(); + ContractManifest currentManifest = engine.Snapshot.Contracts.TryGet(engine.CurrentScriptHash)?.Manifest; + if (currentManifest != null && !currentManifest.CanCall(contract.Manifest, method)) + throw new InvalidOperationException(); + this.contract = engine.Snapshot.Contracts[hash]; + this.method = contract.Manifest.Abi.Methods.First(p => p.Name == method); + } + + public override void LoadContext(ApplicationEngine engine, Array args) + { + engine.Push(args); + engine.Push(method.Name); + engine.Push(contract.ScriptHash.ToArray()); + } + } +} diff --git a/src/neo/SmartContract/Callbacks/PointerCallback.cs b/src/neo/SmartContract/Callbacks/PointerCallback.cs new file mode 100644 index 0000000000..a74c645a01 --- /dev/null +++ b/src/neo/SmartContract/Callbacks/PointerCallback.cs @@ -0,0 +1,27 @@ +using Neo.VM; +using Neo.VM.Types; + +namespace Neo.SmartContract.Callbacks +{ + internal 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); + 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 new file mode 100644 index 0000000000..8ffe0abb32 --- /dev/null +++ b/src/neo/SmartContract/Callbacks/SyscallCallback.cs @@ -0,0 +1,24 @@ +using System; +using Array = Neo.VM.Types.Array; + +namespace Neo.SmartContract.Callbacks +{ + internal class SyscallCallback : CallbackBase + { + public InteropDescriptor Method { get; } + public override int ParametersCount => Method.Parameters.Length; + + public SyscallCallback(uint method) + { + this.Method = ApplicationEngine.Services[method]; + if (!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 86e4c6ba87..b41d2ec57a 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -15,8 +15,9 @@ public class InteropDescriptor public long FixedPrice { get; } public TriggerType AllowedTriggers { get; } public CallFlags RequiredCallFlags { get; } + public bool AllowCallback { get; } - internal InteropDescriptor(string name, MethodInfo handler, long fixedPrice, TriggerType allowedTriggers, CallFlags requiredCallFlags) + internal InteropDescriptor(string name, MethodInfo handler, long fixedPrice, TriggerType allowedTriggers, CallFlags requiredCallFlags, bool allowCallback) { this.Name = name; this.Hash = BitConverter.ToUInt32(Encoding.ASCII.GetBytes(name).Sha256(), 0); @@ -25,6 +26,7 @@ internal InteropDescriptor(string name, MethodInfo handler, long fixedPrice, Tri this.FixedPrice = fixedPrice; this.AllowedTriggers = allowedTriggers; this.RequiredCallFlags = requiredCallFlags; + this.AllowCallback = allowCallback; } public static implicit operator uint(InteropDescriptor descriptor) diff --git a/src/neo/SmartContract/InteropParameterDescriptor.cs b/src/neo/SmartContract/InteropParameterDescriptor.cs index 124f1e27e4..f97d7e6c4e 100644 --- a/src/neo/SmartContract/InteropParameterDescriptor.cs +++ b/src/neo/SmartContract/InteropParameterDescriptor.cs @@ -20,6 +20,7 @@ internal class InteropParameterDescriptor private static readonly Dictionary> converters = new Dictionary> { [typeof(StackItem)] = p => p, + [typeof(VM.Types.Pointer)] = p => p, [typeof(VM.Types.Array)] = p => p, [typeof(InteropInterface)] = p => p, [typeof(sbyte)] = p => (sbyte)p.GetBigInteger(), diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.Callback.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.Callback.cs new file mode 100644 index 0000000000..633df8e7d7 --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.Callback.cs @@ -0,0 +1,93 @@ +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 = new ApplicationEngine(TriggerType.Application, null, null, 100_000_000, false); + 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 = new ApplicationEngine(TriggerType.Application, null, null, 100_000_000, false); + 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.GetBigInteger()); + } + + [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 = new ApplicationEngine(TriggerType.Application, null, null, 100_000_000, false); + 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()); + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 3768e6b8f7..8990620fb5 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -11,7 +11,7 @@ namespace Neo.UnitTests.SmartContract { [TestClass] - public class UT_Syscalls + public partial class UT_Syscalls { [TestInitialize] public void TestSetup() From dd67c97673e62196b8ed3f0850fab51bcdb21ea6 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 19 Jun 2020 15:55:22 +0800 Subject: [PATCH 296/305] Add StrictUTF8 (#1710) --- src/neo/IO/Helper.cs | 25 +++++++++---------- src/neo/IO/Json/JObject.cs | 4 +-- .../ApplicationEngine.Runtime.cs | 4 +-- .../ContractParametersContext.cs | 4 +-- src/neo/SmartContract/JsonSerializer.cs | 2 +- src/neo/Utility.cs | 10 ++++++++ src/neo/VM/Helper.cs | 2 +- .../SmartContract/UT_JsonSerializer.cs | 12 +++++++++ 8 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/neo/IO/Helper.cs b/src/neo/IO/Helper.cs index ab8b2ce404..e165016139 100644 --- a/src/neo/IO/Helper.cs +++ b/src/neo/IO/Helper.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; namespace Neo.IO { @@ -15,7 +14,7 @@ public static class Helper public static T AsSerializable(this byte[] value, int start = 0) where T : ISerializable, new() { using (MemoryStream ms = new MemoryStream(value, start, value.Length - start, false)) - using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) + using (BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8)) { return reader.ReadSerializable(); } @@ -27,7 +26,7 @@ public static unsafe T AsSerializable(this ReadOnlySpan value) where T fixed (byte* pointer = value) { using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, value.Length); - using BinaryReader reader = new BinaryReader(ms, Encoding.UTF8); + using BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8); return reader.ReadSerializable(); } } @@ -38,7 +37,7 @@ public static ISerializable AsSerializable(this byte[] value, Type type) throw new InvalidCastException(); ISerializable serializable = (ISerializable)Activator.CreateInstance(type); using (MemoryStream ms = new MemoryStream(value, false)) - using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) + using (BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8)) { serializable.Deserialize(reader); } @@ -48,7 +47,7 @@ public static ISerializable AsSerializable(this byte[] value, Type type) public static T[] AsSerializableArray(this byte[] value, int max = 0x1000000) where T : ISerializable, new() { using (MemoryStream ms = new MemoryStream(value, false)) - using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) + using (BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8)) { return reader.ReadSerializableArray(max); } @@ -60,7 +59,7 @@ public static unsafe T[] AsSerializableArray(this ReadOnlySpan value, i fixed (byte* pointer = value) { using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, value.Length); - using BinaryReader reader = new BinaryReader(ms, Encoding.UTF8); + using BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8); return reader.ReadSerializableArray(max); } } @@ -138,7 +137,7 @@ public static int GetVarSize(this IReadOnlyCollection value) public static int GetVarSize(this string value) { - int size = Encoding.UTF8.GetByteCount(value); + int size = Utility.StrictUTF8.GetByteCount(value); return GetVarSize(size) + size; } @@ -166,7 +165,7 @@ public static byte[] ReadFixedBytes(this BinaryReader reader, int size) public static string ReadFixedString(this BinaryReader reader, int length) { byte[] data = reader.ReadFixedBytes(length); - return Encoding.UTF8.GetString(data.TakeWhile(p => p != 0).ToArray()); + return Utility.StrictUTF8.GetString(data.TakeWhile(p => p != 0).ToArray()); } public static T[] ReadNullableArray(this BinaryReader reader, int max = 0x1000000) where T : class, ISerializable, new() @@ -218,13 +217,13 @@ public static ulong ReadVarInt(this BinaryReader reader, ulong max = ulong.MaxVa public static string ReadVarString(this BinaryReader reader, int max = 0x1000000) { - return Encoding.UTF8.GetString(reader.ReadVarBytes(max)); + return Utility.StrictUTF8.GetString(reader.ReadVarBytes(max)); } public static byte[] ToArray(this ISerializable value) { using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8)) + using (BinaryWriter writer = new BinaryWriter(ms, Utility.StrictUTF8)) { value.Serialize(writer); writer.Flush(); @@ -235,7 +234,7 @@ public static byte[] ToArray(this ISerializable value) public static byte[] ToByteArray(this IReadOnlyCollection value) where T : ISerializable { using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8)) + using (BinaryWriter writer = new BinaryWriter(ms, Utility.StrictUTF8)) { writer.Write(value); writer.Flush(); @@ -263,7 +262,7 @@ public static void WriteFixedString(this BinaryWriter writer, string value, int throw new ArgumentNullException(nameof(value)); if (value.Length > length) throw new ArgumentException(); - byte[] bytes = Encoding.UTF8.GetBytes(value); + byte[] bytes = Utility.StrictUTF8.GetBytes(value); if (bytes.Length > length) throw new ArgumentException(); writer.Write(bytes); @@ -316,7 +315,7 @@ public static void WriteVarInt(this BinaryWriter writer, long value) public static void WriteVarString(this BinaryWriter writer, string value) { - writer.WriteVarBytes(Encoding.UTF8.GetBytes(value)); + writer.WriteVarBytes(Utility.StrictUTF8.GetBytes(value)); } } } diff --git a/src/neo/IO/Json/JObject.cs b/src/neo/IO/Json/JObject.cs index ef681d5410..adf46f9c90 100644 --- a/src/neo/IO/Json/JObject.cs +++ b/src/neo/IO/Json/JObject.cs @@ -80,7 +80,7 @@ public static JObject Parse(ReadOnlySpan value, int max_nest = 100) public static JObject Parse(string value, int max_nest = 100) { - return Parse(Encoding.UTF8.GetBytes(value), max_nest); + return Parse(Utility.StrictUTF8.GetBytes(value), max_nest); } private static JObject Read(ref Utf8JsonReader reader, bool skipReading = false) @@ -158,7 +158,7 @@ public override string ToString() public string ToString(bool indented) { - return Encoding.UTF8.GetString(ToByteArray(indented)); + return Utility.StrictUTF8.GetString(ToByteArray(indented)); } public virtual T TryGetEnum(T defaultValue = default, bool ignoreCase = false) where T : Enum diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index fd71966113..c1b9e8a62a 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -143,7 +143,7 @@ internal int GetInvocationCounter() internal void RuntimeLog(byte[] state) { if (state.Length > MaxNotificationSize) throw new ArgumentException(); - string message = Encoding.UTF8.GetString(state); + string message = Utility.StrictUTF8.GetString(state); Log?.Invoke(this, new LogEventArgs(ScriptContainer, CurrentScriptHash, message)); } @@ -151,7 +151,7 @@ internal void RuntimeNotify(byte[] eventName, Array state) { if (eventName.Length > MaxEventName) throw new ArgumentException(); if (!CheckItemForNotification(state)) throw new ArgumentException(); - SendNotification(CurrentScriptHash, Encoding.UTF8.GetString(eventName), state); + SendNotification(CurrentScriptHash, Utility.StrictUTF8.GetString(eventName), state); } internal void SendNotification(UInt160 hash, string eventName, Array state) diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index d87ec74967..c494fcf994 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -198,7 +198,7 @@ public static ContractParametersContext FromJson(JObject json) var verifiable = (IVerifiable)Activator.CreateInstance(type); using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(json["hex"].AsString()), false)) - using (BinaryReader reader = new BinaryReader(ms, Encoding.UTF8)) + using (BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8)) { verifiable.DeserializeUnsigned(reader); } @@ -262,7 +262,7 @@ public JObject ToJson() JObject json = new JObject(); json["type"] = Verifiable.GetType().FullName; using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms, Encoding.UTF8)) + using (BinaryWriter writer = new BinaryWriter(ms, Utility.StrictUTF8)) { Verifiable.SerializeUnsigned(writer); writer.Flush(); diff --git a/src/neo/SmartContract/JsonSerializer.cs b/src/neo/SmartContract/JsonSerializer.cs index 5ed1369c8c..e9db3ed18d 100644 --- a/src/neo/SmartContract/JsonSerializer.cs +++ b/src/neo/SmartContract/JsonSerializer.cs @@ -118,7 +118,7 @@ public static byte[] SerializeToByteArray(StackItem item, uint maxSize) writer.WriteEndObject(); break; case JsonTokenType.PropertyName: - writer.WritePropertyName(((ByteString)stack.Pop()).GetSpan()); + writer.WritePropertyName(((ByteString)stack.Pop()).GetString()); break; case Null _: writer.WriteNullValue(); diff --git a/src/neo/Utility.cs b/src/neo/Utility.cs index ee167db70e..7ec4a23d43 100644 --- a/src/neo/Utility.cs +++ b/src/neo/Utility.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Reflection; +using System.Text; namespace Neo { @@ -19,6 +20,15 @@ public Logger() } } + public static Encoding StrictUTF8 { get; } + + static Utility() + { + StrictUTF8 = (Encoding)Encoding.UTF8.Clone(); + StrictUTF8.DecoderFallback = DecoderFallback.ExceptionFallback; + StrictUTF8.EncoderFallback = EncoderFallback.ExceptionFallback; + } + /// /// Load configuration with different Environment Variable /// diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index c99ad135f2..c31c12d512 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -198,7 +198,7 @@ public static ReadOnlySpan GetSpan(this StackItem item) public static string GetString(this StackItem item) { - return Encoding.UTF8.GetString(item.GetSpan()); + return Utility.StrictUTF8.GetString(item.GetSpan()); } /// diff --git a/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs b/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs index 5f2758ad36..c16b6d6a27 100644 --- a/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs +++ b/tests/neo.UnitTests/SmartContract/UT_JsonSerializer.cs @@ -6,6 +6,7 @@ using System; using System.Linq; using System.Numerics; +using System.Text; namespace Neo.UnitTests.SmartContract { @@ -48,6 +49,17 @@ public void JsonTest_Array() Assert.AreEqual("[1,\"a==\",-1.3,null]", parsed.ToString()); } + [TestMethod] + public void JsonTest_Serialize_Map_Test() + { + var entry = new Map + { + [new byte[] { 0xC1 }] = 1, + [new byte[] { 0xC2 }] = 2, + }; + Assert.ThrowsException(() => JsonSerializer.Serialize(entry)); + } + [TestMethod] public void JsonTest_Bool() { From 2e21fd8e46611e5657f087a1a0c1fa176ec35002 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 20 Jun 2020 17:37:46 +0800 Subject: [PATCH 297/305] Restrict notifications (#1708) --- .../ApplicationEngine.Runtime.cs | 3 +- src/neo/SmartContract/NotifyEventArgs.cs | 7 ++- src/neo/neo.csproj | 2 +- .../SmartContract/UT_ApplicationEngine.cs | 43 +++---------------- 4 files changed, 13 insertions(+), 42 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index c1b9e8a62a..a7dd969582 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using Array = Neo.VM.Types.Array; namespace Neo.SmartContract @@ -156,7 +155,7 @@ internal void RuntimeNotify(byte[] eventName, Array state) internal void SendNotification(UInt160 hash, string eventName, Array state) { - NotifyEventArgs notification = new NotifyEventArgs(ScriptContainer, hash, eventName, state); + NotifyEventArgs notification = new NotifyEventArgs(ScriptContainer, hash, eventName, (Array)state.DeepCopy()); Notify?.Invoke(this, notification); notifications.Add(notification); } diff --git a/src/neo/SmartContract/NotifyEventArgs.cs b/src/neo/SmartContract/NotifyEventArgs.cs index d2a0d94605..532c5080e6 100644 --- a/src/neo/SmartContract/NotifyEventArgs.cs +++ b/src/neo/SmartContract/NotifyEventArgs.cs @@ -29,7 +29,12 @@ public void FromStackItem(StackItem stackItem) public StackItem ToStackItem(ReferenceCounter referenceCounter) { - return new Array(referenceCounter) { ScriptHash.ToArray(), EventName, State }; + return new Array(referenceCounter) + { + ScriptHash.ToArray(), + EventName, + State.DeepCopy() + }; } } } diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index edc2301bf1..3cd431d612 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index 187ba61024..6bccb8f179 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -1,16 +1,14 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.IO.Caching; using Neo.Ledger; using Neo.SmartContract; +using Neo.VM.Types; namespace Neo.UnitTests.SmartContract { [TestClass] public class UT_ApplicationEngine { - private string message = null; private string eventName = null; [TestInitialize] @@ -27,33 +25,23 @@ public void TestNotify() ApplicationEngine.Notify += Test_Notify1; const string notifyEvent = "TestEvent"; - engine.SendNotification(UInt160.Zero, notifyEvent, null); + engine.SendNotification(UInt160.Zero, notifyEvent, new Array()); eventName.Should().Be(notifyEvent); ApplicationEngine.Notify += Test_Notify2; - engine.SendNotification(UInt160.Zero, notifyEvent, null); + engine.SendNotification(UInt160.Zero, notifyEvent, new Array()); eventName.Should().Be(null); eventName = notifyEvent; ApplicationEngine.Notify -= Test_Notify1; - engine.SendNotification(UInt160.Zero, notifyEvent, null); + engine.SendNotification(UInt160.Zero, notifyEvent, new Array()); eventName.Should().Be(null); ApplicationEngine.Notify -= Test_Notify2; - engine.SendNotification(UInt160.Zero, notifyEvent, null); + engine.SendNotification(UInt160.Zero, notifyEvent, new Array()); eventName.Should().Be(null); } - private void Test_Log1(object sender, LogEventArgs e) - { - message = e.Message; - } - - private void Test_Log2(object sender, LogEventArgs e) - { - message = null; - } - private void Test_Notify1(object sender, NotifyEventArgs e) { eventName = e.EventName; @@ -75,25 +63,4 @@ public void TestCreateDummyBlock() snapshot.PersistingBlock.MerkleRoot.Should().Be(new UInt256()); } } - - public class TestMetaDataCache : MetaDataCache where T : class, ICloneable, ISerializable, new() - { - public TestMetaDataCache() - : base(null) - { - } - - protected override void AddInternal(T item) - { - } - - protected override T TryGetInternal() - { - return new T(); - } - - protected override void UpdateInternal(T item) - { - } - } } From 3116cdcf0ab8ce7de9f5a30e135fd188c7c59272 Mon Sep 17 00:00:00 2001 From: Tommo-L Date: Wed, 28 Oct 2020 14:53:05 +0800 Subject: [PATCH 298/305] fix storage fee --- src/neo/SmartContract/ApplicationEngine.Storage.cs | 9 ++------- tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index c0965806d7..818168e586 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -85,7 +85,6 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto if (key.Length > MaxStorageKeySize || value.Length > MaxStorageValueSize || context.IsReadOnly) throw new ArgumentException(); - int newDataSize; StorageKey skey = new StorageKey { Id = context.Id, @@ -94,18 +93,14 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto StorageItem item = Snapshot.Storages.GetAndChange(skey); if (item is null) { - newDataSize = key.Length + value.Length; Snapshot.Storages.Add(skey, item = new StorageItem()); + AddGas((key.Length + value.Length) * StoragePrice); } else { if (item.IsConstant) throw new InvalidOperationException(); - if (value.Length <= item.Value.Length) - newDataSize = 1; - else - newDataSize = value.Length - item.Value.Length; + AddGas((value.Length / 4 + 1) * StoragePrice); } - AddGas(newDataSize * StoragePrice); item.Value = value; item.IsConstant = flags.HasFlag(StorageFlags.Constant); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index ab959e7f6e..1c6f857d8b 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -145,7 +145,7 @@ public void ApplicationEngineReusedStorage_PartialReuse() var setupPrice = ae.GasConsumed; debugger.StepInto(); debugger.StepInto(); - (ae.GasConsumed - setupPrice).Should().Be(1 * ApplicationEngine.StoragePrice); + (ae.GasConsumed - setupPrice).Should().Be((sItem.Value.Length / 4 + 1) * ApplicationEngine.StoragePrice); } } @@ -185,7 +185,7 @@ public void ApplicationEngineReusedStorage_PartialReuseTwice() debugger.StepInto(); //syscall Storage.GetContext var setupPrice = ae.GasConsumed; debugger.StepInto(); //syscall Storage.Put - (ae.GasConsumed - setupPrice).Should().Be(1 * ApplicationEngine.StoragePrice); // = PUT basic fee + (ae.GasConsumed - setupPrice).Should().Be((sItem.Value.Length / 4 + 1) * ApplicationEngine.StoragePrice); // = PUT basic fee } } From 48e7a9ba7b1a15f26e31403f8d3b881b8c03b968 Mon Sep 17 00:00:00 2001 From: Tommo-L Date: Thu, 29 Oct 2020 14:31:26 +0800 Subject: [PATCH 299/305] fix --- src/neo/SmartContract/ApplicationEngine.Storage.cs | 5 ++++- tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index 818168e586..1e72fbedf3 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -99,7 +99,10 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto else { if (item.IsConstant) throw new InvalidOperationException(); - AddGas((value.Length / 4 + 1) * StoragePrice); + if (value.Length <= item.Value.Length) + AddGas((1 + value.Length / 4) * StoragePrice); + else + AddGas((1 + item.Value.Length / 4 + value.Length - item.Value.Length) * StoragePrice); // +1 means base fee } item.Value = value; diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index 1c6f857d8b..5d44cfa457 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -74,7 +74,7 @@ public void ApplicationEngineRegularPut() debugger.StepInto(); var setupPrice = ae.GasConsumed; debugger.Execute(); - (ae.GasConsumed - setupPrice).Should().Be(ApplicationEngine.StoragePrice * value.Length); + (ae.GasConsumed - setupPrice).Should().Be(ApplicationEngine.StoragePrice * (value.Length + 1)); } } @@ -145,7 +145,7 @@ public void ApplicationEngineReusedStorage_PartialReuse() var setupPrice = ae.GasConsumed; debugger.StepInto(); debugger.StepInto(); - (ae.GasConsumed - setupPrice).Should().Be((sItem.Value.Length / 4 + 1) * ApplicationEngine.StoragePrice); + (ae.GasConsumed - setupPrice).Should().Be((oldValue.Length / 4 + 1 + value.Length - oldValue.Length) * ApplicationEngine.StoragePrice); } } From 546b67277757100c050530647f1292a43e6a8895 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 29 Oct 2020 09:20:22 +0100 Subject: [PATCH 300/305] Optimize --- src/neo/SmartContract/ApplicationEngine.Storage.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index 1e72fbedf3..0a4bfa40a8 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -85,6 +85,7 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto if (key.Length > MaxStorageKeySize || value.Length > MaxStorageValueSize || context.IsReadOnly) throw new ArgumentException(); + int newDataSize; StorageKey skey = new StorageKey { Id = context.Id, @@ -93,17 +94,17 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto StorageItem item = Snapshot.Storages.GetAndChange(skey); if (item is null) { + newDataSize = key.Length + value.Length; Snapshot.Storages.Add(skey, item = new StorageItem()); - AddGas((key.Length + value.Length) * StoragePrice); } else { if (item.IsConstant) throw new InvalidOperationException(); - if (value.Length <= item.Value.Length) - AddGas((1 + value.Length / 4) * StoragePrice); - else - AddGas((1 + item.Value.Length / 4 + value.Length - item.Value.Length) * StoragePrice); // +1 means base fee + newDataSize = (1 + value.Length / 4); // +1 means base fee + if (value.Length > item.Value.Length) + newDataSize += value.Length - item.Value.Length; } + AddGas(newDataSize * StoragePrice); item.Value = value; item.IsConstant = flags.HasFlag(StorageFlags.Constant); From 8e098b123d50e526b38bfe0ac0474be1a3b6df60 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 29 Oct 2020 09:40:30 +0100 Subject: [PATCH 301/305] Fix --- src/neo/SmartContract/ApplicationEngine.Storage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index 0a4bfa40a8..a81b0609e3 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -100,7 +100,7 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto else { if (item.IsConstant) throw new InvalidOperationException(); - newDataSize = (1 + value.Length / 4); // +1 means base fee + newDataSize = (1 + item.Value.Length / 4); // +1 means base fee if (value.Length > item.Value.Length) newDataSize += value.Length - item.Value.Length; } From a3adbad4e6c460533f7ee33859b0fdeeaedfa412 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 29 Oct 2020 09:42:34 +0100 Subject: [PATCH 302/305] Real Fi --- src/neo/SmartContract/ApplicationEngine.Storage.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index a81b0609e3..c8159da4f6 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -100,9 +100,10 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto else { if (item.IsConstant) throw new InvalidOperationException(); - newDataSize = (1 + item.Value.Length / 4); // +1 means base fee - if (value.Length > item.Value.Length) - newDataSize += value.Length - item.Value.Length; + if (value.Length <= item.Value.Length) + newDataSize = (1 + value.Length / 4); + else + newDataSize = (1 + item.Value.Length / 4 + value.Length - item.Value.Length); } AddGas(newDataSize * StoragePrice); From 5659931854b9c288ba7360dbb958b66e1ab88008 Mon Sep 17 00:00:00 2001 From: Tommo-L Date: Thu, 29 Oct 2020 22:23:48 +0800 Subject: [PATCH 303/305] apply --- src/neo/SmartContract/ApplicationEngine.Storage.cs | 8 +++++--- tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index c8159da4f6..3897f86cf8 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -100,10 +100,12 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto else { if (item.IsConstant) throw new InvalidOperationException(); - if (value.Length <= item.Value.Length) - newDataSize = (1 + value.Length / 4); + if (value.Length < 4) + newDataSize = 1; + else if (value.Length <= item.Value.Length) + newDataSize = value.Length / 4; else - newDataSize = (1 + item.Value.Length / 4 + value.Length - item.Value.Length); + newDataSize = item.Value.Length / 4 + value.Length - item.Value.Length; } AddGas(newDataSize * StoragePrice); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index 5d44cfa457..dcd47df8e0 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -74,7 +74,7 @@ public void ApplicationEngineRegularPut() debugger.StepInto(); var setupPrice = ae.GasConsumed; debugger.Execute(); - (ae.GasConsumed - setupPrice).Should().Be(ApplicationEngine.StoragePrice * (value.Length + 1)); + (ae.GasConsumed - setupPrice).Should().Be(ApplicationEngine.StoragePrice * value.Length); } } @@ -145,7 +145,7 @@ public void ApplicationEngineReusedStorage_PartialReuse() var setupPrice = ae.GasConsumed; debugger.StepInto(); debugger.StepInto(); - (ae.GasConsumed - setupPrice).Should().Be((oldValue.Length / 4 + 1 + value.Length - oldValue.Length) * ApplicationEngine.StoragePrice); + (ae.GasConsumed - setupPrice).Should().Be((oldValue.Length / 4 + value.Length - oldValue.Length) * ApplicationEngine.StoragePrice); } } From a8ce7e3a187bd63b54cb6894ee7c0aa13ad299a7 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 30 Oct 2020 23:08:19 +0800 Subject: [PATCH 304/305] Correct the formula --- src/neo/SmartContract/ApplicationEngine.Storage.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index 3897f86cf8..ce87bc6211 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -100,12 +100,12 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto else { if (item.IsConstant) throw new InvalidOperationException(); - if (value.Length < 4) + if (value.Length == 0) newDataSize = 1; else if (value.Length <= item.Value.Length) - newDataSize = value.Length / 4; + newDataSize = (value.Length - 1) / 4 + 1; else - newDataSize = item.Value.Length / 4 + value.Length - item.Value.Length; + newDataSize = (item.Value.Length - 1) / 4 + 1 + value.Length - item.Value.Length; } AddGas(newDataSize * StoragePrice); From c114db387c241b6eec307df9fe67bc3e6c834d0a Mon Sep 17 00:00:00 2001 From: Tommo-L Date: Sat, 31 Oct 2020 08:59:53 +0800 Subject: [PATCH 305/305] fix ut --- tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index dcd47df8e0..00e3d09444 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -74,7 +74,7 @@ public void ApplicationEngineRegularPut() debugger.StepInto(); var setupPrice = ae.GasConsumed; debugger.Execute(); - (ae.GasConsumed - setupPrice).Should().Be(ApplicationEngine.StoragePrice * value.Length); + (ae.GasConsumed - setupPrice).Should().Be(ApplicationEngine.StoragePrice * (1 + value.Length)); } } @@ -145,7 +145,7 @@ public void ApplicationEngineReusedStorage_PartialReuse() var setupPrice = ae.GasConsumed; debugger.StepInto(); debugger.StepInto(); - (ae.GasConsumed - setupPrice).Should().Be((oldValue.Length / 4 + value.Length - oldValue.Length) * ApplicationEngine.StoragePrice); + (ae.GasConsumed - setupPrice).Should().Be((1 + (oldValue.Length / 4) + value.Length - oldValue.Length) * ApplicationEngine.StoragePrice); } }