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?** 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/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.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/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/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/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_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..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); } @@ -798,7 +797,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] @@ -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); } @@ -1093,7 +1092,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..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); @@ -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/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.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..9131db811a --- /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("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/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_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 7294d3ccb3..a8b212fe37 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; @@ -129,13 +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.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()); @@ -146,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()); } @@ -190,13 +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); + Assert.IsNull(result.NextBlockHash); + Assert.AreEqual(20, result.Confirmations); json["confirmations"] = 20; json["nextblockhash"] = "4c1e879872344349067c3b1a30781eeb4f9040d3795db7922f513f6f9660b9b2"; @@ -352,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()); @@ -451,8 +466,7 @@ public void TestInvokeFunction() ""type"": ""ByteArray"", ""value"": ""262bec084432"" } - ], - ""tx"":""d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"" + ] }"); JObject response = CreateResponse(1); response["result"] = json; @@ -475,8 +489,7 @@ public void TestInvokeScript() ""type"": ""ByteArray"", ""value"": ""262bec084432"" } - ], - ""tx"":""d101361426ae7c6c9861ec418468c1f0fdc4a7f2963eb89151c10962616c616e63654f6667be39e7b562f60cbfe2aebca375a2e5ee28737caf000000000000000000000000"" + ] }"); JObject response = CreateResponse(1); response["result"] = json; @@ -542,5 +555,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(); + } + } +} 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/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/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..5c2a99ff56 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 { @@ -76,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); @@ -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..c0f4a86cf1 --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_Contract.cs @@ -0,0 +1,162 @@ +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[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(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[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(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[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)); + 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[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)); + } + + [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[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)); + 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[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)); + } + } +} 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..f5fd304243 --- /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("0x1a2791a63139294337863c7d822d17454876977c"); + var context = new ContractParametersContext(tx); + context.Completed.Should().BeFalse(); + } + + [TestMethod] + public void TestToString() + { + Transaction tx = TestUtils.GetTransaction(); + 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"":""AAAAAAB8l3ZIRRctgn08hjdDKTkxppEnGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAA=="",""items"":{""0x1a2791a63139294337863c7d822d17454876977c"":{""script"":""IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFlBoCpBq1A=="",""parameters"":[{""type"":""Signature"",""value"":""AQ==""}]}}}"); + } + + [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"); + ((Transaction)ret.Verifiable).Script.ToHexString().Should().Be(new byte[1].ToHexString()); + } + + [TestMethod] + public void TestFromJson() + { + Action action = () => ContractParametersContext.Parse("{\"type\":\"wrongType\",\"hex\":\"00000000007c97764845172d827d3c863743293931a691271a0000000000000000000000000000000000000000000100\",\"items\":{\"0x1a2791a63139294337863c7d822d17454876977c\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1650680a906ad4\",\"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("0x1a2791a63139294337863c7d822d17454876977c"); + 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("0x1a2791a63139294337863c7d822d17454876977c"); + 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("0x1a2791a63139294337863c7d822d17454876977c"); + 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("0x1a2791a63139294337863c7d822d17454876977c"); + 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("0xfc8b59f1a337dcc17b1a201d327a2081d41fac8d"); + 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..243cfa89ef --- /dev/null +++ b/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -0,0 +1,499 @@ +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; +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)); + 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]); + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaVerify).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); + 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); + engine.CurrentContext.EvaluationStack.Push(StackItem.Null); + InteropService.Invoke(engine, InteropService.Neo_Crypto_ECDsaCheckMultiSig).Should().BeFalse(); + + pubkeys = new VMArray + { + 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.Neo_Crypto_ECDsaCheckMultiSig).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); + 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 + { + pubkey1.EncodePoint(false), + new byte[70] + }; + signatures = new VMArray + { + signature1, + signature2 + }; + 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(); + 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(); + + // 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] + 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..cc6eac738a 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.Neo_Crypto_ECDsaVerify).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.Neo_Crypto_ECDsaVerify).Should().BeTrue(); + engine.CurrentContext.EvaluationStack.Peek().GetBoolean().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_JsonSerializer.cs b/neo.UnitTests/SmartContract/UT_JsonSerializer.cs index e7fdf30667..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,\"test\",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,\"test1\",123],[true,\"test2\",321]]"); + Assert.AreEqual(json, "[[true,\"dGVzdDE=\",123],[true,\"dGVzdDI=\",321]]"); } [TestMethod] 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/SmartContract/UT_Syscalls.cs b/neo.UnitTests/SmartContract/UT_Syscalls.cs index 658d125e7f..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(), - "5b22515c7546464644795c75464646445c75464646445c75303030335c75464646445c75464646445c754646464475465c7530303046715c75303132415c625b595c75303434335c75464646445c75464646447d5d767b385c7546464644785c75303032375c75464646445c7546464644222c332c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c322c312c225c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c75303030305c7530303030222c315d"); + "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(), - @"5b225c7546464644445c7546464644615c7546464644732c5c75464646445c7546464644665c75303030375d5c75303030305c75464646445c75303632325c7546464644545c7546464644375c7530303133335c75303031385c7530303033655c75464646445c75464646445c75303032375a5c75464646445c2f5c7546464644222c362c342c225c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c75464646445c7546464644222c332c322c352c225c7530303031225d"); + @"5b225c75303032426b53415959527a4c4b69685a676464414b50596f754655737a63544d7867445a6572584a3172784c37303d222c362c342c225c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f5c2f383d222c332c322c352c2241513d3d225d"); Assert.AreEqual(0, engine.ResultStack.Count); } } 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); 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/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 cf5ad0bff5..41d6270822 100644 --- a/neo.UnitTests/VM/UT_Helper.cs +++ b/neo.UnitTests/VM/UT_Helper.cs @@ -1,6 +1,8 @@ +using FluentAssertions; 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; @@ -8,7 +10,7 @@ using System.Numerics; using System.Text; -namespace Neo.UnitTests.IO +namespace Neo.UnitTests.VMT { [TestClass] public class UT_Helper @@ -83,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() { @@ -138,5 +149,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); + } } } 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.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/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 a65916b5aa..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)) @@ -615,11 +617,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); } 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(); 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/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/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/Ledger/MemoryPool.cs b/neo/Ledger/MemoryPool.cs index 10ee5f9d0b..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; @@ -69,6 +68,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 +272,7 @@ internal bool TryAdd(UInt256 hash, Transaction tx) try { _unsortedTransactions.Add(hash, poolItem); + SendersFeeMonitor.AddSenderFee(tx); _sortedTransactions.Add(poolItem); if (Count > Capacity) @@ -310,6 +315,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 +343,7 @@ internal void InvalidateVerifiedTransactions() // Clear the verified transactions now, since they all must be reverified. _unsortedTransactions.Clear(); + SendersFeeMonitor = new SendersFeeMonitor(); _sortedTransactions.Clear(); } @@ -365,11 +372,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 @@ -409,7 +416,7 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, // 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); @@ -420,9 +427,8 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, _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; @@ -432,6 +438,7 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, { if (_unsortedTransactions.TryAdd(item.Tx.Hash, item)) { + SendersFeeMonitor.AddSenderFee(item.Tx); verifiedSortedTxPool.Add(item); if (item.LastBroadcastTimestamp < rebroadcastCutOffTime) @@ -469,7 +476,7 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, /// /// 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) 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/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/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/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/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) 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/Payloads/Transaction.cs b/neo/Network/P2P/Payloads/Transaction.cs index af38ef6dcc..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; @@ -183,7 +181,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,19 +197,19 @@ 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; } 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); 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..1cd5bf7ee8 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 @@ -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); } @@ -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/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)); } } diff --git a/neo/Network/P2P/ProtocolHandler.cs b/neo/Network/P2P/ProtocolHandler.cs index aa7f0e06f2..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; @@ -158,7 +161,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(); @@ -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(); @@ -213,7 +236,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; @@ -329,7 +352,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); 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/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/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/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 d4a651fd78..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()}"); } @@ -98,10 +106,10 @@ 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(); + 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/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()) { 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() + { + } } } 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/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/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..212d1091b6 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 { @@ -225,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; diff --git a/neo/SmartContract/InteropService.cs b/neo/SmartContract/InteropService.cs index c647cb84fc..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); @@ -409,13 +390,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; } diff --git a/neo/SmartContract/JsonSerializer.cs b/neo/SmartContract/JsonSerializer.cs index 1c674540f0..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.GetString(); + 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/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; 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(); + } + } +} 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(); 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