From 8a5e33ceac4d4f3e9c1c22bdbc5378607780f53c Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 4 Feb 2021 23:23:59 +0800 Subject: [PATCH 01/63] Allow call in verification (#2293) --- src/neo/SmartContract/Helper.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 7d80688934..d1296ed8a8 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -199,15 +199,13 @@ internal static bool VerifyWitness(this IVerifiable verifiable, DataCache snapsh } using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, verifiable, snapshot?.CreateSnapshot(), null, gas)) { - CallFlags callFlags = !witness.VerificationScript.IsStandardContract() ? CallFlags.ReadStates : CallFlags.None; - if (witness.VerificationScript.Length == 0) { ContractState cs = NativeContract.ContractManagement.GetContract(snapshot, hash); if (cs is null) return false; ContractMethodDescriptor md = cs.Manifest.Abi.GetMethod("verify", -1); if (md?.ReturnType != ContractParameterType.Boolean) return false; - engine.LoadContract(cs, md, callFlags); + engine.LoadContract(cs, md, CallFlags.ReadOnly); } else { @@ -224,7 +222,7 @@ internal static bool VerifyWitness(this IVerifiable verifiable, DataCache snapsh } engine.LoadScript(verificationScript, initialPosition: 0, configureState: p => { - p.CallFlags = callFlags; + p.CallFlags = CallFlags.ReadOnly; p.ScriptHash = hash; }); } From 2554cb976d1275c846217cb74bd9356f96b58822 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 5 Feb 2021 02:51:08 +0800 Subject: [PATCH 02/63] Allow larger ExtensiblePayload (#2295) --- src/neo/Network/P2P/Payloads/ExtensiblePayload.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs index 4e405d4814..79ec9886e4 100644 --- a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs +++ b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -67,7 +67,7 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) ValidBlockEnd = reader.ReadUInt32(); if (ValidBlockStart >= ValidBlockEnd) throw new FormatException(); Sender = reader.ReadSerializable(); - Data = reader.ReadVarBytes(ushort.MaxValue); + Data = reader.ReadVarBytes(Message.PayloadMaxSize); } UInt160[] IVerifiable.GetScriptHashesForVerifying(DataCache snapshot) From c3c395c55b9eed8903ab63ae872a6ad02ec3f6c9 Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 4 Feb 2021 21:03:58 +0100 Subject: [PATCH 03/63] Transaction optimization (#2290) * Verification optimizations * Fix UT * Add snapshot to MakeTransaction * Optimize * Update Transaction.cs * Fix * Fix UT * Revert Wallet change Co-authored-by: Erik Zhang --- src/neo/Network/P2P/Payloads/Transaction.cs | 11 ++-- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 4 +- .../Network/P2P/Payloads/UT_Transaction.cs | 56 ++++++++++++------- tests/neo.UnitTests/Network/P2P/UT_Message.cs | 6 +- 4 files changed, 46 insertions(+), 31 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 5ec7297fb2..99c122cfb6 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -153,7 +153,8 @@ void ISerializable.Deserialize(BinaryReader reader) if (reader.BaseStream.CanSeek) startPosition = (int)reader.BaseStream.Position; DeserializeUnsigned(reader); - Witnesses = reader.ReadSerializableArray(); + Witnesses = reader.ReadSerializableArray(Signers.Length); + if (Witnesses.Length != Signers.Length) throw new FormatException(); if (startPosition >= 0) _size = (int)reader.BaseStream.Position - startPosition; } @@ -287,7 +288,8 @@ public virtual VerifyResult VerifyStateDependent(DataCache snapshot, Transaction uint height = NativeContract.Ledger.CurrentIndex(snapshot); if (ValidUntilBlock <= height || ValidUntilBlock > height + MaxValidUntilBlockIncrement) return VerifyResult.Expired; - foreach (UInt160 hash in GetScriptHashesForVerifying(snapshot)) + UInt160[] hashes = GetScriptHashesForVerifying(snapshot); + foreach (UInt160 hash in hashes) if (NativeContract.Policy.IsBlocked(snapshot, hash)) return VerifyResult.PolicyFail; if (NativeContract.Policy.GetMaxBlockSystemFee(snapshot) < SystemFee) @@ -297,9 +299,7 @@ public virtual VerifyResult VerifyStateDependent(DataCache snapshot, Transaction if (!attribute.Verify(snapshot, this)) return VerifyResult.Invalid; long net_fee = NetworkFee - Size * NativeContract.Policy.GetFeePerByte(snapshot); - - UInt160[] hashes = GetScriptHashesForVerifying(snapshot); - if (hashes.Length != witnesses.Length) return VerifyResult.Invalid; + if (net_fee < 0) return VerifyResult.InsufficientFunds; uint execFeeFactor = NativeContract.Policy.GetExecFeeFactor(snapshot); for (int i = 0; i < hashes.Length; i++) @@ -331,7 +331,6 @@ public virtual VerifyResult VerifyStateIndependent() return VerifyResult.Invalid; } UInt160[] hashes = GetScriptHashesForVerifying(null); - if (hashes.Length != witnesses.Length) return VerifyResult.Invalid; for (int i = 0; i < hashes.Length; i++) if (witnesses[i].VerificationScript.IsStandardContract()) if (!this.VerifyWitness(null, hashes[i], witnesses[i], SmartContract.Helper.MaxVerificationGas, out _)) diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index a610c70ccf..c58ff4c18c 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -93,8 +93,8 @@ private Transaction CreateTransactionWithFee(long fee) { new Witness { - InvocationScript = new byte[0], - VerificationScript = new byte[0] + InvocationScript = Array.Empty(), + VerificationScript = Array.Empty() } }; return mock.Object; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index d9e4cab9b8..7e69a59fc7 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -788,7 +788,7 @@ public void Transaction_Serialize_Deserialize_Simple() Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, Attributes = Array.Empty(), Script = new byte[] { (byte)OpCode.PUSH1 }, - Witnesses = new Witness[0] { } + Witnesses = new Witness[] { new Witness() { InvocationScript = new byte[0], VerificationScript = Array.Empty() } } }; byte[] sTx = txSimple.ToArray(); @@ -803,7 +803,7 @@ public void Transaction_Serialize_Deserialize_Simple() "01000000000000000000000000000000000000000000" + // empty signer "00" + // no attributes "0111" + // push1 script - "00"); // no witnesses + "010000"); // empty witnesses // try to deserialize Transaction tx2 = Neo.IO.Helper.AsSerializable(sTx); @@ -824,7 +824,7 @@ public void Transaction_Serialize_Deserialize_Simple() } ); tx2.Script.Should().BeEquivalentTo(new byte[] { (byte)OpCode.PUSH1 }); - tx2.Witnesses.Should().BeEquivalentTo(new Witness[0] { }); + tx2.Witnesses.Should().BeEquivalentTo(new Witness[] { new Witness() { InvocationScript = new byte[0], VerificationScript = Array.Empty() } }); } [TestMethod] @@ -854,13 +854,13 @@ public void Transaction_Serialize_Deserialize_DistinctCosigners() } }, Script = new byte[] { (byte)OpCode.PUSH1 }, - Witnesses = new Witness[0] { } + Witnesses = new Witness[] { new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } } }; byte[] sTx = txDoubleCosigners.ToArray(); // no need for detailed hexstring here (see basic tests for it) - sTx.ToHexString().Should().Be("000403020100e1f505000000000100000000000000040302010209080706050403020100090807060504030201008009080706050403020100090807060504030201000100011100"); + sTx.ToHexString().Should().Be("000403020100e1f5050000000001000000000000000403020102090807060504030201000908070605040302010080090807060504030201000908070605040302010001000111010000"); // back to transaction (should fail, due to non-distinct cosigners) Transaction tx2 = null; @@ -904,14 +904,13 @@ public void Transaction_Serialize_Deserialize_MaxSizeCosigners() Attributes = Array.Empty(), Signers = cosigners1, // max + 1 (should fail) Script = new byte[] { (byte)OpCode.PUSH1 }, - Witnesses = new Witness[0] { } + Witnesses = new Witness[] { new Witness() { InvocationScript = new byte[0], VerificationScript = Array.Empty() } } }; byte[] sTx1 = txCosigners1.ToArray(); // back to transaction (should fail, due to non-distinct cosigners) - Transaction tx1 = Neo.IO.Helper.AsSerializable(sTx1); - Assert.IsNotNull(tx1); + Assert.ThrowsException(() => Neo.IO.Helper.AsSerializable(sTx1)); // ---------------------------- // this should fail (max + 1) @@ -938,7 +937,7 @@ public void Transaction_Serialize_Deserialize_MaxSizeCosigners() Attributes = Array.Empty(), Signers = cosigners, // max + 1 (should fail) Script = new byte[] { (byte)OpCode.PUSH1 }, - Witnesses = new Witness[0] { } + Witnesses = new Witness[] { new Witness() { InvocationScript = new byte[0], VerificationScript = Array.Empty() } } }; byte[] sTx2 = txCosigners.ToArray(); @@ -1110,11 +1109,18 @@ public void Test_VerifyStateIndependent() SystemFee = 0, ValidUntilBlock = 0, Version = 0, - Witnesses = new Witness[0], + Witnesses = new[] + { + new Witness + { + InvocationScript = Array.Empty(), + VerificationScript = Array.Empty() + } + } }; tx.VerifyStateIndependent().Should().Be(VerifyResult.Invalid); tx.Script = new byte[0]; - tx.VerifyStateIndependent().Should().Be(VerifyResult.Invalid); + tx.VerifyStateIndependent().Should().Be(VerifyResult.Succeed); var walletA = TestUtils.GenerateTestWallet(); var walletB = TestUtils.GenerateTestWallet(); @@ -1177,16 +1183,27 @@ public void Test_VerifyStateDependent() var tx = new Transaction() { Attributes = Array.Empty(), - NetworkFee = 0, + NetworkFee = 55000, Nonce = (uint)Environment.TickCount, - Script = new byte[0], + Script = Array.Empty(), Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, SystemFee = 0, ValidUntilBlock = height + 1, Version = 0, - Witnesses = new Witness[0], + Witnesses = new Witness[] { + new Witness() { InvocationScript = Array.Empty(), VerificationScript = new byte[0] }, + new Witness() { InvocationScript = Array.Empty(), VerificationScript = new byte[1] } + } }; + + // Fake balance + + var key = NativeContract.GAS.CreateStorageKey(20, tx.Sender); + var balance = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); + balance.GetInteroperable().Balance = tx.NetworkFee; + tx.VerifyStateDependent(snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.Invalid); + balance.GetInteroperable().Balance = 0; tx.SystemFee = 10; tx.VerifyStateDependent(snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.InsufficientFunds); @@ -1211,15 +1228,14 @@ public void Test_VerifyStateDependent() // Fake balance - var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); - - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - - snapshot.Commit(); + snapshot = Blockchain.Singleton.GetSnapshot(); + key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); + balance = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); + balance.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; // Make transaction + snapshot.Commit(); tx = walletA.MakeTransaction(new TransferOutput[] { new TransferOutput() diff --git a/tests/neo.UnitTests/Network/P2P/UT_Message.cs b/tests/neo.UnitTests/Network/P2P/UT_Message.cs index e688806a75..5ad392f4b5 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_Message.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_Message.cs @@ -131,16 +131,16 @@ public void Compression() { Nonce = 1, Version = 0, - Attributes = new TransactionAttribute[0], + Attributes = Array.Empty(), Script = new byte[] { (byte)OpCode.PUSH1 }, Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, - Witnesses = new Witness[0], + Witnesses = new Witness[] { new Witness() { InvocationScript = Array.Empty(), VerificationScript = new byte[0] } }, }; var msg = Message.Create(MessageCommand.Transaction, payload); var buffer = msg.ToArray(); - buffer.Length.Should().Be(54); + buffer.Length.Should().Be(56); payload.Script = new byte[100]; for (int i = 0; i < payload.Script.Length; i++) payload.Script[i] = (byte)OpCode.PUSH2; From 7f799b8c347e6e1dd100c383c25c6f2b96a38d4b Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 5 Feb 2021 11:42:17 +0800 Subject: [PATCH 04/63] Fix CallFlags (#2292) --- src/neo/SmartContract/ApplicationEngine.Contract.cs | 2 +- src/neo/SmartContract/ApplicationEngine.Runtime.cs | 6 ++---- src/neo/SmartContract/ApplicationEngine.cs | 7 ++++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index c52527bb6b..74149094aa 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -10,7 +10,7 @@ namespace Neo.SmartContract { partial class ApplicationEngine { - public static readonly InteropDescriptor System_Contract_Call = Register("System.Contract.Call", nameof(CallContract), 1 << 15, CallFlags.AllowCall); + public static readonly InteropDescriptor System_Contract_Call = Register("System.Contract.Call", nameof(CallContract), 1 << 15, CallFlags.ReadStates | CallFlags.AllowCall); public static readonly InteropDescriptor System_Contract_CallNative = Register("System.Contract.CallNative", nameof(CallNativeContract), 0, CallFlags.None); public static readonly InteropDescriptor System_Contract_IsStandard = Register("System.Contract.IsStandard", nameof(IsStandardContract), 1 << 10, CallFlags.ReadStates); public static readonly InteropDescriptor System_Contract_GetCallFlags = Register("System.Contract.GetCallFlags", nameof(GetCallFlags), 1 << 10, CallFlags.None); diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index 5a1edd1598..abefbdc575 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -89,8 +89,7 @@ protected internal bool CheckWitnessInternal(UInt160 hash) { // Check allow state callflag - if (!CurrentContext.GetState().CallFlags.HasFlag(CallFlags.ReadStates)) - throw new InvalidOperationException($"Cannot call this SYSCALL without the flag AllowStates."); + ValidateCallFlags(CallFlags.ReadStates); var contract = NativeContract.ContractManagement.GetContract(Snapshot, CallingScriptHash); // check if current group is the required one @@ -102,8 +101,7 @@ protected internal bool CheckWitnessInternal(UInt160 hash) // Check allow state callflag - if (!CurrentContext.GetState().CallFlags.HasFlag(CallFlags.ReadStates)) - throw new InvalidOperationException($"Cannot call this SYSCALL without the flag AllowStates."); + ValidateCallFlags(CallFlags.ReadStates); // only for non-Transaction types (Block, etc) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 39c7f048cb..b16c238303 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -197,6 +197,7 @@ public ExecutionContext LoadScript(Script script, int rvcount = -1, int initialP protected override ExecutionContext LoadToken(ushort tokenId) { + ValidateCallFlags(CallFlags.ReadStates | CallFlags.AllowCall); ContractState contract = CurrentContext.GetState().Contract; if (contract is null || tokenId >= contract.Nef.Tokens.Length) throw new InvalidOperationException(); @@ -281,17 +282,17 @@ public override void Dispose() base.Dispose(); } - protected void ValidateCallFlags(InteropDescriptor descriptor) + protected void ValidateCallFlags(CallFlags requiredCallFlags) { ExecutionContextState state = CurrentContext.GetState(); - if (!state.CallFlags.HasFlag(descriptor.RequiredCallFlags)) + if (!state.CallFlags.HasFlag(requiredCallFlags)) throw new InvalidOperationException($"Cannot call this SYSCALL with the flag {state.CallFlags}."); } protected override void OnSysCall(uint method) { InteropDescriptor descriptor = services[method]; - ValidateCallFlags(descriptor); + ValidateCallFlags(descriptor.RequiredCallFlags); AddGas(descriptor.FixedPrice * exec_fee_factor); List parameters = descriptor.Parameters.Count > 0 ? new List() From e2c68778c921c7ba6d49a1b7bcce627b4f6d3228 Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 5 Feb 2021 05:27:49 +0100 Subject: [PATCH 05/63] Fix IPv4 and IPv6 Regex (#2285) --- src/neo/SmartContract/Native/NameService.cs | 4 +- .../SmartContract/Native/UT_NNS.cs | 122 ++++++++---------- 2 files changed, 57 insertions(+), 69 deletions(-) diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index 4e6bec1d36..9457536450 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -27,8 +27,8 @@ public sealed class NameService : NonfungibleToken private const uint OneYear = 365 * 24 * 3600; private static readonly Regex rootRegex = new Regex("^[a-z][a-z0-9]{0,15}$", RegexOptions.Singleline); private static readonly Regex nameRegex = new Regex("^(?=.{3,255}$)([a-z0-9]{1,62}\\.)+[a-z][a-z0-9]{0,15}$", RegexOptions.Singleline); - private static readonly Regex ipv4Regex = new Regex("^(2(5[0-5]|[0-4]\\d))|1?\\d{1,2}(\\.((2(5[0-5]|[0-4]\\d))|1?\\d{1,2})){3}$", RegexOptions.Singleline); - private static readonly Regex ipv6Regex = new Regex("^([a-f0-9]{1,4}:){7}[a-f0-9]{1,4}$", RegexOptions.Singleline | RegexOptions.IgnoreCase); + private static readonly Regex ipv4Regex = new Regex("^(?=\\d+\\.\\d+\\.\\d+\\.\\d+$)(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\\.?){4}$", RegexOptions.Singleline); + private static readonly Regex ipv6Regex = new Regex("(?:^)(([0-9a-f]{1,4}:){7,7}[0-9a-f]{1,4}|([0-9a-f]{1,4}:){1,7}:|([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}|([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}|([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}|([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:((:[0-9a-f]{1,4}){1,6})|:((:[0-9a-f]{1,4}){1,7}|:))(?=$)", RegexOptions.Singleline); internal NameService() { diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs index 2f2ba3e5c1..2624434dd2 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs @@ -43,7 +43,7 @@ public void TestSetup() public void Check_Symbol() => NativeContract.NameService.Symbol(_snapshot).Should().Be("NNS"); [TestMethod] - public void Test_SetRecord_IPV4() + public void Test_SetRecord_A_AAAA() { var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain @@ -53,7 +53,7 @@ public void Test_SetRecord_IPV4() // committee member,add a new root and then register, setrecord string validroot = "testroot"; - var ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock).Should().BeTrue(); string name = "testname"; string domain = name + "." + validroot; @@ -69,76 +69,64 @@ public void Test_SetRecord_IPV4() register_ret.State.Should().BeTrue(); //check NFT token - Assert.AreEqual(NativeContract.NameService.BalanceOf(snapshot, from), (BigInteger)1); + Assert.AreEqual(NativeContract.NameService.BalanceOf(snapshot, from), 1); //after register checkAvail_ret = Check_IsAvailable(snapshot, UInt160.Zero, domain, persistingBlock); checkAvail_ret.Result.Should().BeTrue(); checkAvail_ret.State.Should().BeFalse(); - //set As IPv4 Address - string testA = "10.10.10.10"; - var setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testA, from, persistingBlock); - setRecord_ret.Should().BeTrue(); - - var getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.A, persistingBlock); - getRecord_ret.State.Should().BeTrue(); - getRecord_ret.Result.Should().Equals(testA); + //test As IPv4 Address + bool setRecord_ret; + (bool State, string Result) getRecord_ret; - testA = "0.0.0.0"; - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testA, from, persistingBlock); - setRecord_ret.Should().BeTrue(); - getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.A, persistingBlock); - getRecord_ret.State.Should().BeTrue(); - getRecord_ret.Result.Should().Equals(testA); - - testA = "255.255.255.255"; - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testA, from, persistingBlock); - setRecord_ret.Should().BeTrue(); - getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.A, persistingBlock); - getRecord_ret.State.Should().BeTrue(); - getRecord_ret.Result.Should().Equals(testA); - - //invalid case - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "1a", from, persistingBlock); - setRecord_ret.Should().BeFalse(); - - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "256.0.0.0", from, persistingBlock); - setRecord_ret.Should().BeFalse(); - - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "0.0.0.-1", from, persistingBlock); - setRecord_ret.Should().BeFalse(); - - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "0.0.0.0.1", from, persistingBlock); - setRecord_ret.Should().BeFalse(); - - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "11111111.11111111.11111111.11111111", from, persistingBlock); - setRecord_ret.Should().BeFalse(); - - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "11111111.11111111.11111111.11111111", from, persistingBlock); - setRecord_ret.Should().BeFalse(); - - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "ff.ff.ff.ff", from, persistingBlock); - setRecord_ret.Should().BeFalse(); - - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "0.0.256", from, persistingBlock); - setRecord_ret.Should().BeFalse(); - - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "0.0.0", from, persistingBlock); - setRecord_ret.Should().BeFalse(); - - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "0.257", from, persistingBlock); - setRecord_ret.Should().BeFalse(); - - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "1.1", from, persistingBlock); - setRecord_ret.Should().BeFalse(); + foreach (var testTrue in new string[] { + "0.0.0.0", "10.10.10.10", "255.255.255.255","192.168.1.1" + }) + { + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testTrue, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.A, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testTrue); + } - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "257", from, persistingBlock); - setRecord_ret.Should().BeFalse(); + //test As IPv6 Address + foreach (var testTrue in new string[] { + "2001:db8::8:800:200c:417a", "ff01::101", "ff01::101", "::1", "::", + "2001:db8:0:0:8:800:200c:417a", "ff01:0:0:0:0:0:0:101", "0:0:0:0:0:0:0:1", "0:0:0:0:0:0:0:0" + }) + { + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.AAAA, testTrue, from, persistingBlock); + setRecord_ret.Should().BeTrue(); + getRecord_ret = Check_GetRecord(snapshot, domain, RecordType.AAAA, persistingBlock); + getRecord_ret.State.Should().BeTrue(); + getRecord_ret.Result.Should().Equals(testTrue); + } - setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, "1", from, persistingBlock); - setRecord_ret.Should().BeFalse(); + //invalid A case + foreach (var testFalse in new string[] { + "1a", "256.0.0.0", "01.01.01.01", "00.0.0.0", "0.0.0.-1", + "0.0.0.0.1", "11111111.11111111.11111111.11111111", + "11111111.11111111.11111111.11111111", "ff.ff.ff.ff", "0.0.256", + "0.0.0", "0.257", "1.1", "257", + "1" + }) + { + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.A, testFalse, from, persistingBlock); + setRecord_ret.Should().BeFalse(); + } + //invalid AAAA case + foreach (var testFalse in new string[] { + "2001:DB8::8:800:200C:417A", "FF01::101", "fF01::101", + "2001:DB8:0:0:8:800:200C:417A", "FF01:0:0:0:0:0:0:101", + "::ffff:1.01.1.01", "2001:DB8:0:0:8:800:200C:4Z", "::13.1.68.3", + }) + { + setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.AAAA, testFalse, from, persistingBlock); + setRecord_ret.Should().BeFalse(); + } } [TestMethod] @@ -152,7 +140,7 @@ public void Test_SetRecord_CNAME() // committee member,add a new root and then register, setrecord string validroot = "testroot"; - var ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock).Should().BeTrue(); string name = "testname"; @@ -229,7 +217,7 @@ public void Test_SetRecord_TxT() // committee member,add a new root and then register, setrecord string validroot = "testroot"; - var ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock).Should().BeTrue(); string name = "testname"; string domain = name + "." + validroot; @@ -320,7 +308,7 @@ public void Test_SetRecord_IPV6() // committee member,add a new root and then register, setrecord string validroot = "testroot"; - var ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock).Should().BeTrue(); string name = "testname"; string domain = name + "." + validroot; @@ -336,7 +324,7 @@ public void Test_SetRecord_IPV6() register_ret.State.Should().BeTrue(); //set as IPV6 address - string testAAAA = "2001:0000:1F1F:0000:0000:0100:11A0:ADDF"; + string testAAAA = "2001:0000:1f1f:0000:0000:0100:11a0:addf"; var setRecord_ret = Check_SetRecord(snapshot, domain, RecordType.AAAA, testAAAA, from, persistingBlock); setRecord_ret.Should().BeTrue(); @@ -452,7 +440,7 @@ public void Test_SetAdmin() // committee member,add a new root and then register, setrecord string validroot = "testroot"; - var ret = Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock); + Check_AddRoot(snapshot, committeeAddress, validroot, persistingBlock).Should().BeTrue(); string name = "testname"; string domain = name + "." + validroot; From b690466dbb5a2eb292d76d6ed80c1f4e5dcf1dc7 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 5 Feb 2021 18:27:58 +0800 Subject: [PATCH 06/63] Remove BlockBase and ConsensusData (#2296) --- src/neo/Cryptography/MerkleTree.cs | 10 +- src/neo/Ledger/Blockchain.cs | 30 ++--- src/neo/Network/P2P/Payloads/Block.cs | 92 +++++-------- src/neo/Network/P2P/Payloads/BlockBase.cs | 126 ----------------- src/neo/Network/P2P/Payloads/ConsensusData.cs | 51 ------- src/neo/Network/P2P/Payloads/Header.cs | 127 ++++++++++++++++-- .../P2P/Payloads/MerkleBlockPayload.cs | 37 +++-- src/neo/SmartContract/ApplicationEngine.cs | 22 +-- src/neo/SmartContract/Native/GasToken.cs | 2 +- .../SmartContract/Native/LedgerContract.cs | 26 +--- src/neo/SmartContract/Native/TrimmedBlock.cs | 69 +++------- .../Cryptography/UT_MerkleTree.cs | 4 - tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 31 +---- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 1 + tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs | 36 +++-- .../Network/P2P/Payloads/UT_Block.cs | 32 ++--- .../Network/P2P/Payloads/UT_Header.cs | 38 +++--- .../P2P/Payloads/UT_MerkleBlockPayload.cs | 8 +- .../SmartContract/Native/UT_GasToken.cs | 4 +- .../SmartContract/Native/UT_NNS.cs | 16 ++- .../SmartContract/Native/UT_NameService.cs | 8 +- .../SmartContract/Native/UT_NeoToken.cs | 120 ++++++++++------- .../SmartContract/Native/UT_PolicyContract.cs | 74 ++++++++-- .../SmartContract/Native/UT_RoleManagement.cs | 2 +- .../SmartContract/UT_InteropService.cs | 4 +- .../SmartContract/UT_SmartContractHelper.cs | 42 +++--- .../SmartContract/UT_Syscalls.cs | 24 ++-- tests/neo.UnitTests/TestUtils.cs | 39 +++--- 28 files changed, 486 insertions(+), 589 deletions(-) delete mode 100644 src/neo/Network/P2P/Payloads/BlockBase.cs delete mode 100644 src/neo/Network/P2P/Payloads/ConsensusData.cs diff --git a/src/neo/Cryptography/MerkleTree.cs b/src/neo/Cryptography/MerkleTree.cs index 870eaabbb9..4fdb31f6e3 100644 --- a/src/neo/Cryptography/MerkleTree.cs +++ b/src/neo/Cryptography/MerkleTree.cs @@ -11,12 +11,12 @@ public class MerkleTree { private readonly MerkleTreeNode root; - public int Depth { get; private set; } + public int Depth { get; } internal MerkleTree(UInt256[] hashes) { - if (hashes is null || hashes.Length == 0) throw new ArgumentException(); this.root = Build(hashes.Select(p => new MerkleTreeNode { Hash = p }).ToArray()); + if (root is null) return; int depth = 1; for (MerkleTreeNode i = root; i.LeftChild != null; i = i.LeftChild) depth++; @@ -25,7 +25,7 @@ internal MerkleTree(UInt256[] hashes) private static MerkleTreeNode Build(MerkleTreeNode[] leaves) { - if (leaves.Length == 0) throw new ArgumentException(); + if (leaves.Length == 0) return null; if (leaves.Length == 1) return leaves[0]; Span buffer = stackalloc byte[64]; @@ -60,7 +60,7 @@ private static UInt256 Concat(Span buffer, UInt256 hash1, UInt256 hash2) public static UInt256 ComputeRoot(UInt256[] hashes) { - if (hashes.Length == 0) throw new ArgumentException(); + if (hashes.Length == 0) return UInt256.Zero; if (hashes.Length == 1) return hashes[0]; MerkleTree tree = new MerkleTree(hashes); return tree.root.Hash; @@ -83,6 +83,7 @@ private static void DepthFirstSearch(MerkleTreeNode node, IList hashes) // depth-first order public UInt256[] ToHashArray() { + if (root is null) return Array.Empty(); List hashes = new List(); DepthFirstSearch(root, hashes); return hashes.ToArray(); @@ -90,6 +91,7 @@ public UInt256[] ToHashArray() public void Trim(BitArray flags) { + if (root is null) return; flags = new BitArray(flags); flags.Length = 1 << (Depth - 1); Trim(root, 0, Depth, flags); diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 8a923e87d0..d8994c893d 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -2,7 +2,6 @@ using Akka.Configuration; using Akka.IO; using Neo.Cryptography.ECC; -using Neo.IO; using Neo.IO.Actors; using Neo.IO.Caching; using Neo.Network.P2P; @@ -13,7 +12,6 @@ using Neo.SmartContract.Native; using Neo.VM; using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -41,19 +39,19 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu public static readonly Block GenesisBlock = new Block { - PrevHash = UInt256.Zero, - Timestamp = (new DateTime(2016, 7, 15, 15, 8, 21, DateTimeKind.Utc)).ToTimestampMS(), - Index = 0, - NextConsensus = Contract.GetBFTAddress(StandbyValidators), - Witness = new Witness - { - InvocationScript = Array.Empty(), - VerificationScript = new[] { (byte)OpCode.PUSH1 } - }, - ConsensusData = new ConsensusData + Header = new Header { + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + Timestamp = (new DateTime(2016, 7, 15, 15, 8, 21, DateTimeKind.Utc)).ToTimestampMS(), + Index = 0, PrimaryIndex = 0, - Nonce = 2083236893 + NextConsensus = Contract.GetBFTAddress(StandbyValidators), + Witness = new Witness + { + InvocationScript = Array.Empty(), + VerificationScript = new[] { (byte)OpCode.PUSH1 } + }, }, Transactions = Array.Empty() }; @@ -63,7 +61,7 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu private static readonly object lockObj = new object(); private readonly NeoSystem system; private readonly IActorRef txrouter; - private readonly ConcurrentDictionary block_cache = new ConcurrentDictionary(); + private readonly Dictionary block_cache = new Dictionary(); private readonly Dictionary block_cache_unverified = new Dictionary(); internal readonly RelayCache RelayCache = new RelayCache(100); private ImmutableHashSet extensibleWitnessWhiteList; @@ -89,8 +87,6 @@ public static Blockchain Singleton static Blockchain() { - GenesisBlock.RebuildMerkleRoot(); - using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitSysCall(ApplicationEngine.System_Contract_NativeOnPersist); @@ -434,7 +430,7 @@ private void Persist(Block block) UpdateExtensibleWitnessWhiteList(snapshot); MemPool.UpdatePoolForBlockPersisted(block, snapshot); } - block_cache.TryRemove(block.PrevHash, out _); + block_cache.Remove(block.PrevHash); Context.System.EventStream.Publish(new PersistCompleted { Block = block }); if (HeaderCache.TryRemoveFirst(out Header header)) Debug.Assert(header.Index == block.Index); diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index 73b1ff97bd..7d8510f3f4 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -1,69 +1,46 @@ using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; +using Neo.Persistence; using System; -using System.Collections.Generic; using System.IO; using System.Linq; namespace Neo.Network.P2P.Payloads { - public class Block : BlockBase, IInventory, IEquatable + public sealed class Block : IEquatable, IInventory { - public const int MaxContentsPerBlock = ushort.MaxValue; - public const int MaxTransactionsPerBlock = MaxContentsPerBlock - 1; + public const int MaxTransactionsPerBlock = ushort.MaxValue; - public ConsensusData ConsensusData; + public Header Header; public Transaction[] Transactions; - private Header _header = null; - public Header Header - { - get - { - if (_header == null) - { - _header = new Header - { - PrevHash = PrevHash, - MerkleRoot = MerkleRoot, - Timestamp = Timestamp, - Index = Index, - NextConsensus = NextConsensus, - Witness = Witness - }; - } - return _header; - } - } + public UInt256 Hash => Header.Hash; + public uint Version => Header.Version; + public UInt256 PrevHash => Header.PrevHash; + public UInt256 MerkleRoot => Header.MerkleRoot; + public ulong Timestamp => Header.Timestamp; + public uint Index => Header.Index; + public byte PrimaryIndex => Header.PrimaryIndex; + public UInt160 NextConsensus => Header.NextConsensus; + public Witness Witness => Header.Witness; InventoryType IInventory.InventoryType => InventoryType.Block; + public int Size => Header.Size + Transactions.GetVarSize(); + Witness[] IVerifiable.Witnesses { get => ((IVerifiable)Header).Witnesses; set => throw new NotSupportedException(); } - public override int Size => base.Size - + IO.Helper.GetVarSize(Transactions.Length + 1) //Count - + ConsensusData.Size //ConsensusData - + Transactions.Sum(p => p.Size); //Transactions - - public static UInt256 CalculateMerkleRoot(UInt256 consensusDataHash, IEnumerable transactionHashes) + public void Deserialize(BinaryReader reader) { - return MerkleTree.ComputeRoot(transactionHashes.Prepend(consensusDataHash).ToArray()); - } - - public override void Deserialize(BinaryReader reader) - { - base.Deserialize(reader); - int count = (int)reader.ReadVarInt(MaxContentsPerBlock); - if (count == 0) throw new FormatException(); - ConsensusData = reader.ReadSerializable(); - Transactions = new Transaction[count - 1]; - for (int i = 0; i < Transactions.Length; i++) - Transactions[i] = reader.ReadSerializable(); + Header = reader.ReadSerializable
(); + Transactions = reader.ReadSerializableArray(MaxTransactionsPerBlock); if (Transactions.Distinct().Count() != Transactions.Length) throw new FormatException(); - if (CalculateMerkleRoot(ConsensusData.Hash, Transactions.Select(p => p.Hash)) != MerkleRoot) + if (MerkleTree.ComputeRoot(Transactions.Select(p => p.Hash).ToArray()) != Header.MerkleRoot) throw new FormatException(); } + void IVerifiable.DeserializeUnsigned(BinaryReader reader) => throw new NotSupportedException(); + public bool Equals(Block other) { if (ReferenceEquals(this, other)) return true; @@ -81,26 +58,27 @@ public override int GetHashCode() return Hash.GetHashCode(); } - public void RebuildMerkleRoot() - { - MerkleRoot = CalculateMerkleRoot(ConsensusData.Hash, Transactions.Select(p => p.Hash)); - } + UInt160[] IVerifiable.GetScriptHashesForVerifying(DataCache snapshot) => ((IVerifiable)Header).GetScriptHashesForVerifying(snapshot); - public override void Serialize(BinaryWriter writer) + public void Serialize(BinaryWriter writer) { - base.Serialize(writer); - writer.WriteVarInt(Transactions.Length + 1); - writer.Write(ConsensusData); - foreach (Transaction tx in Transactions) - writer.Write(tx); + writer.Write(Header); + writer.Write(Transactions); } - public override JObject ToJson() + void IVerifiable.SerializeUnsigned(BinaryWriter writer) => ((IVerifiable)Header).SerializeUnsigned(writer); + + public JObject ToJson() { - JObject json = base.ToJson(); - json["consensusdata"] = ConsensusData.ToJson(); + JObject json = Header.ToJson(); + json["size"] = Size; json["tx"] = Transactions.Select(p => p.ToJson()).ToArray(); return json; } + + public bool Verify(DataCache snapshot) + { + return Header.Verify(snapshot); + } } } diff --git a/src/neo/Network/P2P/Payloads/BlockBase.cs b/src/neo/Network/P2P/Payloads/BlockBase.cs deleted file mode 100644 index a073858cf6..0000000000 --- a/src/neo/Network/P2P/Payloads/BlockBase.cs +++ /dev/null @@ -1,126 +0,0 @@ -using Neo.IO; -using Neo.IO.Json; -using Neo.Ledger; -using Neo.Persistence; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using Neo.Wallets; -using System; -using System.IO; - -namespace Neo.Network.P2P.Payloads -{ - public abstract class BlockBase : IVerifiable - { - public uint Version; - public UInt256 PrevHash; - public UInt256 MerkleRoot; - public ulong Timestamp; - public uint Index; - public UInt160 NextConsensus; - public Witness Witness; - - private UInt256 _hash = null; - public UInt256 Hash - { - get - { - if (_hash == null) - { - _hash = this.CalculateHash(); - } - return _hash; - } - } - - public virtual int Size => - sizeof(uint) + //Version - UInt256.Length + //PrevHash - UInt256.Length + //MerkleRoot - sizeof(ulong) + //Timestamp - sizeof(uint) + //Index - UInt160.Length + //NextConsensus - 1 + //Witness array count - Witness.Size; //Witness - - Witness[] IVerifiable.Witnesses - { - get - { - return new[] { Witness }; - } - set - { - if (value.Length != 1) throw new ArgumentException(); - Witness = value[0]; - } - } - - public virtual void Deserialize(BinaryReader reader) - { - ((IVerifiable)this).DeserializeUnsigned(reader); - Witness[] witnesses = reader.ReadSerializableArray(1); - if (witnesses.Length != 1) throw new FormatException(); - Witness = witnesses[0]; - } - - void IVerifiable.DeserializeUnsigned(BinaryReader reader) - { - Version = reader.ReadUInt32(); - PrevHash = reader.ReadSerializable(); - MerkleRoot = reader.ReadSerializable(); - Timestamp = reader.ReadUInt64(); - Index = reader.ReadUInt32(); - NextConsensus = reader.ReadSerializable(); - } - - UInt160[] IVerifiable.GetScriptHashesForVerifying(DataCache snapshot) - { - if (PrevHash == UInt256.Zero) return new[] { Witness.ScriptHash }; - BlockBase prev = Blockchain.Singleton.HeaderCache[Index - 1] ?? (BlockBase)NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); - if (prev is null) throw new InvalidOperationException(); - return new[] { prev.NextConsensus }; - } - - public virtual void Serialize(BinaryWriter writer) - { - ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write(new Witness[] { Witness }); - } - - void IVerifiable.SerializeUnsigned(BinaryWriter writer) - { - writer.Write(Version); - writer.Write(PrevHash); - writer.Write(MerkleRoot); - writer.Write(Timestamp); - writer.Write(Index); - writer.Write(NextConsensus); - } - - public virtual JObject ToJson() - { - JObject json = new JObject(); - json["hash"] = Hash.ToString(); - json["size"] = Size; - json["version"] = Version; - json["previousblockhash"] = PrevHash.ToString(); - json["merkleroot"] = MerkleRoot.ToString(); - json["time"] = Timestamp; - json["index"] = Index; - json["nextconsensus"] = NextConsensus.ToAddress(); - json["witnesses"] = new JArray(Witness.ToJson()); - return json; - } - - public virtual bool Verify(DataCache snapshot) - { - var prev = Blockchain.Singleton.HeaderCache[Index - 1] ?? NativeContract.Ledger.GetHeader(snapshot, Index - 1); - if (prev is null) return false; - if (prev.Hash != PrevHash) return false; - if (prev.Timestamp >= Timestamp) return false; - if (!this.VerifyWitnesses(snapshot, 1_00000000)) return false; - return true; - } - } -} diff --git a/src/neo/Network/P2P/Payloads/ConsensusData.cs b/src/neo/Network/P2P/Payloads/ConsensusData.cs deleted file mode 100644 index 72726b8b15..0000000000 --- a/src/neo/Network/P2P/Payloads/ConsensusData.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Neo.Cryptography; -using Neo.IO; -using Neo.IO.Json; -using System; -using System.IO; - -namespace Neo.Network.P2P.Payloads -{ - public class ConsensusData : ISerializable - { - public byte PrimaryIndex; - public ulong Nonce; - - private UInt256 _hash = null; - public UInt256 Hash - { - get - { - if (_hash == null) - { - _hash = new UInt256(Crypto.Hash256(this.ToArray())); - } - return _hash; - } - } - - public int Size => sizeof(byte) + sizeof(ulong); - - void ISerializable.Deserialize(BinaryReader reader) - { - PrimaryIndex = reader.ReadByte(); - if (PrimaryIndex >= ProtocolSettings.Default.ValidatorsCount) - throw new FormatException(); - Nonce = reader.ReadUInt64(); - } - - void ISerializable.Serialize(BinaryWriter writer) - { - writer.Write(PrimaryIndex); - writer.Write(Nonce); - } - - public JObject ToJson() - { - JObject json = new JObject(); - json["primary"] = PrimaryIndex; - json["nonce"] = Nonce.ToString("x16"); - return json; - } - } -} diff --git a/src/neo/Network/P2P/Payloads/Header.cs b/src/neo/Network/P2P/Payloads/Header.cs index 301d1cf05b..6e88b5b4bb 100644 --- a/src/neo/Network/P2P/Payloads/Header.cs +++ b/src/neo/Network/P2P/Payloads/Header.cs @@ -1,16 +1,82 @@ +using Neo.IO; +using Neo.IO.Json; +using Neo.Ledger; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.Wallets; using System; using System.IO; namespace Neo.Network.P2P.Payloads { - public class Header : BlockBase, IEquatable
+ public sealed class Header : IEquatable
, IVerifiable { - public override int Size => base.Size + 1; + public uint Version; + public UInt256 PrevHash; + public UInt256 MerkleRoot; + public ulong Timestamp; + public uint Index; + public byte PrimaryIndex; + public UInt160 NextConsensus; + public Witness Witness; - public override void Deserialize(BinaryReader reader) + private UInt256 _hash = null; + public UInt256 Hash { - base.Deserialize(reader); - if (reader.ReadByte() != 0) throw new FormatException(); + get + { + if (_hash == null) + { + _hash = this.CalculateHash(); + } + return _hash; + } + } + + public int Size => + sizeof(uint) + // Version + UInt256.Length + // PrevHash + UInt256.Length + // MerkleRoot + sizeof(ulong) + // Timestamp + sizeof(uint) + // Index + sizeof(byte) + // PrimaryIndex + UInt160.Length + // NextConsensus + 1 + Witness.Size; // Witness + + Witness[] IVerifiable.Witnesses + { + get + { + return new[] { Witness }; + } + set + { + if (value.Length != 1) throw new ArgumentException(); + Witness = value[0]; + } + } + + public void Deserialize(BinaryReader reader) + { + ((IVerifiable)this).DeserializeUnsigned(reader); + Witness[] witnesses = reader.ReadSerializableArray(1); + if (witnesses.Length != 1) throw new FormatException(); + Witness = witnesses[0]; + } + + void IVerifiable.DeserializeUnsigned(BinaryReader reader) + { + Version = reader.ReadUInt32(); + if (Version > 0) throw new FormatException(); + PrevHash = reader.ReadSerializable(); + MerkleRoot = reader.ReadSerializable(); + Timestamp = reader.ReadUInt64(); + Index = reader.ReadUInt32(); + PrimaryIndex = reader.ReadByte(); + if (PrimaryIndex >= ProtocolSettings.Default.ValidatorsCount) + throw new FormatException(); + NextConsensus = reader.ReadSerializable(); } public bool Equals(Header other) @@ -30,10 +96,55 @@ public override int GetHashCode() return Hash.GetHashCode(); } - public override void Serialize(BinaryWriter writer) + UInt160[] IVerifiable.GetScriptHashesForVerifying(DataCache snapshot) + { + if (PrevHash == UInt256.Zero) return new[] { Witness.ScriptHash }; + Header prev = Blockchain.Singleton.HeaderCache[Index - 1] ?? NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash)?.Header; + if (prev is null) throw new InvalidOperationException(); + return new[] { prev.NextConsensus }; + } + + public void Serialize(BinaryWriter writer) + { + ((IVerifiable)this).SerializeUnsigned(writer); + writer.Write(new Witness[] { Witness }); + } + + void IVerifiable.SerializeUnsigned(BinaryWriter writer) + { + writer.Write(Version); + writer.Write(PrevHash); + writer.Write(MerkleRoot); + writer.Write(Timestamp); + writer.Write(Index); + writer.Write(PrimaryIndex); + writer.Write(NextConsensus); + } + + public JObject ToJson() + { + JObject json = new JObject(); + json["hash"] = Hash.ToString(); + json["size"] = Size; + json["version"] = Version; + json["previousblockhash"] = PrevHash.ToString(); + json["merkleroot"] = MerkleRoot.ToString(); + json["time"] = Timestamp; + json["index"] = Index; + json["primary"] = PrimaryIndex; + json["nextconsensus"] = NextConsensus.ToAddress(); + json["witnesses"] = new JArray(Witness.ToJson()); + return json; + } + + public bool Verify(DataCache snapshot) { - base.Serialize(writer); - writer.Write((byte)0); + var prev = Blockchain.Singleton.HeaderCache[Index - 1] ?? NativeContract.Ledger.GetHeader(snapshot, Index - 1); + if (prev is null) return false; + if (prev.Hash != PrevHash) return false; + if (prev.Timestamp >= Timestamp) return false; + if (!this.VerifyWitnesses(snapshot, 1_00000000)) return false; + return true; } } } diff --git a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs index 8a735587cd..034f6cf5a3 100644 --- a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs +++ b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs @@ -1,51 +1,48 @@ using Neo.Cryptography; using Neo.IO; +using System; using System.Collections; using System.IO; using System.Linq; namespace Neo.Network.P2P.Payloads { - public class MerkleBlockPayload : BlockBase + public class MerkleBlockPayload : ISerializable { - public int ContentCount; + public Header Header; + public int TxCount; public UInt256[] Hashes; public byte[] Flags; - public override int Size => base.Size + sizeof(int) + Hashes.GetVarSize() + Flags.GetVarSize(); + public int Size => Header.Size + sizeof(int) + Hashes.GetVarSize() + Flags.GetVarSize(); public static MerkleBlockPayload Create(Block block, BitArray flags) { - MerkleTree tree = new MerkleTree(block.Transactions.Select(p => p.Hash).Prepend(block.ConsensusData.Hash).ToArray()); + MerkleTree tree = new MerkleTree(block.Transactions.Select(p => p.Hash).ToArray()); + tree.Trim(flags); byte[] buffer = new byte[(flags.Length + 7) / 8]; flags.CopyTo(buffer, 0); return new MerkleBlockPayload { - Version = block.Version, - PrevHash = block.PrevHash, - MerkleRoot = block.MerkleRoot, - Timestamp = block.Timestamp, - Index = block.Index, - NextConsensus = block.NextConsensus, - Witness = block.Witness, - ContentCount = block.Transactions.Length + 1, + Header = block.Header, + TxCount = block.Transactions.Length, Hashes = tree.ToHashArray(), Flags = buffer }; } - public override void Deserialize(BinaryReader reader) + public void Deserialize(BinaryReader reader) { - base.Deserialize(reader); - ContentCount = (int)reader.ReadVarInt(Block.MaxTransactionsPerBlock + 1); - Hashes = reader.ReadSerializableArray(ContentCount); - Flags = reader.ReadVarBytes((ContentCount + 7) / 8); + Header = reader.ReadSerializable
(); + TxCount = (int)reader.ReadVarInt(Block.MaxTransactionsPerBlock); + Hashes = reader.ReadSerializableArray(TxCount); + Flags = reader.ReadVarBytes((Math.Max(TxCount, 1) + 7) / 8); } - public override void Serialize(BinaryWriter writer) + public void Serialize(BinaryWriter writer) { - base.Serialize(writer); - writer.WriteVarInt(ContentCount); + writer.Write(Header); + writer.WriteVarInt(TxCount); writer.Write(Hashes); writer.WriteVarBytes(Flags); } diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index b16c238303..0176d86da7 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -325,18 +325,20 @@ private static Block CreateDummyBlock(DataCache snapshot) var currentBlock = NativeContract.Ledger.GetBlock(snapshot, hash); return new Block { - Version = 0, - PrevHash = hash, - MerkleRoot = new UInt256(), - Timestamp = currentBlock.Timestamp + Blockchain.MillisecondsPerBlock, - Index = currentBlock.Index + 1, - NextConsensus = currentBlock.NextConsensus, - Witness = new Witness + Header = new Header { - InvocationScript = Array.Empty(), - VerificationScript = Array.Empty() + Version = 0, + PrevHash = hash, + MerkleRoot = new UInt256(), + Timestamp = currentBlock.Timestamp + Blockchain.MillisecondsPerBlock, + Index = currentBlock.Index + 1, + NextConsensus = currentBlock.NextConsensus, + Witness = new Witness + { + InvocationScript = Array.Empty(), + VerificationScript = Array.Empty() + }, }, - ConsensusData = new ConsensusData(), Transactions = new Transaction[0] }; } diff --git a/src/neo/SmartContract/Native/GasToken.cs b/src/neo/SmartContract/Native/GasToken.cs index a8d51122f7..c1d83f2144 100644 --- a/src/neo/SmartContract/Native/GasToken.cs +++ b/src/neo/SmartContract/Native/GasToken.cs @@ -28,7 +28,7 @@ internal override void OnPersist(ApplicationEngine engine) totalNetworkFee += tx.NetworkFee; } ECPoint[] validators = NEO.GetNextBlockValidators(engine.Snapshot); - UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.PersistingBlock.ConsensusData.PrimaryIndex]).ToScriptHash(); + UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.PersistingBlock.PrimaryIndex]).ToScriptHash(); Mint(engine, primary, totalNetworkFee, false); } } diff --git a/src/neo/SmartContract/Native/LedgerContract.cs b/src/neo/SmartContract/Native/LedgerContract.cs index 2658584c7f..9d54c76aa9 100644 --- a/src/neo/SmartContract/Native/LedgerContract.cs +++ b/src/neo/SmartContract/Native/LedgerContract.cs @@ -111,15 +111,8 @@ public Block GetBlock(DataCache snapshot, UInt256 hash) if (state is null) return null; return new Block { - Version = state.Version, - PrevHash = state.PrevHash, - MerkleRoot = state.MerkleRoot, - Timestamp = state.Timestamp, - Index = state.Index, - NextConsensus = state.NextConsensus, - Witness = state.Witness, - ConsensusData = state.ConsensusData, - Transactions = state.Hashes.Skip(1).Select(p => GetTransaction(snapshot, p)).ToArray() + Header = state.Header, + Transactions = state.Hashes.Select(p => GetTransaction(snapshot, p)).ToArray() }; } @@ -181,24 +174,17 @@ private Transaction GetTransactionFromBlock(DataCache snapshot, byte[] blockInde if (hash is null) return null; TrimmedBlock block = GetTrimmedBlock(snapshot, hash); if (block is null || !IsTraceableBlock(snapshot, block.Index)) return null; - if (txIndex < 0 || txIndex >= block.Hashes.Length - 1) + if (txIndex < 0 || txIndex >= block.Hashes.Length) throw new ArgumentOutOfRangeException(nameof(txIndex)); - return GetTransaction(snapshot, block.Hashes[txIndex + 1]); + return GetTransaction(snapshot, block.Hashes[txIndex]); } private static TrimmedBlock Trim(Block block) { return new TrimmedBlock { - Version = block.Version, - PrevHash = block.PrevHash, - MerkleRoot = block.MerkleRoot, - Timestamp = block.Timestamp, - Index = block.Index, - NextConsensus = block.NextConsensus, - Witness = block.Witness, - Hashes = block.Transactions.Select(p => p.Hash).Prepend(block.ConsensusData.Hash).ToArray(), - ConsensusData = block.ConsensusData + Header = block.Header, + Hashes = block.Transactions.Select(p => p.Hash).ToArray() }; } } diff --git a/src/neo/SmartContract/Native/TrimmedBlock.cs b/src/neo/SmartContract/Native/TrimmedBlock.cs index 11133fb9d7..33627604a7 100644 --- a/src/neo/SmartContract/Native/TrimmedBlock.cs +++ b/src/neo/SmartContract/Native/TrimmedBlock.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.IO.Json; using Neo.Network.P2P.Payloads; using Neo.VM; using Neo.VM.Types; @@ -9,59 +8,26 @@ namespace Neo.SmartContract.Native { - public class TrimmedBlock : BlockBase, IInteroperable + public class TrimmedBlock : IInteroperable, ISerializable { + public Header Header; public UInt256[] Hashes; - public ConsensusData ConsensusData; - private Header _header = null; - public Header Header - { - get - { - if (_header == null) - { - _header = new Header - { - Version = Version, - PrevHash = PrevHash, - MerkleRoot = MerkleRoot, - Timestamp = Timestamp, - Index = Index, - NextConsensus = NextConsensus, - Witness = Witness - }; - } - return _header; - } - } + public UInt256 Hash => Header.Hash; + public uint Index => Header.Index; - public override int Size => base.Size - + Hashes.GetVarSize() //Hashes - + (ConsensusData?.Size ?? 0); //ConsensusData + public int Size => Header.Size + Hashes.GetVarSize(); - public override void Deserialize(BinaryReader reader) + public void Deserialize(BinaryReader reader) { - base.Deserialize(reader); - Hashes = reader.ReadSerializableArray(Block.MaxContentsPerBlock); - if (Hashes.Length > 0) - ConsensusData = reader.ReadSerializable(); + Header = reader.ReadSerializable
(); + Hashes = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); } - public override void Serialize(BinaryWriter writer) + public void Serialize(BinaryWriter writer) { - base.Serialize(writer); + writer.Write(Header); writer.Write(Hashes); - if (Hashes.Length > 0) - writer.Write(ConsensusData); - } - - public override JObject ToJson() - { - JObject json = base.ToJson(); - json["consensusdata"] = ConsensusData?.ToJson(); - json["hashes"] = Hashes.Select(p => (JObject)p.ToString()).ToArray(); - return json; } void IInteroperable.FromStackItem(StackItem stackItem) @@ -74,15 +40,16 @@ StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) return new VM.Types.Array(referenceCounter, new StackItem[] { // Computed properties - Hash.ToArray(), + Header.Hash.ToArray(), // BlockBase properties - Version, - PrevHash.ToArray(), - MerkleRoot.ToArray(), - Timestamp, - Index, - NextConsensus.ToArray(), + Header.Version, + Header.PrevHash.ToArray(), + Header.MerkleRoot.ToArray(), + Header.Timestamp, + Header.Index, + Header.PrimaryIndex, + Header.NextConsensus.ToArray(), // Block properties Hashes.Length - 1 diff --git a/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs b/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs index 600e43faaf..612a013923 100644 --- a/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs +++ b/tests/neo.UnitTests/Cryptography/UT_MerkleTree.cs @@ -21,10 +21,6 @@ public UInt256 GetByteArrayHash(byte[] byteArray) [TestMethod] public void TestBuildAndDepthFirstSearch() { - UInt256[] hashNull = new UInt256[0]; - Action action = () => new MerkleTree(hashNull); - action.Should().Throw(); - byte[] array1 = { 0x01 }; var hash1 = GetByteArrayHash(array1); diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index 9df5c96f6f..4a6ba9bc13 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -1,45 +1,16 @@ using Akka.TestKit.Xunit2; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; -using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.Wallets; using Neo.Wallets.NEP6; using System; -using System.Linq; using System.Numerics; namespace Neo.UnitTests.Ledger { - internal class TestBlock : Block - { - public override bool Verify(DataCache snapshot) - { - return true; - } - - public static TestBlock Cast(Block input) - { - return input.ToArray().AsSerializable(); - } - } - - internal class TestHeader : Header - { - public override bool Verify(DataCache snapshot) - { - return true; - } - - public static TestHeader Cast(Header input) - { - return input.ToArray().AsSerializable(); - } - } - [TestClass] public class UT_Blockchain : TestKit { @@ -100,7 +71,7 @@ internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) return storageKey; } - private Transaction CreateValidTx(NEP6Wallet wallet, UInt160 account, uint nonce) + private static Transaction CreateValidTx(NEP6Wallet wallet, UInt160 account, uint nonce) { var tx = wallet.MakeTransaction(new TransferOutput[] { diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index c58ff4c18c..46670792ae 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -233,6 +233,7 @@ public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() var block = new Block { + Header = new Header(), Transactions = _unit.GetSortedVerifiedTransactions().Take(10).ToArray() }; diff --git a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs index c27413f1d3..80511b81a4 100644 --- a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs +++ b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs @@ -17,16 +17,18 @@ public static TrimmedBlock GetTrimmedBlockWithNoTransaction() { return new TrimmedBlock { - ConsensusData = new ConsensusData(), - MerkleRoot = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02"), - PrevHash = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"), - Timestamp = new DateTime(1988, 06, 01, 0, 0, 0, DateTimeKind.Utc).ToTimestamp(), - Index = 1, - NextConsensus = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), - Witness = new Witness + Header = new Header { - InvocationScript = Array.Empty(), - VerificationScript = new[] { (byte)OpCode.PUSH1 } + MerkleRoot = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02"), + PrevHash = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"), + Timestamp = new DateTime(1988, 06, 01, 0, 0, 0, DateTimeKind.Utc).ToTimestamp(), + Index = 1, + NextConsensus = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01"), + Witness = new Witness + { + InvocationScript = Array.Empty(), + VerificationScript = new[] { (byte)OpCode.PUSH1 } + }, }, Hashes = Array.Empty() }; @@ -72,10 +74,10 @@ public void TestGetBlock() block.Index.Should().Be(1); block.MerkleRoot.Should().Be(UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff02")); - block.Transactions.Length.Should().Be(1); - block.Transactions[0].Hash.Should().Be(tx2.Hash); - block.Witness.InvocationScript.ToHexString().Should().Be(tblock.Witness.InvocationScript.ToHexString()); - block.Witness.VerificationScript.ToHexString().Should().Be(tblock.Witness.VerificationScript.ToHexString()); + block.Transactions.Length.Should().Be(2); + block.Transactions[0].Hash.Should().Be(tx1.Hash); + block.Witness.InvocationScript.ToHexString().Should().Be(tblock.Header.Witness.InvocationScript.ToHexString()); + block.Witness.VerificationScript.ToHexString().Should().Be(tblock.Header.Witness.VerificationScript.ToHexString()); } [TestMethod] @@ -92,7 +94,7 @@ public void TestGetSize() { TrimmedBlock tblock = GetTrimmedBlockWithNoTransaction(); tblock.Hashes = new UInt256[] { TestUtils.GetTransaction(UInt160.Zero).Hash }; - tblock.Size.Should().Be(146); + tblock.Size.Should().Be(138); } [TestMethod] @@ -109,12 +111,8 @@ public void TestDeserialize() ms.Seek(0, SeekOrigin.Begin); newBlock.Deserialize(reader); } - tblock.MerkleRoot.Should().Be(newBlock.MerkleRoot); - tblock.PrevHash.Should().Be(newBlock.PrevHash); - tblock.Timestamp.Should().Be(newBlock.Timestamp); tblock.Hashes.Length.Should().Be(newBlock.Hashes.Length); - tblock.Witness.ScriptHash.Should().Be(newBlock.Witness.ScriptHash); - tblock.ToJson().ToString().Should().Be(newBlock.ToJson().ToString()); + tblock.Header.ToJson().ToString().Should().Be(newBlock.Header.ToJson().ToString()); } } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index a1ea5635c1..29503277f7 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -43,9 +43,9 @@ public void Size_Get() { UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 0); - // blockbase 4 + 64 + 1 + 32 + 4 + 4 + 20 + 4 - // block 9 + 1 - uut.Size.Should().Be(114); + // header 4 + 32 + 32 + 8 + 4 + 1 + 20 + 4 + // tx 1 + uut.Size.Should().Be(106); } [TestMethod] @@ -59,7 +59,7 @@ public void Size_Get_1_Transaction() TestUtils.GetTransaction(UInt160.Zero) }; - uut.Size.Should().Be(167); + uut.Size.Should().Be(159); } [TestMethod] @@ -75,7 +75,7 @@ public void Size_Get_3_Transaction() TestUtils.GetTransaction(UInt160.Zero) }; - uut.Size.Should().Be(273); + uut.Size.Should().Be(265); } [TestMethod] @@ -84,7 +84,7 @@ public void Serialize() UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 1); - var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007ee5991fa69cf4d7902430f5fad89ba13b253b5680cb13167f80bfc3593947e7e913ff854c00000000000000000000000000000000000000000000000000000001000111020000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000003c92d2d37b91caa73ab056d0676d9bd78752a5e2e79e3ae27936e9b3a330e80ee913ff854c0000000000000000000000000000000000000000000000000000000001000111010000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; uut.ToArray().ToHexString().Should().Be(hex); } @@ -94,7 +94,7 @@ public void Deserialize() UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(new Block(), val256, out var merkRoot, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 1); - var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007ee5991fa69cf4d7902430f5fad89ba13b253b5680cb13167f80bfc3593947e7e913ff854c00000000000000000000000000000000000000000000000000000001000111020000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000003c92d2d37b91caa73ab056d0676d9bd78752a5e2e79e3ae27936e9b3a330e80ee913ff854c0000000000000000000000000000000000000000000000000000000001000111010000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) using (BinaryReader reader = new BinaryReader(ms)) @@ -158,18 +158,6 @@ public void Equals_SameHash() uut.Equals(newBlock).Should().BeTrue(); } - [TestMethod] - public void RebuildMerkleRoot_Updates() - { - UInt256 val256 = UInt256.Zero; - TestUtils.SetupBlockWithValues(uut, val256, out UInt256 merkRoot, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, 1); - UInt256 merkleRoot = uut.MerkleRoot; - - TestUtils.SetupBlockWithValues(uut, val256, out merkRoot, out val160, out timestampVal, out indexVal, out scriptVal, out transactionsVal, 3); - uut.RebuildMerkleRoot(); - uut.MerkleRoot.Should().NotBe(merkleRoot); - } - [TestMethod] public void ToJson() { @@ -178,11 +166,11 @@ public void ToJson() JObject jObj = uut.ToJson(); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0xaf156a193ba2d7f05a37dd4e6fdfd1167cb6db3df5474df43a71a645a449fbc8"); - jObj["size"].AsNumber().Should().Be(167); + jObj["hash"].AsString().Should().Be("0x0ffaf8fc7eeb34959e7220d316e3294290e84b6a183b4bdb5c7c14a8f80239ee"); + jObj["size"].AsNumber().Should().Be(159); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - jObj["merkleroot"].AsString().Should().Be("0xe7473959c3bf807f1613cb80563b253ba19bd8faf5302490d7f49ca61f99e57e"); + jObj["merkleroot"].AsString().Should().Be("0x0ee830a3b3e93679e23a9ee7e2a55287d79b6d67d056b03aa7ca917bd3d2923c"); jObj["time"].AsNumber().Should().Be(328665601001); jObj["index"].AsNumber().Should().Be(0); jObj["nextconsensus"].AsString().Should().Be("NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf"); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index fcc9c3e1ee..489a5d1c1c 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -5,6 +5,7 @@ using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; using Neo.UnitTests.SmartContract; +using System; using System.IO; namespace Neo.UnitTests.Network.P2P.Payloads @@ -49,24 +50,27 @@ public void TrimTest() UT_SmartContractHelper.BlocksAdd(snapshot, uut.Hash, new TrimmedBlock() { - Timestamp = uut.Timestamp, - PrevHash = uut.PrevHash, - MerkleRoot = uut.MerkleRoot, - ConsensusData = new ConsensusData(), - Hashes = new UInt256[0], - NextConsensus = uut.NextConsensus, - Witness = uut.Witness + Header = new Header + { + Timestamp = uut.Timestamp, + PrevHash = uut.PrevHash, + MerkleRoot = uut.MerkleRoot, + NextConsensus = uut.NextConsensus, + Witness = uut.Witness + }, + Hashes = Array.Empty() }); var trim = NativeContract.Ledger.GetTrimmedBlock(snapshot, uut.Hash); - - trim.Version.Should().Be(uut.Version); - trim.PrevHash.Should().Be(uut.PrevHash); - trim.MerkleRoot.Should().Be(uut.MerkleRoot); - trim.Timestamp.Should().Be(uut.Timestamp); - trim.Index.Should().Be(uut.Index); - trim.NextConsensus.Should().Be(uut.NextConsensus); - trim.Witness.Should().BeEquivalentTo(uut.Witness); + var header = trim.Header; + + header.Version.Should().Be(uut.Version); + header.PrevHash.Should().Be(uut.PrevHash); + header.MerkleRoot.Should().Be(uut.MerkleRoot); + header.Timestamp.Should().Be(uut.Timestamp); + header.Index.Should().Be(uut.Index); + header.NextConsensus.Should().Be(uut.NextConsensus); + header.Witness.Should().BeEquivalentTo(uut.Witness); trim.Hashes.Length.Should().Be(0); } @@ -78,7 +82,7 @@ public void Deserialize() uut.MerkleRoot = merkRoot; // need to set for deserialise to be valid - var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c0000000000000000000000000000000000000000000000000000000100011100"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c0000000000000000000000000000000000000000000000000000000001000111"; using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) { @@ -137,7 +141,7 @@ public void Serialize() UInt256 val256 = UInt256.Zero; TestUtils.SetupHeaderWithValues(uut, val256, out _, out _, out _, out _, out _); - var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c0000000000000000000000000000000000000000000000000000000100011100"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000007227ba7b747f1a98f68679d4a98b68927646ab195a6f56b542ca5a0e6a412662e913ff854c0000000000000000000000000000000000000000000000000000000001000111"; uut.ToArray().ToHexString().Should().Be(hex); } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs index 146a272f04..b1dfebd72f 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs @@ -20,10 +20,10 @@ public void TestSetup() public void Size_Get() { var test = MerkleBlockPayload.Create(Blockchain.GenesisBlock, new BitArray(1024, false)); - test.Size.Should().Be(270); + test.Size.Should().Be(239); test = MerkleBlockPayload.Create(Blockchain.GenesisBlock, new BitArray(0, false)); - test.Size.Should().Be(142); + test.Size.Should().Be(111); } [TestMethod] @@ -32,8 +32,8 @@ public void DeserializeAndSerialize() var test = MerkleBlockPayload.Create(Blockchain.GenesisBlock, new BitArray(2, false)); var clone = test.ToArray().AsSerializable(); - Assert.AreEqual(test.ContentCount, clone.ContentCount); - Assert.AreEqual(test.Hashes.Length, clone.ContentCount); + Assert.AreEqual(test.TxCount, clone.TxCount); + Assert.AreEqual(test.Hashes.Length, clone.TxCount); CollectionAssert.AreEqual(test.Hashes, clone.Hashes); CollectionAssert.AreEqual(test.Flags, clone.Flags); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs index ada89b0973..dc20d913ed 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -26,7 +26,7 @@ public void TestSetup() TestBlockchain.InitializeMockNeoSystem(); _snapshot = Blockchain.Singleton.GetSnapshot(); - _persistingBlock = new Block() { Index = 0 }; + _persistingBlock = new Block { Header = new Header() }; } [TestMethod] @@ -42,7 +42,7 @@ public void TestSetup() public void Check_BalanceOfTransferAndBurn() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); byte[] to = new byte[20]; var keyCount = snapshot.GetChangeSet().Count(); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs index 2624434dd2..13dd7c4ee4 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs @@ -33,7 +33,11 @@ public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); _snapshot = Blockchain.Singleton.GetSnapshot(); - _persistingBlock = new Block() { Index = 0, Transactions = Array.Empty(), ConsensusData = new ConsensusData() }; + _persistingBlock = new Block + { + Header = new Header(), + Transactions = Array.Empty() + }; } [TestMethod] @@ -48,7 +52,7 @@ public void Test_SetRecord_A_AAAA() var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); // committee member,add a new root and then register, setrecord @@ -135,7 +139,7 @@ public void Test_SetRecord_CNAME() var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); // committee member,add a new root and then register, setrecord @@ -212,7 +216,7 @@ public void Test_SetRecord_TxT() var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); // committee member,add a new root and then register, setrecord @@ -303,7 +307,7 @@ public void Test_SetRecord_IPV6() var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); // committee member,add a new root and then register, setrecord @@ -435,7 +439,7 @@ public void Test_SetAdmin() var snapshot = _snapshot.CreateSnapshot(); // Fake blockchain - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; UInt160 committeeAddress = NativeContract.NEO.GetCommitteeAddress(snapshot); // committee member,add a new root and then register, setrecord diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs index 8e87e36c4c..c255521554 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs @@ -38,7 +38,7 @@ public void TestInfo() public void TestRoots() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; var from = NativeContract.NEO.GetCommitteeAddress(snapshot); // no match @@ -63,7 +63,7 @@ public void TestRoots() public void TestPrice() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; var from = NativeContract.NEO.GetCommitteeAddress(snapshot); // unsigned @@ -88,7 +88,7 @@ public void TestPrice() public void TestRegister() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000, Timestamp = 0 }; + var persistingBlock = new Block { Header = new Header { Index = 1000, Timestamp = 0 } }; var from = NativeContract.NEO.GetCommitteeAddress(snapshot); // add root @@ -127,7 +127,7 @@ public void TestRegister() public void TestSetRecord() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000, Timestamp = 0 }; + var persistingBlock = new Block { Header = new Header { Index = 1000, Timestamp = 0 } }; var from = NativeContract.NEO.GetCommitteeAddress(snapshot); // add root diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 63adec3fb4..2c85b25e65 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -29,7 +29,11 @@ public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); _snapshot = Blockchain.Singleton.GetSnapshot(); - _persistingBlock = new Block() { Index = 0, Transactions = Array.Empty(), ConsensusData = new ConsensusData() }; + _persistingBlock = new Block + { + Header = new Header(), + Transactions = Array.Empty() + }; } [TestMethod] @@ -45,7 +49,7 @@ public void TestSetup() public void Check_Vote() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); @@ -99,7 +103,7 @@ public void Check_Vote() public void Check_Vote_Sameaccounts() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); var accountState = snapshot.TryGet(CreateStorageKey(20, from)).GetInteroperable(); @@ -126,7 +130,7 @@ public void Check_Vote_Sameaccounts() public void Check_Vote_ChangeVote() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; //from vote to G byte[] from = Blockchain.StandbyValidators[0].ToArray(); var from_Account = Contract.CreateSignatureContract(Blockchain.StandbyValidators[0]).ScriptHash.ToArray(); @@ -157,7 +161,7 @@ public void Check_Vote_ChangeVote() public void Check_Vote_VoteToNull() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; byte[] from = Blockchain.StandbyValidators[0].ToArray(); var from_Account = Contract.CreateSignatureContract(Blockchain.StandbyValidators[0]).ScriptHash.ToArray(); @@ -187,7 +191,7 @@ public void Check_Vote_VoteToNull() public void Check_UnclaimedGas() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); @@ -317,13 +321,15 @@ public void Check_GetCommittee() //register more candidates, committee member change persistingBlock = new Block { - Index = (uint)ProtocolSettings.Default.CommitteeMembersCount, - Transactions = Array.Empty(), - ConsensusData = new ConsensusData(), - MerkleRoot = UInt256.Zero, - NextConsensus = UInt160.Zero, - PrevHash = UInt256.Zero, - Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } + Header = new Header + { + Index = (uint)ProtocolSettings.Default.CommitteeMembersCount, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero, + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } + }, + Transactions = Array.Empty() }; for (int i = 0; i < ProtocolSettings.Default.CommitteeMembersCount - 1; i++) { @@ -348,7 +354,7 @@ public void Check_GetCommittee() public void Check_Transfer() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block() { Index = 1000 }; + var persistingBlock = new Block { Header = new Header { Index = 1000 } }; byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); byte[] to = new byte[20]; @@ -414,13 +420,15 @@ public void Check_CommitteeBonus() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { - Index = 1, - Transactions = Array.Empty(), - Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, - ConsensusData = new ConsensusData(), - MerkleRoot = UInt256.Zero, - NextConsensus = UInt160.Zero, - PrevHash = UInt256.Zero + Header = new Header + { + Index = 1, + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero + }, + Transactions = Array.Empty() }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); @@ -457,7 +465,7 @@ public void Check_BadScript() public void TestCalculateBonus() { var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block { Index = 0 }; + var persistingBlock = new Block(); StorageKey key = CreateStorageKey(20, UInt160.Zero.ToArray()); @@ -596,13 +604,15 @@ public void TestCheckCandidate() // Pre-persist var persistingBlock = new Block { - Index = 21, - ConsensusData = new ConsensusData(), - Transactions = Array.Empty(), - Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, - MerkleRoot = UInt256.Zero, - NextConsensus = UInt160.Zero, - PrevHash = UInt256.Zero + Header = new Header + { + Index = 21, + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero + }, + Transactions = Array.Empty() }; Check_OnPersist(snapshot, persistingBlock).Should().BeTrue(); @@ -699,13 +709,13 @@ public void TestEconomicParameter() { const byte Prefix_CurrentBlock = 12; var snapshot = _snapshot.CreateSnapshot(); - var persistingBlock = new Block { Index = 0 }; + var persistingBlock = new Block { Header = new Header() }; (BigInteger, bool) result = Check_GetGasPerBlock(snapshot, persistingBlock); result.Item2.Should().BeTrue(); result.Item1.Should().Be(5 * NativeContract.GAS.Factor); - persistingBlock = new Block { Index = 10 }; + persistingBlock = new Block { Header = new Header { Index = 10 } }; (VM.Types.Boolean, bool) result1 = Check_SetGasPerBlock(snapshot, 10 * NativeContract.GAS.Factor, persistingBlock); result1.Item2.Should().BeTrue(); result1.Item1.GetBoolean().Should().BeTrue(); @@ -752,13 +762,15 @@ public void TestClaimGas() var persistingBlock = new Block { - Index = 0, - Transactions = Array.Empty(), - Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, - ConsensusData = new ConsensusData(), - MerkleRoot = UInt256.Zero, - NextConsensus = UInt160.Zero, - PrevHash = UInt256.Zero + Header = new Header + { + Index = 0, + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero + }, + Transactions = Array.Empty() }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); @@ -776,13 +788,15 @@ public void TestClaimGas() persistingBlock = new Block { - Index = 1, - Transactions = Array.Empty(), - Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, - ConsensusData = new ConsensusData(), - MerkleRoot = UInt256.Zero, - NextConsensus = UInt160.Zero, - PrevHash = UInt256.Zero + Header = new Header + { + Index = 1, + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero + }, + Transactions = Array.Empty() }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); @@ -795,13 +809,15 @@ public void TestClaimGas() persistingBlock = new Block { - Index = 21, - Transactions = Array.Empty(), - Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, - ConsensusData = new ConsensusData(), - MerkleRoot = UInt256.Zero, - NextConsensus = UInt160.Zero, - PrevHash = UInt256.Zero + Header = new Header + { + Index = 21, + Witness = new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() }, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + PrevHash = UInt256.Zero + }, + Transactions = Array.Empty() }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index ede06e3665..fde993fc51 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -23,7 +23,7 @@ public void TestSetup() TestBlockchain.InitializeMockNeoSystem(); _snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, _snapshot, new Block(), 0); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, _snapshot, new Block { Header = new Header() }, 0); NativeContract.ContractManagement.OnPersist(engine); } @@ -56,7 +56,14 @@ public void Check_SetMaxBlockSize() // Fake blockchain - Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() + { + Header = new Header + { + Index = 1000, + PrevHash = UInt256.Zero + } + }; UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); // Without signature @@ -101,7 +108,14 @@ public void Check_SetMaxBlockSystemFee() // Fake blockchain - Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() + { + Header = new Header + { + Index = 1000, + PrevHash = UInt256.Zero + } + }; UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); // Without signature @@ -146,7 +160,14 @@ public void Check_SetMaxTransactionsPerBlock() // Fake blockchain - Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() + { + Header = new Header + { + Index = 1000, + PrevHash = UInt256.Zero + } + }; // Without signature @@ -178,7 +199,14 @@ public void Check_SetFeePerByte() // Fake blockchain - Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() + { + Header = new Header + { + Index = 1000, + PrevHash = UInt256.Zero + } + }; // Without signature @@ -210,7 +238,14 @@ public void Check_SetBaseExecFee() // Fake blockchain - Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() + { + Header = new Header + { + Index = 1000, + PrevHash = UInt256.Zero + } + }; // Without signature @@ -253,7 +288,14 @@ public void Check_SetStoragePrice() // Fake blockchain - Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() + { + Header = new Header + { + Index = 1000, + PrevHash = UInt256.Zero + } + }; // Without signature @@ -296,7 +338,14 @@ public void Check_BlockAccount() // Fake blockchain - Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() + { + Header = new Header + { + Index = 1000, + PrevHash = UInt256.Zero + } + }; // Without signature @@ -345,7 +394,14 @@ public void Check_Block_UnblockAccount() // Fake blockchain - Block block = new Block() { Index = 1000, PrevHash = UInt256.Zero }; + Block block = new Block() + { + Header = new Header + { + Index = 1000, + PrevHash = UInt256.Zero + } + }; UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); // Block without signature diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs index d4088258f8..02030ff94e 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs @@ -34,7 +34,7 @@ public void TestSetAndGet() var ret = NativeContract.RoleManagement.Call( snapshot1, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), - new Block(), + new Block { Header = new Header() }, "designateAsRole", new ContractParameter(ContractParameterType.Integer) { Value = new BigInteger((int)Role.StateValidator) }, new ContractParameter(ContractParameterType.Array) { Value = validators.Select(p => new ContractParameter(ContractParameterType.ByteArray) { Value = p.ToArray() }).ToList() } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 2ef9ac2de8..8fe520577c 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -281,7 +281,7 @@ public void TestRuntime_Log() [TestMethod] public void TestRuntime_GetTime() { - Block block = new Block(); + Block block = new Block { Header = new Header() }; var engine = GetEngine(true, true, hasBlock: true); engine.GetTime().Should().Be(block.Timestamp); } @@ -671,7 +671,7 @@ private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSn { var tx = hasContainer ? TestUtils.GetTransaction(UInt160.Zero) : null; var snapshot = hasSnapshot ? Blockchain.Singleton.GetSnapshot() : null; - var block = hasBlock ? new Block() : null; + var block = hasBlock ? new Block { Header = new Header() } : null; ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, block, gas); if (addScript) engine.LoadScript(new byte[] { 0x01 }); return engine; diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index c3d3209588..e91dbc93e7 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -127,13 +127,15 @@ public void TestVerifyWitnesses() UInt256 index1 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); BlocksAdd(snapshot1, index1, new TrimmedBlock() { - Timestamp = 1, - PrevHash = UInt256.Zero, - MerkleRoot = UInt256.Zero, - ConsensusData = new ConsensusData(), + Header = new Header + { + Timestamp = 1, + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } + }, Hashes = new UInt256[1] { UInt256.Zero }, - NextConsensus = UInt160.Zero, - Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } }); BlocksDelete(snapshot1, index1); Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(new Header() { PrevHash = index1 }, snapshot1, 100)); @@ -142,13 +144,15 @@ public void TestVerifyWitnesses() UInt256 index2 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); TrimmedBlock block2 = new TrimmedBlock() { - Timestamp = 2, - PrevHash = UInt256.Zero, - MerkleRoot = UInt256.Zero, - ConsensusData = new ConsensusData(), + Header = new Header + { + Timestamp = 2, + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } + }, Hashes = new UInt256[1] { UInt256.Zero }, - NextConsensus = UInt160.Zero, - Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } }; BlocksAdd(snapshot2, index2, block2); Header header2 = new Header() { PrevHash = index2, Witness = new Witness { InvocationScript = new byte[0], VerificationScript = new byte[0] } }; @@ -161,13 +165,15 @@ public void TestVerifyWitnesses() UInt256 index3 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); TrimmedBlock block3 = new TrimmedBlock() { - Timestamp = 3, - PrevHash = UInt256.Zero, - MerkleRoot = UInt256.Zero, - ConsensusData = new ConsensusData(), + Header = new Header + { + Timestamp = 3, + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + NextConsensus = UInt160.Zero, + Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } + }, Hashes = new UInt256[1] { UInt256.Zero }, - NextConsensus = UInt160.Zero, - Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] } }; BlocksAdd(snapshot3, index3, block3); Header header3 = new Header() diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 7ce2aed12c..149378e159 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -39,19 +39,21 @@ public void System_Blockchain_GetBlock() var block = new TrimmedBlock() { - Index = 0, - Timestamp = 2, - Version = 3, - Witness = new Witness() + Header = new Header { - InvocationScript = new byte[0], - VerificationScript = new byte[0] + Index = 0, + Timestamp = 2, + Witness = new Witness() + { + InvocationScript = new byte[0], + VerificationScript = new byte[0] + }, + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + PrimaryIndex = 1, + NextConsensus = UInt160.Zero, }, - PrevHash = UInt256.Zero, - MerkleRoot = UInt256.Zero, - NextConsensus = UInt160.Zero, - ConsensusData = new ConsensusData() { Nonce = 1, PrimaryIndex = 1 }, - Hashes = new UInt256[] { new ConsensusData() { Nonce = 1, PrimaryIndex = 1 }.Hash, tx.Hash } + Hashes = new[] { tx.Hash } }; var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index d68ba3a93f..8bf1ff8d5b 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -1,4 +1,5 @@ using FluentAssertions; +using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; using Neo.Network.P2P.Payloads; @@ -158,44 +159,36 @@ internal static StorageKey GetStorageKey(int id, byte[] keyValue) public static void SetupHeaderWithValues(Header header, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal) { - setupBlockBaseWithValues(header, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal); + header.PrevHash = val256; + header.MerkleRoot = merkRootVal = UInt256.Parse("0x6226416a0e5aca42b5566f5a19ab467692688ba9d47986f6981a7f747bba2772"); + header.Timestamp = timestampVal = new DateTime(1980, 06, 01, 0, 0, 1, 001, DateTimeKind.Utc).ToTimestampMS(); // GMT: Sunday, June 1, 1980 12:00:01.001 AM + header.Index = indexVal = 0; + header.NextConsensus = val160 = UInt160.Zero; + header.Witness = scriptVal = new Witness + { + InvocationScript = new byte[0], + VerificationScript = new[] { (byte)OpCode.PUSH1 } + }; } public static void SetupBlockWithValues(Block block, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal, out Transaction[] transactionsVal, int numberOfTransactions) { - setupBlockBaseWithValues(block, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal); + Header header = new Header(); + SetupHeaderWithValues(header, val256, out merkRootVal, out val160, out timestampVal, out indexVal, out scriptVal); transactionsVal = new Transaction[numberOfTransactions]; if (numberOfTransactions > 0) { for (int i = 0; i < numberOfTransactions; i++) { - transactionsVal[i] = TestUtils.GetTransaction(UInt160.Zero); + transactionsVal[i] = GetTransaction(UInt160.Zero); } } - block.ConsensusData = new ConsensusData(); + block.Header = header; block.Transactions = transactionsVal; - block.MerkleRoot = merkRootVal = Block.CalculateMerkleRoot(block.ConsensusData.Hash, block.Transactions.Select(p => p.Hash)); - } - private static void setupBlockBaseWithValues(BlockBase bb, UInt256 val256, out UInt256 merkRootVal, out UInt160 val160, out ulong timestampVal, out uint indexVal, out Witness scriptVal) - { - bb.PrevHash = val256; - merkRootVal = UInt256.Parse("0x6226416a0e5aca42b5566f5a19ab467692688ba9d47986f6981a7f747bba2772"); - bb.MerkleRoot = merkRootVal; - timestampVal = new DateTime(1980, 06, 01, 0, 0, 1, 001, DateTimeKind.Utc).ToTimestampMS(); // GMT: Sunday, June 1, 1980 12:00:01.001 AM - bb.Timestamp = timestampVal; - indexVal = 0; - bb.Index = indexVal; - val160 = UInt160.Zero; - bb.NextConsensus = val160; - scriptVal = new Witness - { - InvocationScript = new byte[0], - VerificationScript = new[] { (byte)OpCode.PUSH1 } - }; - bb.Witness = scriptVal; + header.MerkleRoot = merkRootVal = MerkleTree.ComputeRoot(block.Transactions.Select(p => p.Hash).ToArray()); } public static Transaction CreateRandomHashTransaction() From c290e31dba3cb66ef964d3689f7df50ef6ef19b4 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 6 Feb 2021 02:04:34 +0800 Subject: [PATCH 07/63] Optimize MemoryPool (#2297) --- src/neo/Ledger/HeaderCache.cs | 1 + src/neo/Ledger/MemoryPool.cs | 8 +++++++ tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 25 +++++++++++++++++---- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/neo/Ledger/HeaderCache.cs b/src/neo/Ledger/HeaderCache.cs index ea3380baa9..3c65c408d9 100644 --- a/src/neo/Ledger/HeaderCache.cs +++ b/src/neo/Ledger/HeaderCache.cs @@ -33,6 +33,7 @@ public Header this[uint index] } } + public int Count => headers.Count; public bool Full => headers.Count >= 10000; public Header Last diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index be505878b6..32abaa566a 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -361,6 +361,11 @@ internal void UpdatePoolForBlockPersisted(Block block, DataCache snapshot) _txRwLock.ExitWriteLock(); } + // If we know about headers of future blocks, no point in verifying transactions from the unverified tx pool + // until we get caught up. + if (block.Index > 0 && Blockchain.Singleton.HeaderCache.Count > 0) + return; + uint _maxTxPerBlock = NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, (int)_maxTxPerBlock, MaxMillisecondsToReverifyTx, snapshot); } @@ -457,6 +462,9 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, /// true if more unsorted messages exist, otherwise false internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, DataCache snapshot) { + if (Blockchain.Singleton.HeaderCache.Count > 0) + return false; + if (_unverifiedSortedTransactions.Count > 0) { uint _maxTxPerBlock = NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 46670792ae..62010a03a5 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -184,6 +184,7 @@ public void BlockPersistMovesTxToUnverifiedAndReverification() var block = new Block { + Header = new Header(), Transactions = _unit.GetSortedVerifiedTransactions().Take(10) .Concat(_unit.GetSortedVerifiedTransactions().Take(5)).ToArray() }; @@ -288,7 +289,11 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() VerifyTransactionsSortedDescending(sortedVerifiedTxs); // move all to unverified - var block = new Block { Transactions = new Transaction[0] }; + var block = new Block + { + Header = new Header(), + Transactions = Array.Empty() + }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); _unit.SortedTxCount.Should().Be(0); @@ -309,7 +314,11 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() var verifiedTxs = _unit.GetSortedVerifiedTransactions().ToArray(); verifiedTxs.Length.Should().Be(1); verifiedTxs[0].Should().BeEquivalentTo(maxTransaction); - var blockWith2Tx = new Block { Transactions = new[] { maxTransaction, minTransaction } }; + var blockWith2Tx = new Block + { + Header = new Header(), + Transactions = new[] { maxTransaction, minTransaction } + }; // verify and remove the 2 transactions from the verified pool _unit.UpdatePoolForBlockPersisted(blockWith2Tx, Blockchain.Singleton.GetSnapshot()); _unit.InvalidateVerifiedTransactions(); @@ -349,7 +358,11 @@ public void CapacityTestWithUnverifiedHighProirtyTransactions() AddTransactions(99); // move all to unverified - var block = new Block { Transactions = new Transaction[0] }; + var block = new Block + { + Header = new Header(), + Transactions = Array.Empty() + }; _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); _unit.CanTransactionFitInPool(CreateTransaction()).Should().Be(true); @@ -513,7 +526,11 @@ public void TestUpdatePoolForBlockPersisted() Transaction[] transactions = { tx1, tx2 }; _unit.TryAdd(tx1, snapshot); - var block = new Block { Transactions = transactions }; + var block = new Block + { + Header = new Header(), + Transactions = transactions + }; _unit.UnVerifiedCount.Should().Be(0); _unit.VerifiedCount.Should().Be(1); From 11afccb51b15e8974c2bbc913c8ad4c25b5edc80 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 7 Feb 2021 03:41:46 +0800 Subject: [PATCH 08/63] Call native contracts like a normal contract (#2301) --- .../ApplicationEngine.Contract.cs | 10 +- src/neo/SmartContract/ApplicationEngine.cs | 2 - src/neo/SmartContract/Helper.cs | 10 -- .../Native/ContractMethodMetadata.cs | 46 ++++++- .../SmartContract/Native/NativeContract.cs | 121 ++++++------------ src/neo/Wallets/Wallet.cs | 1 - .../Extensions/NativeContractExtensions.cs | 15 +-- .../Nep17NativeContractExtensions.cs | 45 +++---- .../SmartContract/Native/UT_GasToken.cs | 13 -- .../SmartContract/Native/UT_NNS.cs | 54 ++------ .../SmartContract/Native/UT_NativeContract.cs | 39 ++++-- .../SmartContract/Native/UT_NeoToken.cs | 50 +------- .../SmartContract/Native/UT_PolicyContract.cs | 12 +- .../SmartContract/UT_InteropService.cs | 6 +- tests/neo.UnitTests/VM/UT_Helper.cs | 2 +- 15 files changed, 158 insertions(+), 268 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 74149094aa..30111dcb2d 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -38,12 +38,12 @@ protected internal void CallContract(UInt160 contractHash, string method, CallFl CallContractInternal(contract, md, callFlags, hasReturnValue, args); } - protected internal void CallNativeContract(int id) + protected internal void CallNativeContract(byte version) { - NativeContract contract = NativeContract.GetContract(id); - if (contract is null || contract.ActiveBlockIndex > NativeContract.Ledger.CurrentIndex(Snapshot)) - throw new InvalidOperationException(); - contract.Invoke(this); + NativeContract contract = NativeContract.GetContract(CurrentScriptHash); + if (contract is null) + throw new InvalidOperationException("It is not allowed to use \"System.Contract.CallNative\" directly."); + contract.Invoke(this, version); } protected internal bool IsStandardContract(UInt160 hash) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 0176d86da7..5b9e894fe0 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -119,8 +119,6 @@ private ExecutionContext CallContractInternal(ContractState contract, ContractMe for (int i = args.Count - 1; i >= 0; i--) context_new.EvaluationStack.Push(args[i]); - if (NativeContract.IsNative(contract.Hash)) - context_new.EvaluationStack.Push(method.Name); return context_new; } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index d1296ed8a8..088b6b7c7b 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -229,16 +229,6 @@ internal static bool VerifyWitness(this IVerifiable verifiable, DataCache snapsh engine.LoadScript(invocationScript, configureState: p => p.CallFlags = CallFlags.None); - if (NativeContract.IsNative(hash)) - { - try - { - engine.StepOut(); - engine.Push("verify"); - } - catch { } - } - if (engine.Execute() == VMState.FAULT) return false; if (!engine.ResultStack.Peek().GetBoolean()) return false; fee = engine.GasConsumed; diff --git a/src/neo/SmartContract/Native/ContractMethodMetadata.cs b/src/neo/SmartContract/Native/ContractMethodMetadata.cs index ffe7b7f3e3..eca5f2abc1 100644 --- a/src/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -1,7 +1,12 @@ +using Neo.IO; using Neo.Persistence; +using Neo.SmartContract.Manifest; +using Neo.VM.Types; using System; using System.Linq; +using System.Numerics; using System.Reflection; +using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native { @@ -14,6 +19,7 @@ internal class ContractMethodMetadata public bool NeedSnapshot { get; } public long Price { get; } public CallFlags RequiredCallFlags { get; } + public ContractMethodDescriptor Descriptor { get; } public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribute) { @@ -22,7 +28,7 @@ public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribu { MethodInfo m => m, PropertyInfo p => p.GetMethod, - _ => throw new ArgumentException(nameof(member)) + _ => throw new ArgumentException(null, nameof(member)) }; ParameterInfo[] parameterInfos = this.Handler.GetParameters(); if (parameterInfos.Length > 0) @@ -36,6 +42,44 @@ public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribu this.Parameters = parameterInfos.Select(p => new InteropParameterDescriptor(p)).ToArray(); this.Price = attribute.Price; this.RequiredCallFlags = attribute.RequiredCallFlags; + this.Descriptor = new ContractMethodDescriptor + { + Name = Name, + ReturnType = ToParameterType(Handler.ReturnType), + Parameters = Parameters.Select(p => new ContractParameterDefinition { Type = ToParameterType(p.Type), Name = p.Name }).ToArray(), + Safe = (attribute.RequiredCallFlags & ~CallFlags.ReadOnly) == 0 + }; + } + + private static ContractParameterType ToParameterType(Type type) + { + if (type == typeof(void)) return ContractParameterType.Void; + if (type == typeof(bool)) return ContractParameterType.Boolean; + if (type == typeof(sbyte)) return ContractParameterType.Integer; + if (type == typeof(byte)) return ContractParameterType.Integer; + if (type == typeof(short)) return ContractParameterType.Integer; + if (type == typeof(ushort)) return ContractParameterType.Integer; + if (type == typeof(int)) return ContractParameterType.Integer; + if (type == typeof(uint)) return ContractParameterType.Integer; + if (type == typeof(long)) return ContractParameterType.Integer; + if (type == typeof(ulong)) return ContractParameterType.Integer; + if (type == typeof(BigInteger)) return ContractParameterType.Integer; + if (type == typeof(byte[])) return ContractParameterType.ByteArray; + if (type == typeof(string)) return ContractParameterType.String; + if (type == typeof(UInt160)) return ContractParameterType.Hash160; + if (type == typeof(UInt256)) return ContractParameterType.Hash256; + if (type == typeof(VM.Types.Boolean)) return ContractParameterType.Boolean; + if (type == typeof(Integer)) return ContractParameterType.Integer; + if (type == typeof(ByteString)) return ContractParameterType.ByteArray; + if (type == typeof(VM.Types.Buffer)) return ContractParameterType.ByteArray; + if (type == typeof(Array)) return ContractParameterType.Array; + if (type == typeof(Struct)) return ContractParameterType.Array; + if (type == typeof(Map)) return ContractParameterType.Map; + if (typeof(IInteroperable).IsAssignableFrom(type)) return ContractParameterType.Array; + if (typeof(ISerializable).IsAssignableFrom(type)) return ContractParameterType.ByteArray; + if (type.IsArray) return ContractParameterType.Array; + if (type.IsEnum) return ContractParameterType.Integer; + return ContractParameterType.Any; } } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 4796d2a96a..21eeb54a4b 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -1,22 +1,18 @@ using Neo.IO; using Neo.SmartContract.Manifest; using Neo.VM; -using Neo.VM.Types; using System; using System.Collections.Generic; using System.Linq; -using System.Numerics; using System.Reflection; -using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native { public abstract class NativeContract { private static readonly List contractsList = new List(); - private static readonly Dictionary contractsIdDictionary = new Dictionary(); - private static readonly Dictionary contractsHashDictionary = new Dictionary(); - private readonly Dictionary<(string, int), ContractMethodMetadata> methods = new Dictionary<(string, int), ContractMethodMetadata>(); + private static readonly Dictionary contractsDictionary = new Dictionary(); + private readonly Dictionary methods = new Dictionary(); private static int id_counter = 0; #region Named Native Contracts @@ -33,54 +29,51 @@ public abstract class NativeContract public static IReadOnlyCollection Contracts { get; } = contractsList; public string Name => GetType().Name; public NefFile Nef { get; } - public byte[] Script => Nef.Script; public UInt160 Hash { get; } - public int Id { get; } + public int Id { get; } = --id_counter; public ContractManifest Manifest { get; } public uint ActiveBlockIndex { get; } protected NativeContract() { - this.Id = --id_counter; + List descriptors = new List(); + foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) + { + ContractMethodAttribute attribute = member.GetCustomAttribute(); + if (attribute is null) continue; + descriptors.Add(new ContractMethodMetadata(member, attribute)); + } + descriptors = descriptors.OrderBy(p => p.Name).ThenBy(p => p.Parameters.Length).ToList(); byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitPush(Id); - sb.EmitSysCall(ApplicationEngine.System_Contract_CallNative); + foreach (ContractMethodMetadata method in descriptors) + { + method.Descriptor.Offset = sb.Offset; + sb.EmitPush(0); //version + methods.Add(sb.Offset, method); + sb.EmitSysCall(ApplicationEngine.System_Contract_CallNative); + sb.Emit(OpCode.RET); + } script = sb.ToArray(); } this.Nef = new NefFile { Compiler = "neo-core-v3.0", - Tokens = System.Array.Empty(), + Tokens = Array.Empty(), Script = script }; this.Nef.CheckSum = NefFile.ComputeChecksum(Nef); - this.Hash = Helper.GetContractHash(UInt160.Zero, this.Nef.CheckSum, Name); - List descriptors = new List(); - foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) - { - ContractMethodAttribute attribute = member.GetCustomAttribute(); - if (attribute is null) continue; - ContractMethodMetadata metadata = new ContractMethodMetadata(member, attribute); - descriptors.Add(new ContractMethodDescriptor - { - Name = metadata.Name, - ReturnType = ToParameterType(metadata.Handler.ReturnType), - Parameters = metadata.Parameters.Select(p => new ContractParameterDefinition { Type = ToParameterType(p.Type), Name = p.Name }).ToArray(), - Safe = (attribute.RequiredCallFlags & ~CallFlags.ReadOnly) == 0 - }); - methods.Add((metadata.Name, metadata.Parameters.Length), metadata); - } + this.Hash = Helper.GetContractHash(UInt160.Zero, 0, Name); this.Manifest = new ContractManifest { Name = Name, - Groups = System.Array.Empty(), - SupportedStandards = System.Array.Empty(), + Groups = Array.Empty(), + SupportedStandards = Array.Empty(), Abi = new ContractAbi() { - Events = System.Array.Empty(), - Methods = descriptors.OrderBy(p => p.Name).ThenBy(p => p.Parameters.Length).ToArray() + Events = Array.Empty(), + Methods = descriptors.Select(p => p.Descriptor).ToArray() }, Permissions = new[] { ContractPermission.DefaultPermission }, Trusts = WildcardContainer.Create(), @@ -89,8 +82,7 @@ protected NativeContract() if (ProtocolSettings.Default.NativeActivations.TryGetValue(Name, out uint activationIndex)) this.ActiveBlockIndex = activationIndex; contractsList.Add(this); - contractsIdDictionary.Add(Id, this); - contractsHashDictionary.Add(Hash, this); + contractsDictionary.Add(Hash, this); } protected bool CheckCommittee(ApplicationEngine engine) @@ -106,23 +98,18 @@ private protected KeyBuilder CreateStorageKey(byte prefix) public static NativeContract GetContract(UInt160 hash) { - contractsHashDictionary.TryGetValue(hash, out var contract); - return contract; - } - - public static NativeContract GetContract(int id) - { - contractsIdDictionary.TryGetValue(id, out var contract); + contractsDictionary.TryGetValue(hash, out var contract); return contract; } - internal void Invoke(ApplicationEngine engine) + internal void Invoke(ApplicationEngine engine, byte version) { - if (!engine.CurrentScriptHash.Equals(Hash)) - throw new InvalidOperationException("It is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used."); + if (ActiveBlockIndex > Ledger.CurrentIndex(engine.Snapshot)) + throw new InvalidOperationException($"The native contract {Name} is not active."); + if (version != 0) + throw new InvalidOperationException($"The native contract of version {version} is not active."); ExecutionContext context = engine.CurrentContext; - string operation = context.EvaluationStack.Pop().GetString(); - ContractMethodMetadata method = methods[(operation, context.EvaluationStack.Count)]; + ContractMethodMetadata method = methods[context.InstructionPointer]; ExecutionContextState state = context.GetState(); if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}."); @@ -139,7 +126,7 @@ internal void Invoke(ApplicationEngine engine) public static bool IsNative(UInt160 hash) { - return contractsHashDictionary.ContainsKey(hash); + return contractsDictionary.ContainsKey(hash); } internal virtual void Initialize(ApplicationEngine engine) @@ -156,43 +143,9 @@ internal virtual void PostPersist(ApplicationEngine engine) public ApplicationEngine TestCall(string operation, params object[] args) { - using (ScriptBuilder sb = new ScriptBuilder()) - { - sb.EmitDynamicCall(Hash, operation, args); - return ApplicationEngine.Run(sb.ToArray()); - } - } - - private static ContractParameterType ToParameterType(Type type) - { - if (type == typeof(void)) return ContractParameterType.Void; - if (type == typeof(bool)) return ContractParameterType.Boolean; - if (type == typeof(sbyte)) return ContractParameterType.Integer; - if (type == typeof(byte)) return ContractParameterType.Integer; - if (type == typeof(short)) return ContractParameterType.Integer; - if (type == typeof(ushort)) return ContractParameterType.Integer; - if (type == typeof(int)) return ContractParameterType.Integer; - if (type == typeof(uint)) return ContractParameterType.Integer; - if (type == typeof(long)) return ContractParameterType.Integer; - if (type == typeof(ulong)) return ContractParameterType.Integer; - if (type == typeof(BigInteger)) return ContractParameterType.Integer; - if (type == typeof(byte[])) return ContractParameterType.ByteArray; - if (type == typeof(string)) return ContractParameterType.String; - if (type == typeof(UInt160)) return ContractParameterType.Hash160; - if (type == typeof(UInt256)) return ContractParameterType.Hash256; - if (type == typeof(VM.Types.Boolean)) return ContractParameterType.Boolean; - if (type == typeof(Integer)) return ContractParameterType.Integer; - if (type == typeof(ByteString)) return ContractParameterType.ByteArray; - if (type == typeof(VM.Types.Buffer)) return ContractParameterType.ByteArray; - if (type == typeof(Array)) return ContractParameterType.Array; - if (type == typeof(Struct)) return ContractParameterType.Array; - if (type == typeof(Map)) return ContractParameterType.Map; - if (type == typeof(StackItem)) return ContractParameterType.Any; - if (typeof(IInteroperable).IsAssignableFrom(type)) return ContractParameterType.Array; - if (typeof(ISerializable).IsAssignableFrom(type)) return ContractParameterType.ByteArray; - if (type.IsArray) return ContractParameterType.Array; - if (type.IsEnum) return ContractParameterType.Integer; - return ContractParameterType.Any; + using ScriptBuilder sb = new ScriptBuilder(); + sb.EmitDynamicCall(Hash, operation, args); + return ApplicationEngine.Run(sb.ToArray()); } } } diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 788e0c8e16..186da86be7 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -399,7 +399,6 @@ public long CalculateNetworkFee(DataCache snapshot, Transaction tx) // Check verify cost using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CreateSnapshot()); engine.LoadContract(contract, md, CallFlags.None); - if (NativeContract.IsNative(hash)) engine.Push("verify"); if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); if (!engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.Hash} returns false."); diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index 9faadd2ee4..58ae9331f9 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -96,18 +96,9 @@ public static StackItem Call(this NativeContract contract, DataCache snapshot, s public static StackItem Call(this NativeContract contract, DataCache snapshot, IVerifiable container, Block persistingBlock, string method, params ContractParameter[] args) { - var engine = ApplicationEngine.Create(TriggerType.Application, container, snapshot, persistingBlock); - var contractState = NativeContract.ContractManagement.GetContract(snapshot, contract.Hash); - if (contractState == null) throw new InvalidOperationException(); - var md = contract.Manifest.Abi.GetMethod(method, args.Length); - - var script = new ScriptBuilder(); - - for (var i = args.Length - 1; i >= 0; i--) - script.EmitPush(args[i]); - - script.EmitPush(method); - engine.LoadContract(contractState, md, CallFlags.All); + using var engine = ApplicationEngine.Create(TriggerType.Application, container, snapshot, persistingBlock); + using var script = new ScriptBuilder(); + script.EmitDynamicCall(contract.Hash, method, args); engine.LoadScript(script.ToArray()); if (engine.Execute() != VMState.HALT) diff --git a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs index 4cba1e19e2..784bbee5ed 100644 --- a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs @@ -37,17 +37,11 @@ public void SerializeUnsigned(BinaryWriter writer) { } public static bool Transfer(this NativeContract contract, DataCache snapshot, byte[] from, byte[] to, BigInteger amount, bool signFrom, Block persistingBlock) { - var engine = ApplicationEngine.Create(TriggerType.Application, + using var engine = ApplicationEngine.Create(TriggerType.Application, new ManualWitness(signFrom ? new UInt160(from) : null), snapshot, persistingBlock); - engine.LoadScript(contract.Script, configureState: p => p.ScriptHash = contract.Hash); - - var script = new ScriptBuilder(); - script.Emit(OpCode.PUSHNULL); - script.EmitPush(amount); - script.EmitPush(to); - script.EmitPush(from); - script.EmitPush("transfer"); + using var script = new ScriptBuilder(); + script.EmitDynamicCall(contract.Hash, "transfer", from, to, amount, null); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -63,12 +57,10 @@ public static bool Transfer(this NativeContract contract, DataCache snapshot, by public static BigInteger TotalSupply(this NativeContract contract, DataCache snapshot) { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - - engine.LoadScript(contract.Script, configureState: p => p.ScriptHash = contract.Hash); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - var script = new ScriptBuilder(); - script.EmitPush("totalSupply"); + using var script = new ScriptBuilder(); + script.EmitDynamicCall(contract.Hash, "totalSupply"); engine.LoadScript(script.ToArray()); engine.Execute().Should().Be(VMState.HALT); @@ -81,13 +73,10 @@ public static BigInteger TotalSupply(this NativeContract contract, DataCache sna public static BigInteger BalanceOf(this NativeContract contract, DataCache snapshot, byte[] account) { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - - engine.LoadScript(contract.Script, configureState: p => p.ScriptHash = contract.Hash); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - var script = new ScriptBuilder(); - script.EmitPush(account); - script.EmitPush("balanceOf"); + using var script = new ScriptBuilder(); + script.EmitDynamicCall(contract.Hash, "balanceOf", account); engine.LoadScript(script.ToArray()); engine.Execute().Should().Be(VMState.HALT); @@ -100,12 +89,10 @@ public static BigInteger BalanceOf(this NativeContract contract, DataCache snaps public static BigInteger Decimals(this NativeContract contract, DataCache snapshot) { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(contract.Script, configureState: p => p.ScriptHash = contract.Hash); - - var script = new ScriptBuilder(); - script.EmitPush("decimals"); + using var script = new ScriptBuilder(); + script.EmitDynamicCall(contract.Hash, "decimals"); engine.LoadScript(script.ToArray()); engine.Execute().Should().Be(VMState.HALT); @@ -118,12 +105,10 @@ public static BigInteger Decimals(this NativeContract contract, DataCache snapsh public static string Symbol(this NativeContract contract, DataCache snapshot) { - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - - engine.LoadScript(contract.Script, configureState: p => p.ScriptHash = contract.Hash); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - var script = new ScriptBuilder(); - script.EmitPush("symbol"); + using var script = new ScriptBuilder(); + script.EmitDynamicCall(contract.Hash, "symbol"); engine.LoadScript(script.ToArray()); engine.Execute().Should().Be(VMState.HALT); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs index dc20d913ed..b4f3d42a40 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -7,7 +7,6 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.UnitTests.Extensions; -using Neo.VM; using System; using System.Linq; using System.Numerics; @@ -124,18 +123,6 @@ public void Check_BalanceOfTransferAndBurn() NativeContract.GAS.Transfer(snapshot, from, new byte[19], BigInteger.One, false, persistingBlock).Should().BeFalse(); } - [TestMethod] - public void Check_BadScript() - { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), _persistingBlock, 0); - - using var script = new ScriptBuilder(); - script.Emit(OpCode.NOP); - engine.LoadScript(script.ToArray()); - - Assert.ThrowsException(() => NativeContract.GAS.Invoke(engine)); - } - internal static StorageKey CreateStorageKey(byte prefix, uint key) { return CreateStorageKey(prefix, BitConverter.GetBytes(key)); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs index 13dd7c4ee4..f92936012c 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs @@ -499,11 +499,9 @@ public void Test_SetAdmin() internal static bool Check_AddRoot(DataCache snapshot, UInt160 account, string root, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(account), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); - var script = new ScriptBuilder(); - script.EmitPush(root); - script.EmitPush("addRoot"); + using var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.NameService.Hash, "addRoot", root); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -517,12 +515,9 @@ internal static bool Check_AddRoot(DataCache snapshot, UInt160 account, string r internal static bool Check_Transfer(DataCache snapshot, UInt160 to, string domain, UInt160 owner, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); - var script = new ScriptBuilder(); - script.EmitPush(domain); - script.EmitPush(to); - script.EmitPush("transfer"); + using var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.NameService.Hash, "transfer", to, domain); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -537,11 +532,8 @@ internal static (bool State, bool Result) Check_IsAvailable(DataCache snapshot, { using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(account), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); - - var script = new ScriptBuilder(); - script.EmitPush(name); - script.EmitPush("isAvailable"); + using var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.NameService.Hash, "isAvailable", name); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -560,11 +552,8 @@ internal static (bool State, bool Result) Check_SetPrice(DataCache snapshot, byt using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); - using var script = new ScriptBuilder(); - script.EmitPush(price); - script.EmitPush("setPrice"); + script.EmitDynamicCall(NativeContract.NameService.Hash, "setPrice", price); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -582,10 +571,8 @@ internal static BigDecimal Check_GetPrice(DataCache snapshot, Block persistingBl { using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); - engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); - using var script = new ScriptBuilder(); - script.EmitPush("getPrice"); + script.EmitDynamicCall(NativeContract.NameService.Hash, "getPrice"); engine.LoadScript(script.ToArray()); engine.Execute().Should().Be(VMState.HALT); @@ -601,12 +588,8 @@ internal static (bool State, bool Result) Check_Register(DataCache snapshot, str using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(new UInt160(owner)), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); - using var script = new ScriptBuilder(); - script.EmitPush(new UInt160(owner)); - script.EmitPush(name); - script.EmitPush("register"); + script.EmitDynamicCall(NativeContract.NameService.Hash, "register", name, owner); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -625,13 +608,8 @@ internal static bool Check_SetRecord(DataCache snapshot, string name, RecordType using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(new UInt160(pubkey)), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); - using var script = new ScriptBuilder(); - script.EmitPush(data); - script.EmitPush(type); - script.EmitPush(name); - script.EmitPush("setRecord"); + script.EmitDynamicCall(NativeContract.NameService.Hash, "setRecord", name, type, data); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -647,12 +625,8 @@ internal static (bool State, string Result) Check_GetRecord(DataCache snapshot, using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(UInt160.Zero), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); - using var script = new ScriptBuilder(); - script.EmitPush(type); - script.EmitPush(name); - script.EmitPush("getRecord"); + script.EmitDynamicCall(NativeContract.NameService.Hash, "getRecord", name, type); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -671,12 +645,8 @@ internal static bool Check_SetAdmin(DataCache snapshot, string name, UInt160 adm using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(new UInt160[] { admin, new UInt160(pubkey) }), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NameService.Script, configureState: p => p.ScriptHash = NativeContract.NameService.Hash); - using var script = new ScriptBuilder(); - script.EmitPush(admin); - script.EmitPush(name); - script.EmitPush("setAdmin"); + script.EmitDynamicCall(NativeContract.NameService.Hash, "setAdmin", name, admin); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 4f378c13e1..d7287c6b38 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -4,9 +4,8 @@ using Neo.Ledger; using Neo.SmartContract; using Neo.SmartContract.Native; -using Neo.VM.Types; +using Neo.VM; using System; -using System.Collections.Generic; using System.Numerics; namespace Neo.UnitTests.SmartContract.Native @@ -87,24 +86,36 @@ public void TestToParameter() [TestMethod] public void TestGetContract() { - Assert.IsTrue(NativeContract.NEO == NativeContract.GetContract(NativeContract.NEO.Id)); Assert.IsTrue(NativeContract.NEO == NativeContract.GetContract(NativeContract.NEO.Hash)); } [TestMethod] public void TestInvoke() { - var snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, null, 0); - engine.LoadScript(testNativeContract.Script, configureState: p => p.ScriptHash = testNativeContract.Hash); - - ByteString method1 = new ByteString(System.Text.Encoding.Default.GetBytes("wrongMethod")); - engine.CurrentContext.EvaluationStack.Push(method1); - Assert.ThrowsException(() => testNativeContract.Invoke(engine)); - - ByteString method2 = new ByteString(System.Text.Encoding.Default.GetBytes("helloWorld")); - engine.CurrentContext.EvaluationStack.Push(method2); - testNativeContract.Invoke(engine); + using var snapshot = Blockchain.Singleton.GetSnapshot(); + snapshot.Add(NativeContract.ContractManagement.CreateStorageKey(8, testNativeContract.Hash), new StorageItem(new ContractState + { + Id = 0, + Nef = testNativeContract.Nef, + Hash = testNativeContract.Hash, + Manifest = testNativeContract.Manifest + })); + + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, null, 20_00000000)) + { + using ScriptBuilder script = new ScriptBuilder(); + script.EmitDynamicCall(testNativeContract.Hash, "wrongMethod"); + engine.LoadScript(script.ToArray()); + engine.Execute().Should().Be(VMState.FAULT); + } + + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, null, 20_00000000)) + { + using ScriptBuilder script = new ScriptBuilder(); + script.EmitDynamicCall(testNativeContract.Hash, "helloWorld"); + engine.LoadScript(script.ToArray()); + engine.Execute().Should().Be(VMState.HALT); + } } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 2c85b25e65..fd87eee3e3 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -449,18 +449,6 @@ public void Check_Initialize() Check_GetCommittee(snapshot, null); } - [TestMethod] - public void Check_BadScript() - { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, Blockchain.Singleton.GetSnapshot(), _persistingBlock); - - var script = new ScriptBuilder(); - script.Emit(OpCode.NOP); - engine.LoadScript(script.ToArray()); - - Assert.ThrowsException(() => NativeContract.NEO.Invoke(engine)); - } - [TestMethod] public void TestCalculateBonus() { @@ -936,10 +924,9 @@ internal static bool Check_PostPersist(DataCache snapshot, Block persistingBlock internal static (BigInteger Value, bool State) Check_GetGasPerBlock(DataCache snapshot, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); using var script = new ScriptBuilder(); - script.EmitPush("getGasPerBlock"); + script.EmitDynamicCall(NativeContract.NEO.Hash, "getGasPerBlock"); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -958,11 +945,8 @@ internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(DataCa UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - var script = new ScriptBuilder(); - script.EmitPush(gasPerBlock); - script.EmitPush("setGasPerBlock"); + script.EmitDynamicCall(NativeContract.NEO.Hash, "setGasPerBlock", gasPerBlock); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -978,16 +962,8 @@ internal static (bool State, bool Result) Check_Vote(DataCache snapshot, byte[] using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - using var script = new ScriptBuilder(); - - if (pubkey is null) - script.Emit(OpCode.PUSHNULL); - else - script.EmitPush(pubkey); - script.EmitPush(account); - script.EmitPush("vote"); + script.EmitDynamicCall(NativeContract.NEO.Hash, "vote", account, pubkey); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -1006,11 +982,8 @@ internal static (bool State, bool Result) Check_RegisterValidator(DataCache snap using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock, 1100_00000000); - engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - using var script = new ScriptBuilder(); - script.EmitPush(pubkey); - script.EmitPush("registerCandidate"); + script.EmitDynamicCall(NativeContract.NEO.Hash, "registerCandidate", pubkey); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -1028,10 +1001,8 @@ internal static ECPoint[] Check_GetCommittee(DataCache snapshot, Block persistin { using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - using var script = new ScriptBuilder(); - script.EmitPush("getCommittee"); + script.EmitDynamicCall(NativeContract.NEO.Hash, "getCommittee"); engine.LoadScript(script.ToArray()); engine.Execute().Should().Be(VMState.HALT); @@ -1046,12 +1017,8 @@ internal static (BigInteger Value, bool State) Check_UnclaimedGas(DataCache snap { using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - using var script = new ScriptBuilder(); - script.EmitPush(persistingBlock.Index); - script.EmitPush(address); - script.EmitPush("unclaimedGas"); + script.EmitDynamicCall(NativeContract.NEO.Hash, "unclaimedGas", address, persistingBlock.Index); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) @@ -1106,11 +1073,8 @@ internal static (bool State, bool Result) Check_UnregisterCandidate(DataCache sn using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); - engine.LoadScript(NativeContract.NEO.Script, configureState: p => p.ScriptHash = NativeContract.NEO.Hash); - using var script = new ScriptBuilder(); - script.EmitPush(pubkey); - script.EmitPush("unregisterCandidate"); + script.EmitDynamicCall(NativeContract.NEO.Hash, "unregisterCandidate", pubkey); engine.LoadScript(script.ToArray()); if (engine.Execute() == VMState.FAULT) diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index fde993fc51..4b94cc9d2e 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -94,7 +94,7 @@ public void Check_SetMaxBlockSize() ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); - ret.Should().BeNull(); + ret.IsNull.Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); ret.Should().BeOfType(); @@ -146,7 +146,7 @@ public void Check_SetMaxBlockSystemFee() ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = 1024 * (long)NativeContract.GAS.Factor }); - ret.Should().BeNull(); + ret.IsNull.Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); ret.Should().BeOfType(); @@ -185,7 +185,7 @@ public void Check_SetMaxTransactionsPerBlock() ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(NativeContract.NEO.GetCommitteeAddress(snapshot)), block, "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeNull(); + ret.IsNull.Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); ret.Should().BeOfType(); @@ -224,7 +224,7 @@ public void Check_SetFeePerByte() UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setFeePerByte", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.Should().BeNull(); + ret.IsNull.Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); @@ -274,7 +274,7 @@ public void Check_SetBaseExecFee() // Proper set ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setExecFeeFactor", new ContractParameter(ContractParameterType.Integer) { Value = 50 }); - ret.Should().BeNull(); + ret.IsNull.Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getExecFeeFactor"); ret.Should().BeOfType(); @@ -324,7 +324,7 @@ public void Check_SetStoragePrice() // Proper set ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, "setStoragePrice", new ContractParameter(ContractParameterType.Integer) { Value = 300300 }); - ret.Should().BeNull(); + ret.IsNull.Should().BeTrue(); ret = NativeContract.Policy.Call(snapshot, "getStoragePrice"); ret.Should().BeOfType(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 8fe520577c..25bcb1544b 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -379,11 +379,9 @@ public void TestBlockchain_GetTransactionHeight() Transaction = TestUtils.CreateRandomHashTransaction() }; UT_SmartContractHelper.TransactionAdd(engine.Snapshot, state); - engine.LoadScript(NativeContract.Ledger.Script, configureState: p => p.ScriptHash = NativeContract.Ledger.Hash); - var script = new ScriptBuilder(); - script.EmitPush(state.Transaction.Hash.ToArray()); - script.EmitPush("getTransactionHeight"); + using var script = new ScriptBuilder(); + script.EmitDynamicCall(NativeContract.Ledger.Hash, "getTransactionHeight", state.Transaction.Hash); engine.LoadScript(script.ToArray()); engine.Execute(); Assert.AreEqual(engine.State, VMState.HALT); diff --git a/tests/neo.UnitTests/VM/UT_Helper.cs b/tests/neo.UnitTests/VM/UT_Helper.cs index a903fead4a..e5a1f61985 100644 --- a/tests/neo.UnitTests/VM/UT_Helper.cs +++ b/tests/neo.UnitTests/VM/UT_Helper.cs @@ -158,7 +158,7 @@ public void TestMakeScript() { byte[] testScript = NativeContract.GAS.Hash.MakeScript("balanceOf", UInt160.Zero); - Assert.AreEqual("0c14000000000000000000000000000000000000000011c01f0c0962616c616e63654f660c1428b3adab7269f9c2181db3cb741ebf551930e27041627d5b52", + Assert.AreEqual("0c14000000000000000000000000000000000000000011c01f0c0962616c616e63654f660c14cf76e28bd0062c4a478ee35561011319f3cfa4d241627d5b52", testScript.ToHexString()); } From e3cf64e862adb86c0cc53940fb821551d6f36a60 Mon Sep 17 00:00:00 2001 From: Shargon Date: Sun, 7 Feb 2021 09:39:57 +0100 Subject: [PATCH 09/63] Update TrimmedBlock.cs (#2304) --- src/neo/SmartContract/Native/TrimmedBlock.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/SmartContract/Native/TrimmedBlock.cs b/src/neo/SmartContract/Native/TrimmedBlock.cs index 33627604a7..07d02c3a41 100644 --- a/src/neo/SmartContract/Native/TrimmedBlock.cs +++ b/src/neo/SmartContract/Native/TrimmedBlock.cs @@ -52,7 +52,7 @@ StackItem IInteroperable.ToStackItem(ReferenceCounter referenceCounter) Header.NextConsensus.ToArray(), // Block properties - Hashes.Length - 1 + Hashes.Length }); } } From 8934c0295df40f2a4cfbb70e995a8928afa47c50 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 8 Feb 2021 00:17:49 +0800 Subject: [PATCH 10/63] Remove singletons (#2302) --- src/neo/Ledger/Blockchain.cs | 138 ++++++------------ src/neo/Ledger/MemoryPool.cs | 17 ++- src/neo/NeoSystem.cs | 24 ++- src/neo/Network/P2P/LocalNode.cs | 39 ++--- src/neo/Network/P2P/Payloads/Block.cs | 8 +- .../Network/P2P/Payloads/ExtensiblePayload.cs | 6 +- src/neo/Network/P2P/Payloads/Header.cs | 22 ++- src/neo/Network/P2P/Payloads/IInventory.cs | 4 - src/neo/Network/P2P/Payloads/Transaction.cs | 5 - .../Network/P2P/Payloads/VersionPayload.cs | 4 +- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 33 ++--- src/neo/Network/P2P/RemoteNode.cs | 24 +-- src/neo/Network/P2P/TaskManager.cs | 12 +- src/neo/ProtocolSettings.cs | 36 +++-- src/neo/SmartContract/ApplicationEngine.cs | 6 +- .../ContractParametersContext.cs | 30 ++-- .../SmartContract/Native/NativeContract.cs | 5 +- src/neo/Wallets/AssetDescriptor.cs | 4 +- src/neo/Wallets/Wallet.cs | 19 +-- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 21 ++- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 56 +++---- .../UT_TransactionVerificationContext.cs | 4 +- tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs | 2 +- .../Network/P2P/Payloads/UT_Header.cs | 2 +- .../P2P/Payloads/UT_HighPriorityAttribute.cs | 2 +- .../Network/P2P/Payloads/UT_Transaction.cs | 74 +++++----- .../Network/P2P/Payloads/UT_VersionPayload.cs | 6 +- .../Network/P2P/Payloads/UT_Witness.cs | 4 +- .../neo.UnitTests/Network/P2P/UT_LocalNode.cs | 34 ----- .../Network/P2P/UT_RemoteNode.cs | 4 +- .../SmartContract/Native/UT_FungibleToken.cs | 4 +- .../SmartContract/Native/UT_GasToken.cs | 2 +- .../SmartContract/Native/UT_NNS.cs | 2 +- .../SmartContract/Native/UT_NameService.cs | 2 +- .../SmartContract/Native/UT_NativeContract.cs | 7 +- .../SmartContract/Native/UT_NeoToken.cs | 11 +- .../SmartContract/Native/UT_PolicyContract.cs | 2 +- .../SmartContract/Native/UT_RoleManagement.cs | 2 +- .../SmartContract/UT_ApplicationEngine.cs | 6 +- .../UT_ContractParameterContext.cs | 34 +++-- .../SmartContract/UT_InteropPrices.cs | 8 +- .../SmartContract/UT_InteropService.NEO.cs | 10 +- .../SmartContract/UT_InteropService.cs | 18 +-- .../SmartContract/UT_SmartContractHelper.cs | 6 +- .../SmartContract/UT_Syscalls.cs | 8 +- tests/neo.UnitTests/TestBlockchain.cs | 22 +-- tests/neo.UnitTests/UT_DataCache.cs | 6 +- .../Wallets/UT_AssetDescriptor.cs | 9 +- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 30 ++-- 49 files changed, 382 insertions(+), 452 deletions(-) delete mode 100644 tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index d8994c893d..13c1603dd4 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -3,7 +3,6 @@ using Akka.IO; using Neo.Cryptography.ECC; using Neo.IO.Actors; -using Neo.IO.Caching; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -16,7 +15,6 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Threading; namespace Neo.Ledger { @@ -32,10 +30,8 @@ internal class PreverifyCompleted { public Transaction Transaction; public Verif public class RelayResult { public IInventory Inventory; public VerifyResult Result; } private class UnverifiedBlocksList { public LinkedList Blocks = new LinkedList(); public HashSet Nodes = new HashSet(); } - public static readonly uint MillisecondsPerBlock = ProtocolSettings.Default.MillisecondsPerBlock; - public static readonly TimeSpan TimePerBlock = TimeSpan.FromMilliseconds(MillisecondsPerBlock); - public static readonly ECPoint[] StandbyCommittee = ProtocolSettings.Default.StandbyCommittee.Select(p => ECPoint.DecodePoint(p.HexToBytes(), ECCurve.Secp256r1)).ToArray(); - public static readonly ECPoint[] StandbyValidators = StandbyCommittee[0..ProtocolSettings.Default.ValidatorsCount]; + public static readonly ECPoint[] StandbyCommittee = ProtocolSettings.Default.StandbyCommittee; + public static readonly ECPoint[] StandbyValidators = ProtocolSettings.Default.StandbyValidators; public static readonly Block GenesisBlock = new Block { @@ -58,33 +54,12 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu private readonly static Script onPersistScript, postPersistScript; private const int MaxTxToReverifyPerIdle = 10; - private static readonly object lockObj = new object(); private readonly NeoSystem system; private readonly IActorRef txrouter; private readonly Dictionary block_cache = new Dictionary(); private readonly Dictionary block_cache_unverified = new Dictionary(); - internal readonly RelayCache RelayCache = new RelayCache(100); private ImmutableHashSet extensibleWitnessWhiteList; - public IStore Store { get; } - /// - /// A readonly view of the blockchain store. - /// Note: It doesn't need to be disposed because the inside it is null. - /// - public DataCache View => new SnapshotCache(Store); - public MemoryPool MemPool { get; } - public HeaderCache HeaderCache { get; } = new HeaderCache(); - - private static Blockchain singleton; - public static Blockchain Singleton - { - get - { - while (singleton == null) Thread.Sleep(10); - return singleton; - } - } - static Blockchain() { using (ScriptBuilder sb = new ScriptBuilder()) @@ -99,55 +74,30 @@ static Blockchain() } } - public Blockchain(NeoSystem system, IStore store) + public Blockchain(NeoSystem system) { this.system = system; this.txrouter = Context.ActorOf(TransactionRouter.Props(system)); - this.MemPool = new MemoryPool(system, ProtocolSettings.Default.MemoryPoolMaxTransactions); - this.Store = store; - lock (lockObj) - { - if (singleton != null) - throw new InvalidOperationException(); - DataCache snapshot = View; - if (!NativeContract.Ledger.Initialized(snapshot)) - { - Persist(GenesisBlock); - } - else - { - UpdateExtensibleWitnessWhiteList(snapshot); - } - singleton = this; - } - } - - protected override void PostStop() - { - base.PostStop(); - HeaderCache.Dispose(); + DataCache snapshot = system.StoreView; + if (!NativeContract.Ledger.Initialized(snapshot)) + Persist(GenesisBlock); } private bool ContainsTransaction(UInt256 hash) { - if (MemPool.ContainsKey(hash)) return true; - return NativeContract.Ledger.ContainsTransaction(View, hash); - } - - public SnapshotCache GetSnapshot() - { - return new SnapshotCache(Store.GetSnapshot()); + if (system.MemPool.ContainsKey(hash)) return true; + return NativeContract.Ledger.ContainsTransaction(system.StoreView, hash); } private void OnImport(IEnumerable blocks, bool verify) { - uint currentHeight = NativeContract.Ledger.CurrentIndex(View); + uint currentHeight = NativeContract.Ledger.CurrentIndex(system.StoreView); foreach (Block block in blocks) { if (block.Index <= currentHeight) continue; if (block.Index != currentHeight + 1) throw new InvalidOperationException(); - if (verify && !block.Verify(View)) + if (verify && !block.Verify(system.StoreView)) throw new InvalidOperationException(); Persist(block); ++currentHeight; @@ -187,9 +137,9 @@ private void AddUnverifiedBlockToCache(Block block) private void OnFillMemoryPool(IEnumerable transactions) { // Invalidate all the transactions in the memory pool, to avoid any failures when adding new transactions. - MemPool.InvalidateAllTransactions(); + system.MemPool.InvalidateAllTransactions(); - DataCache snapshot = View; + DataCache snapshot = system.StoreView; // Add the transactions to the memory pool foreach (var tx in transactions) @@ -197,9 +147,9 @@ private void OnFillMemoryPool(IEnumerable transactions) if (NativeContract.Ledger.ContainsTransaction(snapshot, tx.Hash)) continue; // First remove the tx if it is unverified in the pool. - MemPool.TryRemoveUnVerified(tx.Hash, out _); + system.MemPool.TryRemoveUnVerified(tx.Hash, out _); // Add to the memory pool - MemPool.TryAdd(tx, snapshot); + system.MemPool.TryAdd(tx, snapshot); } // Transactions originally in the pool will automatically be reverified based on their priority. @@ -212,7 +162,8 @@ private void OnInventory(IInventory inventory, bool relay = true) { Block block => OnNewBlock(block), Transaction transaction => OnNewTransaction(transaction), - _ => OnNewInventory(inventory) + ExtensiblePayload payload => OnNewExtensiblePayload(payload), + _ => throw new NotSupportedException() }; if (result == VerifyResult.Succeed && relay) { @@ -223,9 +174,9 @@ private void OnInventory(IInventory inventory, bool relay = true) private VerifyResult OnNewBlock(Block block) { - DataCache snapshot = View; + DataCache snapshot = system.StoreView; uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); - uint headerHeight = HeaderCache.Last?.Index ?? NativeContract.Ledger.CurrentIndex(snapshot); + uint headerHeight = system.HeaderCache.Last?.Index ?? currentHeight; if (block.Index <= currentHeight) return VerifyResult.AlreadyExists; if (block.Index - 1 > headerHeight) @@ -235,12 +186,12 @@ private VerifyResult OnNewBlock(Block block) } if (block.Index == headerHeight + 1) { - if (!block.Verify(snapshot)) + if (!block.Verify(snapshot, system.HeaderCache)) return VerifyResult.Invalid; } else { - if (!block.Hash.Equals(HeaderCache[block.Index].Hash)) + if (!block.Hash.Equals(system.HeaderCache[block.Index].Hash)) return VerifyResult.Invalid; } block_cache.TryAdd(block.Hash, block); @@ -252,13 +203,13 @@ private VerifyResult OnNewBlock(Block block) { blocksToPersistList.Add(block_persist); if (block_persist.Index + 1 > headerHeight) break; - UInt256 hash = HeaderCache[block_persist.Index + 1].Hash; + UInt256 hash = system.HeaderCache[block_persist.Index + 1].Hash; if (!block_cache.TryGetValue(hash, out block_persist)) break; } int blocksPersisted = 0; // 15000 is the default among of seconds per block, while MilliSecondsPerBlock is the current - uint extraBlocks = (15000 - MillisecondsPerBlock) / 1000; + uint extraBlocks = (15000 - system.Settings.MillisecondsPerBlock) / 1000; foreach (Block blockToPersist in blocksToPersistList) { block_cache_unverified.Remove(blockToPersist.Index); @@ -283,37 +234,39 @@ private VerifyResult OnNewBlock(Block block) if (block.Index + 99 >= headerHeight) system.LocalNode.Tell(new LocalNode.RelayDirectly { Inventory = block }); if (block.Index == headerHeight + 1) - HeaderCache.Add(block.Header); + system.HeaderCache.Add(block.Header); } return VerifyResult.Succeed; } private void OnNewHeaders(Header[] headers) { - if (HeaderCache.Full) return; - DataCache snapshot = View; - uint headerHeight = HeaderCache.Last?.Index ?? NativeContract.Ledger.CurrentIndex(snapshot); + if (system.HeaderCache.Full) return; + DataCache snapshot = system.StoreView; + uint headerHeight = system.HeaderCache.Last?.Index ?? NativeContract.Ledger.CurrentIndex(snapshot); foreach (Header header in headers) { if (header.Index > headerHeight + 1) break; if (header.Index < headerHeight + 1) continue; - if (!header.Verify(snapshot)) break; - HeaderCache.Add(header); + if (!header.Verify(snapshot, system.HeaderCache)) break; + system.HeaderCache.Add(header); ++headerHeight; } } - private VerifyResult OnNewInventory(IInventory inventory) + private VerifyResult OnNewExtensiblePayload(ExtensiblePayload payload) { - if (!inventory.Verify(View)) return VerifyResult.Invalid; - RelayCache.Add(inventory); + DataCache snapshot = system.StoreView; + extensibleWitnessWhiteList ??= UpdateExtensibleWitnessWhiteList(snapshot); + if (!payload.Verify(snapshot, extensibleWitnessWhiteList)) return VerifyResult.Invalid; + system.RelayCache.Add(payload); return VerifyResult.Succeed; } private VerifyResult OnNewTransaction(Transaction transaction) { if (ContainsTransaction(transaction.Hash)) return VerifyResult.AlreadyExists; - return MemPool.TryAdd(transaction, View); + return system.MemPool.TryAdd(transaction, system.StoreView); } private void OnPreverifyCompleted(PreverifyCompleted task) @@ -350,7 +303,7 @@ protected override void OnReceive(object message) OnPreverifyCompleted(task); break; case Idle _: - if (MemPool.ReVerifyTopUnverifiedTransactionsIfNeeded(MaxTxToReverifyPerIdle, View)) + if (system.MemPool.ReVerifyTopUnverifiedTransactionsIfNeeded(MaxTxToReverifyPerIdle, system.StoreView)) Self.Tell(Idle.Instance, ActorRefs.NoSender); break; } @@ -366,7 +319,7 @@ private void OnTransaction(Transaction tx) private void Persist(Block block) { - using (SnapshotCache snapshot = GetSnapshot()) + using (SnapshotCache snapshot = system.GetSnapshot()) { List all_application_executed = new List(); using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, block)) @@ -427,18 +380,18 @@ private void Persist(Block block) } } if (commitExceptions != null) throw new AggregateException(commitExceptions); - UpdateExtensibleWitnessWhiteList(snapshot); - MemPool.UpdatePoolForBlockPersisted(block, snapshot); + system.MemPool.UpdatePoolForBlockPersisted(block, snapshot); } + extensibleWitnessWhiteList = null; block_cache.Remove(block.PrevHash); Context.System.EventStream.Publish(new PersistCompleted { Block = block }); - if (HeaderCache.TryRemoveFirst(out Header header)) + if (system.HeaderCache.TryRemoveFirst(out Header header)) Debug.Assert(header.Index == block.Index); } - public static Props Props(NeoSystem system, IStore store) + public static Props Props(NeoSystem system) { - return Akka.Actor.Props.Create(() => new Blockchain(system, store)).WithMailbox("blockchain-mailbox"); + return Akka.Actor.Props.Create(() => new Blockchain(system)).WithMailbox("blockchain-mailbox"); } private void SendRelayResult(IInventory inventory, VerifyResult result) @@ -452,7 +405,7 @@ private void SendRelayResult(IInventory inventory, VerifyResult result) Context.System.EventStream.Publish(rr); } - private void UpdateExtensibleWitnessWhiteList(DataCache snapshot) + private static ImmutableHashSet UpdateExtensibleWitnessWhiteList(DataCache snapshot) { uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); var builder = ImmutableHashSet.CreateBuilder(); @@ -472,12 +425,7 @@ private void UpdateExtensibleWitnessWhiteList(DataCache snapshot) builder.Add(Contract.GetBFTAddress(stateValidators)); builder.UnionWith(stateValidators.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash())); } - extensibleWitnessWhiteList = builder.ToImmutable(); - } - - internal bool IsExtensibleWitnessWhiteListed(UInt160 address) - { - return extensibleWitnessWhiteList.Contains(address); + return builder.ToImmutable(); } } diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index 32abaa566a..25496860ed 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -19,10 +19,10 @@ public class MemoryPool : IReadOnlyCollection private const int BlocksTillRebroadcast = 10; private int RebroadcastMultiplierThreshold => Capacity / 10; - private static readonly double MaxMillisecondsToReverifyTx = (double)Blockchain.MillisecondsPerBlock / 3; + private readonly double MaxMillisecondsToReverifyTx; // These two are not expected to be hit, they are just safegaurds. - private static readonly double MaxMillisecondsToReverifyTxPerIdle = (double)Blockchain.MillisecondsPerBlock / 15; + private readonly double MaxMillisecondsToReverifyTxPerIdle; private readonly NeoSystem _system; @@ -95,10 +95,12 @@ public int Count public int UnVerifiedCount => _unverifiedTransactions.Count; - public MemoryPool(NeoSystem system, int capacity) + public MemoryPool(NeoSystem system) { _system = system; - Capacity = capacity; + Capacity = system.Settings.MemoryPoolMaxTransactions; + MaxMillisecondsToReverifyTx = (double)system.Settings.MillisecondsPerBlock / 3; + MaxMillisecondsToReverifyTxPerIdle = (double)system.Settings.MillisecondsPerBlock / 15; } /// @@ -363,7 +365,7 @@ internal void UpdatePoolForBlockPersisted(Block block, DataCache snapshot) // If we know about headers of future blocks, no point in verifying transactions from the unverified tx pool // until we get caught up. - if (block.Index > 0 && Blockchain.Singleton.HeaderCache.Count > 0) + if (block.Index > 0 && _system.HeaderCache.Count > 0) return; uint _maxTxPerBlock = NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); @@ -412,8 +414,7 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, if (Count > RebroadcastMultiplierThreshold) blocksTillRebroadcast = blocksTillRebroadcast * Count / RebroadcastMultiplierThreshold; - var rebroadcastCutOffTime = TimeProvider.Current.UtcNow.AddMilliseconds( - -Blockchain.MillisecondsPerBlock * blocksTillRebroadcast); + var rebroadcastCutOffTime = TimeProvider.Current.UtcNow.AddMilliseconds(-_system.Settings.MillisecondsPerBlock * blocksTillRebroadcast); foreach (PoolItem item in reverifiedItems) { if (_unsortedTransactions.TryAdd(item.Tx.Hash, item)) @@ -462,7 +463,7 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, /// true if more unsorted messages exist, otherwise false internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, DataCache snapshot) { - if (Blockchain.Singleton.HeaderCache.Count > 0) + if (_system.HeaderCache.Count > 0) return false; if (_unverifiedSortedTransactions.Count > 0) diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 9ba6eb202d..28de6b1bcc 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -1,4 +1,5 @@ using Akka.Actor; +using Neo.IO.Caching; using Neo.Ledger; using Neo.Network.P2P; using Neo.Persistence; @@ -9,6 +10,7 @@ namespace Neo { public class NeoSystem : IDisposable { + public ProtocolSettings Settings { get; } public ActorSystem ActorSystem { get; } = ActorSystem.Create(nameof(NeoSystem), $"akka {{ log-dead-letters = off , loglevel = warning, loggers = [ \"{typeof(Utility.Logger).AssemblyQualifiedName}\" ] }}" + $"blockchain-mailbox {{ mailbox-type: \"{typeof(BlockchainMailbox).AssemblyQualifiedName}\" }}" + @@ -17,6 +19,16 @@ public class NeoSystem : IDisposable public IActorRef Blockchain { get; } public IActorRef LocalNode { get; } public IActorRef TaskManager { get; } + /// + /// A readonly view of the store. + /// + /// + /// It doesn't need to be disposed because the inside it is null. + /// + public DataCache StoreView => new SnapshotCache(store); + public MemoryPool MemPool { get; } + public HeaderCache HeaderCache { get; } = new HeaderCache(); + internal RelayCache RelayCache { get; } = new RelayCache(100); private readonly string storage_engine; private readonly IStore store; @@ -29,12 +41,14 @@ static NeoSystem() AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; } - public NeoSystem(string storageEngine = null, string storagePath = null) + public NeoSystem(ProtocolSettings settings, string storageEngine = null, string storagePath = null) { + this.Settings = settings; Plugin.LoadPlugins(this); this.storage_engine = storageEngine; this.store = LoadStore(storagePath); - this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this, store)); + this.MemPool = new MemoryPool(this); + this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); foreach (var plugin in Plugin.Plugins) @@ -54,6 +68,7 @@ public void Dispose() // Dispose will call ActorSystem.Terminate() ActorSystem.Dispose(); ActorSystem.WhenTerminated.Wait(); + HeaderCache.Dispose(); store.Dispose(); } @@ -97,5 +112,10 @@ internal void SuspendNodeStartup() { suspend = true; } + + public SnapshotCache GetSnapshot() + { + return new SnapshotCache(store.GetSnapshot()); + } } } diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index c2941c38bd..990b6572fa 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -8,7 +8,6 @@ using System.Net; using System.Net.Sockets; using System.Reflection; -using System.Threading; using System.Threading.Tasks; namespace Neo.Network.P2P @@ -20,9 +19,8 @@ public class SendDirectly { public IInventory Inventory; } public const uint ProtocolVersion = 0; private const int MaxCountFromSeedList = 5; - private readonly IPEndPoint[] SeedList = new IPEndPoint[ProtocolSettings.Default.SeedList.Length]; + private readonly IPEndPoint[] SeedList; - private static readonly object lockObj = new object(); private readonly NeoSystem system; internal readonly ConcurrentDictionary RemoteNodes = new ConcurrentDictionary(); @@ -31,16 +29,6 @@ public class SendDirectly { public IInventory Inventory; } public static readonly uint Nonce; public static string UserAgent { get; set; } - private static LocalNode singleton; - public static LocalNode Singleton - { - get - { - while (singleton == null) Thread.Sleep(10); - return singleton; - } - } - static LocalNode() { Random rand = new Random(); @@ -50,20 +38,15 @@ static LocalNode() public LocalNode(NeoSystem system) { - lock (lockObj) + this.system = system; + this.SeedList = new IPEndPoint[system.Settings.SeedList.Length]; + + // Start dns resolution in parallel + string[] seedList = system.Settings.SeedList; + for (int i = 0; i < seedList.Length; i++) { - if (singleton != null) - throw new InvalidOperationException(); - this.system = system; - singleton = this; - - // Start dns resolution in parallel - string[] seedList = ProtocolSettings.Default.SeedList; - for (int i = 0; i < seedList.Length; i++) - { - int index = i; - Task.Run(() => SeedList[index] = GetIpEndPoint(seedList[index])); - } + int index = i; + Task.Run(() => SeedList[index] = GetIpEndPoint(seedList[index])); } } @@ -135,7 +118,7 @@ internal static IPEndPoint GetIpEndPoint(string hostAndPort) /// Remote node public bool AllowNewConnection(IActorRef actor, RemoteNode node) { - if (node.Version.Magic != ProtocolSettings.Default.Magic) return false; + if (node.Version.Magic != system.Settings.Magic) return false; if (node.Version.Nonce == Nonce) return false; // filter duplicate connections @@ -230,7 +213,7 @@ public static Props Props(NeoSystem system) protected override Props ProtocolProps(object connection, IPEndPoint remote, IPEndPoint local) { - return RemoteNode.Props(system, connection, remote, local); + return RemoteNode.Props(system, this, connection, remote, local); } } } diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index 7d8510f3f4..56f2585eea 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -1,6 +1,7 @@ using Neo.Cryptography; using Neo.IO; using Neo.IO.Json; +using Neo.Ledger; using Neo.Persistence; using System; using System.IO; @@ -76,9 +77,14 @@ public JObject ToJson() return json; } - public bool Verify(DataCache snapshot) + internal bool Verify(DataCache snapshot) { return Header.Verify(snapshot); } + + internal bool Verify(DataCache snapshot, HeaderCache headerCache) + { + return Header.Verify(snapshot, headerCache); + } } } diff --git a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs index 79ec9886e4..bb5cee7320 100644 --- a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs +++ b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -1,9 +1,9 @@ using Neo.IO; -using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using System; +using System.Collections.Generic; using System.IO; namespace Neo.Network.P2P.Payloads @@ -90,11 +90,11 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.WriteVarBytes(Data); } - public bool Verify(DataCache snapshot) + internal bool Verify(DataCache snapshot, ISet extensibleWitnessWhiteList) { uint height = NativeContract.Ledger.CurrentIndex(snapshot); if (height < ValidBlockStart || height >= ValidBlockEnd) return false; - if (!Blockchain.Singleton.IsExtensibleWitnessWhiteListed(Sender)) return false; + if (!extensibleWitnessWhiteList.Contains(Sender)) return false; return this.VerifyWitnesses(snapshot, 0_02000000); } } diff --git a/src/neo/Network/P2P/Payloads/Header.cs b/src/neo/Network/P2P/Payloads/Header.cs index 6e88b5b4bb..b5a7b7e169 100644 --- a/src/neo/Network/P2P/Payloads/Header.cs +++ b/src/neo/Network/P2P/Payloads/Header.cs @@ -99,9 +99,9 @@ public override int GetHashCode() UInt160[] IVerifiable.GetScriptHashesForVerifying(DataCache snapshot) { if (PrevHash == UInt256.Zero) return new[] { Witness.ScriptHash }; - Header prev = Blockchain.Singleton.HeaderCache[Index - 1] ?? NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash)?.Header; + TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); if (prev is null) throw new InvalidOperationException(); - return new[] { prev.NextConsensus }; + return new[] { prev.Header.NextConsensus }; } public void Serialize(BinaryWriter writer) @@ -137,14 +137,24 @@ public JObject ToJson() return json; } - public bool Verify(DataCache snapshot) + internal bool Verify(DataCache snapshot) { - var prev = Blockchain.Singleton.HeaderCache[Index - 1] ?? NativeContract.Ledger.GetHeader(snapshot, Index - 1); + TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); if (prev is null) return false; - if (prev.Hash != PrevHash) return false; - if (prev.Timestamp >= Timestamp) return false; + if (prev.Index + 1 != Index) return false; + if (prev.Header.Timestamp >= Timestamp) return false; if (!this.VerifyWitnesses(snapshot, 1_00000000)) return false; return true; } + + internal bool Verify(DataCache snapshot, HeaderCache headerCache) + { + Header prev = headerCache.Last; + if (prev is null) return Verify(snapshot); + if (prev.Hash != PrevHash) return false; + if (prev.Index + 1 != Index) return false; + if (prev.Timestamp >= Timestamp) return false; + return this.VerifyWitness(snapshot, prev.NextConsensus, Witness, 1_00000000, out _); + } } } diff --git a/src/neo/Network/P2P/Payloads/IInventory.cs b/src/neo/Network/P2P/Payloads/IInventory.cs index 49256132c6..abd17fbd7f 100644 --- a/src/neo/Network/P2P/Payloads/IInventory.cs +++ b/src/neo/Network/P2P/Payloads/IInventory.cs @@ -1,5 +1,3 @@ -using Neo.Persistence; - namespace Neo.Network.P2P.Payloads { public interface IInventory : IVerifiable @@ -7,7 +5,5 @@ public interface IInventory : IVerifiable UInt256 Hash { get; } InventoryType InventoryType { get; } - - bool Verify(DataCache snapshot); } } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 99c122cfb6..3bedd8a706 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -278,11 +278,6 @@ public JObject ToJson() return json; } - bool IInventory.Verify(DataCache snapshot) - { - return Verify(snapshot, null) == VerifyResult.Succeed; - } - public virtual VerifyResult VerifyStateDependent(DataCache snapshot, TransactionVerificationContext context) { uint height = NativeContract.Ledger.CurrentIndex(snapshot); diff --git a/src/neo/Network/P2P/Payloads/VersionPayload.cs b/src/neo/Network/P2P/Payloads/VersionPayload.cs index 16c401b7db..abb170baf2 100644 --- a/src/neo/Network/P2P/Payloads/VersionPayload.cs +++ b/src/neo/Network/P2P/Payloads/VersionPayload.cs @@ -25,11 +25,11 @@ public class VersionPayload : ISerializable UserAgent.GetVarSize() + // UserAgent Capabilities.GetVarSize(); // Capabilities - public static VersionPayload Create(uint nonce, string userAgent, params NodeCapability[] capabilities) + public static VersionPayload Create(uint magic, uint nonce, string userAgent, params NodeCapability[] capabilities) { return new VersionPayload { - Magic = ProtocolSettings.Default.Magic, + Magic = magic, Version = LocalNode.ProtocolVersion, Timestamp = DateTime.Now.ToTimestamp(), Nonce = nonce, diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 9c3749d415..2c39f0465e 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -1,7 +1,6 @@ using Akka.Actor; using Neo.Cryptography; using Neo.IO.Caching; -using Neo.Ledger; using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -28,8 +27,8 @@ protected override UInt256 GetKeyForItem((UInt256, DateTime) item) } private readonly PendingKnownHashesCollection pendingKnownHashes = new PendingKnownHashesCollection(); - private readonly HashSetCache knownHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); - private readonly HashSetCache sentHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); + private readonly HashSetCache knownHashes; + private readonly HashSetCache sentHashes; private bool verack = false; private BloomFilter bloom_filter; @@ -152,7 +151,7 @@ private void OnFilterLoadMessageReceived(FilterLoadPayload payload) private void OnGetAddrMessageReceived() { Random rand = new Random(); - IEnumerable peers = LocalNode.Singleton.RemoteNodes.Values + IEnumerable peers = localNode.RemoteNodes.Values .Where(p => p.ListenerTcpPort > 0) .GroupBy(p => p.Remote.Address, (k, g) => g.First()) .OrderBy(p => rand.Next()) @@ -172,7 +171,7 @@ private void OnGetBlocksMessageReceived(GetBlocksPayload payload) { // The default value of payload.Count is -1 int count = payload.Count < 0 || payload.Count > InvPayload.MaxHashesCount ? InvPayload.MaxHashesCount : payload.Count; - DataCache snapshot = Blockchain.Singleton.View; + DataCache snapshot = system.StoreView; UInt256 hash = payload.HashStart; TrimmedBlock state = NativeContract.Ledger.GetTrimmedBlock(snapshot, hash); if (state == null) return; @@ -196,7 +195,7 @@ private void OnGetBlockByIndexMessageReceived(GetBlockByIndexPayload payload) uint count = payload.Count == -1 ? InvPayload.MaxHashesCount : Math.Min((uint)payload.Count, InvPayload.MaxHashesCount); for (uint i = payload.IndexStart, max = payload.IndexStart + count; i < max; i++) { - Block block = NativeContract.Ledger.GetBlock(Blockchain.Singleton.View, i); + Block block = NativeContract.Ledger.GetBlock(system.StoreView, i); if (block == null) break; @@ -226,13 +225,13 @@ private void OnGetDataMessageReceived(InvPayload payload) switch (payload.Type) { case InventoryType.TX: - if (Blockchain.Singleton.MemPool.TryGetValue(hash, out Transaction tx)) + if (system.MemPool.TryGetValue(hash, out Transaction tx)) EnqueueMessage(Message.Create(MessageCommand.Transaction, tx)); else notFound.Add(hash); break; case InventoryType.Block: - Block block = NativeContract.Ledger.GetBlock(Blockchain.Singleton.View, hash); + Block block = NativeContract.Ledger.GetBlock(system.StoreView, hash); if (block != null) { if (bloom_filter == null) @@ -251,7 +250,7 @@ private void OnGetDataMessageReceived(InvPayload payload) } break; default: - if (Blockchain.Singleton.RelayCache.TryGet(hash, out IInventory inventory)) + if (system.RelayCache.TryGet(hash, out IInventory inventory)) EnqueueMessage(Message.Create((MessageCommand)payload.Type, inventory)); break; } @@ -272,7 +271,7 @@ private void OnGetDataMessageReceived(InvPayload payload) /// A GetBlockByIndexPayload including start block index and number of blocks' headers requested. private void OnGetHeadersMessageReceived(GetBlockByIndexPayload payload) { - DataCache snapshot = Blockchain.Singleton.View; + DataCache snapshot = system.StoreView; if (payload.IndexStart > NativeContract.Ledger.CurrentIndex(snapshot)) return; List
headers = new List
(); uint count = payload.Count == -1 ? HeadersPayload.MaxHeadersCount : (uint)payload.Count; @@ -299,7 +298,7 @@ private void OnInventoryReceived(IInventory inventory) if (inventory is Block block) { UpdateLastBlockIndex(block.Index); - if (block.Index > NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View) + InvPayload.MaxHashesCount) return; + if (block.Index > NativeContract.Ledger.CurrentIndex(system.StoreView) + InvPayload.MaxHashesCount) return; } knownHashes.Add(inventory.Hash); system.TaskManager.Tell(inventory); @@ -314,13 +313,13 @@ private void OnInvMessageReceived(InvPayload payload) { case InventoryType.Block: { - DataCache snapshot = Blockchain.Singleton.View; + DataCache snapshot = system.StoreView; hashes = hashes.Where(p => !NativeContract.Ledger.ContainsBlock(snapshot, p)).ToArray(); } break; case InventoryType.TX: { - DataCache snapshot = Blockchain.Singleton.View; + DataCache snapshot = system.StoreView; hashes = hashes.Where(p => !NativeContract.Ledger.ContainsTransaction(snapshot, p)).ToArray(); } break; @@ -333,14 +332,14 @@ private void OnInvMessageReceived(InvPayload payload) private void OnMemPoolMessageReceived() { - foreach (InvPayload payload in InvPayload.CreateGroup(InventoryType.TX, Blockchain.Singleton.MemPool.GetVerifiedTransactions().Select(p => p.Hash).ToArray())) + foreach (InvPayload payload in InvPayload.CreateGroup(InventoryType.TX, system.MemPool.GetVerifiedTransactions().Select(p => p.Hash).ToArray())) EnqueueMessage(Message.Create(MessageCommand.Inv, payload)); } private void OnPingMessageReceived(PingPayload payload) { UpdateLastBlockIndex(payload.LastBlockIndex); - EnqueueMessage(Message.Create(MessageCommand.Pong, PingPayload.Create(NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View), payload.Nonce))); + EnqueueMessage(Message.Create(MessageCommand.Pong, PingPayload.Create(NativeContract.Ledger.CurrentIndex(system.StoreView), payload.Nonce))); } private void OnPongMessageReceived(PingPayload payload) @@ -372,7 +371,7 @@ private void OnVersionMessageReceived(VersionPayload payload) break; } } - if (!LocalNode.Singleton.AllowNewConnection(Self, this)) + if (!localNode.AllowNewConnection(Self, this)) { Disconnect(true); return; @@ -390,7 +389,7 @@ private void OnTimer() pendingKnownHashes.RemoveAt(0); } if (oneMinuteAgo > lastSent) - EnqueueMessage(Message.Create(MessageCommand.Ping, PingPayload.Create(NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View)))); + EnqueueMessage(Message.Create(MessageCommand.Ping, PingPayload.Create(NativeContract.Ledger.CurrentIndex(system.StoreView)))); } private void UpdateLastBlockIndex(uint lastBlockIndex) diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index dee2b893f3..d05af34818 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -4,7 +4,7 @@ using Neo.Cryptography; using Neo.IO; using Neo.IO.Actors; -using Neo.Ledger; +using Neo.IO.Caching; using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; @@ -22,6 +22,7 @@ internal class StartProtocol { } internal class Relay { public IInventory Inventory; } private readonly NeoSystem system; + private readonly LocalNode localNode; private readonly Queue message_queue_high = new Queue(); private readonly Queue message_queue_low = new Queue(); private DateTime lastSent = TimeProvider.Current.UtcNow; @@ -36,11 +37,14 @@ internal class Relay { public IInventory Inventory; } public uint LastHeightSent { get; private set; } = 0; public bool IsFullNode { get; private set; } = false; - public RemoteNode(NeoSystem system, object connection, IPEndPoint remote, IPEndPoint local) + public RemoteNode(NeoSystem system, LocalNode localNode, object connection, IPEndPoint remote, IPEndPoint local) : base(connection, remote, local) { this.system = system; - LocalNode.Singleton.RemoteNodes.TryAdd(Self, this); + this.localNode = localNode; + this.knownHashes = new HashSetCache(system.MemPool.Capacity * 2 / 5); + this.sentHashes = new HashSetCache(system.MemPool.Capacity * 2 / 5); + localNode.RemoteNodes.TryAdd(Self, this); } /// @@ -179,25 +183,25 @@ private void OnStartProtocol() { var capabilities = new List { - new FullNodeCapability(NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View)) + new FullNodeCapability(NativeContract.Ledger.CurrentIndex(system.StoreView)) }; - if (LocalNode.Singleton.ListenerTcpPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.TcpServer, (ushort)LocalNode.Singleton.ListenerTcpPort)); - if (LocalNode.Singleton.ListenerWsPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.WsServer, (ushort)LocalNode.Singleton.ListenerWsPort)); + if (localNode.ListenerTcpPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.TcpServer, (ushort)localNode.ListenerTcpPort)); + if (localNode.ListenerWsPort > 0) capabilities.Add(new ServerCapability(NodeCapabilityType.WsServer, (ushort)localNode.ListenerWsPort)); - SendMessage(Message.Create(MessageCommand.Version, VersionPayload.Create(LocalNode.Nonce, LocalNode.UserAgent, capabilities.ToArray()))); + SendMessage(Message.Create(MessageCommand.Version, VersionPayload.Create(system.Settings.Magic, LocalNode.Nonce, LocalNode.UserAgent, capabilities.ToArray()))); } protected override void PostStop() { timer.CancelIfNotNull(); - LocalNode.Singleton.RemoteNodes.TryRemove(Self, out _); + localNode.RemoteNodes.TryRemove(Self, out _); base.PostStop(); } - internal static Props Props(NeoSystem system, object connection, IPEndPoint remote, IPEndPoint local) + internal static Props Props(NeoSystem system, LocalNode localNode, object connection, IPEndPoint remote, IPEndPoint local) { - return Akka.Actor.Props.Create(() => new RemoteNode(system, connection, remote, local)).WithMailbox("remote-node-mailbox"); + return Akka.Actor.Props.Create(() => new RemoteNode(system, localNode, connection, remote, local)).WithMailbox("remote-node-mailbox"); } private void SendMessage(Message message) diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 8ea8457a72..60aed1772d 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -44,7 +44,7 @@ private class Timer { } public TaskManager(NeoSystem system) { this.system = system; - this.knownHashes = new HashSetCache(Blockchain.Singleton.MemPool.Capacity * 2 / 5); + this.knownHashes = new HashSetCache(system.MemPool.Capacity * 2 / 5); Context.System.EventStream.Subscribe(Self, typeof(Blockchain.PersistCompleted)); Context.System.EventStream.Subscribe(Self, typeof(Blockchain.RelayResult)); } @@ -72,8 +72,8 @@ private void OnNewTasks(InvPayload payload) return; // Do not accept payload of type InventoryType.TX if not synced on HeaderHeight - uint currentHeight = NativeContract.Ledger.CurrentIndex(Blockchain.Singleton.View); - uint headerHeight = Blockchain.Singleton.HeaderCache.Last?.Index ?? currentHeight; + uint currentHeight = NativeContract.Ledger.CurrentIndex(system.StoreView); + uint headerHeight = system.HeaderCache.Last?.Index ?? currentHeight; if (currentHeight < headerHeight && (payload.Type == InventoryType.TX || (payload.Type == InventoryType.Block && currentHeight < session.LastBlockIndex - InvPayload.MaxHashesCount))) { RequestTasks(Sender, session); @@ -313,7 +313,7 @@ private void RequestTasks(IActorRef remoteNode, TaskSession session) { if (session.HasTooManyTasks) return; - DataCache snapshot = Blockchain.Singleton.View; + DataCache snapshot = system.StoreView; // If there are pending tasks of InventoryType.Block we should process them if (session.AvailableTasks.Count > 0) @@ -339,10 +339,10 @@ private void RequestTasks(IActorRef remoteNode, TaskSession session) } uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); - uint headerHeight = Blockchain.Singleton.HeaderCache.Last?.Index ?? currentHeight; + uint headerHeight = system.HeaderCache.Last?.Index ?? currentHeight; // When the number of AvailableTasks is no more than 0, no pending tasks of InventoryType.Block, it should process pending the tasks of headers // If not HeaderTask pending to be processed it should ask for more Blocks - if ((!HasHeaderTask || globalInvTasks[HeaderTaskHash] < MaxConncurrentTasks) && headerHeight < session.LastBlockIndex && !Blockchain.Singleton.HeaderCache.Full) + if ((!HasHeaderTask || globalInvTasks[HeaderTaskHash] < MaxConncurrentTasks) && headerHeight < session.LastBlockIndex && !system.HeaderCache.Full) { session.InvTasks[HeaderTaskHash] = DateTime.UtcNow; IncrementGlobalTask(HeaderTaskHash); diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index 4e4cd3decc..1b9360979b 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.Configuration; +using Neo.Cryptography.ECC; using System; using System.Collections.Generic; using System.Linq; @@ -6,18 +7,20 @@ namespace Neo { - public class ProtocolSettings + public record ProtocolSettings { - public uint Magic { get; } - public byte AddressVersion { get; } - public string[] StandbyCommittee { get; } - public int CommitteeMembersCount { get; } - public int ValidatorsCount { get; } - public string[] SeedList { get; } - public uint MillisecondsPerBlock { get; } - public int MemoryPoolMaxTransactions { get; } - public uint MaxTraceableBlocks { get; } - public IReadOnlyDictionary NativeActivations { get; } + public uint Magic { get; init; } + public byte AddressVersion { get; init; } + public ECPoint[] StandbyCommittee { get; init; } + public ECPoint[] StandbyValidators { get; init; } + public int CommitteeMembersCount { get; init; } + public int ValidatorsCount { get; init; } + public string[] SeedList { get; init; } + public uint MillisecondsPerBlock { get; init; } + public TimeSpan TimePerBlock { get; init; } + public int MemoryPoolMaxTransactions { get; init; } + public uint MaxTraceableBlocks { get; init; } + public IReadOnlyDictionary NativeActivations { get; init; } static ProtocolSettings _default; @@ -51,10 +54,9 @@ private ProtocolSettings(IConfigurationSection section) this.Magic = section.GetValue("Magic", 0x4F454Eu); this.AddressVersion = section.GetValue("AddressVersion", (byte)0x35); IConfigurationSection section_sc = section.GetSection("StandbyCommittee"); - if (section_sc.Exists()) - this.StandbyCommittee = section_sc.GetChildren().Select(p => p.Get()).ToArray(); - else - this.StandbyCommittee = new[] + string[] s = section_sc.Exists() + ? section_sc.GetChildren().Select(p => p.Get()).ToArray() + : new[] { //Validators "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", @@ -81,8 +83,10 @@ private ProtocolSettings(IConfigurationSection section) "03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", "02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a" }; + this.StandbyCommittee = s.Select(p => ECPoint.Parse(p, ECCurve.Secp256r1)).ToArray(); this.CommitteeMembersCount = StandbyCommittee.Length; this.ValidatorsCount = section.GetValue("ValidatorsCount", (byte)7); + this.StandbyValidators = StandbyCommittee[0..ValidatorsCount]; IConfigurationSection section_sl = section.GetSection("SeedList"); if (section_sl.Exists()) this.SeedList = section_sl.GetChildren().Select(p => p.Get()).ToArray(); @@ -96,6 +100,7 @@ private ProtocolSettings(IConfigurationSection section) "seed5.neo.org:10333" }; this.MillisecondsPerBlock = section.GetValue("MillisecondsPerBlock", 15000u); + this.TimePerBlock = TimeSpan.FromMilliseconds(MillisecondsPerBlock); this.MemoryPoolMaxTransactions = Math.Max(1, section.GetValue("MemoryPoolMaxTransactions", 50_000)); this.MaxTraceableBlocks = section.GetValue("MaxTraceableBlocks", 2_102_400u);// 365 days IConfigurationSection section_na = section.GetSection("NativeActivations"); @@ -103,7 +108,6 @@ private ProtocolSettings(IConfigurationSection section) this.NativeActivations = section_na.GetChildren().ToDictionary((a) => a.Key, b => uint.Parse(b.Value)); else this.NativeActivations = new Dictionary(); - } } } diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 5b9e894fe0..f73b150f6b 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -1,6 +1,5 @@ using Neo.IO; using Neo.IO.Json; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.Plugins; @@ -328,7 +327,7 @@ private static Block CreateDummyBlock(DataCache snapshot) Version = 0, PrevHash = hash, MerkleRoot = new UInt256(), - Timestamp = currentBlock.Timestamp + Blockchain.MillisecondsPerBlock, + Timestamp = currentBlock.Timestamp + 1, Index = currentBlock.Index + 1, NextConsensus = currentBlock.NextConsensus, Witness = new Witness @@ -356,9 +355,8 @@ internal static void ResetApplicationEngineProvider() Exchange(ref applicationEngineProvider, null); } - public static ApplicationEngine Run(byte[] script, DataCache snapshot = null, IVerifiable container = null, Block persistingBlock = null, int offset = 0, long gas = TestModeGas) + public static ApplicationEngine Run(byte[] script, DataCache snapshot, IVerifiable container = null, Block persistingBlock = null, int offset = 0, long gas = TestModeGas) { - snapshot ??= Blockchain.Singleton.View; persistingBlock ??= CreateDummyBlock(snapshot); ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock, gas); engine.LoadScript(script, initialPosition: offset); diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index c68f4974b9..b64527ffd1 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -1,7 +1,7 @@ using Neo.Cryptography.ECC; using Neo.IO.Json; -using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.VM; using System; using System.Collections.Generic; @@ -58,6 +58,7 @@ public JObject ToJson() } public readonly IVerifiable Verifiable; + public readonly DataCache Snapshot; private readonly Dictionary ContextItems; public bool Completed @@ -79,25 +80,12 @@ public bool Completed /// ScriptHashes are the verifiable ScriptHashes from Verifiable element /// Equivalent to: Verifiable.GetScriptHashesForVerifying(Blockchain.Singleton.GetSnapshot()) /// - public IReadOnlyList ScriptHashes - { - get - { - if (_ScriptHashes is null) - { - // snapshot is not necessary for Transaction - if (Verifiable is Transaction) - _ScriptHashes = Verifiable.GetScriptHashesForVerifying(null); - else - _ScriptHashes = Verifiable.GetScriptHashesForVerifying(Blockchain.Singleton.View); - } - return _ScriptHashes; - } - } + public IReadOnlyList ScriptHashes => _ScriptHashes ??= Verifiable.GetScriptHashesForVerifying(Snapshot); - public ContractParametersContext(IVerifiable verifiable) + public ContractParametersContext(DataCache snapshot, IVerifiable verifiable) { this.Verifiable = verifiable; + this.Snapshot = snapshot; this.ContextItems = new Dictionary(); } @@ -183,7 +171,7 @@ private ContextItem CreateItem(Contract contract) return item; } - public static ContractParametersContext FromJson(JObject json) + public static ContractParametersContext FromJson(JObject json, DataCache snapshot) { var type = typeof(ContractParametersContext).GetTypeInfo().Assembly.GetType(json["type"].AsString()); if (!typeof(IVerifiable).IsAssignableFrom(type)) throw new FormatException(); @@ -194,7 +182,7 @@ public static ContractParametersContext FromJson(JObject json) { verifiable.DeserializeUnsigned(reader); } - ContractParametersContext context = new ContractParametersContext(verifiable); + ContractParametersContext context = new ContractParametersContext(snapshot, verifiable); foreach (var property in json["items"].Properties) { context.ContextItems.Add(UInt160.Parse(property.Key), ContextItem.FromJson(property.Value)); @@ -244,9 +232,9 @@ public Witness[] GetWitnesses() return witnesses; } - public static ContractParametersContext Parse(string value) + public static ContractParametersContext Parse(string value, DataCache snapshot) { - return FromJson(JObject.Parse(value)); + return FromJson(JObject.Parse(value), snapshot); } public JObject ToJson() diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 21eeb54a4b..07c7673579 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -1,4 +1,5 @@ using Neo.IO; +using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.VM; using System; @@ -141,11 +142,11 @@ internal virtual void PostPersist(ApplicationEngine engine) { } - public ApplicationEngine TestCall(string operation, params object[] args) + public ApplicationEngine TestCall(DataCache snapshot, string operation, params object[] args) { using ScriptBuilder sb = new ScriptBuilder(); sb.EmitDynamicCall(Hash, operation, args); - return ApplicationEngine.Run(sb.ToArray()); + return ApplicationEngine.Run(sb.ToArray(), snapshot); } } } diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index 15430e9a2b..e137471249 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -1,4 +1,3 @@ -using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -14,9 +13,8 @@ public class AssetDescriptor public string Symbol { get; } public byte Decimals { get; } - public AssetDescriptor(UInt160 asset_id) + public AssetDescriptor(DataCache snapshot, UInt160 asset_id) { - DataCache snapshot = Blockchain.Singleton.View; var contract = NativeContract.ContractManagement.GetContract(snapshot, asset_id); if (contract is null) throw new ArgumentException(); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 186da86be7..893a8e46bf 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -1,6 +1,5 @@ using Neo.Cryptography; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -124,13 +123,13 @@ public WalletAccount GetAccount(ECPoint pubkey) return GetAccount(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash()); } - public BigDecimal GetAvailable(UInt160 asset_id) + public BigDecimal GetAvailable(DataCache snapshot, UInt160 asset_id) { UInt160[] accounts = GetAccounts().Where(p => !p.WatchOnly).Select(p => p.ScriptHash).ToArray(); - return GetBalance(asset_id, accounts); + return GetBalance(snapshot, asset_id, accounts); } - public BigDecimal GetBalance(UInt160 asset_id, params UInt160[] accounts) + public BigDecimal GetBalance(DataCache snapshot, UInt160 asset_id, params UInt160[] accounts) { byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) @@ -144,8 +143,8 @@ public BigDecimal GetBalance(UInt160 asset_id, params UInt160[] accounts) sb.EmitDynamicCall(asset_id, "decimals"); script = sb.ToArray(); } - using ApplicationEngine engine = ApplicationEngine.Run(script, gas: 20000000L * accounts.Length); - if (engine.State.HasFlag(VMState.FAULT)) + using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, gas: 20000000L * accounts.Length); + if (engine.State == VMState.FAULT) return new BigDecimal(BigInteger.Zero, 0); byte decimals = (byte)engine.ResultStack.Pop().GetInteger(); BigInteger amount = engine.ResultStack.Pop().GetInteger(); @@ -241,7 +240,7 @@ public virtual WalletAccount Import(string nep2, string passphrase, int N = 1638 return account; } - public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null, Signer[] cosigners = null) + public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, UInt160 from = null, Signer[] cosigners = null) { UInt160[] accounts; if (from is null) @@ -252,7 +251,6 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null { accounts = new[] { from }; } - DataCache snapshot = Blockchain.Singleton.View; Dictionary cosignerList = cosigners?.ToDictionary(p => p.Account) ?? new Dictionary(); byte[] script; List<(UInt160 Account, BigInteger Value)> balances_gas = null; @@ -310,7 +308,7 @@ public Transaction MakeTransaction(TransferOutput[] outputs, UInt160 from = null return MakeTransaction(snapshot, script, cosignerList.Values.ToArray(), Array.Empty(), balances_gas); } - public Transaction MakeTransaction(byte[] script, UInt160 sender = null, Signer[] cosigners = null, TransactionAttribute[] attributes = null, long maxGas = ApplicationEngine.TestModeGas) + public Transaction MakeTransaction(DataCache snapshot, byte[] script, UInt160 sender = null, Signer[] cosigners = null, TransactionAttribute[] attributes = null, long maxGas = ApplicationEngine.TestModeGas) { UInt160[] accounts; if (sender is null) @@ -321,7 +319,6 @@ public Transaction MakeTransaction(byte[] script, UInt160 sender = null, Signer[ { accounts = new[] { sender }; } - DataCache snapshot = Blockchain.Singleton.View; var balances_gas = accounts.Select(p => (Account: p, Value: NativeContract.GAS.BalanceOf(snapshot, p))).Where(p => p.Value.Sign > 0).ToList(); return MakeTransaction(snapshot, script, cosigners ?? Array.Empty(), attributes ?? Array.Empty(), balances_gas, maxGas); } @@ -470,7 +467,7 @@ public bool Sign(ContractParametersContext context) // Try Smart contract verification - var contract = NativeContract.ContractManagement.GetContract(Blockchain.Singleton.View, scriptHash); + var contract = NativeContract.ContractManagement.GetContract(context.Snapshot, scriptHash); if (contract != null) { diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index 4a6ba9bc13..a1d50e4884 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -1,7 +1,9 @@ +using Akka.TestKit; using Akka.TestKit.Xunit2; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; using Neo.Network.P2P.Payloads; +using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.Wallets; @@ -16,11 +18,17 @@ public class UT_Blockchain : TestKit { private NeoSystem system; private Transaction txSample; + private TestProbe senderProbe; [TestInitialize] public void Initialize() { system = TestBlockchain.TheNeoSystem; + + senderProbe = CreateTestProbe(); + senderProbe.Send(system.Blockchain, new object()); + senderProbe.ExpectNoMsg(); // Ensure blockchain it's created + txSample = new Transaction() { Attributes = Array.Empty(), @@ -28,14 +36,13 @@ public void Initialize() Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, Witnesses = Array.Empty() }; - Blockchain.Singleton.MemPool.TryAdd(txSample, Blockchain.Singleton.GetSnapshot()); + system.MemPool.TryAdd(txSample, TestBlockchain.GetTestSnapshot()); } [TestMethod] public void TestValidTransaction() { - var senderProbe = CreateTestProbe(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.TheNeoSystem.GetSnapshot(); var walletA = TestUtils.GenerateTestWallet(); using var unlockA = walletA.Unlock("123"); @@ -50,7 +57,7 @@ public void TestValidTransaction() // Make transaction - var tx = CreateValidTx(walletA, acc.ScriptHash, 0); + var tx = CreateValidTx(snapshot, walletA, acc.ScriptHash, 0); senderProbe.Send(system.Blockchain, tx); senderProbe.ExpectMsg(p => p.Result == VerifyResult.Succeed); @@ -71,9 +78,9 @@ internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) return storageKey; } - private static Transaction CreateValidTx(NEP6Wallet wallet, UInt160 account, uint nonce) + private static Transaction CreateValidTx(DataCache snapshot, NEP6Wallet wallet, UInt160 account, uint nonce) { - var tx = wallet.MakeTransaction(new TransferOutput[] + var tx = wallet.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -86,7 +93,7 @@ private static Transaction CreateValidTx(NEP6Wallet wallet, UInt160 account, uin tx.Nonce = nonce; - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); Assert.IsTrue(wallet.Sign(data)); Assert.IsTrue(data.Completed); diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 62010a03a5..36465c2446 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -42,6 +42,11 @@ public static void TestSetup(TestContext ctx) testBlockchain = TestBlockchain.TheNeoSystem; } + private DataCache GetSnapshot() + { + return testBlockchain.StoreView.CreateSnapshot(); + } + [TestInitialize] public void TestSetup() { @@ -51,7 +56,7 @@ public void TestSetup() TestBlockchain.InitializeMockNeoSystem(); // Create a MemoryPool with capacity of 100 - _unit = new MemoryPool(TestBlockchain.TheNeoSystem, 100); + _unit = new MemoryPool(new NeoSystem(ProtocolSettings.Default with { MemoryPoolMaxTransactions = 100 })); // Verify capacity equals the amount specified _unit.Capacity.Should().Be(100); @@ -59,7 +64,7 @@ public void TestSetup() _unit.VerifiedCount.Should().Be(0); _unit.UnVerifiedCount.Should().Be(0); _unit.Count.Should().Be(0); - _unit2 = new MemoryPool(TestBlockchain.TheNeoSystem, 0); + _unit2 = new MemoryPool(new NeoSystem(ProtocolSettings.Default with { MemoryPoolMaxTransactions = 0 })); plugin = new TestIMemoryPoolTxObserverPlugin(); } @@ -134,7 +139,7 @@ private Transaction CreateTransaction(long fee = -1) private void AddTransactions(int count) { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = GetSnapshot(); for (int i = 0; i < count; i++) { var txToAdd = CreateTransaction(); @@ -146,7 +151,7 @@ private void AddTransactions(int count) private void AddTransaction(Transaction txToAdd) { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = GetSnapshot(); _unit.TryAdd(txToAdd, snapshot); } @@ -188,32 +193,32 @@ public void BlockPersistMovesTxToUnverifiedAndReverification() Transactions = _unit.GetSortedVerifiedTransactions().Take(10) .Concat(_unit.GetSortedVerifiedTransactions().Take(5)).ToArray() }; - _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); + _unit.UpdatePoolForBlockPersisted(block, GetSnapshot()); _unit.InvalidateVerifiedTransactions(); _unit.SortedTxCount.Should().Be(0); _unit.UnverifiedSortedTxCount.Should().Be(60); - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); + _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, GetSnapshot()); _unit.SortedTxCount.Should().Be(10); _unit.UnverifiedSortedTxCount.Should().Be(50); - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); + _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, GetSnapshot()); _unit.SortedTxCount.Should().Be(20); _unit.UnverifiedSortedTxCount.Should().Be(40); - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); + _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, GetSnapshot()); _unit.SortedTxCount.Should().Be(30); _unit.UnverifiedSortedTxCount.Should().Be(30); - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); + _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, GetSnapshot()); _unit.SortedTxCount.Should().Be(40); _unit.UnverifiedSortedTxCount.Should().Be(20); - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); + _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, GetSnapshot()); _unit.SortedTxCount.Should().Be(50); _unit.UnverifiedSortedTxCount.Should().Be(10); - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, Blockchain.Singleton.GetSnapshot()); + _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(10, GetSnapshot()); _unit.SortedTxCount.Should().Be(60); _unit.UnverifiedSortedTxCount.Should().Be(0); } @@ -221,7 +226,7 @@ public void BlockPersistMovesTxToUnverifiedAndReverification() [TestMethod] public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() { - using SnapshotCache snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = GetSnapshot(); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, senderAccount); ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, long.MaxValue); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); @@ -294,7 +299,7 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() Header = new Header(), Transactions = Array.Empty() }; - _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); + _unit.UpdatePoolForBlockPersisted(block, GetSnapshot()); _unit.InvalidateVerifiedTransactions(); _unit.SortedTxCount.Should().Be(0); _unit.UnverifiedSortedTxCount.Should().Be(100); @@ -310,7 +315,7 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() var minTransaction = sortedUnverifiedArray.Last(); // reverify 1 high priority and 1 low priority transaction - _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(1, Blockchain.Singleton.GetSnapshot()); + _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(1, GetSnapshot()); var verifiedTxs = _unit.GetSortedVerifiedTransactions().ToArray(); verifiedTxs.Length.Should().Be(1); verifiedTxs[0].Should().BeEquivalentTo(maxTransaction); @@ -320,7 +325,7 @@ public void VerifySortOrderAndThatHighetFeeTransactionsAreReverifiedFirst() Transactions = new[] { maxTransaction, minTransaction } }; // verify and remove the 2 transactions from the verified pool - _unit.UpdatePoolForBlockPersisted(blockWith2Tx, Blockchain.Singleton.GetSnapshot()); + _unit.UpdatePoolForBlockPersisted(blockWith2Tx, GetSnapshot()); _unit.InvalidateVerifiedTransactions(); _unit.SortedTxCount.Should().Be(0); } @@ -363,7 +368,7 @@ public void CapacityTestWithUnverifiedHighProirtyTransactions() Header = new Header(), Transactions = Array.Empty() }; - _unit.UpdatePoolForBlockPersisted(block, Blockchain.Singleton.GetSnapshot()); + _unit.UpdatePoolForBlockPersisted(block, GetSnapshot()); _unit.CanTransactionFitInPool(CreateTransaction()).Should().Be(true); AddTransactions(1); @@ -385,7 +390,7 @@ public void TestInvalidateAll() [TestMethod] public void TestContainsKey() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = GetSnapshot(); AddTransactions(10); var txToAdd = CreateTransaction(); @@ -425,7 +430,7 @@ public void TestIEnumerableGetEnumerator() [TestMethod] public void TestGetVerifiedTransactions() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = GetSnapshot(); var tx1 = CreateTransaction(); var tx2 = CreateTransaction(); _unit.TryAdd(tx1, snapshot); @@ -441,7 +446,8 @@ public void TestGetVerifiedTransactions() [TestMethod] public void TestReVerifyTopUnverifiedTransactionsIfNeeded() { - _unit = new MemoryPool(TestBlockchain.TheNeoSystem, 600); + _unit = new MemoryPool(new NeoSystem(ProtocolSettings.Default with { MemoryPoolMaxTransactions = 600 })); + AddTransaction(CreateTransaction(100000001)); AddTransaction(CreateTransaction(100000001)); AddTransaction(CreateTransaction(100000001)); @@ -457,17 +463,17 @@ public void TestReVerifyTopUnverifiedTransactionsIfNeeded() _unit.VerifiedCount.Should().Be(511); _unit.UnVerifiedCount.Should().Be(4); - var result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(1, Blockchain.Singleton.GetSnapshot()); + var result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(1, GetSnapshot()); result.Should().BeTrue(); _unit.VerifiedCount.Should().Be(512); _unit.UnVerifiedCount.Should().Be(3); - result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(2, Blockchain.Singleton.GetSnapshot()); + result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(2, GetSnapshot()); result.Should().BeTrue(); _unit.VerifiedCount.Should().Be(514); _unit.UnVerifiedCount.Should().Be(1); - result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(3, Blockchain.Singleton.GetSnapshot()); + result = _unit.ReVerifyTopUnverifiedTransactionsIfNeeded(3, GetSnapshot()); result.Should().BeFalse(); _unit.VerifiedCount.Should().Be(515); _unit.UnVerifiedCount.Should().Be(0); @@ -476,7 +482,7 @@ public void TestReVerifyTopUnverifiedTransactionsIfNeeded() [TestMethod] public void TestTryAdd() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = GetSnapshot(); var tx1 = CreateTransaction(); _unit.TryAdd(tx1, snapshot).Should().Be(VerifyResult.Succeed); _unit.TryAdd(tx1, snapshot).Should().NotBe(VerifyResult.Succeed); @@ -486,7 +492,7 @@ public void TestTryAdd() [TestMethod] public void TestTryGetValue() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = GetSnapshot(); var tx1 = CreateTransaction(); _unit.TryAdd(tx1, snapshot); _unit.TryGetValue(tx1.Hash, out Transaction tx).Should().BeTrue(); @@ -503,7 +509,7 @@ public void TestTryGetValue() [TestMethod] public void TestUpdatePoolForBlockPersisted() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = GetSnapshot(); byte[] transactionsPerBlock = { 0x18, 0x00, 0x00, 0x00 }; // 24 byte[] feePerByte = { 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00 }; // 1048576 StorageItem item1 = new StorageItem diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs b/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs index 291d9b7d1a..63e500abec 100644 --- a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs +++ b/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs @@ -51,7 +51,7 @@ private Transaction CreateTransactionWithFee(long networkFee, long systemFee) public void TestDuplicateOracle() { // Fake balance - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, long.MaxValue); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, UInt160.Zero); @@ -73,7 +73,7 @@ public void TestDuplicateOracle() [TestMethod] public void TestTransactionSenderFee() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, long.MaxValue); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, UInt160.Zero); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); diff --git a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs index 80511b81a4..19e78c51f2 100644 --- a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs +++ b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs @@ -43,7 +43,7 @@ public void Init() [TestMethod] public void TestGetBlock() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var tx1 = TestUtils.GetTransaction(UInt160.Zero); tx1.Script = new byte[] { 0x01,0x01,0x01,0x01, 0x01,0x01,0x01,0x01, diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index 489a5d1c1c..846c35de0f 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -44,7 +44,7 @@ public void GetHashCodeTest() public void TrimTest() { UInt256 val256 = UInt256.Zero; - var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot().CreateSnapshot(); TestUtils.SetupHeaderWithValues(uut, val256, out _, out _, out _, out _, out _); uut.Witness = new Witness() { InvocationScript = new byte[0], VerificationScript = new byte[0] }; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs index 645e599cce..16d801f188 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs @@ -70,7 +70,7 @@ public void DeserializeAndSerialize() public void Verify() { var test = new HighPriorityAttribute(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); Assert.IsFalse(test.Verify(snapshot, new Transaction() { Signers = new Signer[] { } })); Assert.IsFalse(test.Verify(snapshot, new Transaction() { Signers = new Signer[] { new Signer() { Account = UInt160.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01") } } })); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 7e69a59fc7..f27a2e136a 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -104,7 +104,7 @@ public void FeeIsMultiSigContract() { var walletA = TestUtils.GenerateTestWallet(); var walletB = TestUtils.GenerateTestWallet(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); using (var unlockA = walletA.Unlock("123")) using (var unlockB = walletB.Unlock("123")) @@ -133,7 +133,7 @@ public void FeeIsMultiSigContract() // Make transaction - var tx = walletA.MakeTransaction(new TransferOutput[] + var tx = walletA.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -147,7 +147,7 @@ public void FeeIsMultiSigContract() // Sign - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); Assert.IsTrue(walletA.Sign(data)); Assert.IsTrue(walletB.Sign(data)); Assert.IsTrue(data.Completed); @@ -185,7 +185,7 @@ public void FeeIsMultiSigContract() public void FeeIsSignatureContractDetailed() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); using (var unlock = wallet.Unlock("123")) { @@ -204,7 +204,7 @@ public void FeeIsSignatureContractDetailed() // Make transaction // self-transfer of 1e-8 GAS - var tx = wallet.MakeTransaction(new TransferOutput[] + var tx = wallet.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -224,7 +224,7 @@ public void FeeIsSignatureContractDetailed() // Sign // ---- - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); // 'from' is always required as witness // if not included on cosigner with a scope, its scope should be considered 'CalledByEntry' data.ScriptHashes.Count.Should().Be(1); @@ -294,7 +294,7 @@ public void FeeIsSignatureContractDetailed() public void FeeIsSignatureContract_TestScope_Global() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -333,7 +333,7 @@ public void FeeIsSignatureContract_TestScope_Global() // using this... - var tx = wallet.MakeTransaction(script, acc.ScriptHash, signers); + var tx = wallet.MakeTransaction(snapshot, script, acc.ScriptHash, signers); Assert.IsNotNull(tx); Assert.IsNull(tx.Witnesses); @@ -342,7 +342,7 @@ public void FeeIsSignatureContract_TestScope_Global() // Sign // ---- - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); bool signed = wallet.Sign(data); Assert.IsTrue(signed); @@ -380,7 +380,7 @@ public void FeeIsSignatureContract_TestScope_Global() public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -420,7 +420,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() // using this... - var tx = wallet.MakeTransaction(script, acc.ScriptHash, signers); + var tx = wallet.MakeTransaction(snapshot, script, acc.ScriptHash, signers); Assert.IsNotNull(tx); Assert.IsNull(tx.Witnesses); @@ -429,7 +429,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() // Sign // ---- - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); bool signed = wallet.Sign(data); Assert.IsTrue(signed); @@ -467,7 +467,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -510,7 +510,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() // using this... - var tx = wallet.MakeTransaction(script, acc.ScriptHash, signers); + var tx = wallet.MakeTransaction(snapshot, script, acc.ScriptHash, signers); Assert.IsNotNull(tx); Assert.IsNull(tx.Witnesses); @@ -519,7 +519,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() // Sign // ---- - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); bool signed = wallet.Sign(data); Assert.IsTrue(signed); @@ -557,7 +557,7 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -598,7 +598,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() // expects FAULT on execution of 'transfer' Application script // due to lack of a valid witness validation Transaction tx = null; - Assert.ThrowsException(() => tx = wallet.MakeTransaction(script, acc.ScriptHash, signers)); + Assert.ThrowsException(() => tx = wallet.MakeTransaction(snapshot, script, acc.ScriptHash, signers)); Assert.IsNull(tx); } } @@ -607,7 +607,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_FAULT() public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -647,7 +647,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() // using this... - var tx = wallet.MakeTransaction(script, acc.ScriptHash, signers); + var tx = wallet.MakeTransaction(snapshot, script, acc.ScriptHash, signers); Assert.IsNotNull(tx); Assert.IsNull(tx.Witnesses); @@ -656,7 +656,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() // Sign // ---- - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); bool signed = wallet.Sign(data); Assert.IsTrue(signed); @@ -699,7 +699,7 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() public void FeeIsSignatureContract_TestScope_NoScopeFAULT() { var wallet = TestUtils.GenerateTestWallet(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -742,7 +742,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() // expects FAULT on execution of 'transfer' Application script // due to lack of a valid witness validation Transaction tx = null; - Assert.ThrowsException(() => tx = wallet.MakeTransaction(script, acc.ScriptHash, signers, attributes)); + Assert.ThrowsException(() => tx = wallet.MakeTransaction(snapshot, script, acc.ScriptHash, signers, attributes)); Assert.IsNull(tx); } } @@ -750,7 +750,7 @@ public void FeeIsSignatureContract_TestScope_NoScopeFAULT() [TestMethod] public void Transaction_Reverify_Hashes_Length_Unequal_To_Witnesses_Length() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); Transaction txSimple = new Transaction { Version = 0x00, @@ -959,7 +959,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() cosigner.Scopes.Should().Be(WitnessScope.None); var wallet = TestUtils.GenerateTestWallet(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); // no password on this wallet using (var unlock = wallet.Unlock("")) @@ -996,12 +996,12 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() Scopes = WitnessScope.None } }; - Assert.ThrowsException(() => wallet.MakeTransaction(script, acc.ScriptHash, signers)); + Assert.ThrowsException(() => wallet.MakeTransaction(snapshot, script, acc.ScriptHash, signers)); // change to global scope signers[0].Scopes = WitnessScope.Global; - var tx = wallet.MakeTransaction(script, acc.ScriptHash, signers); + var tx = wallet.MakeTransaction(snapshot, script, acc.ScriptHash, signers); Assert.IsNotNull(tx); Assert.IsNull(tx.Witnesses); @@ -1010,7 +1010,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() // Sign // ---- - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); bool signed = wallet.Sign(data); Assert.IsTrue(signed); @@ -1124,7 +1124,7 @@ public void Test_VerifyStateIndependent() var walletA = TestUtils.GenerateTestWallet(); var walletB = TestUtils.GenerateTestWallet(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); using (var unlockA = walletA.Unlock("123")) using (var unlockB = walletB.Unlock("123")) @@ -1153,7 +1153,7 @@ public void Test_VerifyStateIndependent() // Make transaction - tx = walletA.MakeTransaction(new TransferOutput[] + tx = walletA.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -1165,7 +1165,7 @@ public void Test_VerifyStateIndependent() // Sign - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); Assert.IsTrue(walletA.Sign(data)); Assert.IsTrue(walletB.Sign(data)); Assert.IsTrue(data.Completed); @@ -1178,7 +1178,7 @@ public void Test_VerifyStateIndependent() [TestMethod] public void Test_VerifyStateDependent() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var height = NativeContract.Ledger.CurrentIndex(snapshot); var tx = new Transaction() { @@ -1228,7 +1228,7 @@ public void Test_VerifyStateDependent() // Fake balance - snapshot = Blockchain.Singleton.GetSnapshot(); + snapshot = TestBlockchain.GetTestSnapshot(); key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); balance = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); balance.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -1236,7 +1236,7 @@ public void Test_VerifyStateDependent() // Make transaction snapshot.Commit(); - tx = walletA.MakeTransaction(new TransferOutput[] + tx = walletA.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -1248,7 +1248,7 @@ public void Test_VerifyStateDependent() // Sign - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); Assert.IsTrue(walletA.Sign(data)); Assert.IsTrue(walletB.Sign(data)); Assert.IsTrue(data.Completed); @@ -1261,7 +1261,7 @@ public void Test_VerifyStateDependent() [TestMethod] public void Test_Verify() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var tx = new Transaction() { Attributes = Array.Empty(), @@ -1306,7 +1306,7 @@ public void Test_Verify() // Make transaction - tx = walletA.MakeTransaction(new TransferOutput[] + tx = walletA.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -1318,7 +1318,7 @@ public void Test_Verify() // Sign - var data = new ContractParametersContext(tx); + var data = new ContractParametersContext(snapshot, tx); Assert.IsTrue(walletA.Sign(data)); Assert.IsTrue(walletB.Sign(data)); Assert.IsTrue(data.Completed); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_VersionPayload.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_VersionPayload.cs index ebe39427d1..ccd61f4718 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_VersionPayload.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_VersionPayload.cs @@ -16,14 +16,14 @@ public void SizeAndEndPoint_Get() var test = new VersionPayload() { Capabilities = new NodeCapability[0], UserAgent = "neo3" }; test.Size.Should().Be(22); - test = VersionPayload.Create(123, "neo3", new NodeCapability[] { new ServerCapability(NodeCapabilityType.TcpServer, 22) }); + test = VersionPayload.Create(123, 456, "neo3", new NodeCapability[] { new ServerCapability(NodeCapabilityType.TcpServer, 22) }); test.Size.Should().Be(25); } [TestMethod] public void DeserializeAndSerialize() { - var test = VersionPayload.Create(123, "neo3", new NodeCapability[] { new ServerCapability(NodeCapabilityType.TcpServer, 22) }); + var test = VersionPayload.Create(123, 456, "neo3", new NodeCapability[] { new ServerCapability(NodeCapabilityType.TcpServer, 22) }); var clone = test.ToArray().AsSerializable(); CollectionAssert.AreEqual(test.Capabilities.ToByteArray(), clone.Capabilities.ToByteArray()); @@ -32,7 +32,7 @@ public void DeserializeAndSerialize() Assert.AreEqual(test.Timestamp, clone.Timestamp); CollectionAssert.AreEqual(test.Capabilities.ToByteArray(), clone.Capabilities.ToByteArray()); - Assert.ThrowsException(() => VersionPayload.Create(123, "neo3", + Assert.ThrowsException(() => VersionPayload.Create(123, 456, "neo3", new NodeCapability[] { new ServerCapability(NodeCapabilityType.TcpServer, 22) , new ServerCapability(NodeCapabilityType.TcpServer, 22) diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index 9681276a1c..2b10047615 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -20,6 +20,7 @@ public class UT_Witness public void TestSetup() { uut = new Witness(); + TestBlockchain.InitializeMockNeoSystem(); } [TestMethod] @@ -33,6 +34,7 @@ private Witness PrepareDummyWitness(int pubKeys, int m) var address = new WalletAccount[pubKeys]; var wallets = new NEP6Wallet[pubKeys]; var walletsUnlocks = new IDisposable[pubKeys]; + var snapshot = TestBlockchain.GetTestSnapshot(); for (int x = 0; x < pubKeys; x++) { @@ -52,7 +54,7 @@ private Witness PrepareDummyWitness(int pubKeys, int m) // Sign - var data = new ContractParametersContext(new Transaction() + var data = new ContractParametersContext(snapshot, new Transaction() { Attributes = Array.Empty(), Signers = new[] {new Signer() diff --git a/tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs b/tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs deleted file mode 100644 index 81b610ab14..0000000000 --- a/tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Network.P2P; -using System; -using System.Linq; -using System.Net; - -namespace Neo.UnitTests.Network.P2P -{ - [TestClass] - public class UT_LocalNode - { - private static NeoSystem testBlockchain; - - [TestInitialize] - public void Init() - { - testBlockchain = TestBlockchain.TheNeoSystem; - } - - [TestMethod] - public void TestDefaults() - { - Assert.AreEqual(0, LocalNode.Singleton.ListenerTcpPort); - Assert.AreEqual(0, LocalNode.Singleton.ListenerWsPort); - Assert.AreEqual(3, LocalNode.Singleton.MaxConnectionsPerAddress); - Assert.AreEqual(10, LocalNode.Singleton.MinDesiredConnections); - Assert.AreEqual(40, LocalNode.Singleton.MaxConnections); - Assert.AreEqual(0, LocalNode.Singleton.UnconnectedCount); - - CollectionAssert.AreEqual(Array.Empty(), LocalNode.Singleton.GetRemoteNodes().ToArray()); - CollectionAssert.AreEqual(Array.Empty(), LocalNode.Singleton.GetUnconnectedPeers().ToArray()); - } - } -} diff --git a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs index 8f5b9c8000..9698474460 100644 --- a/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs +++ b/tests/neo.UnitTests/Network/P2P/UT_RemoteNode.cs @@ -30,7 +30,7 @@ public static void TestSetup(TestContext ctx) public void RemoteNode_Test_Abort_DifferentMagic() { var connectionTestProbe = CreateTestProbe(); - var remoteNodeActor = ActorOfAsTestActorRef(() => new RemoteNode(testBlockchain, connectionTestProbe, null, null)); + var remoteNodeActor = ActorOfAsTestActorRef(() => new RemoteNode(testBlockchain, new LocalNode(testBlockchain), connectionTestProbe, null, null)); var msg = Message.Create(MessageCommand.Version, new VersionPayload { @@ -55,7 +55,7 @@ public void RemoteNode_Test_Abort_DifferentMagic() public void RemoteNode_Test_Accept_IfSameMagic() { var connectionTestProbe = CreateTestProbe(); - var remoteNodeActor = ActorOfAsTestActorRef(() => new RemoteNode(testBlockchain, connectionTestProbe, new IPEndPoint(IPAddress.Parse("192.168.1.2"), 8080), new IPEndPoint(IPAddress.Parse("192.168.1.1"), 8080))); + var remoteNodeActor = ActorOfAsTestActorRef(() => new RemoteNode(testBlockchain, new LocalNode(testBlockchain), connectionTestProbe, new IPEndPoint(IPAddress.Parse("192.168.1.2"), 8080), new IPEndPoint(IPAddress.Parse("192.168.1.1"), 8080))); var msg = Message.Create(MessageCommand.Version, new VersionPayload() { diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs index fdf93436f4..53799abba2 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs @@ -30,7 +30,7 @@ public void TestName() [TestMethod] public void TestTotalSupply() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); StorageItem item = new StorageItem { @@ -47,7 +47,7 @@ public void TestTotalSupply() [TestMethod] public void TestTotalSupplyDecimal() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); BigInteger totalSupply = 100_000_000; totalSupply *= test.Factor; diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs index b4f3d42a40..d920a7b593 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -24,7 +24,7 @@ public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - _snapshot = Blockchain.Singleton.GetSnapshot(); + _snapshot = TestBlockchain.GetTestSnapshot(); _persistingBlock = new Block { Header = new Header() }; } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs index f92936012c..7865d80983 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs @@ -32,7 +32,7 @@ public class UT_NNS public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - _snapshot = Blockchain.Singleton.GetSnapshot(); + _snapshot = TestBlockchain.GetTestSnapshot(); _persistingBlock = new Block { Header = new Header(), diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs index c255521554..854afeb7b9 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs @@ -24,7 +24,7 @@ public class UT_NameService : TestKit public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - _snapshot = Blockchain.Singleton.GetSnapshot(); + _snapshot = TestBlockchain.GetTestSnapshot(); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index d7287c6b38..9899b8748d 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -92,7 +92,7 @@ public void TestGetContract() [TestMethod] public void TestInvoke() { - using var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); snapshot.Add(NativeContract.ContractManagement.CreateStorageKey(8, testNativeContract.Hash), new StorageItem(new ContractState { Id = 0, @@ -121,7 +121,7 @@ public void TestInvoke() [TestMethod] public void TestTrigger() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); ApplicationEngine engine1 = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, 0); Assert.ThrowsException(() => testNativeContract.TestTrigger(engine1)); @@ -133,7 +133,8 @@ public void TestTrigger() [TestMethod] public void TestTestCall() { - ApplicationEngine engine = testNativeContract.TestCall("System.Blockchain.GetHeight", 0); + var snapshot = TestBlockchain.GetTestSnapshot(); + ApplicationEngine engine = testNativeContract.TestCall(snapshot, "System.Blockchain.GetHeight", 0); engine.ResultStack.Should().BeEmpty(); } } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index fd87eee3e3..c34bc70bc5 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -28,7 +28,7 @@ public class UT_NeoToken public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - _snapshot = Blockchain.Singleton.GetSnapshot(); + _snapshot = TestBlockchain.GetTestSnapshot(); _persistingBlock = new Block { Header = new Header(), @@ -521,7 +521,8 @@ public void TestCalculateBonus() [TestMethod] public void TestGetNextBlockValidators1() { - using (ApplicationEngine engine = NativeContract.NEO.TestCall("getNextBlockValidators")) + var snapshot = TestBlockchain.GetTestSnapshot(); + using (ApplicationEngine engine = NativeContract.NEO.TestCall(snapshot, "getNextBlockValidators")) { var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); @@ -554,7 +555,8 @@ public void TestGetNextBlockValidators2() [TestMethod] public void TestGetCandidates1() { - using ApplicationEngine engine = NativeContract.NEO.TestCall("getCandidates"); + var snapshot = TestBlockchain.GetTestSnapshot(); + using ApplicationEngine engine = NativeContract.NEO.TestCall(snapshot, "getCandidates"); var array = engine.ResultStack.Pop(); array.Count.Should().Be(0); } @@ -626,7 +628,8 @@ public void TestCheckCandidate() [TestMethod] public void TestGetCommittee() { - using (ApplicationEngine engine = NativeContract.NEO.TestCall("getCommittee")) + var snapshot = TestBlockchain.GetTestSnapshot(); + using (ApplicationEngine engine = NativeContract.NEO.TestCall(snapshot, "getCommittee")) { var result = engine.ResultStack.Peek(); result.GetType().Should().Be(typeof(VM.Types.Array)); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 4b94cc9d2e..4a3499d22d 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -21,7 +21,7 @@ public class UT_PolicyContract public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - _snapshot = Blockchain.Singleton.GetSnapshot(); + _snapshot = TestBlockchain.GetTestSnapshot(); ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, _snapshot, new Block { Header = new Header() }, 0); NativeContract.ContractManagement.OnPersist(engine); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs index 02030ff94e..2ba9ed6a20 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs @@ -22,7 +22,7 @@ public class UT_RoleManagement public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); - _snapshot = Blockchain.Singleton.GetSnapshot(); + _snapshot = TestBlockchain.GetTestSnapshot(); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index be99c15830..e743f38885 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -21,7 +21,7 @@ public void TestSetup() [TestMethod] public void TestBinary() { - using var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); var data = new byte[0]; @@ -58,7 +58,7 @@ public void TestItoaAtoi() [TestMethod] public void TestNotify() { - using var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); ApplicationEngine.Notify += Test_Notify1; const string notifyEvent = "TestEvent"; @@ -93,7 +93,7 @@ private void Test_Notify2(object sender, NotifyEventArgs e) [TestMethod] public void TestCreateDummyBlock() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; ApplicationEngine engine = ApplicationEngine.Run(SyscallSystemRuntimeCheckWitnessHash, snapshot); engine.PersistingBlock.Version.Should().Be(0); diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index fa6b4239db..fa3aca53df 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -33,16 +33,18 @@ public static void ClassSetUp(TestContext context) [TestMethod] public void TestGetComplete() { + var snapshot = TestBlockchain.GetTestSnapshot(); Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066")); - var context = new ContractParametersContext(tx); + var context = new ContractParametersContext(snapshot, tx); context.Completed.Should().BeFalse(); } [TestMethod] public void TestToString() { + var snapshot = TestBlockchain.GetTestSnapshot(); Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x1bd5c777ec35768892bd3daab60fb7a1cb905066")); - var context = new ContractParametersContext(tx); + var context = new ContractParametersContext(snapshot, tx); context.Add(contract, 0, new byte[] { 0x01 }); string str = context.ToString(); str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""hex"":""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI="",""items"":{}}"); @@ -51,7 +53,8 @@ public void TestToString() [TestMethod] public void TestParse() { - var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI=\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}]}}}"); + var snapshot = TestBlockchain.GetTestSnapshot(); + var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI=\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}]}}}", snapshot); ret.ScriptHashes[0].ToString().Should().Be("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); ((Transaction)ret.Verifiable).Script.ToHexString().Should().Be(new byte[] { 18 }.ToHexString()); } @@ -59,19 +62,21 @@ public void TestParse() [TestMethod] public void TestFromJson() { - Action action = () => ContractParametersContext.Parse("{\"type\":\"wrongType\",\"hex\":\"00000000007c97764845172d827d3c863743293931a691271a0000000000000000000000000000000000000000000100\",\"items\":{\"0x1bd5c777ec35768892bd3daab60fb7a1cb905066\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1650680a906ad4\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}"); + var snapshot = TestBlockchain.GetTestSnapshot(); + Action action = () => ContractParametersContext.Parse("{\"type\":\"wrongType\",\"hex\":\"00000000007c97764845172d827d3c863743293931a691271a0000000000000000000000000000000000000000000100\",\"items\":{\"0x1bd5c777ec35768892bd3daab60fb7a1cb905066\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1650680a906ad4\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}", snapshot); action.Should().Throw(); } [TestMethod] public void TestAdd() { + var snapshot = TestBlockchain.GetTestSnapshot(); Transaction tx = TestUtils.GetTransaction(UInt160.Zero); - var context1 = new ContractParametersContext(tx); + var context1 = new ContractParametersContext(snapshot, tx); context1.Add(contract, 0, new byte[] { 0x01 }).Should().BeFalse(); tx = TestUtils.GetTransaction(UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0")); - var context2 = new ContractParametersContext(tx); + var context2 = new ContractParametersContext(snapshot, tx); context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); //test repeatlly createItem context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); @@ -80,8 +85,9 @@ public void TestAdd() [TestMethod] public void TestGetParameter() { + var snapshot = TestBlockchain.GetTestSnapshot(); Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0")); - var context = new ContractParametersContext(tx); + var context = new ContractParametersContext(snapshot, tx); context.GetParameter(tx.Sender, 0).Should().BeNull(); context.Add(contract, 0, new byte[] { 0x01 }); @@ -92,8 +98,9 @@ public void TestGetParameter() [TestMethod] public void TestGetWitnesses() { + var snapshot = TestBlockchain.GetTestSnapshot(); Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0")); - var context = new ContractParametersContext(tx); + var context = new ContractParametersContext(snapshot, tx); context.Add(contract, 0, new byte[] { 0x01 }); Witness[] witnesses = context.GetWitnesses(); witnesses.Length.Should().Be(1); @@ -104,17 +111,18 @@ public void TestGetWitnesses() [TestMethod] public void TestAddSignature() { + var snapshot = TestBlockchain.GetTestSnapshot(); var singleSender = UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0"); Transaction tx = TestUtils.GetTransaction(singleSender); //singleSign - var context = new ContractParametersContext(tx); + var context = new ContractParametersContext(snapshot, 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 = new ContractParametersContext(snapshot, tx); context.AddSignature(contract1, key.PublicKey, new byte[] { 0x01 }).Should().BeFalse(); contract1.ParameterList = new[] { ContractParameterType.Signature, ContractParameterType.Signature }; @@ -136,16 +144,16 @@ public void TestAddSignature() }); var multiSender = UInt160.Parse("0x3593816cc1085a6328fea2b899c24d78cd0ba372"); tx = TestUtils.GetTransaction(multiSender); - context = new ContractParametersContext(tx); + context = new ContractParametersContext(snapshot, tx); context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); context.AddSignature(multiSignContract, key2.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); tx = TestUtils.GetTransaction(singleSender); - context = new ContractParametersContext(tx); + context = new ContractParametersContext(snapshot, tx); context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeFalse(); tx = TestUtils.GetTransaction(multiSender); - context = new ContractParametersContext(tx); + context = new ContractParametersContext(snapshot, 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, diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index 24c9907029..6f6a383a90 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -60,7 +60,7 @@ public void ApplicationEngineRegularPut() StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); StorageItem sItem = TestUtils.GetStorageItem(new byte[0] { }); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); snapshot.Add(skey, sItem); snapshot.AddContract(script.ToScriptHash(), contractState); @@ -93,7 +93,7 @@ public void ApplicationEngineReusedStorage_FullReuse() StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); StorageItem sItem = TestUtils.GetStorageItem(value); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); snapshot.Add(skey, sItem); snapshot.AddContract(script.ToScriptHash(), contractState); @@ -128,7 +128,7 @@ public void ApplicationEngineReusedStorage_PartialReuse() StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); StorageItem sItem = TestUtils.GetStorageItem(oldValue); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); snapshot.Add(skey, sItem); snapshot.AddContract(script.ToScriptHash(), contractState); @@ -164,7 +164,7 @@ public void ApplicationEngineReusedStorage_PartialReuseTwice() StorageKey skey = TestUtils.GetStorageKey(contractState.Id, key); StorageItem sItem = TestUtils.GetStorageItem(oldValue); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); snapshot.Add(skey, sItem); snapshot.AddContract(script.ToScriptHash(), contractState); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 32c48095a2..3e45542200 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -115,7 +115,7 @@ public void TestAccount_IsStandard() 0x01, 0x01, 0x01, 0x01, 0x01 }; engine.IsStandardContract(new UInt160(hash)).Should().BeFalse(); - var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var state = TestUtils.GetContract(); snapshot.AddContract(state.Hash, state); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); @@ -131,7 +131,7 @@ public void TestAccount_IsStandard() [TestMethod] public void TestContract_Create() { - var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var nef = new NefFile() { Script = Enumerable.Repeat((byte)OpCode.RET, byte.MaxValue).ToArray(), @@ -174,7 +174,7 @@ public void TestContract_Create() [TestMethod] public void TestContract_Update() { - var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var nef = new NefFile() { Script = new[] { (byte)OpCode.RET }, @@ -234,7 +234,7 @@ public void TestContract_Update_Invalid() }; nefFile.CheckSum = NefFile.ComputeChecksum(nefFile); - var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); Assert.ThrowsException(() => snapshot.UpdateContract(null, null, new byte[] { 0x01 })); Assert.ThrowsException(() => snapshot.UpdateContract(null, nefFile.ToArray(), null)); @@ -255,7 +255,7 @@ public void TestContract_Update_Invalid() [TestMethod] public void TestStorage_Find() { - var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var state = TestUtils.GetContract(); var storageItem = new StorageItem diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 25bcb1544b..2064f97bd2 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -32,7 +32,7 @@ public void TestSetup() public void Runtime_GetNotifications_Test() { UInt160 scriptHash2; - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); using (var script = new ScriptBuilder()) { @@ -401,7 +401,7 @@ public void TestBlockchain_GetContract() 0x01, 0x01, 0x01, 0x01, 0x01 }; NativeContract.ContractManagement.GetContract(engine.Snapshot, new UInt160(data1)).Should().BeNull(); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var state = TestUtils.GetContract(); snapshot.AddContract(state.Hash, state); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); @@ -432,7 +432,7 @@ public void TestStorage_GetReadOnlyContext() [TestMethod] public void TestStorage_Get() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var state = TestUtils.GetContract(); var storageKey = new StorageKey @@ -491,7 +491,7 @@ public void TestStorage_Put() Assert.ThrowsException(() => engine.Put(storageContext, key, value)); //storage value is constant - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var storageKey = new StorageKey { @@ -525,7 +525,7 @@ public void TestStorage_Put() [TestMethod] public void TestStorage_PutEx() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var state = TestUtils.GetContract(); var storageKey = new StorageKey { @@ -555,7 +555,7 @@ public void TestStorage_PutEx() public void TestStorage_Delete() { var engine = GetEngine(false, true); - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var state = TestUtils.GetContract(); var storageKey = new StorageKey { @@ -600,7 +600,7 @@ public void TestStorageContext_AsReadOnly() [TestMethod] public void TestContract_Call() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); string method = "method"; var args = new VM.Types.Array { 0, 1 }; var state = TestUtils.GetContract(method, args.Count); @@ -625,7 +625,7 @@ public void TestContract_Call() [TestMethod] public void TestContract_Destroy() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var state = TestUtils.GetContract(); var scriptHash = UInt160.Parse("0xcb9f3b7c6fb1cf2c13a40637c189bdd066a272b4"); var storageItem = new StorageItem @@ -668,7 +668,7 @@ public static void LogEvent(object sender, LogEventArgs args) private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSnapshot = false, bool hasBlock = false, bool addScript = true, long gas = 20_00000000) { var tx = hasContainer ? TestUtils.GetTransaction(UInt160.Zero) : null; - var snapshot = hasSnapshot ? Blockchain.Singleton.GetSnapshot() : null; + var snapshot = hasSnapshot ? TestBlockchain.GetTestSnapshot() : null; var block = hasBlock ? new Block { Header = new Header() } : null; ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, block, gas); if (addScript) engine.LoadScript(new byte[] { 0x01 }); diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index e91dbc93e7..3ff78ac2dc 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -123,7 +123,7 @@ public void TestIsStandardContract() [TestMethod] public void TestVerifyWitnesses() { - var snapshot1 = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); + var snapshot1 = TestBlockchain.GetTestSnapshot().CreateSnapshot(); UInt256 index1 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); BlocksAdd(snapshot1, index1, new TrimmedBlock() { @@ -140,7 +140,7 @@ public void TestVerifyWitnesses() BlocksDelete(snapshot1, index1); Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(new Header() { PrevHash = index1 }, snapshot1, 100)); - var snapshot2 = Blockchain.Singleton.GetSnapshot(); + var snapshot2 = TestBlockchain.GetTestSnapshot(); UInt256 index2 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); TrimmedBlock block2 = new TrimmedBlock() { @@ -161,7 +161,7 @@ public void TestVerifyWitnesses() snapshot2.DeleteContract(UInt160.Zero); Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header2, snapshot2, 100)); - var snapshot3 = Blockchain.Singleton.GetSnapshot(); + var snapshot3 = TestBlockchain.GetTestSnapshot(); UInt256 index3 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); TrimmedBlock block3 = new TrimmedBlock() { diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 149378e159..7398c29e43 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -56,7 +56,7 @@ public void System_Blockchain_GetBlock() Hashes = new[] { tx.Hash } }; - var snapshot = Blockchain.Singleton.GetSnapshot().CreateSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); using (var script = new ScriptBuilder()) { @@ -223,7 +223,7 @@ public void Json_Serialize() [TestMethod] public void System_ExecutionEngine_GetScriptContainer() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); using (var script = new ScriptBuilder()) { script.EmitSysCall(ApplicationEngine.System_Runtime_GetScriptContainer); @@ -266,7 +266,7 @@ public void System_ExecutionEngine_GetScriptContainer() [TestMethod] public void System_Runtime_GasLeft() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); using (var script = new ScriptBuilder()) { @@ -318,7 +318,7 @@ public void System_Runtime_GasLeft() public void System_Runtime_GetInvocationCounter() { ContractState contractA, contractB, contractC; - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); // Create dummy contracts diff --git a/tests/neo.UnitTests/TestBlockchain.cs b/tests/neo.UnitTests/TestBlockchain.cs index 18d04454d3..a031604c2f 100644 --- a/tests/neo.UnitTests/TestBlockchain.cs +++ b/tests/neo.UnitTests/TestBlockchain.cs @@ -1,8 +1,5 @@ -using Neo.Ledger; +using Neo.Persistence; using System; -using System.Collections.Immutable; -using System.Linq; -using System.Reflection; namespace Neo.UnitTests { @@ -14,25 +11,14 @@ public static class TestBlockchain static TestBlockchain() { Console.WriteLine("initialize NeoSystem"); - TheNeoSystem = new NeoSystem(); - - // Ensure that blockchain is loaded - - var bc = Blockchain.Singleton; - - DefaultExtensibleWitnessWhiteList = (typeof(Blockchain).GetField("extensibleWitnessWhiteList", - BindingFlags.Instance | BindingFlags.NonPublic).GetValue(bc) as ImmutableHashSet).ToArray(); - AddWhiteList(DefaultExtensibleWitnessWhiteList); // Add other address + TheNeoSystem = new NeoSystem(ProtocolSettings.Default, null, null); } public static void InitializeMockNeoSystem() { } - public static void AddWhiteList(params UInt160[] address) + internal static DataCache GetTestSnapshot() { - var builder = ImmutableHashSet.CreateBuilder(); - foreach (var entry in address) builder.Add(entry); - - typeof(Blockchain).GetField("extensibleWitnessWhiteList", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(Blockchain.Singleton, builder.ToImmutable()); + return TheNeoSystem.GetSnapshot().CreateSnapshot(); } } } diff --git a/tests/neo.UnitTests/UT_DataCache.cs b/tests/neo.UnitTests/UT_DataCache.cs index ca15234c9b..9e3022e7e8 100644 --- a/tests/neo.UnitTests/UT_DataCache.cs +++ b/tests/neo.UnitTests/UT_DataCache.cs @@ -18,7 +18,7 @@ public void TestSetup() [TestMethod] public void TestCachedFind_Between() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var storages = snapshot.CreateSnapshot(); var cache = new ClonedCache(storages); @@ -57,7 +57,7 @@ public void TestCachedFind_Between() [TestMethod] public void TestCachedFind_Last() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var storages = snapshot.CreateSnapshot(); var cache = new ClonedCache(storages); @@ -89,7 +89,7 @@ public void TestCachedFind_Last() [TestMethod] public void TestCachedFind_Empty() { - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var storages = snapshot.CreateSnapshot(); var cache = new ClonedCache(storages); diff --git a/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs b/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs index a7920ded24..c28954557d 100644 --- a/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs +++ b/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs @@ -17,9 +17,10 @@ public void TestSetup() [TestMethod] public void TestConstructorWithNonexistAssetId() { + var snapshot = TestBlockchain.GetTestSnapshot(); Action action = () => { - var descriptor = new Neo.Wallets.AssetDescriptor(UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4")); + var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4")); }; action.Should().Throw(); } @@ -27,7 +28,8 @@ public void TestConstructorWithNonexistAssetId() [TestMethod] public void Check_GAS() { - var descriptor = new Neo.Wallets.AssetDescriptor(NativeContract.GAS.Hash); + var snapshot = TestBlockchain.GetTestSnapshot(); + var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, NativeContract.GAS.Hash); descriptor.AssetId.Should().Be(NativeContract.GAS.Hash); descriptor.AssetName.Should().Be(nameof(GasToken)); descriptor.ToString().Should().Be(nameof(GasToken)); @@ -38,7 +40,8 @@ public void Check_GAS() [TestMethod] public void Check_NEO() { - var descriptor = new Neo.Wallets.AssetDescriptor(NativeContract.NEO.Hash); + var snapshot = TestBlockchain.GetTestSnapshot(); + var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, NativeContract.NEO.Hash); descriptor.AssetId.Should().Be(NativeContract.NEO.Hash); descriptor.AssetName.Should().Be(nameof(NeoToken)); descriptor.ToString().Should().Be(nameof(NeoToken)); diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 75d38c25a1..14066c0864 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -201,13 +201,13 @@ public void TestGetAvailable() account.Lock = false; // Fake balance - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); - wallet.GetAvailable(NativeContract.GAS.Hash).Should().Be(new BigDecimal(new BigInteger(1000000000000M), 8)); + wallet.GetAvailable(snapshot, NativeContract.GAS.Hash).Should().Be(new BigDecimal(new BigInteger(1000000000000M), 8)); entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; @@ -223,14 +223,14 @@ public void TestGetBalance() account.Lock = false; // Fake balance - var snapshot = Blockchain.Singleton.GetSnapshot(); + var snapshot = TestBlockchain.GetTestSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; snapshot.Commit(); - wallet.GetBalance(UInt160.Zero, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(BigInteger.Zero, 0)); - wallet.GetBalance(NativeContract.GAS.Hash, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(new BigInteger(1000000000000M), 8)); + wallet.GetBalance(snapshot, UInt160.Zero, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(BigInteger.Zero, 0)); + wallet.GetBalance(snapshot, NativeContract.GAS.Hash, new UInt160[] { account.ScriptHash }).Should().Be(new BigDecimal(new BigInteger(1000000000000M), 8)); entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 0; @@ -284,12 +284,13 @@ public void TestImport2() [TestMethod] public void TestMakeTransaction1() { + var snapshot = TestBlockchain.GetTestSnapshot(); MyWallet wallet = new MyWallet(); Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 }); WalletAccount account = wallet.CreateAccount(contract, glkey.PrivateKey); account.Lock = false; - Action action = () => wallet.MakeTransaction(new TransferOutput[] + Action action = () => wallet.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -301,7 +302,7 @@ public void TestMakeTransaction1() }, UInt160.Zero); action.Should().Throw(); - action = () => wallet.MakeTransaction(new TransferOutput[] + action = () => wallet.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -313,7 +314,7 @@ public void TestMakeTransaction1() }, account.ScriptHash); action.Should().Throw(); - action = () => wallet.MakeTransaction(new TransferOutput[] + action = () => wallet.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -326,7 +327,6 @@ public void TestMakeTransaction1() action.Should().Throw(); // Fake balance - var snapshot = Blockchain.Singleton.GetSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry1 = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry1.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; @@ -337,7 +337,7 @@ public void TestMakeTransaction1() snapshot.Commit(); - var tx = wallet.MakeTransaction(new TransferOutput[] + var tx = wallet.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -348,7 +348,7 @@ public void TestMakeTransaction1() }); tx.Should().NotBeNull(); - tx = wallet.MakeTransaction(new TransferOutput[] + tx = wallet.MakeTransaction(snapshot, new TransferOutput[] { new TransferOutput() { @@ -370,8 +370,9 @@ public void TestMakeTransaction1() [TestMethod] public void TestMakeTransaction2() { + var snapshot = TestBlockchain.GetTestSnapshot(); MyWallet wallet = new MyWallet(); - Action action = () => wallet.MakeTransaction(new byte[] { }, null, null, Array.Empty()); + Action action = () => wallet.MakeTransaction(snapshot, new byte[] { }, null, null, Array.Empty()); action.Should().Throw(); Contract contract = Contract.Create(new ContractParameterType[] { ContractParameterType.Boolean }, new byte[] { 1 }); @@ -379,13 +380,12 @@ public void TestMakeTransaction2() account.Lock = false; // Fake balance - var snapshot = Blockchain.Singleton.GetSnapshot(); var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash); var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); entry.GetInteroperable().Balance = 1000000 * NativeContract.GAS.Factor; snapshot.Commit(); - var tx = wallet.MakeTransaction(new byte[] { }, account.ScriptHash, new[]{ new Signer() + var tx = wallet.MakeTransaction(snapshot, new byte[] { }, account.ScriptHash, new[]{ new Signer() { Account = account.ScriptHash, Scopes = WitnessScope.CalledByEntry @@ -393,7 +393,7 @@ public void TestMakeTransaction2() tx.Should().NotBeNull(); - tx = wallet.MakeTransaction(new byte[] { }, null, null, Array.Empty()); + tx = wallet.MakeTransaction(snapshot, new byte[] { }, null, null, Array.Empty()); tx.Should().NotBeNull(); entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); From 130529bf99b42660b2fa944c397d87ceea085a93 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 8 Feb 2021 19:09:25 +0800 Subject: [PATCH 11/63] Add ProtocolSettings to ApplicationEngine (#2308) --- src/neo/Ledger/Blockchain.cs | 45 ++----- src/neo/Ledger/MemoryPool.cs | 4 +- src/neo/Ledger/TransactionRouter.cs | 10 +- src/neo/NeoSystem.cs | 22 ++++ src/neo/Network/P2P/Payloads/Block.cs | 8 +- .../Network/P2P/Payloads/ExtensiblePayload.cs | 4 +- src/neo/Network/P2P/Payloads/Header.cs | 16 +-- src/neo/Network/P2P/Payloads/Transaction.cs | 16 +-- src/neo/Plugins/IApplicationEngineProvider.cs | 2 +- src/neo/ProtocolSettings.cs | 7 ++ .../ApplicationEngine.Contract.cs | 4 +- src/neo/SmartContract/ApplicationEngine.cs | 15 ++- src/neo/SmartContract/Helper.cs | 8 +- .../Native/ContractManagement.cs | 2 +- src/neo/SmartContract/Native/GasToken.cs | 5 +- .../SmartContract/Native/LedgerContract.cs | 34 +++--- .../SmartContract/Native/NativeContract.cs | 5 +- src/neo/SmartContract/Native/NeoToken.cs | 38 +++--- .../Extensions/NativeContractExtensions.cs | 8 +- .../Nep17NativeContractExtensions.cs | 10 +- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 28 +++-- .../UT_TransactionVerificationContext.cs | 9 +- tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs | 1 - .../Network/P2P/Payloads/UT_Header.cs | 1 - .../P2P/Payloads/UT_HighPriorityAttribute.cs | 1 - .../P2P/Payloads/UT_MerkleBlockPayload.cs | 7 +- .../Network/P2P/Payloads/UT_Transaction.cs | 112 ++++-------------- .../SmartContract/Native/UT_FungibleToken.cs | 1 - .../SmartContract/Native/UT_GasToken.cs | 5 +- .../SmartContract/Native/UT_NNS.cs | 31 +++-- .../SmartContract/Native/UT_NameService.cs | 17 ++- .../SmartContract/Native/UT_NativeContract.cs | 11 +- .../SmartContract/Native/UT_NeoToken.cs | 75 ++++++------ .../SmartContract/Native/UT_PolicyContract.cs | 3 +- .../SmartContract/Native/UT_RoleManagement.cs | 3 +- .../SmartContract/UT_ApplicationEngine.cs | 9 +- .../UT_ApplicationEngineProvider.cs | 12 +- .../SmartContract/UT_Contract.cs | 4 +- .../SmartContract/UT_InteropPrices.cs | 7 +- .../SmartContract/UT_InteropService.NEO.cs | 3 +- .../SmartContract/UT_InteropService.cs | 12 +- .../SmartContract/UT_SmartContractHelper.cs | 9 +- .../SmartContract/UT_Syscalls.cs | 18 ++- tests/neo.UnitTests/UT_DataCache.cs | 1 - tests/neo.UnitTests/Wallets/UT_Wallet.cs | 1 - 45 files changed, 290 insertions(+), 354 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 13c1603dd4..3fdaee4ab2 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -1,7 +1,6 @@ using Akka.Actor; using Akka.Configuration; using Akka.IO; -using Neo.Cryptography.ECC; using Neo.IO.Actors; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; @@ -30,28 +29,6 @@ internal class PreverifyCompleted { public Transaction Transaction; public Verif public class RelayResult { public IInventory Inventory; public VerifyResult Result; } private class UnverifiedBlocksList { public LinkedList Blocks = new LinkedList(); public HashSet Nodes = new HashSet(); } - public static readonly ECPoint[] StandbyCommittee = ProtocolSettings.Default.StandbyCommittee; - public static readonly ECPoint[] StandbyValidators = ProtocolSettings.Default.StandbyValidators; - - public static readonly Block GenesisBlock = new Block - { - Header = new Header - { - PrevHash = UInt256.Zero, - MerkleRoot = UInt256.Zero, - Timestamp = (new DateTime(2016, 7, 15, 15, 8, 21, DateTimeKind.Utc)).ToTimestampMS(), - Index = 0, - PrimaryIndex = 0, - NextConsensus = Contract.GetBFTAddress(StandbyValidators), - Witness = new Witness - { - InvocationScript = Array.Empty(), - VerificationScript = new[] { (byte)OpCode.PUSH1 } - }, - }, - Transactions = Array.Empty() - }; - private readonly static Script onPersistScript, postPersistScript; private const int MaxTxToReverifyPerIdle = 10; private readonly NeoSystem system; @@ -80,7 +57,7 @@ public Blockchain(NeoSystem system) this.txrouter = Context.ActorOf(TransactionRouter.Props(system)); DataCache snapshot = system.StoreView; if (!NativeContract.Ledger.Initialized(snapshot)) - Persist(GenesisBlock); + Persist(system.GenesisBlock); } private bool ContainsTransaction(UInt256 hash) @@ -97,7 +74,7 @@ private void OnImport(IEnumerable blocks, bool verify) if (block.Index <= currentHeight) continue; if (block.Index != currentHeight + 1) throw new InvalidOperationException(); - if (verify && !block.Verify(system.StoreView)) + if (verify && !block.Verify(system.Settings, system.StoreView)) throw new InvalidOperationException(); Persist(block); ++currentHeight; @@ -186,7 +163,7 @@ private VerifyResult OnNewBlock(Block block) } if (block.Index == headerHeight + 1) { - if (!block.Verify(snapshot, system.HeaderCache)) + if (!block.Verify(system.Settings, snapshot, system.HeaderCache)) return VerifyResult.Invalid; } else @@ -248,7 +225,7 @@ private void OnNewHeaders(Header[] headers) { if (header.Index > headerHeight + 1) break; if (header.Index < headerHeight + 1) continue; - if (!header.Verify(snapshot, system.HeaderCache)) break; + if (!header.Verify(system.Settings, snapshot, system.HeaderCache)) break; system.HeaderCache.Add(header); ++headerHeight; } @@ -257,8 +234,8 @@ private void OnNewHeaders(Header[] headers) private VerifyResult OnNewExtensiblePayload(ExtensiblePayload payload) { DataCache snapshot = system.StoreView; - extensibleWitnessWhiteList ??= UpdateExtensibleWitnessWhiteList(snapshot); - if (!payload.Verify(snapshot, extensibleWitnessWhiteList)) return VerifyResult.Invalid; + extensibleWitnessWhiteList ??= UpdateExtensibleWitnessWhiteList(system.Settings, snapshot); + if (!payload.Verify(system.Settings, snapshot, extensibleWitnessWhiteList)) return VerifyResult.Invalid; system.RelayCache.Add(payload); return VerifyResult.Succeed; } @@ -322,7 +299,7 @@ private void Persist(Block block) using (SnapshotCache snapshot = system.GetSnapshot()) { List all_application_executed = new List(); - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, block)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, block, system.Settings, 0)) { engine.LoadScript(onPersistScript); if (engine.Execute() != VMState.HALT) throw new InvalidOperationException(); @@ -334,7 +311,7 @@ private void Persist(Block block) // Warning: Do not write into variable snapshot directly. Write into variable clonedSnapshot and commit instead. foreach (Transaction tx in block.Transactions) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, clonedSnapshot, block, tx.SystemFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, clonedSnapshot, block, system.Settings, tx.SystemFee)) { engine.LoadScript(tx.Script); if (engine.Execute() == VMState.HALT) @@ -350,7 +327,7 @@ private void Persist(Block block) all_application_executed.Add(application_executed); } } - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, block)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, block, system.Settings, 0)) { engine.LoadScript(postPersistScript); if (engine.Execute() != VMState.HALT) throw new InvalidOperationException(); @@ -405,12 +382,12 @@ private void SendRelayResult(IInventory inventory, VerifyResult result) Context.System.EventStream.Publish(rr); } - private static ImmutableHashSet UpdateExtensibleWitnessWhiteList(DataCache snapshot) + private static ImmutableHashSet UpdateExtensibleWitnessWhiteList(ProtocolSettings settings, DataCache snapshot) { uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); var builder = ImmutableHashSet.CreateBuilder(); builder.Add(NativeContract.NEO.GetCommitteeAddress(snapshot)); - var validators = NativeContract.NEO.GetNextBlockValidators(snapshot); + var validators = NativeContract.NEO.GetNextBlockValidators(snapshot, settings.ValidatorsCount); builder.Add(Contract.GetBFTAddress(validators)); builder.UnionWith(validators.Select(u => Contract.CreateSignatureRedeemScript(u).ToScriptHash())); var oracles = NativeContract.RoleManagement.GetDesignatedByRole(snapshot, Role.Oracle, currentHeight); diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index 25496860ed..842f7fcf64 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -260,7 +260,7 @@ internal VerifyResult TryAdd(Transaction tx, DataCache snapshot) _txRwLock.EnterWriteLock(); try { - VerifyResult result = tx.VerifyStateDependent(snapshot, VerificationContext); + VerifyResult result = tx.VerifyStateDependent(_system.Settings, snapshot, VerificationContext); if (result != VerifyResult.Succeed) return result; _unsortedTransactions.Add(tx.Hash, poolItem); @@ -398,7 +398,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.VerifyStateDependent(snapshot, VerificationContext) == VerifyResult.Succeed) + if (item.Tx.VerifyStateDependent(_system.Settings, snapshot, VerificationContext) == VerifyResult.Succeed) { reverifiedItems.Add(item); VerificationContext.AddTransaction(item.Tx); diff --git a/src/neo/Ledger/TransactionRouter.cs b/src/neo/Ledger/TransactionRouter.cs index 786f33fe85..2d1e38f114 100644 --- a/src/neo/Ledger/TransactionRouter.cs +++ b/src/neo/Ledger/TransactionRouter.cs @@ -7,20 +7,20 @@ namespace Neo.Ledger { internal class TransactionRouter : UntypedActor { - private readonly IActorRef blockchain; + private readonly NeoSystem system; public TransactionRouter(NeoSystem system) { - this.blockchain = system.Blockchain; + this.system = system; } protected override void OnReceive(object message) { - if (!(message is Transaction tx)) return; - blockchain.Tell(new Blockchain.PreverifyCompleted + if (message is not Transaction tx) return; + system.Blockchain.Tell(new Blockchain.PreverifyCompleted { Transaction = tx, - Result = tx.VerifyStateIndependent() + Result = tx.VerifyStateIndependent(system.Settings) }, Sender); } diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 28de6b1bcc..1bdfe5ed9c 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -2,8 +2,11 @@ using Neo.IO.Caching; using Neo.Ledger; using Neo.Network.P2P; +using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.Plugins; +using Neo.SmartContract; +using Neo.VM; using System; namespace Neo @@ -16,6 +19,7 @@ public class NeoSystem : IDisposable $"blockchain-mailbox {{ mailbox-type: \"{typeof(BlockchainMailbox).AssemblyQualifiedName}\" }}" + $"task-manager-mailbox {{ mailbox-type: \"{typeof(TaskManagerMailbox).AssemblyQualifiedName}\" }}" + $"remote-node-mailbox {{ mailbox-type: \"{typeof(RemoteNodeMailbox).AssemblyQualifiedName}\" }}"); + public Block GenesisBlock { get; } public IActorRef Blockchain { get; } public IActorRef LocalNode { get; } public IActorRef TaskManager { get; } @@ -44,6 +48,24 @@ static NeoSystem() public NeoSystem(ProtocolSettings settings, string storageEngine = null, string storagePath = null) { this.Settings = settings; + this.GenesisBlock = new Block + { + Header = new Header + { + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + Timestamp = (new DateTime(2016, 7, 15, 15, 8, 21, DateTimeKind.Utc)).ToTimestampMS(), + Index = 0, + PrimaryIndex = 0, + NextConsensus = Contract.GetBFTAddress(settings.StandbyValidators), + Witness = new Witness + { + InvocationScript = Array.Empty(), + VerificationScript = new[] { (byte)OpCode.PUSH1 } + }, + }, + Transactions = Array.Empty() + }; Plugin.LoadPlugins(this); this.storage_engine = storageEngine; this.store = LoadStore(storagePath); diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index 56f2585eea..ef73254544 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -77,14 +77,14 @@ public JObject ToJson() return json; } - internal bool Verify(DataCache snapshot) + internal bool Verify(ProtocolSettings settings, DataCache snapshot) { - return Header.Verify(snapshot); + return Header.Verify(settings, snapshot); } - internal bool Verify(DataCache snapshot, HeaderCache headerCache) + internal bool Verify(ProtocolSettings settings, DataCache snapshot, HeaderCache headerCache) { - return Header.Verify(snapshot, headerCache); + return Header.Verify(settings, snapshot, headerCache); } } } diff --git a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs index bb5cee7320..ca86c4b0f5 100644 --- a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs +++ b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -90,12 +90,12 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.WriteVarBytes(Data); } - internal bool Verify(DataCache snapshot, ISet extensibleWitnessWhiteList) + internal bool Verify(ProtocolSettings settings, DataCache snapshot, ISet extensibleWitnessWhiteList) { uint height = NativeContract.Ledger.CurrentIndex(snapshot); if (height < ValidBlockStart || height >= ValidBlockEnd) return false; if (!extensibleWitnessWhiteList.Contains(Sender)) return false; - return this.VerifyWitnesses(snapshot, 0_02000000); + return this.VerifyWitnesses(settings, snapshot, 0_02000000); } } } diff --git a/src/neo/Network/P2P/Payloads/Header.cs b/src/neo/Network/P2P/Payloads/Header.cs index b5a7b7e169..51b94587bc 100644 --- a/src/neo/Network/P2P/Payloads/Header.cs +++ b/src/neo/Network/P2P/Payloads/Header.cs @@ -74,8 +74,6 @@ void IVerifiable.DeserializeUnsigned(BinaryReader reader) Timestamp = reader.ReadUInt64(); Index = reader.ReadUInt32(); PrimaryIndex = reader.ReadByte(); - if (PrimaryIndex >= ProtocolSettings.Default.ValidatorsCount) - throw new FormatException(); NextConsensus = reader.ReadSerializable(); } @@ -137,24 +135,28 @@ public JObject ToJson() return json; } - internal bool Verify(DataCache snapshot) + internal bool Verify(ProtocolSettings settings, DataCache snapshot) { + if (PrimaryIndex >= settings.ValidatorsCount) + return false; TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); if (prev is null) return false; if (prev.Index + 1 != Index) return false; if (prev.Header.Timestamp >= Timestamp) return false; - if (!this.VerifyWitnesses(snapshot, 1_00000000)) return false; + if (!this.VerifyWitnesses(settings, snapshot, 1_00000000)) return false; return true; } - internal bool Verify(DataCache snapshot, HeaderCache headerCache) + internal bool Verify(ProtocolSettings settings, DataCache snapshot, HeaderCache headerCache) { Header prev = headerCache.Last; - if (prev is null) return Verify(snapshot); + if (prev is null) return Verify(settings, snapshot); + if (PrimaryIndex >= settings.ValidatorsCount) + return false; if (prev.Hash != PrevHash) return false; if (prev.Index + 1 != Index) return false; if (prev.Timestamp >= Timestamp) return false; - return this.VerifyWitness(snapshot, prev.NextConsensus, Witness, 1_00000000, out _); + return this.VerifyWitness(settings, snapshot, prev.NextConsensus, Witness, 1_00000000, out _); } } } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 3bedd8a706..335c4241d5 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -278,7 +278,7 @@ public JObject ToJson() return json; } - public virtual VerifyResult VerifyStateDependent(DataCache snapshot, TransactionVerificationContext context) + public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) { uint height = NativeContract.Ledger.CurrentIndex(snapshot); if (ValidUntilBlock <= height || ValidUntilBlock > height + MaxValidUntilBlockIncrement) @@ -305,7 +305,7 @@ public virtual VerifyResult VerifyStateDependent(DataCache snapshot, Transaction net_fee -= execFeeFactor * SmartContract.Helper.MultiSignatureContractCost(m, n); else { - if (!this.VerifyWitness(snapshot, hashes[i], witnesses[i], net_fee, out long fee)) + if (!this.VerifyWitness(settings, snapshot, hashes[i], witnesses[i], net_fee, out long fee)) return VerifyResult.Invalid; net_fee -= fee; } @@ -314,7 +314,7 @@ public virtual VerifyResult VerifyStateDependent(DataCache snapshot, Transaction return VerifyResult.Succeed; } - public virtual VerifyResult VerifyStateIndependent() + public virtual VerifyResult VerifyStateIndependent(ProtocolSettings settings) { if (Size > MaxTransactionSize) return VerifyResult.Invalid; try @@ -328,19 +328,11 @@ public virtual VerifyResult VerifyStateIndependent() UInt160[] hashes = GetScriptHashesForVerifying(null); for (int i = 0; i < hashes.Length; i++) if (witnesses[i].VerificationScript.IsStandardContract()) - if (!this.VerifyWitness(null, hashes[i], witnesses[i], SmartContract.Helper.MaxVerificationGas, out _)) + if (!this.VerifyWitness(settings, null, hashes[i], witnesses[i], SmartContract.Helper.MaxVerificationGas, out _)) return VerifyResult.Invalid; return VerifyResult.Succeed; } - public virtual VerifyResult Verify(DataCache snapshot, TransactionVerificationContext context) - { - VerifyResult result = VerifyStateIndependent(); - if (result != VerifyResult.Succeed) return result; - result = VerifyStateDependent(snapshot, context); - return result; - } - public StackItem ToStackItem(ReferenceCounter referenceCounter) { return new Array(referenceCounter, new StackItem[] diff --git a/src/neo/Plugins/IApplicationEngineProvider.cs b/src/neo/Plugins/IApplicationEngineProvider.cs index 43f50daa3a..c217cf4926 100644 --- a/src/neo/Plugins/IApplicationEngineProvider.cs +++ b/src/neo/Plugins/IApplicationEngineProvider.cs @@ -6,6 +6,6 @@ namespace Neo.Plugins { public interface IApplicationEngineProvider { - ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, long gas); + ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas); } } diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index 1b9360979b..fa6e8f3b7e 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -109,5 +109,12 @@ private ProtocolSettings(IConfigurationSection section) else this.NativeActivations = new Dictionary(); } + + public uint GetNativeActivation(string name) + { + if (NativeActivations.TryGetValue(name, out uint index)) + return index; + return 0; + } } } diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 30111dcb2d..76aa7474b0 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -88,7 +88,7 @@ protected internal void NativeOnPersist() if (Trigger != TriggerType.OnPersist) throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) - if (contract.ActiveBlockIndex <= PersistingBlock.Index) + if (ProtocolSettings.GetNativeActivation(contract.Name) <= PersistingBlock.Index) contract.OnPersist(this); } @@ -97,7 +97,7 @@ protected internal void NativePostPersist() if (Trigger != TriggerType.PostPersist) throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) - if (contract.ActiveBlockIndex <= PersistingBlock.Index) + if (ProtocolSettings.GetNativeActivation(contract.Name) <= PersistingBlock.Index) contract.PostPersist(this); } } diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index f73b150f6b..3b1e48c280 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -43,6 +43,7 @@ public partial class ApplicationEngine : ExecutionEngine public IVerifiable ScriptContainer { get; } public DataCache Snapshot { get; } public Block PersistingBlock { get; } + public ProtocolSettings ProtocolSettings { get; } public long GasConsumed { get; private set; } = 0; public long GasLeft => gas_amount - GasConsumed; public Exception FaultException { get; private set; } @@ -51,12 +52,13 @@ public partial class ApplicationEngine : ExecutionEngine public UInt160 EntryScriptHash => EntryContext?.GetScriptHash(); public IReadOnlyList Notifications => notifications ?? (IReadOnlyList)Array.Empty(); - protected ApplicationEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, long gas) + protected ApplicationEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas) { this.Trigger = trigger; this.ScriptContainer = container; this.Snapshot = snapshot; this.PersistingBlock = persistingBlock; + this.ProtocolSettings = settings; this.gas_amount = gas; this.exec_fee_factor = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultExecFeeFactor : NativeContract.Policy.GetExecFeeFactor(Snapshot); this.StoragePrice = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultStoragePrice : NativeContract.Policy.GetStoragePrice(Snapshot); @@ -143,10 +145,10 @@ internal T CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, st return (T)Convert(Pop(), new InteropParameterDescriptor(typeof(T))); } - public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, long gas = TestModeGas) + public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, ProtocolSettings settings = null, long gas = TestModeGas) { - return applicationEngineProvider?.Create(trigger, container, snapshot, persistingBlock, gas) - ?? new ApplicationEngine(trigger, container, snapshot, persistingBlock, gas); + return applicationEngineProvider?.Create(trigger, container, snapshot, persistingBlock, settings, gas) + ?? new ApplicationEngine(trigger, container, snapshot, persistingBlock, settings, gas); } protected override void LoadContext(ExecutionContext context) @@ -355,10 +357,11 @@ internal static void ResetApplicationEngineProvider() Exchange(ref applicationEngineProvider, null); } - public static ApplicationEngine Run(byte[] script, DataCache snapshot, IVerifiable container = null, Block persistingBlock = null, int offset = 0, long gas = TestModeGas) + public static ApplicationEngine Run(byte[] script, DataCache snapshot, IVerifiable container = null, Block persistingBlock = null, ProtocolSettings settings = null, int offset = 0, long gas = TestModeGas) { persistingBlock ??= CreateDummyBlock(snapshot); - ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock, gas); + settings ??= ProtocolSettings.Default; + ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock, settings, gas); engine.LoadScript(script, initialPosition: offset); engine.Execute(); return engine; diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 088b6b7c7b..7b2d55e562 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -161,7 +161,7 @@ public static UInt160 ToScriptHash(this ReadOnlySpan script) return new UInt160(Crypto.Hash160(script)); } - public static bool VerifyWitnesses(this IVerifiable verifiable, DataCache snapshot, long gas) + public static bool VerifyWitnesses(this IVerifiable verifiable, ProtocolSettings settings, DataCache snapshot, long gas) { if (gas < 0) return false; if (gas > MaxVerificationGas) gas = MaxVerificationGas; @@ -178,14 +178,14 @@ public static bool VerifyWitnesses(this IVerifiable verifiable, DataCache snapsh if (hashes.Length != verifiable.Witnesses.Length) return false; for (int i = 0; i < hashes.Length; i++) { - if (!verifiable.VerifyWitness(snapshot, hashes[i], verifiable.Witnesses[i], gas, out long fee)) + if (!verifiable.VerifyWitness(settings, snapshot, hashes[i], verifiable.Witnesses[i], gas, out long fee)) return false; gas -= fee; } return true; } - internal static bool VerifyWitness(this IVerifiable verifiable, DataCache snapshot, UInt160 hash, Witness witness, long gas, out long fee) + internal static bool VerifyWitness(this IVerifiable verifiable, ProtocolSettings settings, DataCache snapshot, UInt160 hash, Witness witness, long gas, out long fee) { fee = 0; Script invocationScript; @@ -197,7 +197,7 @@ internal static bool VerifyWitness(this IVerifiable verifiable, DataCache snapsh { return false; } - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, verifiable, snapshot?.CreateSnapshot(), null, gas)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, verifiable, snapshot?.CreateSnapshot(), null, settings, gas)) { if (witness.VerificationScript.Length == 0) { diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 5f36991b61..886e93ea9d 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -91,7 +91,7 @@ internal override void OnPersist(ApplicationEngine engine) { foreach (NativeContract contract in Contracts) { - if (contract.ActiveBlockIndex != engine.PersistingBlock.Index) + if (engine.ProtocolSettings.GetNativeActivation(contract.Name) != engine.PersistingBlock.Index) continue; engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState { diff --git a/src/neo/SmartContract/Native/GasToken.cs b/src/neo/SmartContract/Native/GasToken.cs index c1d83f2144..48eb9affc1 100644 --- a/src/neo/SmartContract/Native/GasToken.cs +++ b/src/neo/SmartContract/Native/GasToken.cs @@ -1,5 +1,4 @@ using Neo.Cryptography.ECC; -using Neo.Ledger; using Neo.Network.P2P.Payloads; namespace Neo.SmartContract.Native @@ -15,7 +14,7 @@ internal GasToken() internal override void Initialize(ApplicationEngine engine) { - UInt160 account = Contract.GetBFTAddress(Blockchain.StandbyValidators); + UInt160 account = Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators); Mint(engine, account, 30_000_000 * Factor, false); } @@ -27,7 +26,7 @@ internal override void OnPersist(ApplicationEngine engine) Burn(engine, tx.Sender, tx.SystemFee + tx.NetworkFee); totalNetworkFee += tx.NetworkFee; } - ECPoint[] validators = NEO.GetNextBlockValidators(engine.Snapshot); + ECPoint[] validators = NEO.GetNextBlockValidators(engine.Snapshot, engine.ProtocolSettings.ValidatorsCount); UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.PersistingBlock.PrimaryIndex]).ToScriptHash(); Mint(engine, primary, totalNetworkFee, false); } diff --git a/src/neo/SmartContract/Native/LedgerContract.cs b/src/neo/SmartContract/Native/LedgerContract.cs index 9d54c76aa9..7d723e26c3 100644 --- a/src/neo/SmartContract/Native/LedgerContract.cs +++ b/src/neo/SmartContract/Native/LedgerContract.cs @@ -46,11 +46,11 @@ internal bool Initialized(DataCache snapshot) return snapshot.Find(CreateStorageKey(Prefix_Block).ToArray()).Any(); } - private bool IsTraceableBlock(DataCache snapshot, uint index) + private bool IsTraceableBlock(DataCache snapshot, uint index, uint maxTraceableBlocks) { uint currentIndex = CurrentIndex(snapshot); if (index > currentIndex) return false; - return index + ProtocolSettings.Default.MaxTraceableBlocks > currentIndex; + return index + maxTraceableBlocks > currentIndex; } public UInt256 GetBlockHash(DataCache snapshot, uint index) @@ -90,18 +90,18 @@ public TrimmedBlock GetTrimmedBlock(DataCache snapshot, UInt256 hash) } [ContractMethod(0_01000000, CallFlags.ReadStates)] - private TrimmedBlock GetBlock(DataCache snapshot, byte[] indexOrHash) + private TrimmedBlock GetBlock(ApplicationEngine engine, byte[] indexOrHash) { UInt256 hash; if (indexOrHash.Length < UInt256.Length) - hash = GetBlockHash(snapshot, (uint)new BigInteger(indexOrHash)); + hash = GetBlockHash(engine.Snapshot, (uint)new BigInteger(indexOrHash)); else if (indexOrHash.Length == UInt256.Length) hash = new UInt256(indexOrHash); else throw new ArgumentException(null, nameof(indexOrHash)); if (hash is null) return null; - TrimmedBlock block = GetTrimmedBlock(snapshot, hash); - if (block is null || !IsTraceableBlock(snapshot, block.Index)) return null; + TrimmedBlock block = GetTrimmedBlock(engine.Snapshot, hash); + if (block is null || !IsTraceableBlock(engine.Snapshot, block.Index, engine.ProtocolSettings.MaxTraceableBlocks)) return null; return block; } @@ -146,37 +146,37 @@ public Transaction GetTransaction(DataCache snapshot, UInt256 hash) } [ContractMethod(0_01000000, CallFlags.ReadStates, Name = "getTransaction")] - private Transaction GetTransactionForContract(DataCache snapshot, UInt256 hash) + private Transaction GetTransactionForContract(ApplicationEngine engine, UInt256 hash) { - TransactionState state = GetTransactionState(snapshot, hash); - if (state is null || !IsTraceableBlock(snapshot, state.BlockIndex)) return null; + TransactionState state = GetTransactionState(engine.Snapshot, hash); + if (state is null || !IsTraceableBlock(engine.Snapshot, state.BlockIndex, engine.ProtocolSettings.MaxTraceableBlocks)) return null; return state.Transaction; } [ContractMethod(0_01000000, CallFlags.ReadStates)] - private int GetTransactionHeight(DataCache snapshot, UInt256 hash) + private int GetTransactionHeight(ApplicationEngine engine, UInt256 hash) { - TransactionState state = GetTransactionState(snapshot, hash); - if (state is null || !IsTraceableBlock(snapshot, state.BlockIndex)) return -1; + TransactionState state = GetTransactionState(engine.Snapshot, hash); + if (state is null || !IsTraceableBlock(engine.Snapshot, state.BlockIndex, engine.ProtocolSettings.MaxTraceableBlocks)) return -1; return (int)state.BlockIndex; } [ContractMethod(0_02000000, CallFlags.ReadStates)] - private Transaction GetTransactionFromBlock(DataCache snapshot, byte[] blockIndexOrHash, int txIndex) + private Transaction GetTransactionFromBlock(ApplicationEngine engine, byte[] blockIndexOrHash, int txIndex) { UInt256 hash; if (blockIndexOrHash.Length < UInt256.Length) - hash = GetBlockHash(snapshot, (uint)new BigInteger(blockIndexOrHash)); + hash = GetBlockHash(engine.Snapshot, (uint)new BigInteger(blockIndexOrHash)); else if (blockIndexOrHash.Length == UInt256.Length) hash = new UInt256(blockIndexOrHash); else throw new ArgumentException(null, nameof(blockIndexOrHash)); if (hash is null) return null; - TrimmedBlock block = GetTrimmedBlock(snapshot, hash); - if (block is null || !IsTraceableBlock(snapshot, block.Index)) return null; + TrimmedBlock block = GetTrimmedBlock(engine.Snapshot, hash); + if (block is null || !IsTraceableBlock(engine.Snapshot, block.Index, engine.ProtocolSettings.MaxTraceableBlocks)) return null; if (txIndex < 0 || txIndex >= block.Hashes.Length) throw new ArgumentOutOfRangeException(nameof(txIndex)); - return GetTransaction(snapshot, block.Hashes[txIndex]); + return GetTransaction(engine.Snapshot, block.Hashes[txIndex]); } private static TrimmedBlock Trim(Block block) diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 07c7673579..16e9448150 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -33,7 +33,6 @@ public abstract class NativeContract public UInt160 Hash { get; } public int Id { get; } = --id_counter; public ContractManifest Manifest { get; } - public uint ActiveBlockIndex { get; } protected NativeContract() { @@ -80,8 +79,6 @@ protected NativeContract() Trusts = WildcardContainer.Create(), Extra = null }; - if (ProtocolSettings.Default.NativeActivations.TryGetValue(Name, out uint activationIndex)) - this.ActiveBlockIndex = activationIndex; contractsList.Add(this); contractsDictionary.Add(Hash, this); } @@ -105,7 +102,7 @@ public static NativeContract GetContract(UInt160 hash) internal void Invoke(ApplicationEngine engine, byte version) { - if (ActiveBlockIndex > Ledger.CurrentIndex(engine.Snapshot)) + if (engine.ProtocolSettings.GetNativeActivation(Name) > Ledger.CurrentIndex(engine.Snapshot)) throw new InvalidOperationException($"The native contract {Name} is not active."); if (version != 0) throw new InvalidOperationException($"The native contract of version {version} is not active."); diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index dee56c1095..d69e4c1d08 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -2,7 +2,6 @@ using Neo.Cryptography.ECC; using Neo.IO; -using Neo.Ledger; using Neo.Persistence; using Neo.VM; using Neo.VM.Types; @@ -113,29 +112,29 @@ private void CheckCandidate(DataCache snapshot, ECPoint pubkey, CandidateState c } } - public bool ShouldRefreshCommittee(uint height) => height % ProtocolSettings.Default.CommitteeMembersCount == 0; + public static bool ShouldRefreshCommittee(uint height, int committeeMembersCount) => height % committeeMembersCount == 0; internal override void Initialize(ApplicationEngine engine) { - var cachedCommittee = new CachedCommittee(Blockchain.StandbyCommittee.Select(p => (p, BigInteger.Zero))); + var cachedCommittee = new CachedCommittee(engine.ProtocolSettings.StandbyCommittee.Select(p => (p, BigInteger.Zero))); engine.Snapshot.Add(CreateStorageKey(Prefix_Committee), new StorageItem(cachedCommittee)); engine.Snapshot.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(new byte[0])); // Initialize economic parameters engine.Snapshot.Add(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(0u), new StorageItem(5 * GAS.Factor)); - Mint(engine, Contract.GetBFTAddress(Blockchain.StandbyValidators), TotalAmount, false); + Mint(engine, Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators), TotalAmount, false); } internal override void OnPersist(ApplicationEngine engine) { // Set next committee - if (ShouldRefreshCommittee(engine.PersistingBlock.Index)) + if (ShouldRefreshCommittee(engine.PersistingBlock.Index, engine.ProtocolSettings.CommitteeMembersCount)) { StorageItem storageItem = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Committee)); var cachedCommittee = storageItem.GetInteroperable(); cachedCommittee.Clear(); - cachedCommittee.AddRange(ComputeCommitteeMembers(engine.Snapshot)); + cachedCommittee.AddRange(ComputeCommitteeMembers(engine.Snapshot, engine.ProtocolSettings)); } } @@ -143,8 +142,8 @@ internal override void PostPersist(ApplicationEngine engine) { // Distribute GAS for committee - int m = ProtocolSettings.Default.CommitteeMembersCount; - int n = ProtocolSettings.Default.ValidatorsCount; + int m = engine.ProtocolSettings.CommitteeMembersCount; + int n = engine.ProtocolSettings.ValidatorsCount; int index = (int)(engine.PersistingBlock.Index % (uint)m); var gasPerBlock = GetGasPerBlock(engine.Snapshot); var committee = GetCommitteeFromCache(engine.Snapshot); @@ -154,7 +153,7 @@ internal override void PostPersist(ApplicationEngine engine) // Record the cumulative reward of the voters of committee - if (ShouldRefreshCommittee(engine.PersistingBlock.Index)) + if (ShouldRefreshCommittee(engine.PersistingBlock.Index, m)) { BigInteger voterRewardOfEachCommittee = gasPerBlock * VoterRewardRatio * 100000000L * m / (m + n) / 100; // Zoom in 100000000 times, and the final calculation should be divided 100000000L for (index = 0; index < committee.Count; index++) @@ -301,26 +300,31 @@ private CachedCommittee GetCommitteeFromCache(DataCache snapshot) return snapshot[CreateStorageKey(Prefix_Committee)].GetInteroperable(); } - public ECPoint[] ComputeNextBlockValidators(DataCache snapshot) + public ECPoint[] ComputeNextBlockValidators(DataCache snapshot, ProtocolSettings settings) { - return ComputeCommitteeMembers(snapshot).Select(p => p.PublicKey).Take(ProtocolSettings.Default.ValidatorsCount).OrderBy(p => p).ToArray(); + return ComputeCommitteeMembers(snapshot, settings).Select(p => p.PublicKey).Take(settings.ValidatorsCount).OrderBy(p => p).ToArray(); } - private IEnumerable<(ECPoint PublicKey, BigInteger Votes)> ComputeCommitteeMembers(DataCache snapshot) + private IEnumerable<(ECPoint PublicKey, BigInteger Votes)> ComputeCommitteeMembers(DataCache snapshot, ProtocolSettings settings) { decimal votersCount = (decimal)(BigInteger)snapshot[CreateStorageKey(Prefix_VotersCount)]; decimal voterTurnout = votersCount / (decimal)TotalAmount; var candidates = GetCandidates(snapshot); - if (voterTurnout < EffectiveVoterTurnout || candidates.Length < ProtocolSettings.Default.CommitteeMembersCount) - return Blockchain.StandbyCommittee.Select(p => (p, candidates.FirstOrDefault(k => k.PublicKey.Equals(p)).Votes)); - return candidates.OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Take(ProtocolSettings.Default.CommitteeMembersCount); + if (voterTurnout < EffectiveVoterTurnout || candidates.Length < settings.CommitteeMembersCount) + return settings.StandbyCommittee.Select(p => (p, candidates.FirstOrDefault(k => k.PublicKey.Equals(p)).Votes)); + return candidates.OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Take(settings.CommitteeMembersCount); } [ContractMethod(1_00000000, CallFlags.ReadStates)] - public ECPoint[] GetNextBlockValidators(DataCache snapshot) + private ECPoint[] GetNextBlockValidators(ApplicationEngine engine) + { + return GetNextBlockValidators(engine.Snapshot, engine.ProtocolSettings.ValidatorsCount); + } + + public ECPoint[] GetNextBlockValidators(DataCache snapshot, int validatorsCount) { return GetCommitteeFromCache(snapshot) - .Take(ProtocolSettings.Default.ValidatorsCount) + .Take(validatorsCount) .Select(p => p.PublicKey) .OrderBy(p => p) .ToArray(); diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index 58ae9331f9..697b9f76b0 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -16,7 +16,7 @@ public static ContractState DeployContract(this DataCache snapshot, UInt160 send script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", nefFile, manifest, null); var engine = ApplicationEngine.Create(TriggerType.Application, - sender != null ? new Transaction() { Signers = new Signer[] { new Signer() { Account = sender } } } : null, snapshot, null, gas); + sender != null ? new Transaction() { Signers = new Signer[] { new Signer() { Account = sender } } } : null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: gas); engine.LoadScript(script.ToArray()); if (engine.Execute() != VMState.HALT) @@ -36,7 +36,7 @@ public static void UpdateContract(this DataCache snapshot, UInt160 callingScript var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "update", nefFile, manifest, null); - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); // Fake calling script hash @@ -59,7 +59,7 @@ public static void DestroyContract(this DataCache snapshot, UInt160 callingScrip var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.ContractManagement.Hash, "destroy"); - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); // Fake calling script hash @@ -96,7 +96,7 @@ public static StackItem Call(this NativeContract contract, DataCache snapshot, s public static StackItem Call(this NativeContract contract, DataCache snapshot, IVerifiable container, Block persistingBlock, string method, params ContractParameter[] args) { - using var engine = ApplicationEngine.Create(TriggerType.Application, container, snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, container, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(contract.Hash, method, args); engine.LoadScript(script.ToArray()); diff --git a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs index 784bbee5ed..1f088849fd 100644 --- a/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/Nep17NativeContractExtensions.cs @@ -38,7 +38,7 @@ public void SerializeUnsigned(BinaryWriter writer) { } public static bool Transfer(this NativeContract contract, DataCache snapshot, byte[] from, byte[] to, BigInteger amount, bool signFrom, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, - new ManualWitness(signFrom ? new UInt160(from) : null), snapshot, persistingBlock); + new ManualWitness(signFrom ? new UInt160(from) : null), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(contract.Hash, "transfer", from, to, amount, null); @@ -57,7 +57,7 @@ public static bool Transfer(this NativeContract contract, DataCache snapshot, by public static BigInteger TotalSupply(this NativeContract contract, DataCache snapshot) { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(contract.Hash, "totalSupply"); @@ -73,7 +73,7 @@ public static BigInteger TotalSupply(this NativeContract contract, DataCache sna public static BigInteger BalanceOf(this NativeContract contract, DataCache snapshot, byte[] account) { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(contract.Hash, "balanceOf", account); @@ -89,7 +89,7 @@ public static BigInteger BalanceOf(this NativeContract contract, DataCache snaps public static BigInteger Decimals(this NativeContract contract, DataCache snapshot) { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(contract.Hash, "decimals"); @@ -105,7 +105,7 @@ public static BigInteger Decimals(this NativeContract contract, DataCache snapsh public static string Symbol(this NativeContract contract, DataCache snapshot) { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(contract.Hash, "symbol"); diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 36465c2446..7c53ae9934 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -1,3 +1,5 @@ +using Akka.TestKit; +using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; @@ -25,7 +27,7 @@ public void TransactionsRemoved(MemoryPoolTxRemovalReason reason, IEnumerable mock = new Mock(); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); - mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); - mock.Setup(p => p.VerifyStateIndependent()).Returns(VerifyResult.Succeed); + mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.VerifyStateIndependent(It.IsAny())).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.NetworkFee = fee; mock.Object.Attributes = Array.Empty(); @@ -112,9 +123,8 @@ private Transaction CreateTransactionWithFeeAndBalanceVerify(long fee) random.NextBytes(randomBytes); Mock mock = new Mock(); UInt160 sender = senderAccount; - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); - mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny())).Returns((DataCache snapshot, TransactionVerificationContext context) => context.CheckTransaction(mock.Object, snapshot) ? VerifyResult.Succeed : VerifyResult.InsufficientFunds); - mock.Setup(p => p.VerifyStateIndependent()).Returns(VerifyResult.Succeed); + mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny(), It.IsAny())).Returns((ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) => context.CheckTransaction(mock.Object, snapshot) ? VerifyResult.Succeed : VerifyResult.InsufficientFunds); + mock.Setup(p => p.VerifyStateIndependent(It.IsAny())).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.NetworkFee = fee; mock.Object.Attributes = Array.Empty(); @@ -228,7 +238,7 @@ public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() { var snapshot = GetSnapshot(); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, senderAccount); - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, long.MaxValue); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); NativeContract.GAS.Mint(engine, UInt160.Zero, 70, true); @@ -246,7 +256,7 @@ public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() // Simulate the transfer process in tx by burning the balance UInt160 sender = block.Transactions[0].Sender; - ApplicationEngine applicationEngine = ApplicationEngine.Create(TriggerType.All, block, snapshot, block, (long)balance); + ApplicationEngine applicationEngine = ApplicationEngine.Create(TriggerType.All, block, snapshot, block, settings: TestBlockchain.TheNeoSystem.Settings, gas: (long)balance); NativeContract.GAS.Burn(applicationEngine, sender, NativeContract.GAS.BalanceOf(snapshot, sender)); NativeContract.GAS.Mint(applicationEngine, sender, txFee * 30, true); // Set the balance to meet 30 txs only diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs b/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs index 63e500abec..b393fab8c2 100644 --- a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs +++ b/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs @@ -28,9 +28,8 @@ private Transaction CreateTransactionWithFee(long networkFee, long systemFee) var randomBytes = new byte[16]; random.NextBytes(randomBytes); Mock mock = new Mock(); - mock.Setup(p => p.Verify(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); - mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); - mock.Setup(p => p.VerifyStateIndependent()).Returns(VerifyResult.Succeed); + mock.Setup(p => p.VerifyStateDependent(It.IsAny(), It.IsAny(), It.IsAny())).Returns(VerifyResult.Succeed); + mock.Setup(p => p.VerifyStateIndependent(It.IsAny())).Returns(VerifyResult.Succeed); mock.Object.Script = randomBytes; mock.Object.NetworkFee = networkFee; mock.Object.SystemFee = systemFee; @@ -53,7 +52,7 @@ public void TestDuplicateOracle() // Fake balance var snapshot = TestBlockchain.GetTestSnapshot(); - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, long.MaxValue); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, UInt160.Zero); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); NativeContract.GAS.Mint(engine, UInt160.Zero, 8, false); @@ -74,7 +73,7 @@ public void TestDuplicateOracle() public void TestTransactionSenderFee() { var snapshot = TestBlockchain.GetTestSnapshot(); - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, long.MaxValue); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, UInt160.Zero); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); NativeContract.GAS.Mint(engine, UInt160.Zero, 8, true); diff --git a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs index 19e78c51f2..725e0f37df 100644 --- a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs +++ b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs @@ -1,6 +1,5 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; using Neo.UnitTests.SmartContract; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index 846c35de0f..c1d97885c4 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; using Neo.UnitTests.SmartContract; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs index 16d801f188..558b9e1048 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; using System; diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs index b1dfebd72f..853afffa9d 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using System.Collections; @@ -19,17 +18,17 @@ public void TestSetup() [TestMethod] public void Size_Get() { - var test = MerkleBlockPayload.Create(Blockchain.GenesisBlock, new BitArray(1024, false)); + var test = MerkleBlockPayload.Create(TestBlockchain.TheNeoSystem.GenesisBlock, new BitArray(1024, false)); test.Size.Should().Be(239); - test = MerkleBlockPayload.Create(Blockchain.GenesisBlock, new BitArray(0, false)); + test = MerkleBlockPayload.Create(TestBlockchain.TheNeoSystem.GenesisBlock, new BitArray(0, false)); test.Size.Should().Be(111); } [TestMethod] public void DeserializeAndSerialize() { - var test = MerkleBlockPayload.Create(Blockchain.GenesisBlock, new BitArray(2, false)); + var test = MerkleBlockPayload.Create(TestBlockchain.TheNeoSystem.GenesisBlock, new BitArray(2, false)); var clone = test.ToArray().AsSerializable(); Assert.AreEqual(test.TxCount, clone.TxCount); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index f27a2e136a..87b0175211 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -156,14 +156,14 @@ public void FeeIsMultiSigContract() // Fast check - Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -238,14 +238,14 @@ public void FeeIsSignatureContractDetailed() // Fast check - Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -351,13 +351,13 @@ public void FeeIsSignatureContract_TestScope_Global() tx.Witnesses.Length.Should().Be(1); // Fast check - Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -438,13 +438,13 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_GAS() tx.Witnesses.Length.Should().Be(1); // Fast check - Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -528,13 +528,13 @@ public void FeeIsSignatureContract_TestScope_CalledByEntry_Plus_GAS() tx.Witnesses.Length.Should().Be(1); // Fast check - Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -670,13 +670,13 @@ public void FeeIsSignatureContract_TestScope_CurrentHash_NEO_GAS() tx.Signers.Length.Should().Be(1); // Fast check - Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -771,7 +771,7 @@ public void Transaction_Reverify_Hashes_Length_Unequal_To_Witnesses_Length() }; UInt160[] hashes = txSimple.GetScriptHashesForVerifying(snapshot); Assert.AreEqual(1, hashes.Length); - Assert.AreNotEqual(VerifyResult.Succeed, txSimple.VerifyStateDependent(snapshot, new TransactionVerificationContext())); + Assert.AreNotEqual(VerifyResult.Succeed, txSimple.VerifyStateDependent(ProtocolSettings.Default, snapshot, new TransactionVerificationContext())); } [TestMethod] @@ -1019,13 +1019,13 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() tx.Witnesses.Length.Should().Be(1); // Fast check - Assert.IsTrue(tx.VerifyWitnesses(snapshot, tx.NetworkFee)); + Assert.IsTrue(tx.VerifyWitnesses(ProtocolSettings.Default, snapshot, tx.NetworkFee)); // Check long verificationGas = 0; foreach (var witness in tx.Witnesses) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, null, tx.NetworkFee)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: tx.NetworkFee)) { engine.LoadScript(witness.VerificationScript); engine.LoadScript(witness.InvocationScript); @@ -1118,9 +1118,9 @@ public void Test_VerifyStateIndependent() } } }; - tx.VerifyStateIndependent().Should().Be(VerifyResult.Invalid); + tx.VerifyStateIndependent(ProtocolSettings.Default).Should().Be(VerifyResult.Invalid); tx.Script = new byte[0]; - tx.VerifyStateIndependent().Should().Be(VerifyResult.Succeed); + tx.VerifyStateIndependent(ProtocolSettings.Default).Should().Be(VerifyResult.Succeed); var walletA = TestUtils.GenerateTestWallet(); var walletB = TestUtils.GenerateTestWallet(); @@ -1171,7 +1171,7 @@ public void Test_VerifyStateIndependent() Assert.IsTrue(data.Completed); tx.Witnesses = data.GetWitnesses(); - tx.VerifyStateIndependent().Should().Be(VerifyResult.Succeed); + tx.VerifyStateIndependent(ProtocolSettings.Default).Should().Be(VerifyResult.Succeed); } } @@ -1202,10 +1202,10 @@ public void Test_VerifyStateDependent() var balance = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); balance.GetInteroperable().Balance = tx.NetworkFee; - tx.VerifyStateDependent(snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.Invalid); + tx.VerifyStateDependent(ProtocolSettings.Default, snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.Invalid); balance.GetInteroperable().Balance = 0; tx.SystemFee = 10; - tx.VerifyStateDependent(snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.InsufficientFunds); + tx.VerifyStateDependent(ProtocolSettings.Default, snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.InsufficientFunds); var walletA = TestUtils.GenerateTestWallet(); var walletB = TestUtils.GenerateTestWallet(); @@ -1254,77 +1254,7 @@ public void Test_VerifyStateDependent() Assert.IsTrue(data.Completed); tx.Witnesses = data.GetWitnesses(); - tx.VerifyStateDependent(snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.Succeed); - } - } - - [TestMethod] - public void Test_Verify() - { - var snapshot = TestBlockchain.GetTestSnapshot(); - var tx = new Transaction() - { - Attributes = Array.Empty(), - NetworkFee = 0, - Nonce = (uint)Environment.TickCount, - Script = new byte[Transaction.MaxTransactionSize], - Signers = new Signer[] { new Signer() { Account = UInt160.Zero } }, - SystemFee = 0, - ValidUntilBlock = 0, - Version = 0, - Witnesses = new Witness[0], - }; - tx.Verify(snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.Invalid); - - var walletA = TestUtils.GenerateTestWallet(); - var walletB = TestUtils.GenerateTestWallet(); - - using (var unlockA = walletA.Unlock("123")) - using (var unlockB = walletB.Unlock("123")) - { - var a = walletA.CreateAccount(); - var b = walletB.CreateAccount(); - - var multiSignContract = Contract.CreateMultiSigContract(2, - new ECPoint[] - { - a.GetKey().PublicKey, - b.GetKey().PublicKey - }); - - walletA.CreateAccount(multiSignContract, a.GetKey()); - var acc = walletB.CreateAccount(multiSignContract, b.GetKey()); - - // Fake balance - - var key = NativeContract.GAS.CreateStorageKey(20, acc.ScriptHash); - var entry = snapshot.GetAndChange(key, () => new StorageItem(new AccountState())); - - entry.GetInteroperable().Balance = 10000 * NativeContract.GAS.Factor; - - snapshot.Commit(); - - // Make transaction - - tx = walletA.MakeTransaction(snapshot, new TransferOutput[] - { - new TransferOutput() - { - AssetId = NativeContract.GAS.Hash, - ScriptHash = acc.ScriptHash, - Value = new BigDecimal(BigInteger.One,8) - } - }, acc.ScriptHash); - - // Sign - - var data = new ContractParametersContext(snapshot, tx); - Assert.IsTrue(walletA.Sign(data)); - Assert.IsTrue(walletB.Sign(data)); - Assert.IsTrue(data.Completed); - - tx.Witnesses = data.GetWitnesses(); - tx.Verify(snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.Succeed); + tx.VerifyStateDependent(ProtocolSettings.Default, snapshot, new TransactionVerificationContext()).Should().Be(VerifyResult.Succeed); } } } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs index 53799abba2..3a813886ce 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs @@ -1,7 +1,6 @@ using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Ledger; using Neo.SmartContract; using Neo.SmartContract.Native; using System; diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs index d920a7b593..811e3fcf3c 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -42,7 +41,7 @@ public void Check_BalanceOfTransferAndBurn() { var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); byte[] to = new byte[20]; var keyCount = snapshot.GetChangeSet().Count(); var supply = NativeContract.GAS.TotalSupply(snapshot); @@ -91,7 +90,7 @@ public void Check_BalanceOfTransferAndBurn() // Burn - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, 0); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); keyCount = snapshot.GetChangeSet().Count(); Assert.ThrowsException(() => diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs index 7865d80983..73cd03195c 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs @@ -3,7 +3,6 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -67,7 +66,7 @@ public void Test_SetRecord_A_AAAA() checkAvail_ret.Result.Should().BeTrue(); checkAvail_ret.State.Should().BeTrue(); - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); var register_ret = Check_Register(snapshot, domain, from, persistingBlock); register_ret.Result.Should().BeTrue(); register_ret.State.Should().BeTrue(); @@ -150,7 +149,7 @@ public void Test_SetRecord_CNAME() string domain = name + "." + validroot; - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); var register_ret = Check_Register(snapshot, domain, from, persistingBlock); register_ret.Result.Should().BeTrue(); register_ret.State.Should().BeTrue(); @@ -231,7 +230,7 @@ public void Test_SetRecord_TxT() checkAvail_ret.Result.Should().BeTrue(); checkAvail_ret.State.Should().BeTrue(); - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); var register_ret = Check_Register(snapshot, domain, from, persistingBlock); register_ret.Result.Should().BeTrue(); register_ret.State.Should().BeTrue(); @@ -322,7 +321,7 @@ public void Test_SetRecord_IPV6() checkAvail_ret.Result.Should().BeTrue(); checkAvail_ret.State.Should().BeTrue(); - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); var register_ret = Check_Register(snapshot, domain, from, persistingBlock); register_ret.Result.Should().BeTrue(); register_ret.State.Should().BeTrue(); @@ -395,7 +394,7 @@ public void Test_AddRootInvalid() var persistingBlock = _persistingBlock; //non-committee member string validroot = "testroot"; - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); var ret = Check_AddRoot(snapshot, new UInt160(from), validroot, persistingBlock); ret.Should().BeFalse(); @@ -454,7 +453,7 @@ public void Test_SetAdmin() checkAvail_ret.Result.Should().BeTrue(); checkAvail_ret.State.Should().BeTrue(); - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); var register_ret = Check_Register(snapshot, domain, from, persistingBlock); register_ret.Result.Should().BeTrue(); register_ret.State.Should().BeTrue(); @@ -498,7 +497,7 @@ public void Test_SetAdmin() internal static bool Check_AddRoot(DataCache snapshot, UInt160 account, string root, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(account), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(account), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "addRoot", root); @@ -514,7 +513,7 @@ internal static bool Check_AddRoot(DataCache snapshot, UInt160 account, string r internal static bool Check_Transfer(DataCache snapshot, UInt160 to, string domain, UInt160 owner, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "transfer", to, domain); @@ -530,7 +529,7 @@ internal static bool Check_Transfer(DataCache snapshot, UInt160 to, string domai internal static (bool State, bool Result) Check_IsAvailable(DataCache snapshot, UInt160 account, string name, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(account), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(account), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "isAvailable", name); @@ -550,7 +549,7 @@ internal static (bool State, bool Result) Check_IsAvailable(DataCache snapshot, internal static (bool State, bool Result) Check_SetPrice(DataCache snapshot, byte[] pubkey, long price, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); + new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "setPrice", price); @@ -569,7 +568,7 @@ internal static (bool State, bool Result) Check_SetPrice(DataCache snapshot, byt internal static BigDecimal Check_GetPrice(DataCache snapshot, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "getPrice"); @@ -586,7 +585,7 @@ internal static BigDecimal Check_GetPrice(DataCache snapshot, Block persistingBl internal static (bool State, bool Result) Check_Register(DataCache snapshot, string name, byte[] owner, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(new UInt160(owner)), snapshot, persistingBlock); + new Nep17NativeContractExtensions.ManualWitness(new UInt160(owner)), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "register", name, owner); @@ -606,7 +605,7 @@ internal static (bool State, bool Result) Check_Register(DataCache snapshot, str internal static bool Check_SetRecord(DataCache snapshot, string name, RecordType type, string data, byte[] pubkey, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(new UInt160(pubkey)), snapshot, persistingBlock); + new Nep17NativeContractExtensions.ManualWitness(new UInt160(pubkey)), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "setRecord", name, type, data); @@ -623,7 +622,7 @@ internal static bool Check_SetRecord(DataCache snapshot, string name, RecordType internal static (bool State, string Result) Check_GetRecord(DataCache snapshot, string name, RecordType type, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(UInt160.Zero), snapshot, persistingBlock); + new Nep17NativeContractExtensions.ManualWitness(UInt160.Zero), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "getRecord", name, type); @@ -643,7 +642,7 @@ internal static (bool State, string Result) Check_GetRecord(DataCache snapshot, internal static bool Check_SetAdmin(DataCache snapshot, string name, UInt160 admin, byte[] pubkey, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(new UInt160[] { admin, new UInt160(pubkey) }), snapshot, persistingBlock); + new Nep17NativeContractExtensions.ManualWitness(new UInt160[] { admin, new UInt160(pubkey) }), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "setAdmin", name, admin); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs index 854afeb7b9..184919a448 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -109,7 +108,7 @@ public void TestRegister() // good register - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); Assert.IsTrue(NativeContract.NameService.IsAvailable(snapshot, "neo.com")); result = Check_Register(snapshot, "neo.com", UInt160.Zero, persistingBlock); @@ -160,7 +159,7 @@ public void TestSetRecord() internal static bool Check_DeleteRecord(DataCache snapshot, string name, RecordType type, UInt160 signedBy, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "deleteRecord", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, @@ -178,7 +177,7 @@ internal static bool Check_DeleteRecord(DataCache snapshot, string name, RecordT internal static bool Check_SetRecord(DataCache snapshot, string name, RecordType type, string data, UInt160 signedBy, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "setRecord", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, @@ -197,7 +196,7 @@ internal static bool Check_SetRecord(DataCache snapshot, string name, RecordType internal static BigInteger Check_Renew(DataCache snapshot, string name, UInt160 signedBy, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "renew", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name } @@ -217,7 +216,7 @@ internal static BigInteger Check_Renew(DataCache snapshot, string name, UInt160 internal static bool Check_SetAdmin(DataCache snapshot, string name, UInt160 admin, UInt160 signedBy, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(admin, signedBy), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(admin, signedBy), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "setAdmin", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, @@ -235,7 +234,7 @@ internal static bool Check_SetAdmin(DataCache snapshot, string name, UInt160 adm internal static bool Check_Register(DataCache snapshot, string name, UInt160 owner, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(owner), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "register", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = name }, @@ -256,7 +255,7 @@ internal static bool Check_Register(DataCache snapshot, string name, UInt160 own internal static bool Check_SetPrice(DataCache snapshot, UInt160 signedBy, long price, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "setPrice", new ContractParameter[] { new ContractParameter(ContractParameterType.Integer) { Value = price } }); engine.LoadScript(script.ToArray()); @@ -271,7 +270,7 @@ internal static bool Check_SetPrice(DataCache snapshot, UInt160 signedBy, long p internal static bool Check_AddRoot(DataCache snapshot, UInt160 signedBy, string root, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(signedBy), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NameService.Hash, "addRoot", new ContractParameter[] { new ContractParameter(ContractParameterType.String) { Value = root } }); engine.LoadScript(script.ToArray()); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 9899b8748d..d2571e8e2f 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.Ledger; using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; @@ -24,7 +23,7 @@ public void TestSetup() [TestMethod] public void TestInitialize() { - ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0); + ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); testNativeContract.Initialize(ae); } @@ -101,7 +100,7 @@ public void TestInvoke() Manifest = testNativeContract.Manifest })); - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, null, 20_00000000)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: 20_00000000)) { using ScriptBuilder script = new ScriptBuilder(); script.EmitDynamicCall(testNativeContract.Hash, "wrongMethod"); @@ -109,7 +108,7 @@ public void TestInvoke() engine.Execute().Should().Be(VMState.FAULT); } - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, null, 20_00000000)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: 20_00000000)) { using ScriptBuilder script = new ScriptBuilder(); script.EmitDynamicCall(testNativeContract.Hash, "helloWorld"); @@ -123,10 +122,10 @@ public void TestTrigger() { var snapshot = TestBlockchain.GetTestSnapshot(); - ApplicationEngine engine1 = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, 0); + ApplicationEngine engine1 = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); Assert.ThrowsException(() => testNativeContract.TestTrigger(engine1)); - ApplicationEngine engine2 = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, null, 0); + ApplicationEngine engine2 = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); testNativeContract.TestTrigger(engine2); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index c34bc70bc5..0b5983443b 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -3,7 +3,6 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -51,7 +50,7 @@ public void Check_Vote() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); // No signature @@ -105,7 +104,7 @@ public void Check_Vote_Sameaccounts() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); var accountState = snapshot.TryGet(CreateStorageKey(20, from)).GetInteroperable(); accountState.Balance = 100; snapshot.Add(CreateStorageKey(33, ECCurve.Secp256r1.G.ToArray()), new StorageItem(new CandidateState())); @@ -132,8 +131,8 @@ public void Check_Vote_ChangeVote() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; //from vote to G - byte[] from = Blockchain.StandbyValidators[0].ToArray(); - var from_Account = Contract.CreateSignatureContract(Blockchain.StandbyValidators[0]).ScriptHash.ToArray(); + byte[] from = ProtocolSettings.Default.StandbyValidators[0].ToArray(); + var from_Account = Contract.CreateSignatureContract(ProtocolSettings.Default.StandbyValidators[0]).ScriptHash.ToArray(); snapshot.Add(CreateStorageKey(20, from_Account), new StorageItem(new NeoAccountState())); var accountState = snapshot.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.Balance = 100; @@ -163,8 +162,8 @@ public void Check_Vote_VoteToNull() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Blockchain.StandbyValidators[0].ToArray(); - var from_Account = Contract.CreateSignatureContract(Blockchain.StandbyValidators[0]).ScriptHash.ToArray(); + byte[] from = ProtocolSettings.Default.StandbyValidators[0].ToArray(); + var from_Account = Contract.CreateSignatureContract(ProtocolSettings.Default.StandbyValidators[0]).ScriptHash.ToArray(); snapshot.Add(CreateStorageKey(20, from_Account), new StorageItem(new NeoAccountState())); var accountState = snapshot.TryGet(CreateStorageKey(20, from_Account)).GetInteroperable(); accountState.Balance = 100; @@ -193,7 +192,7 @@ public void Check_UnclaimedGas() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); var unclaim = Check_UnclaimedGas(snapshot, from, persistingBlock); unclaim.Value.Should().Be(new BigInteger(0.5 * 1000 * 100000000L)); @@ -210,7 +209,7 @@ public void Check_RegisterValidator() var snapshot = _snapshot.CreateSnapshot(); var keyCount = snapshot.GetChangeSet().Count(); - var point = Blockchain.StandbyValidators[0].EncodePoint(true).Clone() as byte[]; + var point = ProtocolSettings.Default.StandbyValidators[0].EncodePoint(true).Clone() as byte[]; var ret = Check_RegisterValidator(snapshot, point, _persistingBlock); // Exists ret.State.Should().BeTrue(); @@ -238,7 +237,7 @@ public void Check_UnregisterCandidate() var snapshot = _snapshot.CreateSnapshot(); var keyCount = snapshot.GetChangeSet().Count(); - var point = Blockchain.StandbyValidators[0].EncodePoint(true); + var point = ProtocolSettings.Default.StandbyValidators[0].EncodePoint(true); //without register var ret = Check_UnregisterCandidate(snapshot, point, _persistingBlock); @@ -273,7 +272,7 @@ public void Check_UnregisterCandidate() snapshot.Add(CreateStorageKey(20, G_Account), new StorageItem(new NeoAccountState())); var accountState = snapshot.TryGet(CreateStorageKey(20, G_Account)).GetInteroperable(); accountState.Balance = 100; - Check_Vote(snapshot, G_Account, Blockchain.StandbyValidators[0].ToArray(), true, _persistingBlock); + Check_Vote(snapshot, G_Account, ProtocolSettings.Default.StandbyValidators[0].ToArray(), true, _persistingBlock); ret = Check_UnregisterCandidate(snapshot, point, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); @@ -284,10 +283,10 @@ public void Check_UnregisterCandidate() pointState.Votes.Should().Be(100); //vote fail - ret = Check_Vote(snapshot, G_Account, Blockchain.StandbyValidators[0].ToArray(), true, _persistingBlock); + ret = Check_Vote(snapshot, G_Account, ProtocolSettings.Default.StandbyValidators[0].ToArray(), true, _persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeFalse(); - accountState.VoteTo.Should().Be(Blockchain.StandbyValidators[0]); + accountState.VoteTo.Should().Be(ProtocolSettings.Default.StandbyValidators[0]); } [TestMethod] @@ -295,7 +294,7 @@ public void Check_GetCommittee() { var snapshot = _snapshot.CreateSnapshot(); var keyCount = snapshot.GetChangeSet().Count(); - var point = Blockchain.StandbyValidators[0].EncodePoint(true); + var point = ProtocolSettings.Default.StandbyValidators[0].EncodePoint(true); var persistingBlock = _persistingBlock; //register with votes with 20000000 @@ -311,7 +310,7 @@ public void Check_GetCommittee() ret.Result.Should().BeTrue(); var committeemembers = NativeContract.NEO.GetCommittee(snapshot); - var defaultCommittee = Blockchain.StandbyCommittee.OrderBy(p => p).ToArray(); + var defaultCommittee = ProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray(); committeemembers.GetType().Should().Be(typeof(ECPoint[])); for (int i = 0; i < ProtocolSettings.Default.CommitteeMembersCount; i++) { @@ -333,7 +332,7 @@ public void Check_GetCommittee() }; for (int i = 0; i < ProtocolSettings.Default.CommitteeMembersCount - 1; i++) { - ret = Check_RegisterValidator(snapshot, Blockchain.StandbyCommittee[i].ToArray(), persistingBlock); + ret = Check_RegisterValidator(snapshot, ProtocolSettings.Default.StandbyCommittee[i].ToArray(), persistingBlock); ret.State.Should().BeTrue(); ret.Result.Should().BeTrue(); } @@ -345,9 +344,9 @@ public void Check_GetCommittee() committeemembers.Contains(ECCurve.Secp256r1.G).Should().BeTrue(); for (int i = 0; i < ProtocolSettings.Default.CommitteeMembersCount - 1; i++) { - committeemembers.Contains(Blockchain.StandbyCommittee[i]).Should().BeTrue(); + committeemembers.Contains(ProtocolSettings.Default.StandbyCommittee[i]).Should().BeTrue(); } - committeemembers.Contains(Blockchain.StandbyCommittee[ProtocolSettings.Default.CommitteeMembersCount - 1]).Should().BeFalse(); + committeemembers.Contains(ProtocolSettings.Default.StandbyCommittee[ProtocolSettings.Default.CommitteeMembersCount - 1]).Should().BeFalse(); } [TestMethod] @@ -356,7 +355,7 @@ public void Check_Transfer() var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; - byte[] from = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] from = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); byte[] to = new byte[20]; var keyCount = snapshot.GetChangeSet().Count(); @@ -405,7 +404,7 @@ public void Check_Transfer() public void Check_BalanceOf() { var snapshot = _snapshot.CreateSnapshot(); - byte[] account = Contract.GetBFTAddress(Blockchain.StandbyValidators).ToArray(); + byte[] account = Contract.GetBFTAddress(ProtocolSettings.Default.StandbyValidators).ToArray(); NativeContract.NEO.BalanceOf(snapshot, account).Should().Be(100_000_000); @@ -433,7 +432,7 @@ public void Check_CommitteeBonus() Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); - var committee = Blockchain.StandbyCommittee; + var committee = ProtocolSettings.Default.StandbyCommittee; NativeContract.GAS.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[0]).ScriptHash.ToArray()).Should().Be(50000000); NativeContract.GAS.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[1]).ScriptHash.ToArray()).Should().Be(50000000); NativeContract.GAS.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[2]).ScriptHash.ToArray()).Should().Be(0); @@ -511,9 +510,9 @@ public void TestCalculateBonus() snapshot.GetAndChange(key, () => new StorageItem(new NeoAccountState { Balance = 100, - VoteTo = Blockchain.StandbyCommittee[0] + VoteTo = ProtocolSettings.Default.StandbyCommittee[0] })); - snapshot.Add(new KeyBuilder(NativeContract.NEO.Id, 23).Add(Blockchain.StandbyCommittee[0]).AddBigEndian(uint.MaxValue - 50), new StorageItem() { Value = new BigInteger(50 * 10000L).ToByteArray() }); + snapshot.Add(new KeyBuilder(NativeContract.NEO.Id, 23).Add(ProtocolSettings.Default.StandbyCommittee[0]).AddBigEndian(uint.MaxValue - 50), new StorageItem() { Value = new BigInteger(50 * 10000L).ToByteArray() }); NativeContract.NEO.UnclaimedGas(snapshot, UInt160.Zero, 100).Should().Be(new BigInteger(50 * 100)); snapshot.Delete(key); } @@ -541,7 +540,7 @@ public void TestGetNextBlockValidators1() public void TestGetNextBlockValidators2() { var snapshot = _snapshot.CreateSnapshot(); - var result = NativeContract.NEO.GetNextBlockValidators(snapshot); + var result = NativeContract.NEO.GetNextBlockValidators(snapshot, 7); result.Length.Should().Be(7); result[0].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); result[1].ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); @@ -662,7 +661,7 @@ public void TestGetCommittee() public void TestGetValidators() { var snapshot = _snapshot.CreateSnapshot(); - var result = NativeContract.NEO.ComputeNextBlockValidators(snapshot); + var result = NativeContract.NEO.ComputeNextBlockValidators(snapshot, ProtocolSettings.Default); result[0].ToArray().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); result[1].ToArray().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); result[2].ToArray().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); @@ -734,7 +733,7 @@ public void TestClaimGas() // Initialize block snapshot.Add(CreateStorageKey(1), new StorageItem(new BigInteger(30000000))); - ECPoint[] standbyCommittee = Blockchain.StandbyCommittee.OrderBy(p => p).ToArray(); + ECPoint[] standbyCommittee = ProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray(); CachedCommittee cachedCommittee = new CachedCommittee(); for (var i = 0; i < ProtocolSettings.Default.CommitteeMembersCount; i++) { @@ -765,7 +764,7 @@ public void TestClaimGas() }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); - var committee = Blockchain.StandbyCommittee.OrderBy(p => p).ToArray(); + var committee = ProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray(); var accountA = committee[0]; var accountB = committee[ProtocolSettings.Default.CommitteeMembersCount - 1]; NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(accountA).ScriptHash).Should().Be(0); @@ -812,7 +811,7 @@ public void TestClaimGas() }; Check_PostPersist(snapshot, persistingBlock).Should().BeTrue(); - accountA = Blockchain.StandbyCommittee.OrderBy(p => p).ToArray()[2]; + accountA = ProtocolSettings.Default.StandbyCommittee.OrderBy(p => p).ToArray()[2]; NativeContract.NEO.BalanceOf(snapshot, Contract.CreateSignatureContract(committee[2]).ScriptHash).Should().Be(0); storageItem = snapshot.TryGet(new KeyBuilder(NativeContract.NEO.Id, 23).Add(committee[2]).AddBigEndian(22)); @@ -875,7 +874,7 @@ public void TestVote() internal (bool State, bool Result) Transfer4TesingOnBalanceChanging(BigInteger amount, bool addVotes) { var snapshot = _snapshot.CreateSnapshot(); - var engine = ApplicationEngine.Create(TriggerType.Application, Blockchain.GenesisBlock, snapshot, Blockchain.GenesisBlock); + var engine = ApplicationEngine.Create(TriggerType.Application, TestBlockchain.TheNeoSystem.GenesisBlock, snapshot, TestBlockchain.TheNeoSystem.GenesisBlock, settings: TestBlockchain.TheNeoSystem.Settings); ScriptBuilder sb = new ScriptBuilder(); var tmp = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot); UInt160 from = engine.ScriptContainer.GetScriptHashesForVerifying(engine.Snapshot)[0]; @@ -908,7 +907,7 @@ internal static bool Check_OnPersist(DataCache snapshot, Block persistingBlock) { var script = new ScriptBuilder(); script.EmitSysCall(ApplicationEngine.System_Contract_NativeOnPersist); - var engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, persistingBlock); + var engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); return engine.Execute() == VMState.HALT; @@ -918,7 +917,7 @@ internal static bool Check_PostPersist(DataCache snapshot, Block persistingBlock { using var script = new ScriptBuilder(); script.EmitSysCall(ApplicationEngine.System_Contract_NativePostPersist); - using var engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); return engine.Execute() == VMState.HALT; @@ -926,7 +925,7 @@ internal static bool Check_PostPersist(DataCache snapshot, Block persistingBlock internal static (BigInteger Value, bool State) Check_GetGasPerBlock(DataCache snapshot, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NEO.Hash, "getGasPerBlock"); @@ -946,7 +945,7 @@ internal static (BigInteger Value, bool State) Check_GetGasPerBlock(DataCache sn internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(DataCache snapshot, BigInteger gasPerBlock, Block persistingBlock) { UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); - using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NEO.Hash, "setGasPerBlock", gasPerBlock); @@ -963,7 +962,7 @@ internal static (VM.Types.Boolean Value, bool State) Check_SetGasPerBlock(DataCa internal static (bool State, bool Result) Check_Vote(DataCache snapshot, byte[] account, byte[] pubkey, bool signAccount, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot, persistingBlock); + new Nep17NativeContractExtensions.ManualWitness(signAccount ? new UInt160(account) : UInt160.Zero), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NEO.Hash, "vote", account, pubkey); @@ -983,7 +982,7 @@ internal static (bool State, bool Result) Check_Vote(DataCache snapshot, byte[] internal static (bool State, bool Result) Check_RegisterValidator(DataCache snapshot, byte[] pubkey, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock, 1100_00000000); + new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NEO.Hash, "registerCandidate", pubkey); @@ -1002,7 +1001,7 @@ internal static (bool State, bool Result) Check_RegisterValidator(DataCache snap internal static ECPoint[] Check_GetCommittee(DataCache snapshot, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NEO.Hash, "getCommittee"); @@ -1018,7 +1017,7 @@ internal static ECPoint[] Check_GetCommittee(DataCache snapshot, Block persistin internal static (BigInteger Value, bool State) Check_UnclaimedGas(DataCache snapshot, byte[] address, Block persistingBlock) { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NEO.Hash, "unclaimedGas", address, persistingBlock.Index); @@ -1074,7 +1073,7 @@ internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) internal static (bool State, bool Result) Check_UnregisterCandidate(DataCache snapshot, byte[] pubkey, Block persistingBlock) { using var engine = ApplicationEngine.Create(TriggerType.Application, - new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock); + new Nep17NativeContractExtensions.ManualWitness(Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(pubkey, ECCurve.Secp256r1)).ToScriptHash()), snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings); using var script = new ScriptBuilder(); script.EmitDynamicCall(NativeContract.NEO.Hash, "unregisterCandidate", pubkey); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 4a3499d22d..389bf98e6d 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -23,7 +22,7 @@ public void TestSetup() TestBlockchain.InitializeMockNeoSystem(); _snapshot = TestBlockchain.GetTestSnapshot(); - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, _snapshot, new Block { Header = new Header() }, 0); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, _snapshot, new Block { Header = new Header() }, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); NativeContract.ContractManagement.OnPersist(engine); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs index 2ba9ed6a20..d292fb8776 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -30,7 +29,7 @@ public void TestSetAndGet() { var snapshot1 = _snapshot.CreateSnapshot(); UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot1); - ECPoint[] validators = NativeContract.NEO.ComputeNextBlockValidators(snapshot1); + ECPoint[] validators = NativeContract.NEO.ComputeNextBlockValidators(snapshot1, ProtocolSettings.Default); var ret = NativeContract.RoleManagement.Call( snapshot1, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index e743f38885..bcb307a252 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -1,6 +1,5 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Ledger; using Neo.SmartContract; using Neo.VM.Types; using System.Numerics; @@ -22,7 +21,7 @@ public void TestSetup() public void TestBinary() { var snapshot = TestBlockchain.GetTestSnapshot(); - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); var data = new byte[0]; CollectionAssert.AreEqual(data, engine.Base64Decode(engine.Base64Encode(data))); @@ -42,7 +41,7 @@ public void TestBinary() [TestMethod] public void TestItoaAtoi() { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, null); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestBlockchain.TheNeoSystem.Settings); Assert.AreEqual("1", engine.Itoa(BigInteger.One, 10)); Assert.AreEqual("1", engine.Itoa(BigInteger.One, 16)); @@ -59,7 +58,7 @@ public void TestItoaAtoi() public void TestNotify() { var snapshot = TestBlockchain.GetTestSnapshot(); - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); ApplicationEngine.Notify += Test_Notify1; const string notifyEvent = "TestEvent"; @@ -97,7 +96,7 @@ public void TestCreateDummyBlock() byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; ApplicationEngine engine = ApplicationEngine.Run(SyscallSystemRuntimeCheckWitnessHash, snapshot); engine.PersistingBlock.Version.Should().Be(0); - engine.PersistingBlock.PrevHash.Should().Be(Blockchain.GenesisBlock.Hash); + engine.PersistingBlock.PrevHash.Should().Be(TestBlockchain.TheNeoSystem.GenesisBlock.Hash); engine.PersistingBlock.MerkleRoot.Should().Be(new UInt256()); } } diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs index ce0cbf9677..fb1b8f6e4f 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngineProvider.cs @@ -28,14 +28,14 @@ public void TestSetAppEngineProvider() var provider = new TestProvider(); ApplicationEngine.SetApplicationEngineProvider(provider).Should().BeTrue(); - using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0); + using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0, settings: TestBlockchain.TheNeoSystem.Settings); (appEngine is TestEngine).Should().BeTrue(); } [TestMethod] public void TestDefaultAppEngineProvider() { - using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0); + using var appEngine = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0, settings: TestBlockchain.TheNeoSystem.Settings); (appEngine is ApplicationEngine).Should().BeTrue(); } @@ -63,16 +63,16 @@ public void TestCanResetAppEngineProviderTwice() class TestProvider : IApplicationEngineProvider { - public ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, long gas) + public ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas) { - return new TestEngine(trigger, container, snapshot, persistingBlock, gas); + return new TestEngine(trigger, container, snapshot, persistingBlock, settings, gas); } } class TestEngine : ApplicationEngine { - public TestEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, long gas) - : base(trigger, container, snapshot, persistingBlock, gas) + public TestEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas) + : base(trigger, container, snapshot, persistingBlock, settings, gas) { } } diff --git a/tests/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/neo.UnitTests/SmartContract/UT_Contract.cs index cc67603991..ce3a9d729f 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Contract.cs @@ -178,7 +178,7 @@ public void TestSignatureRedeemScriptFee() var fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * 2 + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.ECDsaVerifyPrice); - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null, settings: TestBlockchain.TheNeoSystem.Settings)) { engine.LoadScript(invocation.Concat(verification).ToArray(), configureState: p => p.CallFlags = CallFlags.None); engine.Execute(); @@ -206,7 +206,7 @@ public void TestCreateMultiSigRedeemScriptFee() long fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * (2 + 2) + ApplicationEngine.OpCodePrices[OpCode.PUSHINT8] * 2 + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.ECDsaVerifyPrice * 2); - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null)) + using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null, settings: TestBlockchain.TheNeoSystem.Settings)) { engine.LoadScript(invocation.Concat(verification).ToArray(), configureState: p => p.CallFlags = CallFlags.None); engine.Execute(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index 6f6a383a90..9a7c7e1095 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -1,6 +1,5 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Ledger; using Neo.SmartContract; using Neo.UnitTests.Extensions; using Neo.VM; @@ -21,7 +20,7 @@ public void ApplicationEngineFixedPrices() { // System.Runtime.CheckWitness: f827ec8c (price is 200) byte[] SyscallSystemRuntimeCheckWitnessHash = new byte[] { 0x68, 0xf8, 0x27, 0xec, 0x8c }; - using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0)) + using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0)) { ae.LoadScript(SyscallSystemRuntimeCheckWitnessHash); ApplicationEngine.System_Runtime_CheckWitness.FixedPrice.Should().Be(0_00001024L); @@ -29,7 +28,7 @@ public void ApplicationEngineFixedPrices() // System.Storage.GetContext: 9bf667ce (price is 1) byte[] SyscallSystemStorageGetContextHash = new byte[] { 0x68, 0x9b, 0xf6, 0x67, 0xce }; - using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0)) + using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0)) { ae.LoadScript(SyscallSystemStorageGetContextHash); ApplicationEngine.System_Storage_GetContext.FixedPrice.Should().Be(0_00000016L); @@ -37,7 +36,7 @@ public void ApplicationEngineFixedPrices() // System.Storage.Get: 925de831 (price is 100) byte[] SyscallSystemStorageGetHash = new byte[] { 0x68, 0x92, 0x5d, 0xe8, 0x31 }; - using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, null, 0)) + using (ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, gas: 0)) { ae.LoadScript(SyscallSystemStorageGetHash); ApplicationEngine.System_Storage_Get.FixedPrice.Should().Be(32768L); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 3e45542200..4ccc9346c6 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -3,7 +3,6 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.SmartContract; @@ -122,7 +121,7 @@ public void TestAccount_IsStandard() engine.LoadScript(new byte[] { 0x01 }); engine.IsStandardContract(state.Hash).Should().BeFalse(); - state.Nef.Script = Contract.CreateSignatureRedeemScript(Blockchain.StandbyValidators[0]); + state.Nef.Script = Contract.CreateSignatureRedeemScript(ProtocolSettings.Default.StandbyValidators[0]); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(new byte[] { 0x01 }); engine.IsStandardContract(state.Hash).Should().BeTrue(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 2064f97bd2..253840b46a 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -1,9 +1,10 @@ +using Akka.TestKit; +using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P; using Neo.Network.P2P.Payloads; using Neo.SmartContract; @@ -20,12 +21,17 @@ namespace Neo.UnitTests.SmartContract { [TestClass] - public partial class UT_InteropService + public partial class UT_InteropService : TestKit { + private TestProbe senderProbe; + [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); + senderProbe = CreateTestProbe(); + senderProbe.Send(TestBlockchain.TheNeoSystem.Blockchain, new object()); + senderProbe.ExpectNoMsg(); // Ensure blockchain it's created } [TestMethod] @@ -670,7 +676,7 @@ private static ApplicationEngine GetEngine(bool hasContainer = false, bool hasSn var tx = hasContainer ? TestUtils.GetTransaction(UInt160.Zero) : null; var snapshot = hasSnapshot ? TestBlockchain.GetTestSnapshot() : null; var block = hasBlock ? new Block { Header = new Header() } : null; - ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, block, gas); + ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, snapshot, block, TestBlockchain.TheNeoSystem.Settings, gas: gas); if (addScript) engine.LoadScript(new byte[] { 0x01 }); return engine; } diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 3ff78ac2dc..71eafaaa38 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -1,6 +1,5 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract; @@ -138,7 +137,7 @@ public void TestVerifyWitnesses() Hashes = new UInt256[1] { UInt256.Zero }, }); BlocksDelete(snapshot1, index1); - Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(new Header() { PrevHash = index1 }, snapshot1, 100)); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(new Header() { PrevHash = index1 }, ProtocolSettings.Default, snapshot1, 100)); var snapshot2 = TestBlockchain.GetTestSnapshot(); UInt256 index2 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); @@ -159,7 +158,7 @@ public void TestVerifyWitnesses() snapshot2.AddContract(UInt160.Zero, new ContractState()); snapshot2.DeleteContract(UInt160.Zero); - Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header2, snapshot2, 100)); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header2, ProtocolSettings.Default, snapshot2, 100)); var snapshot3 = TestBlockchain.GetTestSnapshot(); UInt256 index3 = UInt256.Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01"); @@ -191,7 +190,7 @@ public void TestVerifyWitnesses() Hash = Array.Empty().ToScriptHash(), Manifest = TestUtils.CreateManifest("verify", ContractParameterType.Boolean, ContractParameterType.Signature), }); - Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header3, snapshot3, 100)); + Assert.AreEqual(false, Neo.SmartContract.Helper.VerifyWitnesses(header3, ProtocolSettings.Default, snapshot3, 100)); // Smart contract verification @@ -207,7 +206,7 @@ public void TestVerifyWitnesses() Witnesses = new Witness[] { new Witness() { InvocationScript = Array.Empty(), VerificationScript = Array.Empty() } } }; - Assert.AreEqual(true, Neo.SmartContract.Helper.VerifyWitnesses(tx, snapshot3, 1000)); + Assert.AreEqual(true, Neo.SmartContract.Helper.VerifyWitnesses(tx, ProtocolSettings.Default, snapshot3, 1000)); } private void BlocksDelete(DataCache snapshot, UInt256 hash) diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 7398c29e43..562433cbbd 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -1,6 +1,7 @@ +using Akka.TestKit; +using Akka.TestKit.Xunit2; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Native; @@ -13,12 +14,17 @@ namespace Neo.UnitTests.SmartContract { [TestClass] - public partial class UT_Syscalls + public partial class UT_Syscalls : TestKit { + private TestProbe senderProbe; + [TestInitialize] public void TestSetup() { TestBlockchain.InitializeMockNeoSystem(); + senderProbe = CreateTestProbe(); + senderProbe.Send(TestBlockchain.TheNeoSystem.Blockchain, new object()); + senderProbe.ExpectNoMsg(); // Ensure blockchain it's created } [TestMethod] @@ -64,7 +70,7 @@ public void System_Blockchain_GetBlock() // Without block - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.HALT); @@ -86,7 +92,7 @@ public void System_Blockchain_GetBlock() Transaction = tx }, true)); - engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.HALT); @@ -97,7 +103,7 @@ public void System_Blockchain_GetBlock() height.Index = block.Index; - engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); + engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.HALT); @@ -281,7 +287,7 @@ public void System_Runtime_GasLeft() // Execute - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, null, 100_000_000); + var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, gas: 100_000_000); engine.LoadScript(script.ToArray()); Assert.AreEqual(engine.Execute(), VMState.HALT); diff --git a/tests/neo.UnitTests/UT_DataCache.cs b/tests/neo.UnitTests/UT_DataCache.cs index 9e3022e7e8..80eff4723d 100644 --- a/tests/neo.UnitTests/UT_DataCache.cs +++ b/tests/neo.UnitTests/UT_DataCache.cs @@ -1,5 +1,4 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract; using System.Linq; diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 14066c0864..36b7026f92 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -1,7 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Cryptography.ECC; -using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.SmartContract; using Neo.SmartContract.Native; From 6b1337015ec2d0cd85b07725c230dd8da1fe77d3 Mon Sep 17 00:00:00 2001 From: Shargon Date: Mon, 8 Feb 2021 15:39:18 +0100 Subject: [PATCH 12/63] Add GetInstance to LocalNode (#2310) * Add GetInstance * Add UT Co-authored-by: Erik Zhang --- src/neo/Network/P2P/LocalNode.cs | 4 ++ .../neo.UnitTests/Network/P2P/UT_LocalNode.cs | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index 990b6572fa..88232c34a0 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -16,6 +16,7 @@ public class LocalNode : Peer { public class RelayDirectly { public IInventory Inventory; } public class SendDirectly { public IInventory Inventory; } + public class GetInstance { } public const uint ProtocolVersion = 0; private const int MaxCountFromSeedList = 5; @@ -179,6 +180,9 @@ protected override void OnReceive(object message) case SendDirectly send: OnSendDirectly(send.Inventory); break; + case GetInstance _: + Sender.Tell(this); + break; } } diff --git a/tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs b/tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs new file mode 100644 index 0000000000..1e4da7590b --- /dev/null +++ b/tests/neo.UnitTests/Network/P2P/UT_LocalNode.cs @@ -0,0 +1,39 @@ +using Akka.TestKit.Xunit2; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Network.P2P; +using System; +using System.Linq; +using System.Net; + +namespace Neo.UnitTests.Network.P2P +{ + [TestClass] + public class UT_LocalNode : TestKit + { + private static NeoSystem testBlockchain; + + [TestInitialize] + public void Init() + { + testBlockchain = TestBlockchain.TheNeoSystem; + } + + [TestMethod] + public void TestDefaults() + { + var senderProbe = CreateTestProbe(); + senderProbe.Send(testBlockchain.LocalNode, new LocalNode.GetInstance()); + var localnode = senderProbe.ExpectMsg(); + + Assert.AreEqual(0, localnode.ListenerTcpPort); + Assert.AreEqual(0, localnode.ListenerWsPort); + Assert.AreEqual(3, localnode.MaxConnectionsPerAddress); + Assert.AreEqual(10, localnode.MinDesiredConnections); + Assert.AreEqual(40, localnode.MaxConnections); + Assert.AreEqual(0, localnode.UnconnectedCount); + + CollectionAssert.AreEqual(Array.Empty(), localnode.GetRemoteNodes().ToArray()); + CollectionAssert.AreEqual(Array.Empty(), localnode.GetUnconnectedPeers().ToArray()); + } + } +} From 9fe018c6617887534eb9f9573bde1a8509055b6e Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Tue, 9 Feb 2021 02:57:21 +0800 Subject: [PATCH 13/63] Add native contract: StdLib and CryptoLib (#2298) --- src/neo/Ledger/Blockchain.cs | 3 +- src/neo/NeoSystem.cs | 3 + .../SmartContract/ApplicationEngine.Binary.cs | 71 ------ .../SmartContract/ApplicationEngine.Crypto.cs | 77 +------ .../SmartContract/ApplicationEngine.Json.cs | 21 -- src/neo/SmartContract/BinarySerializer.cs | 8 +- src/neo/SmartContract/Contract.cs | 6 +- src/neo/SmartContract/Helper.cs | 34 +-- .../Iterators/StorageIterator.cs | 2 +- src/neo/SmartContract/Native/CryptoLib.cs | 43 ++++ src/neo/SmartContract/Native/NamedCurve.cs | 14 ++ .../SmartContract/Native/NativeContract.cs | 4 +- .../SmartContract/Native/OracleContract.cs | 2 +- src/neo/SmartContract/Native/StdLib.cs | 86 +++++++ src/neo/SmartContract/StorageItem.cs | 2 +- src/neo/Wallets/Wallet.cs | 11 +- .../Network/P2P/Payloads/UT_Transaction.cs | 28 +-- .../Network/P2P/Payloads/UT_Witness.cs | 6 +- .../SmartContract/Native/UT_NeoToken.cs | 2 +- .../SmartContract/Native/UT_StdLib.cs | 212 ++++++++++++++++++ .../SmartContract/UT_ApplicationEngine.cs | 38 ---- .../SmartContract/UT_BinarySerializer.cs | 14 +- .../SmartContract/UT_Contract.cs | 46 ++-- .../UT_ContractParameterContext.cs | 10 +- .../SmartContract/UT_InteropService.NEO.cs | 27 +-- .../SmartContract/UT_InteropService.cs | 29 +-- .../SmartContract/UT_Syscalls.cs | 112 --------- .../Wallets/SQLite/UT_VerificationContract.cs | 8 +- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 4 +- 29 files changed, 466 insertions(+), 457 deletions(-) delete mode 100644 src/neo/SmartContract/ApplicationEngine.Binary.cs delete mode 100644 src/neo/SmartContract/ApplicationEngine.Json.cs create mode 100644 src/neo/SmartContract/Native/CryptoLib.cs create mode 100644 src/neo/SmartContract/Native/NamedCurve.cs create mode 100644 src/neo/SmartContract/Native/StdLib.cs create mode 100644 tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 3fdaee4ab2..982b28f193 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -55,8 +55,7 @@ public Blockchain(NeoSystem system) { this.system = system; this.txrouter = Context.ActorOf(TransactionRouter.Props(system)); - DataCache snapshot = system.StoreView; - if (!NativeContract.Ledger.Initialized(snapshot)) + if (!NativeContract.Ledger.Initialized(system.StoreView)) Persist(system.GenesisBlock); } diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 1bdfe5ed9c..ceded56843 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -8,6 +8,8 @@ using Neo.SmartContract; using Neo.VM; using System; +using System.Linq; +using static Neo.Ledger.Blockchain; namespace Neo { @@ -75,6 +77,7 @@ public NeoSystem(ProtocolSettings settings, string storageEngine = null, string this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); foreach (var plugin in Plugin.Plugins) plugin.OnPluginsLoaded(); + Blockchain.Ask(new FillMemoryPool { Transactions = Enumerable.Empty() }).Wait(); } private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) diff --git a/src/neo/SmartContract/ApplicationEngine.Binary.cs b/src/neo/SmartContract/ApplicationEngine.Binary.cs deleted file mode 100644 index cfc8197bd6..0000000000 --- a/src/neo/SmartContract/ApplicationEngine.Binary.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Neo.Cryptography; -using Neo.VM.Types; -using System; -using System.Globalization; -using System.Numerics; -using static System.Convert; - -namespace Neo.SmartContract -{ - partial class ApplicationEngine - { - public static readonly InteropDescriptor System_Binary_Serialize = Register("System.Binary.Serialize", nameof(BinarySerialize), 1 << 12, CallFlags.None); - public static readonly InteropDescriptor System_Binary_Deserialize = Register("System.Binary.Deserialize", nameof(BinaryDeserialize), 1 << 14, CallFlags.None); - public static readonly InteropDescriptor System_Binary_Base64Encode = Register("System.Binary.Base64Encode", nameof(Base64Encode), 1 << 12, CallFlags.None); - public static readonly InteropDescriptor System_Binary_Base64Decode = Register("System.Binary.Base64Decode", nameof(Base64Decode), 1 << 12, CallFlags.None); - public static readonly InteropDescriptor System_Binary_Base58Encode = Register("System.Binary.Base58Encode", nameof(Base58Encode), 1 << 12, CallFlags.None); - public static readonly InteropDescriptor System_Binary_Base58Decode = Register("System.Binary.Base58Decode", nameof(Base58Decode), 1 << 12, CallFlags.None); - public static readonly InteropDescriptor System_Binary_Itoa = Register("System.Binary.Itoa", nameof(Itoa), 1 << 12, CallFlags.None); - public static readonly InteropDescriptor System_Binary_Atoi = Register("System.Binary.Atoi", nameof(Atoi), 1 << 12, CallFlags.None); - - protected internal byte[] BinarySerialize(StackItem item) - { - return BinarySerializer.Serialize(item, Limits.MaxItemSize); - } - - protected internal StackItem BinaryDeserialize(byte[] data) - { - return BinarySerializer.Deserialize(data, Limits.MaxStackSize, Limits.MaxItemSize, ReferenceCounter); - } - - protected internal string Itoa(BigInteger value, int @base) - { - return @base switch - { - 10 => value.ToString(), - 16 => value.ToString("x"), - _ => throw new ArgumentOutOfRangeException(nameof(@base)) - }; - } - - protected internal BigInteger Atoi(string value, int @base) - { - return @base switch - { - 10 => BigInteger.Parse(value), - 16 => BigInteger.Parse(value, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture), - _ => throw new ArgumentOutOfRangeException(nameof(@base)) - }; - } - - protected internal string Base64Encode(byte[] data) - { - return ToBase64String(data); - } - - protected internal byte[] Base64Decode(string s) - { - return FromBase64String(s); - } - - protected internal string Base58Encode(byte[] data) - { - return Base58.Encode(data); - } - - protected internal byte[] Base58Decode(string s) - { - return Base58.Decode(s); - } - } -} diff --git a/src/neo/SmartContract/ApplicationEngine.Crypto.cs b/src/neo/SmartContract/ApplicationEngine.Crypto.cs index e3988534cf..89fd57b179 100644 --- a/src/neo/SmartContract/ApplicationEngine.Crypto.cs +++ b/src/neo/SmartContract/ApplicationEngine.Crypto.cs @@ -1,66 +1,22 @@ using Neo.Cryptography; using Neo.Cryptography.ECC; using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; -using Neo.VM.Types; using System; namespace Neo.SmartContract { partial class ApplicationEngine { - public const long ECDsaVerifyPrice = 1 << 15; + public const long CheckSigPrice = 1 << 15; - public static readonly InteropDescriptor Neo_Crypto_RIPEMD160 = Register("Neo.Crypto.RIPEMD160", nameof(RIPEMD160), 1 << 15, CallFlags.None); - public static readonly InteropDescriptor Neo_Crypto_SHA256 = Register("Neo.Crypto.SHA256", nameof(Sha256), 1 << 15, CallFlags.None); - public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256r1 = Register("Neo.Crypto.VerifyWithECDsaSecp256r1", nameof(VerifyWithECDsaSecp256r1), ECDsaVerifyPrice, CallFlags.None); - public static readonly InteropDescriptor Neo_Crypto_VerifyWithECDsaSecp256k1 = Register("Neo.Crypto.VerifyWithECDsaSecp256k1", nameof(VerifyWithECDsaSecp256k1), ECDsaVerifyPrice, CallFlags.None); - public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256r1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256r1", nameof(CheckMultisigWithECDsaSecp256r1), 0, CallFlags.None); - public static readonly InteropDescriptor Neo_Crypto_CheckMultisigWithECDsaSecp256k1 = Register("Neo.Crypto.CheckMultisigWithECDsaSecp256k1", nameof(CheckMultisigWithECDsaSecp256k1), 0, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_CheckSig = Register("Neo.Crypto.CheckSig", nameof(CheckSig), CheckSigPrice, CallFlags.None); + public static readonly InteropDescriptor Neo_Crypto_CheckMultisig = Register("Neo.Crypto.CheckMultisig", nameof(CheckMultisig), 0, CallFlags.None); - protected internal byte[] RIPEMD160(StackItem item) + protected internal bool CheckSig(byte[] pubkey, byte[] signature) { - ReadOnlySpan value = item switch - { - InteropInterface _interface => _interface.GetInterface().GetHashData(), - Null _ => ScriptContainer.GetHashData(), - _ => item.GetSpan() - }; - return value.RIPEMD160(); - } - - protected internal byte[] Sha256(StackItem item) - { - ReadOnlySpan value = item switch - { - InteropInterface _interface => _interface.GetInterface().GetHashData(), - Null _ => ScriptContainer.GetHashData(), - _ => item.GetSpan() - }; - return value.Sha256(); - } - - protected internal bool VerifyWithECDsaSecp256r1(StackItem item, byte[] pubkey, byte[] signature) - { - return VerifyWithECDsa(item, pubkey, signature, ECCurve.Secp256r1); - } - - protected internal bool VerifyWithECDsaSecp256k1(StackItem item, byte[] pubkey, byte[] signature) - { - return VerifyWithECDsa(item, pubkey, signature, ECCurve.Secp256k1); - } - - private bool VerifyWithECDsa(StackItem item, byte[] pubkey, byte[] signature, ECCurve curve) - { - ReadOnlySpan message = item switch - { - InteropInterface _interface => _interface.GetInterface().GetHashData(), - Null _ => ScriptContainer.GetHashData(), - _ => item.GetSpan() - }; try { - return Crypto.VerifySignature(message, signature, pubkey, curve); + return Crypto.VerifySignature(ScriptContainer.GetHashData(), signature, pubkey, ECCurve.Secp256r1); } catch (ArgumentException) { @@ -68,32 +24,17 @@ private bool VerifyWithECDsa(StackItem item, byte[] pubkey, byte[] signature, EC } } - protected internal bool CheckMultisigWithECDsaSecp256r1(StackItem item, byte[][] pubkeys, byte[][] signatures) - { - return CheckMultiSigWithECDsa(item, pubkeys, signatures, ECCurve.Secp256r1); - } - - protected internal bool CheckMultisigWithECDsaSecp256k1(StackItem item, byte[][] pubkeys, byte[][] signatures) - { - return CheckMultiSigWithECDsa(item, pubkeys, signatures, ECCurve.Secp256k1); - } - - private bool CheckMultiSigWithECDsa(StackItem item0, byte[][] pubkeys, byte[][] signatures, ECCurve curve) + protected internal bool CheckMultisig(byte[][] pubkeys, byte[][] signatures) { + byte[] message = ScriptContainer.GetHashData(); int m = signatures.Length, n = pubkeys.Length; - ReadOnlySpan message = item0 switch - { - InteropInterface _interface => _interface.GetInterface().GetHashData(), - Null _ => ScriptContainer.GetHashData(), - _ => item0.GetSpan() - }; if (n == 0 || m == 0 || m > n) throw new ArgumentException(); - AddGas(ECDsaVerifyPrice * n * exec_fee_factor); + AddGas(CheckSigPrice * n * exec_fee_factor); try { for (int i = 0, j = 0; i < m && j < n;) { - if (Crypto.VerifySignature(message, signatures[i], pubkeys[j], curve)) + if (Crypto.VerifySignature(message, signatures[i], pubkeys[j], ECCurve.Secp256r1)) i++; j++; if (m - i > n - j) diff --git a/src/neo/SmartContract/ApplicationEngine.Json.cs b/src/neo/SmartContract/ApplicationEngine.Json.cs deleted file mode 100644 index 40676ed580..0000000000 --- a/src/neo/SmartContract/ApplicationEngine.Json.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Neo.IO.Json; -using Neo.VM.Types; - -namespace Neo.SmartContract -{ - partial class ApplicationEngine - { - public static readonly InteropDescriptor System_Json_Serialize = Register("System.Json.Serialize", nameof(JsonSerialize), 1 << 12, CallFlags.None); - public static readonly InteropDescriptor System_Json_Deserialize = Register("System.Json.Deserialize", nameof(JsonDeserialize), 1 << 14, CallFlags.None); - - protected internal byte[] JsonSerialize(StackItem item) - { - return JsonSerializer.SerializeToByteArray(item, Limits.MaxItemSize); - } - - protected internal StackItem JsonDeserialize(byte[] json) - { - return JsonSerializer.Deserialize(JObject.Parse(json, 10), ReferenceCounter); - } - } -} diff --git a/src/neo/SmartContract/BinarySerializer.cs b/src/neo/SmartContract/BinarySerializer.cs index 806c59fd08..4b41984b6b 100644 --- a/src/neo/SmartContract/BinarySerializer.cs +++ b/src/neo/SmartContract/BinarySerializer.cs @@ -32,21 +32,21 @@ public ContainerPlaceholder(StackItemType type, int count) public override bool GetBoolean() => throw new NotSupportedException(); } - public static StackItem Deserialize(byte[] data, uint maxArraySize, uint maxItemSize, ReferenceCounter referenceCounter = null) + public static StackItem Deserialize(byte[] data, uint maxArraySize, ReferenceCounter referenceCounter = null) { using MemoryStream ms = new MemoryStream(data, false); using BinaryReader reader = new BinaryReader(ms); - return Deserialize(reader, maxArraySize, maxItemSize, referenceCounter); + return Deserialize(reader, maxArraySize, (uint)data.Length, referenceCounter); } - public static unsafe StackItem Deserialize(ReadOnlySpan data, uint maxArraySize, uint maxItemSize, ReferenceCounter referenceCounter = null) + public static unsafe StackItem Deserialize(ReadOnlySpan data, uint maxArraySize, ReferenceCounter referenceCounter = null) { if (data.IsEmpty) throw new FormatException(); fixed (byte* pointer = data) { using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, data.Length); using BinaryReader reader = new BinaryReader(ms); - return Deserialize(reader, maxArraySize, maxItemSize, referenceCounter); + return Deserialize(reader, maxArraySize, (uint)data.Length, referenceCounter); } } diff --git a/src/neo/SmartContract/Contract.cs b/src/neo/SmartContract/Contract.cs index 13246faf8b..3ffdb8b672 100644 --- a/src/neo/SmartContract/Contract.cs +++ b/src/neo/SmartContract/Contract.cs @@ -81,8 +81,7 @@ public static byte[] CreateMultiSigRedeemScript(int m, params ECPoint[] publicKe sb.EmitPush(publicKey.EncodePoint(true)); } sb.EmitPush(publicKeys.Length); - sb.Emit(OpCode.PUSHNULL); - sb.EmitSysCall(ApplicationEngine.Neo_Crypto_CheckMultisigWithECDsaSecp256r1); + sb.EmitSysCall(ApplicationEngine.Neo_Crypto_CheckMultisig); return sb.ToArray(); } } @@ -101,8 +100,7 @@ public static byte[] CreateSignatureRedeemScript(ECPoint publicKey) using (ScriptBuilder sb = new ScriptBuilder()) { sb.EmitPush(publicKey.EncodePoint(true)); - sb.Emit(OpCode.PUSHNULL); - sb.EmitSysCall(ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1); + sb.EmitSysCall(ApplicationEngine.Neo_Crypto_CheckSig); return sb.ToArray(); } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 7b2d55e562..8f282adaee 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -18,16 +18,20 @@ public static class Helper public static long SignatureContractCost() => ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * 2 + - ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + - ApplicationEngine.ECDsaVerifyPrice; + ApplicationEngine.CheckSigPrice; - public static long MultiSignatureContractCost(int m, int n) => - ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * (m + n) + - ApplicationEngine.OpCodePrices[OpCode.PUSHINT8] * 2 + - ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + - ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + - ApplicationEngine.ECDsaVerifyPrice * n; + public static long MultiSignatureContractCost(int m, int n) + { + long fee = ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * (m + n); + using (ScriptBuilder sb = new ScriptBuilder()) + fee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; + using (ScriptBuilder sb = new ScriptBuilder()) + fee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; + fee += ApplicationEngine.OpCodePrices[OpCode.SYSCALL]; + fee += ApplicationEngine.CheckSigPrice * n; + return fee; + } public static UInt160 GetContractHash(UInt160 sender, uint nefCheckSum, string name) { @@ -74,7 +78,7 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List { m = 0; n = 0; int i = 0; - if (script.Length < 43) return false; + if (script.Length < 42) return false; switch (script[i]) { case (byte)OpCode.PUSHINT8: @@ -119,22 +123,20 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List default: return false; } - if (script.Length != i + 6) return false; - if (script[i++] != (byte)OpCode.PUSHNULL) return false; + if (script.Length != i + 5) return false; if (script[i++] != (byte)OpCode.SYSCALL) return false; - if (BitConverter.ToUInt32(script, i) != ApplicationEngine.Neo_Crypto_CheckMultisigWithECDsaSecp256r1) + if (BitConverter.ToUInt32(script, i) != ApplicationEngine.Neo_Crypto_CheckMultisig) return false; return true; } public static bool IsSignatureContract(this byte[] script) { - if (script.Length != 41) return false; + if (script.Length != 40) return false; if (script[0] != (byte)OpCode.PUSHDATA1 || script[1] != 33 - || script[35] != (byte)OpCode.PUSHNULL - || script[36] != (byte)OpCode.SYSCALL - || BitConverter.ToUInt32(script, 37) != ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1) + || script[35] != (byte)OpCode.SYSCALL + || BitConverter.ToUInt32(script, 36) != ApplicationEngine.Neo_Crypto_CheckSig) return false; return true; } diff --git a/src/neo/SmartContract/Iterators/StorageIterator.cs b/src/neo/SmartContract/Iterators/StorageIterator.cs index 74dc5015cc..00b701fc5e 100644 --- a/src/neo/SmartContract/Iterators/StorageIterator.cs +++ b/src/neo/SmartContract/Iterators/StorageIterator.cs @@ -36,7 +36,7 @@ public StackItem Value() key = key[1..]; StackItem item = options.HasFlag(FindOptions.DeserializeValues) - ? BinarySerializer.Deserialize(value, 1024, (uint)value.Length, referenceCounter) + ? BinarySerializer.Deserialize(value, 1024, referenceCounter) : value; if (options.HasFlag(FindOptions.PickField0)) diff --git a/src/neo/SmartContract/Native/CryptoLib.cs b/src/neo/SmartContract/Native/CryptoLib.cs new file mode 100644 index 0000000000..9295104da3 --- /dev/null +++ b/src/neo/SmartContract/Native/CryptoLib.cs @@ -0,0 +1,43 @@ +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using System; +using System.Collections.Generic; + +namespace Neo.SmartContract.Native +{ + public sealed class CryptoLib : NativeContract + { + private static readonly Dictionary curves = new Dictionary + { + [NamedCurve.secp256k1] = ECCurve.Secp256k1, + [NamedCurve.secp256r1] = ECCurve.Secp256r1 + }; + + internal CryptoLib() { } + + [ContractMethod(1 << 15, CallFlags.None, Name = "ripemd160")] + public static byte[] RIPEMD160(byte[] data) + { + return data.RIPEMD160(); + } + + [ContractMethod(1 << 15, CallFlags.None)] + public static byte[] Sha256(byte[] data) + { + return data.Sha256(); + } + + [ContractMethod(1 << 15, CallFlags.None)] + public static bool VerifyWithECDsa(byte[] message, byte[] pubkey, byte[] signature, NamedCurve curve) + { + try + { + return Crypto.VerifySignature(message, signature, pubkey, curves[curve]); + } + catch (ArgumentException) + { + return false; + } + } + } +} diff --git a/src/neo/SmartContract/Native/NamedCurve.cs b/src/neo/SmartContract/Native/NamedCurve.cs new file mode 100644 index 0000000000..f7b149856e --- /dev/null +++ b/src/neo/SmartContract/Native/NamedCurve.cs @@ -0,0 +1,14 @@ +namespace Neo.SmartContract.Native +{ + /// + /// RFC 4492 + /// + /// + /// https://tools.ietf.org/html/rfc4492#section-5.1.1 + /// + public enum NamedCurve : byte + { + secp256k1 = 22, + secp256r1 = 23 + } +} diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 16e9448150..487de86bd7 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -18,6 +18,8 @@ public abstract class NativeContract #region Named Native Contracts public static ContractManagement ContractManagement { get; } = new ContractManagement(); + public static StdLib StdLib { get; } = new StdLib(); + public static CryptoLib CryptoLib { get; } = new CryptoLib(); public static LedgerContract Ledger { get; } = new LedgerContract(); public static NeoToken NEO { get; } = new NeoToken(); public static GasToken GAS { get; } = new GasToken(); @@ -37,7 +39,7 @@ public abstract class NativeContract protected NativeContract() { List descriptors = new List(); - foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) + foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) { ContractMethodAttribute attribute = member.GetCustomAttribute(); if (attribute is null) continue; diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index 8bb656540c..9ec83975e6 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -90,7 +90,7 @@ private void Finish(ApplicationEngine engine) OracleRequest request = GetRequest(engine.Snapshot, response.Id); if (request == null) throw new ArgumentException("Oracle request was not found"); engine.SendNotification(Hash, "OracleResponse", new VM.Types.Array { response.Id, request.OriginalTxid.ToArray() }); - StackItem userData = BinarySerializer.Deserialize(request.UserData, engine.Limits.MaxStackSize, engine.Limits.MaxItemSize, engine.ReferenceCounter); + StackItem userData = BinarySerializer.Deserialize(request.UserData, engine.Limits.MaxStackSize, engine.ReferenceCounter); engine.CallFromNativeContract(Hash, request.CallbackContract, request.CallbackMethod, request.Url, userData, (int)response.Code, response.Result); } diff --git a/src/neo/SmartContract/Native/StdLib.cs b/src/neo/SmartContract/Native/StdLib.cs new file mode 100644 index 0000000000..505a132601 --- /dev/null +++ b/src/neo/SmartContract/Native/StdLib.cs @@ -0,0 +1,86 @@ +#pragma warning disable IDE0051 + +using Neo.Cryptography; +using Neo.IO.Json; +using Neo.VM.Types; +using System; +using System.Globalization; +using System.Numerics; + +namespace Neo.SmartContract.Native +{ + public sealed class StdLib : NativeContract + { + internal StdLib() { } + + [ContractMethod(1 << 12, CallFlags.None)] + private static byte[] Serialize(ApplicationEngine engine, StackItem item) + { + return BinarySerializer.Serialize(item, engine.Limits.MaxItemSize); + } + + [ContractMethod(1 << 14, CallFlags.None)] + private static StackItem Deserialize(ApplicationEngine engine, byte[] data) + { + return BinarySerializer.Deserialize(data, engine.Limits.MaxStackSize, engine.ReferenceCounter); + } + + [ContractMethod(1 << 12, CallFlags.None)] + private static byte[] JsonSerialize(ApplicationEngine engine, StackItem item) + { + return JsonSerializer.SerializeToByteArray(item, engine.Limits.MaxItemSize); + } + + [ContractMethod(1 << 14, CallFlags.None)] + private static StackItem JsonDeserialize(ApplicationEngine engine, byte[] json) + { + return JsonSerializer.Deserialize(JObject.Parse(json, 10), engine.ReferenceCounter); + } + + [ContractMethod(1 << 12, CallFlags.None)] + public static string Itoa(BigInteger value, int @base) + { + return @base switch + { + 10 => value.ToString(), + 16 => value.ToString("x"), + _ => throw new ArgumentOutOfRangeException(nameof(@base)) + }; + } + + [ContractMethod(1 << 12, CallFlags.None)] + public static BigInteger Atoi(string value, int @base) + { + return @base switch + { + 10 => BigInteger.Parse(value), + 16 => BigInteger.Parse(value, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture), + _ => throw new ArgumentOutOfRangeException(nameof(@base)) + }; + } + + [ContractMethod(1 << 12, CallFlags.None)] + public static string Base64Encode(byte[] data) + { + return Convert.ToBase64String(data); + } + + [ContractMethod(1 << 12, CallFlags.None)] + public static byte[] Base64Decode(string s) + { + return Convert.FromBase64String(s); + } + + [ContractMethod(1 << 12, CallFlags.None)] + public static string Base58Encode(byte[] data) + { + return Base58.Encode(data); + } + + [ContractMethod(1 << 12, CallFlags.None)] + public static byte[] Base58Decode(string s) + { + return Base58.Decode(s); + } + } +} diff --git a/src/neo/SmartContract/StorageItem.cs b/src/neo/SmartContract/StorageItem.cs index ff3d0d5bd1..9da764cd9a 100644 --- a/src/neo/SmartContract/StorageItem.cs +++ b/src/neo/SmartContract/StorageItem.cs @@ -86,7 +86,7 @@ public void FromReplica(StorageItem replica) if (cache is null) { var interoperable = new T(); - interoperable.FromStackItem(BinarySerializer.Deserialize(value, ExecutionEngineLimits.Default.MaxStackSize, ExecutionEngineLimits.Default.MaxItemSize)); + interoperable.FromStackItem(BinarySerializer.Deserialize(value, ExecutionEngineLimits.Default.MaxStackSize)); cache = interoperable; } value = null; diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 893a8e46bf..5d5b0ea501 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -13,6 +13,7 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; +using static Neo.SmartContract.Helper; using static Neo.Wallets.Helper; using ECPoint = Neo.Cryptography.ECC.ECPoint; @@ -404,19 +405,13 @@ public long CalculateNetworkFee(DataCache snapshot, Transaction tx) else if (witness_script.IsSignatureContract()) { size += 67 + witness_script.GetVarSize(); - networkFee += exec_fee_factor * (ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + ApplicationEngine.ECDsaVerifyPrice); + networkFee += exec_fee_factor * SignatureContractCost(); } else if (witness_script.IsMultiSigContract(out int m, out int n)) { int size_inv = 66 * m; size += IO.Helper.GetVarSize(size_inv) + size_inv + witness_script.GetVarSize(); - networkFee += exec_fee_factor * ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * m; - using (ScriptBuilder sb = new ScriptBuilder()) - networkFee += exec_fee_factor * ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; - networkFee += exec_fee_factor * ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * n; - using (ScriptBuilder sb = new ScriptBuilder()) - networkFee += exec_fee_factor * ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; - networkFee += exec_fee_factor * (ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + ApplicationEngine.ECDsaVerifyPrice * n); + networkFee += exec_fee_factor * MultiSignatureContractCost(m, n); } else { diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 87b0175211..0171922b7a 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -175,9 +175,9 @@ public void FeeIsMultiSigContract() } var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); - Assert.AreEqual(1967130, verificationGas); - Assert.AreEqual(349000, sizeGas); - Assert.AreEqual(2316130, tx.NetworkFee); + Assert.AreEqual(1967100, verificationGas); + Assert.AreEqual(348000, sizeGas); + Assert.AreEqual(2315100, tx.NetworkFee); } } @@ -218,7 +218,7 @@ public void FeeIsSignatureContractDetailed() Assert.IsNull(tx.Witnesses); // check pre-computed network fee (already guessing signature sizes) - tx.NetworkFee.Should().Be(1229550L); + tx.NetworkFee.Should().Be(1228520L); // ---- // Sign @@ -259,7 +259,7 @@ public void FeeIsSignatureContractDetailed() // ------------------ // check tx_size cost // ------------------ - Assert.AreEqual(246, tx.Size); + Assert.AreEqual(245, tx.Size); // will verify tx size, step by step @@ -274,16 +274,16 @@ public void FeeIsSignatureContractDetailed() // Part III Assert.AreEqual(88, tx.Script.GetVarSize()); // Part IV - Assert.AreEqual(110, tx.Witnesses.GetVarSize()); + Assert.AreEqual(109, tx.Witnesses.GetVarSize()); // I + II + III + IV - Assert.AreEqual(25 + 22 + 1 + 88 + 110, tx.Size); + Assert.AreEqual(25 + 22 + 1 + 88 + 109, tx.Size); Assert.AreEqual(1000, NativeContract.Policy.GetFeePerByte(snapshot)); var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check: verification_cost and tx_size - Assert.AreEqual(246000, sizeGas); - Assert.AreEqual(983550, verificationGas); + Assert.AreEqual(245000, sizeGas); + Assert.AreEqual(983520, verificationGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); @@ -370,7 +370,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(1229550, verificationGas + sizeGas); + Assert.AreEqual(1228520, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -457,7 +457,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(1250550, verificationGas + sizeGas); + Assert.AreEqual(1249520, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -547,7 +547,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(1250550, verificationGas + sizeGas); + Assert.AreEqual(1249520, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -689,7 +689,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(1270550, verificationGas + sizeGas); + Assert.AreEqual(1269520, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } @@ -1038,7 +1038,7 @@ public void FeeIsSignatureContract_TestScope_FeeOnly_Default() // get sizeGas var sizeGas = tx.Size * NativeContract.Policy.GetFeePerByte(snapshot); // final check on sum: verification_cost + tx_size - Assert.AreEqual(1229550, verificationGas + sizeGas); + Assert.AreEqual(1228520, verificationGas + sizeGas); // final assert Assert.AreEqual(tx.NetworkFee, verificationGas + sizeGas); } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index 2b10047615..e562e73478 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -87,11 +87,9 @@ public void MaxSize_OK() // Check max size - witness.Size.Should().Be(1024); + witness.Size.Should().Be(1023); witness.InvocationScript.GetVarSize().Should().Be(663); - witness.VerificationScript.GetVarSize().Should().Be(361); - - Assert.IsTrue(witness.Size <= 1024); + witness.VerificationScript.GetVarSize().Should().Be(360); var copy = witness.ToArray().AsSerializable(); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 0b5983443b..3208bec053 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -1045,7 +1045,7 @@ internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackab internal static void CheckBalance(byte[] account, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint voteTo) { - var st = (VM.Types.Struct)BinarySerializer.Deserialize(trackable.Item.Value, 16, 32); + var st = (VM.Types.Struct)BinarySerializer.Deserialize(trackable.Item.Value, 16); st.Count.Should().Be(3); st.Select(u => u.GetType()).ToArray().Should().BeEquivalentTo(new Type[] { typeof(VM.Types.Integer), typeof(VM.Types.Integer), typeof(VM.Types.ByteString) }); // Balance diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs new file mode 100644 index 0000000000..7b2b26f835 --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -0,0 +1,212 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.VM.Types; +using System.Collections.Generic; +using System.Numerics; + +namespace Neo.UnitTests.SmartContract.Native +{ + [TestClass] + public class UT_StdLib + { + [TestInitialize] + public void Init() + { + TestBlockchain.InitializeMockNeoSystem(); + } + + [TestMethod] + public void TestBinary() + { + var data = System.Array.Empty(); + + CollectionAssert.AreEqual(data, StdLib.Base64Decode(StdLib.Base64Encode(data))); + CollectionAssert.AreEqual(data, StdLib.Base58Decode(StdLib.Base58Encode(data))); + + data = new byte[] { 1, 2, 3 }; + + CollectionAssert.AreEqual(data, StdLib.Base64Decode(StdLib.Base64Encode(data))); + CollectionAssert.AreEqual(data, StdLib.Base58Decode(StdLib.Base58Encode(data))); + Assert.AreEqual("AQIDBA==", StdLib.Base64Encode(new byte[] { 1, 2, 3, 4 })); + Assert.AreEqual("2VfUX", StdLib.Base58Encode(new byte[] { 1, 2, 3, 4 })); + } + + [TestMethod] + public void TestItoaAtoi() + { + Assert.AreEqual("1", StdLib.Itoa(BigInteger.One, 10)); + Assert.AreEqual("1", StdLib.Itoa(BigInteger.One, 16)); + Assert.AreEqual("-1", StdLib.Itoa(BigInteger.MinusOne, 10)); + Assert.AreEqual("f", StdLib.Itoa(BigInteger.MinusOne, 16)); + Assert.AreEqual("3b9aca00", StdLib.Itoa(1_000_000_000, 16)); + Assert.AreEqual(-1, StdLib.Atoi("-1", 10)); + Assert.AreEqual(1, StdLib.Atoi("+1", 10)); + Assert.AreEqual(-1, StdLib.Atoi("ff", 16)); + Assert.AreEqual(-1, StdLib.Atoi("FF", 16)); + Assert.ThrowsException(() => StdLib.Atoi("a", 10)); + Assert.ThrowsException(() => StdLib.Atoi("g", 16)); + Assert.ThrowsException(() => StdLib.Atoi("a", 11)); + } + + [TestMethod] + public void Json_Deserialize() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + + // Good + + using (var script = new ScriptBuilder()) + { + script.EmitDynamicCall(NativeContract.StdLib.Hash, "jsonDeserialize", "123"); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "jsonDeserialize", "null"); + + using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(2, engine.ResultStack.Count); + + engine.ResultStack.Pop(); + Assert.IsTrue(engine.ResultStack.Pop().GetInteger() == 123); + } + } + + // Error 1 - Wrong Json + + using (var script = new ScriptBuilder()) + { + script.EmitDynamicCall(NativeContract.StdLib.Hash, "jsonDeserialize", "***"); + + using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.FAULT); + Assert.AreEqual(0, engine.ResultStack.Count); + } + } + + // Error 2 - No decimals + + using (var script = new ScriptBuilder()) + { + script.EmitDynamicCall(NativeContract.StdLib.Hash, "jsonDeserialize", "123.45"); + + using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.FAULT); + Assert.AreEqual(0, engine.ResultStack.Count); + } + } + } + + [TestMethod] + public void Json_Serialize() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + + // Good + + using (var script = new ScriptBuilder()) + { + script.EmitDynamicCall(NativeContract.StdLib.Hash, "jsonSerialize", 5); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "jsonSerialize", true); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "jsonSerialize", "test"); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "jsonSerialize", new object[] { null }); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "jsonSerialize", new ContractParameter(ContractParameterType.Map) + { + Value = new List>() { + { new KeyValuePair( + new ContractParameter(ContractParameterType.String){ Value="key" }, + new ContractParameter(ContractParameterType.String){ Value= "value" }) + } + } + }); + + using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(5, engine.ResultStack.Count); + + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "{\"key\":\"value\"}"); + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "null"); + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "\"test\""); + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "1"); + Assert.IsTrue(engine.ResultStack.Pop().GetString() == "5"); + } + } + + // Error + + using (var script = new ScriptBuilder()) + { + script.EmitDynamicCall(NativeContract.StdLib.Hash, "jsonSerialize"); + + using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.FAULT); + Assert.AreEqual(0, engine.ResultStack.Count); + } + } + } + + [TestMethod] + public void TestRuntime_Serialize() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + + // Good + + using (var script = new ScriptBuilder()) + { + script.EmitDynamicCall(NativeContract.StdLib.Hash, "serialize", 100); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "serialize", "test"); + + using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(2, engine.ResultStack.Count); + + Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(), "280474657374"); + Assert.AreEqual(engine.ResultStack.Pop().GetSpan().ToHexString(), "210164"); + } + } + } + + [TestMethod] + public void TestRuntime_Deserialize() + { + var snapshot = TestBlockchain.GetTestSnapshot(); + + // Good + + using (var script = new ScriptBuilder()) + { + script.EmitDynamicCall(NativeContract.StdLib.Hash, "deserialize", "280474657374".HexToBytes()); + script.EmitDynamicCall(NativeContract.StdLib.Hash, "deserialize", "210164".HexToBytes()); + + using (var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings)) + { + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(engine.Execute(), VMState.HALT); + Assert.AreEqual(2, engine.ResultStack.Count); + + Assert.AreEqual(engine.ResultStack.Pop().GetInteger(), 100); + Assert.AreEqual(engine.ResultStack.Pop().GetString(), "test"); + } + } + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index bcb307a252..5e850c81a8 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -2,7 +2,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.SmartContract; using Neo.VM.Types; -using System.Numerics; namespace Neo.UnitTests.SmartContract { @@ -17,43 +16,6 @@ public void TestSetup() TestBlockchain.InitializeMockNeoSystem(); } - [TestMethod] - public void TestBinary() - { - var snapshot = TestBlockchain.GetTestSnapshot(); - using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); - - var data = new byte[0]; - CollectionAssert.AreEqual(data, engine.Base64Decode(engine.Base64Encode(data))); - - CollectionAssert.AreEqual(data, engine.Base58Decode(engine.Base58Encode(data))); - - data = new byte[] { 1, 2, 3 }; - CollectionAssert.AreEqual(data, engine.Base64Decode(engine.Base64Encode(data))); - - CollectionAssert.AreEqual(data, engine.Base58Decode(engine.Base58Encode(data))); - - Assert.AreEqual("AQIDBA==", engine.Base64Encode(new byte[] { 1, 2, 3, 4 })); - - Assert.AreEqual("2VfUX", engine.Base58Encode(new byte[] { 1, 2, 3, 4 })); - } - - [TestMethod] - public void TestItoaAtoi() - { - using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestBlockchain.TheNeoSystem.Settings); - - Assert.AreEqual("1", engine.Itoa(BigInteger.One, 10)); - Assert.AreEqual("1", engine.Itoa(BigInteger.One, 16)); - Assert.AreEqual("-1", engine.Itoa(BigInteger.MinusOne, 10)); - Assert.AreEqual("f", engine.Itoa(BigInteger.MinusOne, 16)); - Assert.AreEqual(-1, engine.Atoi("-1", 10)); - Assert.AreEqual(1, engine.Atoi("+1", 10)); - Assert.AreEqual(-1, engine.Atoi("ff", 16)); - Assert.ThrowsException(() => engine.Atoi("a", 10)); - Assert.ThrowsException(() => engine.Atoi("a", 11)); - } - [TestMethod] public void TestNotify() { diff --git a/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs b/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs index e5c0b0063d..d7749f499e 100644 --- a/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs +++ b/tests/neo.UnitTests/SmartContract/UT_BinarySerializer.cs @@ -78,41 +78,41 @@ public void TestDeserializeStackItem() { StackItem stackItem1 = new ByteString(new byte[5]); byte[] byteArray1 = BinarySerializer.Serialize(stackItem1, MaxItemSize); - StackItem result1 = BinarySerializer.Deserialize(byteArray1, 2048, (uint)byteArray1.Length); + StackItem result1 = BinarySerializer.Deserialize(byteArray1, 2048); Assert.AreEqual(stackItem1, result1); StackItem stackItem2 = new VM.Types.Boolean(true); byte[] byteArray2 = BinarySerializer.Serialize(stackItem2, MaxItemSize); - StackItem result2 = BinarySerializer.Deserialize(byteArray2, 2048, (uint)byteArray2.Length); + StackItem result2 = BinarySerializer.Deserialize(byteArray2, 2048); Assert.AreEqual(stackItem2, result2); StackItem stackItem3 = new Integer(1); byte[] byteArray3 = BinarySerializer.Serialize(stackItem3, MaxItemSize); - StackItem result3 = BinarySerializer.Deserialize(byteArray3, 2048, (uint)byteArray3.Length); + StackItem result3 = BinarySerializer.Deserialize(byteArray3, 2048); Assert.AreEqual(stackItem3, result3); byte[] byteArray4 = BinarySerializer.Serialize(1, MaxItemSize); byteArray4[0] = 0x40; - Action action4 = () => BinarySerializer.Deserialize(byteArray4, 2048, (uint)byteArray4.Length); + Action action4 = () => BinarySerializer.Deserialize(byteArray4, 2048); action4.Should().Throw(); List list5 = new List { 1 }; StackItem stackItem52 = new VM.Types.Array(list5); byte[] byteArray5 = BinarySerializer.Serialize(stackItem52, MaxItemSize); - StackItem result5 = BinarySerializer.Deserialize(byteArray5, 2048, (uint)byteArray5.Length); + StackItem result5 = BinarySerializer.Deserialize(byteArray5, 2048); Assert.AreEqual(((VM.Types.Array)stackItem52).Count, ((VM.Types.Array)result5).Count); Assert.AreEqual(((VM.Types.Array)stackItem52).GetEnumerator().Current, ((VM.Types.Array)result5).GetEnumerator().Current); List list6 = new List { 1 }; StackItem stackItem62 = new Struct(list6); byte[] byteArray6 = BinarySerializer.Serialize(stackItem62, MaxItemSize); - StackItem result6 = BinarySerializer.Deserialize(byteArray6, 2048, (uint)byteArray6.Length); + StackItem result6 = BinarySerializer.Deserialize(byteArray6, 2048); Assert.AreEqual(((Struct)stackItem62).Count, ((Struct)result6).Count); Assert.AreEqual(((Struct)stackItem62).GetEnumerator().Current, ((Struct)result6).GetEnumerator().Current); StackItem stackItem72 = new Map { [2] = 1 }; byte[] byteArray7 = BinarySerializer.Serialize(stackItem72, MaxItemSize); - StackItem result7 = BinarySerializer.Deserialize(byteArray7, 2048, (uint)byteArray7.Length); + StackItem result7 = BinarySerializer.Deserialize(byteArray7, 2048); Assert.AreEqual(((Map)stackItem72).Count, ((Map)result7).Count); CollectionAssert.AreEqual(((Map)stackItem72).Keys.ToArray(), ((Map)result7).Keys.ToArray()); CollectionAssert.AreEqual(((Map)stackItem72).Values.ToArray(), ((Map)result7).Values.ToArray()); diff --git a/tests/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/neo.UnitTests/SmartContract/UT_Contract.cs index ce3a9d729f..94f929e0ac 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Contract.cs @@ -22,13 +22,12 @@ public void TestGetAddress() rng.GetBytes(privateKey); KeyPair key = new KeyPair(privateKey); Contract contract = Contract.CreateSignatureContract(key.PublicKey); - byte[] expectedArray = new byte[41]; + byte[] expectedArray = new byte[40]; expectedArray[0] = (byte)OpCode.PUSHDATA1; expectedArray[1] = 0x21; Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); - expectedArray[35] = (byte)OpCode.PUSHNULL; - expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); + expectedArray[35] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckSig), 0, expectedArray, 36, 4); Assert.AreEqual(expectedArray.ToScriptHash().ToAddress(), contract.Address); } @@ -40,13 +39,12 @@ public void TestGetScriptHash() rng.GetBytes(privateKey); KeyPair key = new KeyPair(privateKey); Contract contract = Contract.CreateSignatureContract(key.PublicKey); - byte[] expectedArray = new byte[41]; + byte[] expectedArray = new byte[40]; expectedArray[0] = (byte)OpCode.PUSHDATA1; expectedArray[1] = 0x21; Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); - expectedArray[35] = (byte)OpCode.PUSHNULL; - expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); + expectedArray[35] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckSig), 0, expectedArray, 36, 4); Assert.AreEqual(expectedArray.ToScriptHash(), contract.ScriptHash); } @@ -77,7 +75,7 @@ public void TestCreateMultiSigContract() publicKeys[1] = key2.PublicKey; publicKeys = publicKeys.OrderBy(p => p).ToArray(); Contract contract = Contract.CreateMultiSigContract(2, publicKeys); - byte[] expectedArray = new byte[78]; + byte[] expectedArray = new byte[77]; expectedArray[0] = (byte)OpCode.PUSH2; expectedArray[1] = (byte)OpCode.PUSHDATA1; expectedArray[2] = 0x21; @@ -86,9 +84,8 @@ public void TestCreateMultiSigContract() expectedArray[37] = 0x21; Array.Copy(publicKeys[1].EncodePoint(true), 0, expectedArray, 38, 33); expectedArray[71] = (byte)OpCode.PUSH2; - expectedArray[72] = (byte)OpCode.PUSHNULL; - expectedArray[73] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckMultisigWithECDsaSecp256r1), 0, expectedArray, 74, 4); + expectedArray[72] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckMultisig), 0, expectedArray, 73, 4); CollectionAssert.AreEqual(expectedArray, contract.Script); Assert.AreEqual(2, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); @@ -113,7 +110,7 @@ public void TestCreateMultiSigRedeemScript() Action action = () => Contract.CreateMultiSigRedeemScript(0, publicKeys); action.Should().Throw(); byte[] script = Contract.CreateMultiSigRedeemScript(2, publicKeys); - byte[] expectedArray = new byte[78]; + byte[] expectedArray = new byte[77]; expectedArray[0] = (byte)OpCode.PUSH2; expectedArray[1] = (byte)OpCode.PUSHDATA1; expectedArray[2] = 0x21; @@ -122,9 +119,8 @@ public void TestCreateMultiSigRedeemScript() expectedArray[37] = 0x21; Array.Copy(publicKeys[1].EncodePoint(true), 0, expectedArray, 38, 33); expectedArray[71] = (byte)OpCode.PUSH2; - expectedArray[72] = (byte)OpCode.PUSHNULL; - expectedArray[73] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckMultisigWithECDsaSecp256r1), 0, expectedArray, 74, 4); + expectedArray[72] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckMultisig), 0, expectedArray, 73, 4); CollectionAssert.AreEqual(expectedArray, script); } @@ -136,13 +132,12 @@ public void TestCreateSignatureContract() rng.GetBytes(privateKey); KeyPair key = new KeyPair(privateKey); Contract contract = Contract.CreateSignatureContract(key.PublicKey); - byte[] expectedArray = new byte[41]; + byte[] expectedArray = new byte[40]; expectedArray[0] = (byte)OpCode.PUSHDATA1; expectedArray[1] = 0x21; Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); - expectedArray[35] = (byte)OpCode.PUSHNULL; - expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); + expectedArray[35] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckSig), 0, expectedArray, 36, 4); CollectionAssert.AreEqual(expectedArray, contract.Script); Assert.AreEqual(1, contract.ParameterList.Length); Assert.AreEqual(ContractParameterType.Signature, contract.ParameterList[0]); @@ -156,13 +151,12 @@ public void TestCreateSignatureRedeemScript() rng.GetBytes(privateKey); KeyPair key = new KeyPair(privateKey); byte[] script = Contract.CreateSignatureRedeemScript(key.PublicKey); - byte[] expectedArray = new byte[41]; + byte[] expectedArray = new byte[40]; expectedArray[0] = (byte)OpCode.PUSHDATA1; expectedArray[1] = 0x21; Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); - expectedArray[35] = (byte)OpCode.PUSHNULL; - expectedArray[36] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_VerifyWithECDsaSecp256r1), 0, expectedArray, 37, 4); + expectedArray[35] = (byte)OpCode.SYSCALL; + Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckSig), 0, expectedArray, 36, 4); CollectionAssert.AreEqual(expectedArray, script); } @@ -176,7 +170,7 @@ public void TestSignatureRedeemScriptFee() byte[] verification = Contract.CreateSignatureRedeemScript(key.PublicKey); byte[] invocation = new ScriptBuilder().EmitPush(UInt160.Zero).ToArray(); - var fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * 2 + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.ECDsaVerifyPrice); + var fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * 2 + ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.CheckSigPrice); using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null, settings: TestBlockchain.TheNeoSystem.Settings)) { @@ -204,7 +198,7 @@ public void TestCreateMultiSigRedeemScriptFee() byte[] verification = Contract.CreateMultiSigRedeemScript(2, publicKeys); byte[] invocation = new ScriptBuilder().EmitPush(UInt160.Zero).EmitPush(UInt160.Zero).ToArray(); - long fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * (2 + 2) + ApplicationEngine.OpCodePrices[OpCode.PUSHINT8] * 2 + ApplicationEngine.OpCodePrices[OpCode.PUSHNULL] + ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.ECDsaVerifyPrice * 2); + long fee = PolicyContract.DefaultExecFeeFactor * (ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * (2 + 2) + ApplicationEngine.OpCodePrices[OpCode.PUSHINT8] * 2 + ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.CheckSigPrice * 2); using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, new Transaction { Signers = Array.Empty(), Attributes = Array.Empty() }, null, settings: TestBlockchain.TheNeoSystem.Settings)) { diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index fa3aca53df..1a99c4341d 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -75,7 +75,7 @@ public void TestAdd() var context1 = new ContractParametersContext(snapshot, tx); context1.Add(contract, 0, new byte[] { 0x01 }).Should().BeFalse(); - tx = TestUtils.GetTransaction(UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0")); + tx = TestUtils.GetTransaction(UInt160.Parse("0x4bc1b25796f4a13fa3cc7538168d86f7d3bc5356")); var context2 = new ContractParametersContext(snapshot, tx); context2.Add(contract, 0, new byte[] { 0x01 }).Should().BeTrue(); //test repeatlly createItem @@ -86,7 +86,7 @@ public void TestAdd() public void TestGetParameter() { var snapshot = TestBlockchain.GetTestSnapshot(); - Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0")); + Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x4bc1b25796f4a13fa3cc7538168d86f7d3bc5356")); var context = new ContractParametersContext(snapshot, tx); context.GetParameter(tx.Sender, 0).Should().BeNull(); @@ -99,7 +99,7 @@ public void TestGetParameter() public void TestGetWitnesses() { var snapshot = TestBlockchain.GetTestSnapshot(); - Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0")); + Transaction tx = TestUtils.GetTransaction(UInt160.Parse("0x4bc1b25796f4a13fa3cc7538168d86f7d3bc5356")); var context = new ContractParametersContext(snapshot, tx); context.Add(contract, 0, new byte[] { 0x01 }); Witness[] witnesses = context.GetWitnesses(); @@ -112,7 +112,7 @@ public void TestGetWitnesses() public void TestAddSignature() { var snapshot = TestBlockchain.GetTestSnapshot(); - var singleSender = UInt160.Parse("0x282646ee0afa5508bb999318f35074b84a17c9f0"); + var singleSender = UInt160.Parse("0x4bc1b25796f4a13fa3cc7538168d86f7d3bc5356"); Transaction tx = TestUtils.GetTransaction(singleSender); //singleSign @@ -142,7 +142,7 @@ public void TestAddSignature() key.PublicKey, key2.PublicKey }); - var multiSender = UInt160.Parse("0x3593816cc1085a6328fea2b899c24d78cd0ba372"); + var multiSender = UInt160.Parse("0x7eae53e544e3c1507bfa7892d4793b8784be4c31"); tx = TestUtils.GetTransaction(multiSender); context = new ContractParametersContext(snapshot, tx); context.AddSignature(multiSignContract, key.PublicKey, new byte[] { 0x01 }).Should().BeTrue(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 4ccc9346c6..ebd197d62e 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -15,7 +15,6 @@ using Neo.Wallets; using System; using System.Linq; -using System.Text; using VMArray = Neo.VM.Types.Array; namespace Neo.UnitTests.SmartContract @@ -33,8 +32,8 @@ public void TestCheckSig() KeyPair keyPair = new KeyPair(privateKey); ECPoint pubkey = keyPair.PublicKey; byte[] signature = Crypto.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); - engine.VerifyWithECDsaSecp256r1(StackItem.Null, pubkey.EncodePoint(false), signature).Should().BeTrue(); - engine.VerifyWithECDsaSecp256r1(StackItem.Null, new byte[70], signature).Should().BeFalse(); + engine.CheckSig(pubkey.EncodePoint(false), signature).Should().BeTrue(); + engine.CheckSig(new byte[70], signature).Should().BeFalse(); } [TestMethod] @@ -66,10 +65,10 @@ public void TestCrypto_CheckMultiSig() signature1, signature2 }; - engine.CheckMultisigWithECDsaSecp256r1(StackItem.Null, pubkeys, signatures).Should().BeTrue(); + engine.CheckMultisig(pubkeys, signatures).Should().BeTrue(); pubkeys = new byte[0][]; - Assert.ThrowsException(() => engine.CheckMultisigWithECDsaSecp256r1(StackItem.Null, pubkeys, signatures)); + Assert.ThrowsException(() => engine.CheckMultisig(pubkeys, signatures)); pubkeys = new[] { @@ -77,7 +76,7 @@ public void TestCrypto_CheckMultiSig() pubkey2.EncodePoint(false) }; signatures = new byte[0][]; - Assert.ThrowsException(() => engine.CheckMultisigWithECDsaSecp256r1(StackItem.Null, pubkeys, signatures)); + Assert.ThrowsException(() => engine.CheckMultisig(pubkeys, signatures)); pubkeys = new[] { @@ -89,7 +88,7 @@ public void TestCrypto_CheckMultiSig() signature1, new byte[64] }; - engine.CheckMultisigWithECDsaSecp256r1(StackItem.Null, pubkeys, signatures).Should().BeFalse(); + engine.CheckMultisig(pubkeys, signatures).Should().BeFalse(); pubkeys = new[] { @@ -101,7 +100,7 @@ public void TestCrypto_CheckMultiSig() signature1, signature2 }; - engine.CheckMultisigWithECDsaSecp256r1(StackItem.Null, pubkeys, signatures).Should().BeFalse(); + engine.CheckMultisig(pubkeys, signatures).Should().BeFalse(); } [TestMethod] @@ -332,17 +331,5 @@ public void TestIterator_Create() @struct[0].GetInteger().Should().Be(1); @struct[1].GetInteger().Should().Be(2); } - - [TestMethod] - public void TestJson_Deserialize() - { - GetEngine().JsonDeserialize(new byte[] { (byte)'1' }).GetInteger().Should().Be(1); - } - - [TestMethod] - public void TestJson_Serialize() - { - Encoding.UTF8.GetString(GetEngine().JsonSerialize(1)).Should().Be("1"); - } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 253840b46a..ed001dd241 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -292,29 +292,6 @@ public void TestRuntime_GetTime() engine.GetTime().Should().Be(block.Timestamp); } - [TestMethod] - public void TestRuntime_Serialize() - { - var engine = GetEngine(); - engine.BinarySerialize(100).ToHexString().Should().Be(new byte[] { 0x21, 0x01, 0x64 }.ToHexString()); - - //Larger than MaxItemSize - Assert.ThrowsException(() => engine.BinarySerialize(new byte[1024 * 1024 * 2])); - - //NotSupportedException - Assert.ThrowsException(() => engine.BinarySerialize(new InteropInterface(new object()))); - } - - [TestMethod] - public void TestRuntime_Deserialize() - { - var engine = GetEngine(); - engine.BinaryDeserialize(engine.BinarySerialize(100)).GetInteger().Should().Be(100); - - //FormatException - Assert.ThrowsException(() => engine.BinaryDeserialize(new byte[] { 0xfa, 0x01 })); - } - [TestMethod] public void TestRuntime_GetInvocationCounter() { @@ -333,11 +310,11 @@ public void TestCrypto_Verify() KeyPair keyPair = new KeyPair(privateKey); ECPoint pubkey = keyPair.PublicKey; byte[] signature = Crypto.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); - engine.VerifyWithECDsaSecp256r1(message, pubkey.EncodePoint(false), signature).Should().BeTrue(); + engine.CheckSig(pubkey.EncodePoint(false), signature).Should().BeTrue(); byte[] wrongkey = pubkey.EncodePoint(false); wrongkey[0] = 5; - engine.VerifyWithECDsaSecp256r1(new InteropInterface(engine.ScriptContainer), wrongkey, signature).Should().BeFalse(); + engine.CheckSig(wrongkey, signature).Should().BeFalse(); } [TestMethod] @@ -662,7 +639,7 @@ public void TestContract_CreateStandardAccount() { var engine = GetEngine(true, true); ECPoint pubkey = ECPoint.Parse("024b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e", ECCurve.Secp256r1); - engine.CreateStandardAccount(pubkey).ToArray().ToHexString().Should().Be("a17e91aff4bb5e0ad54d7ce8de8472e17ce88bf1"); + engine.CreateStandardAccount(pubkey).ToArray().ToHexString().Should().Be("a78796ab56598585c80dbe95059324eabde764db"); } public static void LogEvent(object sender, LogEventArgs args) diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 562433cbbd..fc37d96a76 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -114,118 +114,6 @@ public void System_Blockchain_GetBlock() } } - [TestMethod] - public void Json_Deserialize() - { - // Good - - using (var script = new ScriptBuilder()) - { - script.EmitPush("123"); - script.EmitSysCall(ApplicationEngine.System_Json_Deserialize); - script.EmitPush("null"); - script.EmitSysCall(ApplicationEngine.System_Json_Deserialize); - - using (var engine = ApplicationEngine.Create(TriggerType.Application, null, null)) - { - engine.LoadScript(script.ToArray()); - - Assert.AreEqual(engine.Execute(), VMState.HALT); - Assert.AreEqual(2, engine.ResultStack.Count); - - engine.ResultStack.Pop(); - Assert.IsTrue(engine.ResultStack.Pop().GetInteger() == 123); - } - } - - // Error 1 - Wrong Json - - using (var script = new ScriptBuilder()) - { - script.EmitPush("***"); - script.EmitSysCall(ApplicationEngine.System_Json_Deserialize); - - using (var engine = ApplicationEngine.Create(TriggerType.Application, null, null)) - { - engine.LoadScript(script.ToArray()); - - Assert.AreEqual(engine.Execute(), VMState.FAULT); - Assert.AreEqual(0, engine.ResultStack.Count); - } - } - - // Error 2 - No decimals - - using (var script = new ScriptBuilder()) - { - script.EmitPush("123.45"); - script.EmitSysCall(ApplicationEngine.System_Json_Deserialize); - - using (var engine = ApplicationEngine.Create(TriggerType.Application, null, null)) - { - engine.LoadScript(script.ToArray()); - - Assert.AreEqual(engine.Execute(), VMState.FAULT); - Assert.AreEqual(0, engine.ResultStack.Count); - } - } - } - - [TestMethod] - public void Json_Serialize() - { - // Good - - using (var script = new ScriptBuilder()) - { - script.EmitPush(5); - script.EmitSysCall(ApplicationEngine.System_Json_Serialize); - script.Emit(OpCode.PUSH0); - script.Emit(OpCode.NOT); - script.EmitSysCall(ApplicationEngine.System_Json_Serialize); - script.EmitPush("test"); - script.EmitSysCall(ApplicationEngine.System_Json_Serialize); - script.Emit(OpCode.PUSHNULL); - script.EmitSysCall(ApplicationEngine.System_Json_Serialize); - script.Emit(OpCode.NEWMAP); - script.Emit(OpCode.DUP); - script.EmitPush("key"); - script.EmitPush("value"); - script.Emit(OpCode.SETITEM); - script.EmitSysCall(ApplicationEngine.System_Json_Serialize); - - using (var engine = ApplicationEngine.Create(TriggerType.Application, null, null)) - { - engine.LoadScript(script.ToArray()); - - Assert.AreEqual(engine.Execute(), VMState.HALT); - Assert.AreEqual(5, engine.ResultStack.Count); - - Assert.IsTrue(engine.ResultStack.Pop().GetString() == "{\"key\":\"value\"}"); - Assert.IsTrue(engine.ResultStack.Pop().GetString() == "null"); - Assert.IsTrue(engine.ResultStack.Pop().GetString() == "\"test\""); - Assert.IsTrue(engine.ResultStack.Pop().GetString() == "true"); - Assert.IsTrue(engine.ResultStack.Pop().GetString() == "5"); - } - } - - // Error - - using (var script = new ScriptBuilder()) - { - script.EmitSysCall(ApplicationEngine.System_Storage_GetContext); - script.EmitSysCall(ApplicationEngine.System_Json_Serialize); - - using (var engine = ApplicationEngine.Create(TriggerType.Application, null, null)) - { - engine.LoadScript(script.ToArray()); - - Assert.AreEqual(engine.Execute(), VMState.FAULT); - Assert.AreEqual(0, engine.ResultStack.Count); - } - } - } - [TestMethod] public void System_ExecutionEngine_GetScriptContainer() { diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs index f89ca08e6b..879eda73b0 100644 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs +++ b/tests/neo.UnitTests/Wallets/SQLite/UT_VerificationContract.cs @@ -116,11 +116,11 @@ public void TestSerialize() }; byte[] byteArray = contract1.ToArray(); byte[] script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey); - byte[] result = new byte[44]; + byte[] result = new byte[43]; result[0] = 0x01; result[1] = (byte)ContractParameterType.Signature; - result[2] = 0x29; - Array.Copy(script, 0, result, 3, 41); + result[2] = 0x28; + Array.Copy(script, 0, result, 3, 40); CollectionAssert.AreEqual(result, byteArray); } @@ -138,7 +138,7 @@ public void TestGetSize() Script = Neo.SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), ParameterList = new[] { ContractParameterType.Signature } }; - Assert.AreEqual(44, contract1.Size); + Assert.AreEqual(43, contract1.Size); } } } diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 36b7026f92..04921ef5b5 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -170,9 +170,9 @@ public void TestGetVersion() public void TestGetAccount1() { MyWallet wallet = new MyWallet(); - wallet.CreateAccount(UInt160.Parse("0xf55f6873ae944cf4ec9626e8855b8554e798a7d1")); + wallet.CreateAccount(UInt160.Parse("0xd92defaf95bf5307ffbc3ab1c5cdf7ddfc1b89b3")); WalletAccount account = wallet.GetAccount(ECCurve.Secp256r1.G); - account.ScriptHash.Should().Be(UInt160.Parse("0xf55f6873ae944cf4ec9626e8855b8554e798a7d1")); + account.ScriptHash.Should().Be(UInt160.Parse("0xd92defaf95bf5307ffbc3ab1c5cdf7ddfc1b89b3")); } [TestMethod] From 6cb8cc951ee22415a531fca7811f76c3966771f7 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 9 Feb 2021 20:27:20 +0100 Subject: [PATCH 14/63] Change GetScriptContainer returns (#2312) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add native contract: StdLib * format * Add CryptoLib * Add SCriptContainer.GetHashData * Return object instead of interop * Fix UT * Fix of fix Co-authored-by: Erik Zhang Co-authored-by: Vitor Nazário Coelho --- src/neo/SmartContract/ApplicationEngine.Runtime.cs | 6 ++++-- tests/neo.UnitTests/SmartContract/UT_InteropService.cs | 2 +- tests/neo.UnitTests/SmartContract/UT_Syscalls.cs | 5 ++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index abefbdc575..b8e3ac0faa 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -2,6 +2,7 @@ using Neo.IO; using Neo.Network.P2P.Payloads; using Neo.SmartContract.Native; +using Neo.VM.Types; using System; using System.Collections.Generic; using System.IO; @@ -39,9 +40,10 @@ protected internal ulong GetTime() return PersistingBlock.Timestamp; } - protected internal IInteroperable GetScriptContainer() + protected internal StackItem GetScriptContainer() { - return ScriptContainer as IInteroperable; + if (ScriptContainer is not IInteroperable interop) throw new InvalidOperationException(); + return interop.ToStackItem(ReferenceCounter); } protected internal bool CheckWitness(byte[] hashOrPubkey) diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index ed001dd241..d83398a43f 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -203,7 +203,7 @@ private static void AssertNotification(StackItem stackItem, UInt160 scriptHash, [TestMethod] public void TestExecutionEngine_GetScriptContainer() { - GetEngine(true).GetScriptContainer().Should().BeOfType(); + GetEngine(true).GetScriptContainer().Should().BeOfType(); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index fc37d96a76..6982395f86 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -127,9 +127,8 @@ public void System_ExecutionEngine_GetScriptContainer() var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); engine.LoadScript(script.ToArray()); - Assert.AreEqual(engine.Execute(), VMState.HALT); - Assert.AreEqual(1, engine.ResultStack.Count); - Assert.IsTrue(engine.ResultStack.Peek().IsNull); + Assert.AreEqual(engine.Execute(), VMState.FAULT); + Assert.AreEqual(0, engine.ResultStack.Count); // With tx From 16499e28bf97e2d765b0053d5b19738e4adb2556 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 10 Feb 2021 15:02:17 +0800 Subject: [PATCH 15/63] Optimize UT (#2324) --- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 4 ---- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 13 ------------- tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs | 6 ------ .../neo.UnitTests/Network/P2P/Payloads/UT_Header.cs | 1 - .../P2P/Payloads/UT_HighPriorityAttribute.cs | 6 ------ .../Network/P2P/Payloads/UT_MerkleBlockPayload.cs | 6 ------ .../Network/P2P/Payloads/UT_Transaction.cs | 1 - .../Network/P2P/Payloads/UT_Witness.cs | 1 - .../SmartContract/Native/UT_FungibleToken.cs | 6 ------ .../SmartContract/Native/UT_GasToken.cs | 2 -- tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs | 1 - .../SmartContract/Native/UT_NameService.cs | 1 - .../SmartContract/Native/UT_NativeContract.cs | 6 ------ .../SmartContract/Native/UT_NeoToken.cs | 1 - .../SmartContract/Native/UT_PolicyContract.cs | 1 - .../SmartContract/Native/UT_RoleManagement.cs | 1 - .../neo.UnitTests/SmartContract/Native/UT_StdLib.cs | 6 ------ .../SmartContract/UT_ApplicationEngine.cs | 6 ------ .../SmartContract/UT_ContractParameterContext.cs | 1 - .../neo.UnitTests/SmartContract/UT_InteropPrices.cs | 6 ------ .../SmartContract/UT_InteropService.cs | 12 ------------ .../SmartContract/UT_SmartContractHelper.cs | 6 ------ tests/neo.UnitTests/SmartContract/UT_Syscalls.cs | 12 ------------ tests/neo.UnitTests/TestBlockchain.cs | 2 -- tests/neo.UnitTests/UT_DataCache.cs | 6 ------ tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs | 6 ------ tests/neo.UnitTests/Wallets/UT_Wallet.cs | 6 ------ 27 files changed, 126 deletions(-) diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index a1d50e4884..9cba972afe 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -24,11 +24,7 @@ public class UT_Blockchain : TestKit public void Initialize() { system = TestBlockchain.TheNeoSystem; - senderProbe = CreateTestProbe(); - senderProbe.Send(system.Blockchain, new object()); - senderProbe.ExpectNoMsg(); // Ensure blockchain it's created - txSample = new Transaction() { Attributes = Array.Empty(), diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index 7c53ae9934..c3bca08016 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -1,4 +1,3 @@ -using Akka.TestKit; using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -37,8 +36,6 @@ public class UT_MemoryPool : TestKit private MemoryPool _unit; private MemoryPool _unit2; private TestIMemoryPoolTxObserverPlugin plugin; - private TestProbe senderProbe; - private bool hasChecked = false; [ClassInitialize] public static void TestSetup(TestContext ctx) @@ -54,19 +51,9 @@ private DataCache GetSnapshot() [TestInitialize] public void TestSetup() { - if (!hasChecked) - { - senderProbe = CreateTestProbe(); - senderProbe.Send(testBlockchain.Blockchain, new object()); - senderProbe.ExpectNoMsg(); // Ensure blockchain it's created - hasChecked = true; - } - // protect against external changes on TimeProvider TimeProvider.ResetToDefault(); - TestBlockchain.InitializeMockNeoSystem(); - // Create a MemoryPool with capacity of 100 _unit = new MemoryPool(new NeoSystem(ProtocolSettings.Default with { MemoryPoolMaxTransactions = 100 })); diff --git a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs index 725e0f37df..92ce52bbaa 100644 --- a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs +++ b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs @@ -33,12 +33,6 @@ public static TrimmedBlock GetTrimmedBlockWithNoTransaction() }; } - [TestInitialize] - public void Init() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void TestGetBlock() { diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs index c1d97885c4..c9d83d73e5 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Header.cs @@ -17,7 +17,6 @@ public class UT_Header [TestInitialize] public void TestSetup() { - TestBlockchain.InitializeMockNeoSystem(); uut = new Header(); } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs index 558b9e1048..8b9bb42ca7 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_HighPriorityAttribute.cs @@ -11,12 +11,6 @@ namespace Neo.UnitTests.Network.P2P.Payloads [TestClass] public class UT_HighPriorityAttribute { - [TestInitialize] - public void Init() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void Size_Get() { diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs index 853afffa9d..ff10d1ac86 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_MerkleBlockPayload.cs @@ -9,12 +9,6 @@ namespace Neo.UnitTests.Network.P2P.Payloads [TestClass] public class UT_MerkleBlockPayload { - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void Size_Get() { diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index 0171922b7a..b58c94110e 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -23,7 +23,6 @@ public class UT_Transaction [TestInitialize] public void TestSetup() { - TestBlockchain.InitializeMockNeoSystem(); uut = new Transaction(); } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs index e562e73478..5f08973d2e 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Witness.cs @@ -20,7 +20,6 @@ public class UT_Witness public void TestSetup() { uut = new Witness(); - TestBlockchain.InitializeMockNeoSystem(); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs index 3a813886ce..74309efc1f 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs @@ -11,12 +11,6 @@ namespace Neo.UnitTests.SmartContract.Native [TestClass] public class UT_FungibleToken : TestKit { - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - } - protected const byte Prefix_TotalSupply = 11; private static readonly TestNep17Token test = new TestNep17Token(); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs index 811e3fcf3c..d415ca5fb5 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -21,8 +21,6 @@ public class UT_GasToken [TestInitialize] public void TestSetup() { - TestBlockchain.InitializeMockNeoSystem(); - _snapshot = TestBlockchain.GetTestSnapshot(); _persistingBlock = new Block { Header = new Header() }; } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs index 73cd03195c..a2adb573b0 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NNS.cs @@ -30,7 +30,6 @@ public class UT_NNS [TestInitialize] public void TestSetup() { - TestBlockchain.InitializeMockNeoSystem(); _snapshot = TestBlockchain.GetTestSnapshot(); _persistingBlock = new Block { diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs index 184919a448..f0f342af13 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NameService.cs @@ -22,7 +22,6 @@ public class UT_NameService : TestKit [TestInitialize] public void TestSetup() { - TestBlockchain.InitializeMockNeoSystem(); _snapshot = TestBlockchain.GetTestSnapshot(); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index d2571e8e2f..22d8361ac1 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -12,12 +12,6 @@ namespace Neo.UnitTests.SmartContract.Native [TestClass] public class UT_NativeContract { - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - } - private static readonly TestNativeContract testNativeContract = new TestNativeContract(); [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 3208bec053..e952bb61aa 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -26,7 +26,6 @@ public class UT_NeoToken [TestInitialize] public void TestSetup() { - TestBlockchain.InitializeMockNeoSystem(); _snapshot = TestBlockchain.GetTestSnapshot(); _persistingBlock = new Block { diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 389bf98e6d..8f52063e82 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -19,7 +19,6 @@ public class UT_PolicyContract [TestInitialize] public void TestSetup() { - TestBlockchain.InitializeMockNeoSystem(); _snapshot = TestBlockchain.GetTestSnapshot(); ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, _snapshot, new Block { Header = new Header() }, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs index d292fb8776..bcceb7f6fd 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_RoleManagement.cs @@ -20,7 +20,6 @@ public class UT_RoleManagement [TestInitialize] public void TestSetup() { - TestBlockchain.InitializeMockNeoSystem(); _snapshot = TestBlockchain.GetTestSnapshot(); } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs b/tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs index 7b2b26f835..29b1cd6032 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_StdLib.cs @@ -11,12 +11,6 @@ namespace Neo.UnitTests.SmartContract.Native [TestClass] public class UT_StdLib { - [TestInitialize] - public void Init() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void TestBinary() { diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index 5e850c81a8..9b808a907c 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -10,12 +10,6 @@ public class UT_ApplicationEngine { private string eventName = null; - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void TestNotify() { diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index 1a99c4341d..e36fc38790 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -27,7 +27,6 @@ public static void ClassSetUp(TestContext context) key = new KeyPair(privateKey); contract = Contract.CreateSignatureContract(key.PublicKey); } - TestBlockchain.InitializeMockNeoSystem(); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index 9a7c7e1095..3c0a997157 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -9,12 +9,6 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_InteropPrices { - [TestInitialize] - public void Initialize() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void ApplicationEngineFixedPrices() { diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index d83398a43f..57cca0834a 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -1,4 +1,3 @@ -using Akka.TestKit; using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -23,17 +22,6 @@ namespace Neo.UnitTests.SmartContract [TestClass] public partial class UT_InteropService : TestKit { - private TestProbe senderProbe; - - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - senderProbe = CreateTestProbe(); - senderProbe.Send(TestBlockchain.TheNeoSystem.Blockchain, new object()); - senderProbe.ExpectNoMsg(); // Ensure blockchain it's created - } - [TestMethod] public void Runtime_GetNotifications_Test() { diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index 71eafaaa38..d6bba55e6b 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -20,12 +20,6 @@ public class UT_SmartContractHelper const byte Prefix_BlockHash = 9; const byte Prefix_Transaction = 11; - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void TestIsMultiSigContract() { diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index 6982395f86..f5dc93aafb 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -1,4 +1,3 @@ -using Akka.TestKit; using Akka.TestKit.Xunit2; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO; @@ -16,17 +15,6 @@ namespace Neo.UnitTests.SmartContract [TestClass] public partial class UT_Syscalls : TestKit { - private TestProbe senderProbe; - - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - senderProbe = CreateTestProbe(); - senderProbe.Send(TestBlockchain.TheNeoSystem.Blockchain, new object()); - senderProbe.ExpectNoMsg(); // Ensure blockchain it's created - } - [TestMethod] public void System_Blockchain_GetBlock() { diff --git a/tests/neo.UnitTests/TestBlockchain.cs b/tests/neo.UnitTests/TestBlockchain.cs index a031604c2f..f1ea5eb975 100644 --- a/tests/neo.UnitTests/TestBlockchain.cs +++ b/tests/neo.UnitTests/TestBlockchain.cs @@ -14,8 +14,6 @@ static TestBlockchain() TheNeoSystem = new NeoSystem(ProtocolSettings.Default, null, null); } - public static void InitializeMockNeoSystem() { } - internal static DataCache GetTestSnapshot() { return TheNeoSystem.GetSnapshot().CreateSnapshot(); diff --git a/tests/neo.UnitTests/UT_DataCache.cs b/tests/neo.UnitTests/UT_DataCache.cs index 80eff4723d..6f458da991 100644 --- a/tests/neo.UnitTests/UT_DataCache.cs +++ b/tests/neo.UnitTests/UT_DataCache.cs @@ -8,12 +8,6 @@ namespace Neo.UnitTests [TestClass] public class UT_DataCache { - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void TestCachedFind_Between() { diff --git a/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs b/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs index c28954557d..f50c9065f8 100644 --- a/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs +++ b/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs @@ -8,12 +8,6 @@ namespace Neo.UnitTests.Wallets [TestClass] public class UT_AssetDescriptor { - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void TestConstructorWithNonexistAssetId() { diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 04921ef5b5..2802d68e0a 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -103,12 +103,6 @@ public static void ClassInit(TestContext context) nep2Key = glkey.Export("pwd", 2, 1, 1); } - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void TestContains() { From c3c94f0c2bc6d8e4c9fa5bcbedb5ecd0174c605e Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 10 Feb 2021 17:50:32 +0800 Subject: [PATCH 16/63] Eliminate the use of ProtocolSettings.Default (#2314) --- src/neo/Network/P2P/Helper.cs | 33 ++-- src/neo/Network/P2P/Payloads/Block.cs | 6 +- src/neo/Network/P2P/Payloads/Header.cs | 4 +- src/neo/Network/P2P/Payloads/IInventory.cs | 2 - src/neo/Network/P2P/Payloads/IVerifiable.cs | 2 + src/neo/Network/P2P/Payloads/Transaction.cs | 4 +- src/neo/ProtocolSettings.cs | 154 ++++++++---------- .../SmartContract/ApplicationEngine.Crypto.cs | 4 +- src/neo/SmartContract/ApplicationEngine.cs | 1 - src/neo/SmartContract/Contract.cs | 27 +-- .../SmartContract/Native/NativeContract.cs | 8 - src/neo/Wallets/AssetDescriptor.cs | 6 +- src/neo/Wallets/Helper.cs | 15 +- src/neo/Wallets/KeyPair.cs | 4 +- src/neo/Wallets/NEP6/NEP6Account.cs | 18 +- src/neo/Wallets/NEP6/NEP6Wallet.cs | 16 +- src/neo/Wallets/SQLite/UserWallet.cs | 34 ++-- src/neo/Wallets/SQLite/UserWalletAccount.cs | 6 +- src/neo/Wallets/Wallet.cs | 26 ++- src/neo/Wallets/WalletAccount.cs | 6 +- tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs | 2 +- .../Network/P2P/Payloads/UT_Block.cs | 12 +- .../Network/P2P/Payloads/UT_Transaction.cs | 4 +- .../SmartContract/Native/UT_NativeContract.cs | 8 - .../SmartContract/Native/UT_NeoToken.cs | 75 ++++----- .../SmartContract/UT_Contract.cs | 17 -- .../SmartContract/UT_DeployedContract.cs | 3 +- .../SmartContract/UT_InteropService.NEO.cs | 4 +- .../SmartContract/UT_InteropService.cs | 2 +- tests/neo.UnitTests/TestUtils.cs | 2 +- tests/neo.UnitTests/TestWalletAccount.cs | 2 +- tests/neo.UnitTests/UT_Helper.cs | 8 +- tests/neo.UnitTests/UT_ProtocolSettings.cs | 105 +----------- .../Wallets/NEP6/UT_NEP6Account.cs | 4 +- .../Wallets/NEP6/UT_NEP6Wallet.cs | 37 +++-- .../Wallets/SQLite/UT_UserWallet.cs | 12 +- .../Wallets/SQLite/UT_UserWalletAccount.cs | 6 +- .../Wallets/UT_AssetDescriptor.cs | 6 +- tests/neo.UnitTests/Wallets/UT_Wallet.cs | 16 +- .../neo.UnitTests/Wallets/UT_WalletAccount.cs | 2 +- .../Wallets/UT_Wallets_Helper.cs | 8 +- tests/neo.UnitTests/neo.UnitTests.csproj | 6 - tests/neo.UnitTests/protocol.json | 5 - 43 files changed, 265 insertions(+), 457 deletions(-) delete mode 100644 tests/neo.UnitTests/protocol.json diff --git a/src/neo/Network/P2P/Helper.cs b/src/neo/Network/P2P/Helper.cs index db95362bd6..98f9c7518d 100644 --- a/src/neo/Network/P2P/Helper.cs +++ b/src/neo/Network/P2P/Helper.cs @@ -1,4 +1,5 @@ using Neo.Cryptography; +using Neo.IO; using Neo.Network.P2P.Payloads; using System.IO; @@ -6,31 +7,23 @@ namespace Neo.Network.P2P { public static class Helper { - public static byte[] GetHashData(this IVerifiable verifiable) - { - return GetHashData(verifiable, ProtocolSettings.Default.Magic); - } - - public static byte[] GetHashData(this IVerifiable verifiable, uint magic) - { - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms)) - { - writer.Write(magic); - verifiable.SerializeUnsigned(writer); - writer.Flush(); - return ms.ToArray(); - } - } - public static UInt256 CalculateHash(this IVerifiable verifiable) { - return new UInt256(Crypto.Hash256(verifiable.GetHashData(ProtocolSettings.Default.Magic))); + using MemoryStream ms = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(ms); + verifiable.SerializeUnsigned(writer); + writer.Flush(); + return new UInt256(ms.ToArray().Sha256()); } - public static UInt256 CalculateHash(this IVerifiable verifiable, uint magic) + public static byte[] GetSignData(this IVerifiable verifiable, uint magic) { - return new UInt256(Crypto.Hash256(verifiable.GetHashData(magic))); + using MemoryStream ms = new MemoryStream(); + using BinaryWriter writer = new BinaryWriter(ms); + writer.Write(magic); + writer.Write(verifiable.Hash); + writer.Flush(); + return ms.ToArray(); } } } diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index ef73254544..5901c7988e 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -69,11 +69,11 @@ public void Serialize(BinaryWriter writer) void IVerifiable.SerializeUnsigned(BinaryWriter writer) => ((IVerifiable)Header).SerializeUnsigned(writer); - public JObject ToJson() + public JObject ToJson(ProtocolSettings settings) { - JObject json = Header.ToJson(); + JObject json = Header.ToJson(settings); json["size"] = Size; - json["tx"] = Transactions.Select(p => p.ToJson()).ToArray(); + json["tx"] = Transactions.Select(p => p.ToJson(settings)).ToArray(); return json; } diff --git a/src/neo/Network/P2P/Payloads/Header.cs b/src/neo/Network/P2P/Payloads/Header.cs index 51b94587bc..8d3fb31194 100644 --- a/src/neo/Network/P2P/Payloads/Header.cs +++ b/src/neo/Network/P2P/Payloads/Header.cs @@ -119,7 +119,7 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.Write(NextConsensus); } - public JObject ToJson() + public JObject ToJson(ProtocolSettings settings) { JObject json = new JObject(); json["hash"] = Hash.ToString(); @@ -130,7 +130,7 @@ public JObject ToJson() json["time"] = Timestamp; json["index"] = Index; json["primary"] = PrimaryIndex; - json["nextconsensus"] = NextConsensus.ToAddress(); + json["nextconsensus"] = NextConsensus.ToAddress(settings.AddressVersion); json["witnesses"] = new JArray(Witness.ToJson()); return json; } diff --git a/src/neo/Network/P2P/Payloads/IInventory.cs b/src/neo/Network/P2P/Payloads/IInventory.cs index abd17fbd7f..d4846e8d76 100644 --- a/src/neo/Network/P2P/Payloads/IInventory.cs +++ b/src/neo/Network/P2P/Payloads/IInventory.cs @@ -2,8 +2,6 @@ namespace Neo.Network.P2P.Payloads { public interface IInventory : IVerifiable { - UInt256 Hash { get; } - InventoryType InventoryType { get; } } } diff --git a/src/neo/Network/P2P/Payloads/IVerifiable.cs b/src/neo/Network/P2P/Payloads/IVerifiable.cs index 3e574366ed..c9d0a9acb5 100644 --- a/src/neo/Network/P2P/Payloads/IVerifiable.cs +++ b/src/neo/Network/P2P/Payloads/IVerifiable.cs @@ -6,6 +6,8 @@ namespace Neo.Network.P2P.Payloads { public interface IVerifiable : ISerializable { + UInt256 Hash => this.CalculateHash(); + Witness[] Witnesses { get; set; } void DeserializeUnsigned(BinaryReader reader); diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 335c4241d5..050e75d2d6 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -260,14 +260,14 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.WriteVarBytes(Script); } - public JObject ToJson() + public JObject ToJson(ProtocolSettings settings) { JObject json = new JObject(); json["hash"] = Hash.ToString(); json["size"] = Size; json["version"] = Version; json["nonce"] = Nonce; - json["sender"] = Sender.ToAddress(); + json["sender"] = Sender.ToAddress(settings.AddressVersion); json["sysfee"] = SystemFee.ToString(); json["netfee"] = NetworkFee.ToString(); json["validuntilblock"] = ValidUntilBlock; diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index fa6e8f3b7e..3ca2723f10 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -2,8 +2,8 @@ using Neo.Cryptography.ECC; using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; -using System.Threading; namespace Neo { @@ -11,103 +11,85 @@ public record ProtocolSettings { public uint Magic { get; init; } public byte AddressVersion { get; init; } - public ECPoint[] StandbyCommittee { get; init; } - public ECPoint[] StandbyValidators { get; init; } - public int CommitteeMembersCount { get; init; } + public IReadOnlyList StandbyCommittee { get; init; } + public int CommitteeMembersCount => StandbyCommittee.Count; public int ValidatorsCount { get; init; } public string[] SeedList { get; init; } public uint MillisecondsPerBlock { get; init; } - public TimeSpan TimePerBlock { get; init; } + public TimeSpan TimePerBlock => TimeSpan.FromMilliseconds(MillisecondsPerBlock); public int MemoryPoolMaxTransactions { get; init; } public uint MaxTraceableBlocks { get; init; } public IReadOnlyDictionary NativeActivations { get; init; } - static ProtocolSettings _default; + private IReadOnlyList _standbyValidators; + public IReadOnlyList StandbyValidators => _standbyValidators ??= StandbyCommittee.Take(ValidatorsCount).ToArray(); - static bool UpdateDefault(IConfiguration configuration) + public static ProtocolSettings Default { get; } = new ProtocolSettings { - var settings = new ProtocolSettings(configuration.GetSection("ProtocolConfiguration")); - return null == Interlocked.CompareExchange(ref _default, settings, null); - } - - public static bool Initialize(IConfiguration configuration) - { - return UpdateDefault(configuration); - } - - public static ProtocolSettings Default - { - get + Magic = 0x4F454Eu, + AddressVersion = 0x35, + StandbyCommittee = new[] { - if (_default == null) - { - var configuration = Utility.LoadConfig("protocol"); - UpdateDefault(configuration); - } - - return _default; - } - } + //Validators + ECPoint.Parse("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", ECCurve.Secp256r1), + ECPoint.Parse("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", ECCurve.Secp256r1), + ECPoint.Parse("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", ECCurve.Secp256r1), + ECPoint.Parse("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", ECCurve.Secp256r1), + ECPoint.Parse("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", ECCurve.Secp256r1), + ECPoint.Parse("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", ECCurve.Secp256r1), + ECPoint.Parse("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", ECCurve.Secp256r1), + //Other Members + ECPoint.Parse("023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", ECCurve.Secp256r1), + ECPoint.Parse("03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", ECCurve.Secp256r1), + ECPoint.Parse("03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", ECCurve.Secp256r1), + ECPoint.Parse("03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", ECCurve.Secp256r1), + ECPoint.Parse("02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", ECCurve.Secp256r1), + ECPoint.Parse("03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", ECCurve.Secp256r1), + ECPoint.Parse("0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", ECCurve.Secp256r1), + ECPoint.Parse("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", ECCurve.Secp256r1), + ECPoint.Parse("0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", ECCurve.Secp256r1), + ECPoint.Parse("03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", ECCurve.Secp256r1), + ECPoint.Parse("02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", ECCurve.Secp256r1), + ECPoint.Parse("0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", ECCurve.Secp256r1), + ECPoint.Parse("03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", ECCurve.Secp256r1), + ECPoint.Parse("02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a", ECCurve.Secp256r1) + }, + ValidatorsCount = 7, + SeedList = new[] + { + "seed1.neo.org:10333", + "seed2.neo.org:10333", + "seed3.neo.org:10333", + "seed4.neo.org:10333", + "seed5.neo.org:10333" + }, + MillisecondsPerBlock = 15000, + MemoryPoolMaxTransactions = 50_000, + MaxTraceableBlocks = 2_102_400, + NativeActivations = ImmutableDictionary.Empty + }; - private ProtocolSettings(IConfigurationSection section) + public static ProtocolSettings Load(string config) { - this.Magic = section.GetValue("Magic", 0x4F454Eu); - this.AddressVersion = section.GetValue("AddressVersion", (byte)0x35); - IConfigurationSection section_sc = section.GetSection("StandbyCommittee"); - string[] s = section_sc.Exists() - ? section_sc.GetChildren().Select(p => p.Get()).ToArray() - : new[] - { - //Validators - "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", - "02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", - "03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", - "02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", - "024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", - "02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", - "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", - - //Other Members - "023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", - "03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", - "03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", - "03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", - "02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", - "03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", - "0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", - "020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", - "0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", - "03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", - "02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", - "0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", - "03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", - "02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a" - }; - this.StandbyCommittee = s.Select(p => ECPoint.Parse(p, ECCurve.Secp256r1)).ToArray(); - this.CommitteeMembersCount = StandbyCommittee.Length; - this.ValidatorsCount = section.GetValue("ValidatorsCount", (byte)7); - this.StandbyValidators = StandbyCommittee[0..ValidatorsCount]; - IConfigurationSection section_sl = section.GetSection("SeedList"); - if (section_sl.Exists()) - this.SeedList = section_sl.GetChildren().Select(p => p.Get()).ToArray(); - else - this.SeedList = new[] - { - "seed1.neo.org:10333", - "seed2.neo.org:10333", - "seed3.neo.org:10333", - "seed4.neo.org:10333", - "seed5.neo.org:10333" - }; - this.MillisecondsPerBlock = section.GetValue("MillisecondsPerBlock", 15000u); - this.TimePerBlock = TimeSpan.FromMilliseconds(MillisecondsPerBlock); - this.MemoryPoolMaxTransactions = Math.Max(1, section.GetValue("MemoryPoolMaxTransactions", 50_000)); - this.MaxTraceableBlocks = section.GetValue("MaxTraceableBlocks", 2_102_400u);// 365 days - IConfigurationSection section_na = section.GetSection("NativeActivations"); - if (section_na.Exists()) - this.NativeActivations = section_na.GetChildren().ToDictionary((a) => a.Key, b => uint.Parse(b.Value)); - else - this.NativeActivations = new Dictionary(); + IConfigurationSection section = Utility.LoadConfig(config).GetSection("ProtocolConfiguration"); + return new ProtocolSettings + { + Magic = section.GetValue("Magic", Default.Magic), + AddressVersion = section.GetValue("AddressVersion", Default.AddressVersion), + StandbyCommittee = section.GetSection("StandbyCommittee").Exists() + ? section.GetSection("StandbyCommittee").GetChildren().Select(p => ECPoint.Parse(p.Get(), ECCurve.Secp256r1)).ToArray() + : Default.StandbyCommittee, + ValidatorsCount = section.GetValue("ValidatorsCount", Default.ValidatorsCount), + SeedList = section.GetSection("SeedList").Exists() + ? section.GetSection("SeedList").GetChildren().Select(p => p.Get()).ToArray() + : Default.SeedList, + MillisecondsPerBlock = section.GetValue("MillisecondsPerBlock", Default.MillisecondsPerBlock), + MemoryPoolMaxTransactions = section.GetValue("MemoryPoolMaxTransactions", Default.MemoryPoolMaxTransactions), + MaxTraceableBlocks = section.GetValue("MaxTraceableBlocks", Default.MaxTraceableBlocks), + NativeActivations = section.GetSection("NativeActivations").Exists() + ? section.GetSection("NativeActivations").GetChildren().ToDictionary((a) => a.Key, b => uint.Parse(b.Value)) + : Default.NativeActivations + }; } public uint GetNativeActivation(string name) diff --git a/src/neo/SmartContract/ApplicationEngine.Crypto.cs b/src/neo/SmartContract/ApplicationEngine.Crypto.cs index 89fd57b179..5fdc592be2 100644 --- a/src/neo/SmartContract/ApplicationEngine.Crypto.cs +++ b/src/neo/SmartContract/ApplicationEngine.Crypto.cs @@ -16,7 +16,7 @@ protected internal bool CheckSig(byte[] pubkey, byte[] signature) { try { - return Crypto.VerifySignature(ScriptContainer.GetHashData(), signature, pubkey, ECCurve.Secp256r1); + return Crypto.VerifySignature(ScriptContainer.GetSignData(ProtocolSettings.Magic), signature, pubkey, ECCurve.Secp256r1); } catch (ArgumentException) { @@ -26,7 +26,7 @@ protected internal bool CheckSig(byte[] pubkey, byte[] signature) protected internal bool CheckMultisig(byte[][] pubkeys, byte[][] signatures) { - byte[] message = ScriptContainer.GetHashData(); + byte[] message = ScriptContainer.GetSignData(ProtocolSettings.Magic); int m = signatures.Length, n = pubkeys.Length; if (n == 0 || m == 0 || m > n) throw new ArgumentException(); AddGas(CheckSigPrice * n * exec_fee_factor); diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 3b1e48c280..54337815b5 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -360,7 +360,6 @@ internal static void ResetApplicationEngineProvider() public static ApplicationEngine Run(byte[] script, DataCache snapshot, IVerifiable container = null, Block persistingBlock = null, ProtocolSettings settings = null, int offset = 0, long gas = TestModeGas) { persistingBlock ??= CreateDummyBlock(snapshot); - settings ??= ProtocolSettings.Default; ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock, settings, gas); engine.LoadScript(script, initialPosition: offset); engine.Execute(); diff --git a/src/neo/SmartContract/Contract.cs b/src/neo/SmartContract/Contract.cs index 3ffdb8b672..a730ad4d86 100644 --- a/src/neo/SmartContract/Contract.cs +++ b/src/neo/SmartContract/Contract.cs @@ -1,7 +1,7 @@ using Neo.Cryptography.ECC; using Neo.VM; -using Neo.Wallets; using System; +using System.Collections.Generic; using System.Linq; namespace Neo.SmartContract @@ -11,19 +11,6 @@ public class Contract public byte[] Script; public ContractParameterType[] ParameterList; - private string _address; - public string Address - { - get - { - if (_address == null) - { - _address = ScriptHash.ToAddress(); - } - return _address; - } - } - private UInt160 _scriptHash; public virtual UInt160 ScriptHash { @@ -60,7 +47,7 @@ public static Contract Create(UInt160 scriptHash, params ContractParameterType[] }; } - public static Contract CreateMultiSigContract(int m, params ECPoint[] publicKeys) + public static Contract CreateMultiSigContract(int m, IReadOnlyCollection publicKeys) { return new Contract { @@ -69,9 +56,9 @@ public static Contract CreateMultiSigContract(int m, params ECPoint[] publicKeys }; } - public static byte[] CreateMultiSigRedeemScript(int m, params ECPoint[] publicKeys) + public static byte[] CreateMultiSigRedeemScript(int m, IReadOnlyCollection publicKeys) { - if (!(1 <= m && m <= publicKeys.Length && publicKeys.Length <= 1024)) + if (!(1 <= m && m <= publicKeys.Count && publicKeys.Count <= 1024)) throw new ArgumentException(); using (ScriptBuilder sb = new ScriptBuilder()) { @@ -80,7 +67,7 @@ public static byte[] CreateMultiSigRedeemScript(int m, params ECPoint[] publicKe { sb.EmitPush(publicKey.EncodePoint(true)); } - sb.EmitPush(publicKeys.Length); + sb.EmitPush(publicKeys.Count); sb.EmitSysCall(ApplicationEngine.Neo_Crypto_CheckMultisig); return sb.ToArray(); } @@ -105,9 +92,9 @@ public static byte[] CreateSignatureRedeemScript(ECPoint publicKey) } } - public static UInt160 GetBFTAddress(ECPoint[] pubkeys) + public static UInt160 GetBFTAddress(IReadOnlyCollection pubkeys) { - return CreateMultiSigRedeemScript(pubkeys.Length - (pubkeys.Length - 1) / 3, pubkeys).ToScriptHash(); + return CreateMultiSigRedeemScript(pubkeys.Count - (pubkeys.Count - 1) / 3, pubkeys).ToScriptHash(); } } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 487de86bd7..cb808759df 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -1,5 +1,4 @@ using Neo.IO; -using Neo.Persistence; using Neo.SmartContract.Manifest; using Neo.VM; using System; @@ -140,12 +139,5 @@ internal virtual void OnPersist(ApplicationEngine engine) internal virtual void PostPersist(ApplicationEngine engine) { } - - public ApplicationEngine TestCall(DataCache snapshot, string operation, params object[] args) - { - using ScriptBuilder sb = new ScriptBuilder(); - sb.EmitDynamicCall(Hash, operation, args); - return ApplicationEngine.Run(sb.ToArray(), snapshot); - } } } diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index e137471249..a85b4cc874 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -13,7 +13,7 @@ public class AssetDescriptor public string Symbol { get; } public byte Decimals { get; } - public AssetDescriptor(DataCache snapshot, UInt160 asset_id) + public AssetDescriptor(DataCache snapshot, ProtocolSettings settings, UInt160 asset_id) { var contract = NativeContract.ContractManagement.GetContract(snapshot, asset_id); if (contract is null) throw new ArgumentException(); @@ -25,8 +25,8 @@ public AssetDescriptor(DataCache snapshot, UInt160 asset_id) sb.EmitDynamicCall(asset_id, "symbol"); script = sb.ToArray(); } - using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, gas: 0_10000000); - if (engine.State.HasFlag(VMState.FAULT)) throw new ArgumentException(); + using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, settings: settings, gas: 0_10000000); + if (engine.State == VMState.FAULT) throw new ArgumentException(null, nameof(asset_id)); this.AssetId = asset_id; this.AssetName = contract.Manifest.Name; this.Symbol = engine.ResultStack.Pop().GetString(); diff --git a/src/neo/Wallets/Helper.cs b/src/neo/Wallets/Helper.cs index 4dd06b39e5..b24c99b112 100644 --- a/src/neo/Wallets/Helper.cs +++ b/src/neo/Wallets/Helper.cs @@ -8,30 +8,25 @@ namespace Neo.Wallets { public static class Helper { - public static byte[] Sign(this IVerifiable verifiable, KeyPair key) - { - return Sign(verifiable, key, ProtocolSettings.Default.Magic); - } - public static byte[] Sign(this IVerifiable verifiable, KeyPair key, uint magic) { - return Crypto.Sign(verifiable.GetHashData(magic), key.PrivateKey, key.PublicKey.EncodePoint(false)[1..]); + return Crypto.Sign(verifiable.GetSignData(magic), key.PrivateKey, key.PublicKey.EncodePoint(false)[1..]); } - public static string ToAddress(this UInt160 scriptHash) + public static string ToAddress(this UInt160 scriptHash, byte version) { Span data = stackalloc byte[21]; - data[0] = ProtocolSettings.Default.AddressVersion; + data[0] = version; scriptHash.ToArray().CopyTo(data[1..]); return Base58.Base58CheckEncode(data); } - public static UInt160 ToScriptHash(this string address) + public static UInt160 ToScriptHash(this string address, byte version) { byte[] data = address.Base58CheckDecode(); if (data.Length != 21) throw new FormatException(); - if (data[0] != ProtocolSettings.Default.AddressVersion) + if (data[0] != version) throw new FormatException(); return new UInt160(data.AsSpan(1)); } diff --git a/src/neo/Wallets/KeyPair.cs b/src/neo/Wallets/KeyPair.cs index 8acf280afe..3730bbf9e6 100644 --- a/src/neo/Wallets/KeyPair.cs +++ b/src/neo/Wallets/KeyPair.cs @@ -52,10 +52,10 @@ public string Export() return wif; } - public string Export(string passphrase, int N = 16384, int r = 8, int p = 8) + public string Export(string passphrase, byte version, int N = 16384, int r = 8, int p = 8) { UInt160 script_hash = Contract.CreateSignatureRedeemScript(PublicKey).ToScriptHash(); - string address = script_hash.ToAddress(); + string address = script_hash.ToAddress(version); byte[] addresshash = Encoding.ASCII.GetBytes(address).Sha256().Sha256()[..4]; byte[] derivedkey = SCrypt.Generate(Encoding.UTF8.GetBytes(passphrase), addresshash, N, r, p, 64); byte[] derivedhalf1 = derivedkey[..32]; diff --git a/src/neo/Wallets/NEP6/NEP6Account.cs b/src/neo/Wallets/NEP6/NEP6Account.cs index 870b449400..1be45b604b 100644 --- a/src/neo/Wallets/NEP6/NEP6Account.cs +++ b/src/neo/Wallets/NEP6/NEP6Account.cs @@ -4,7 +4,7 @@ namespace Neo.Wallets.NEP6 { - internal class NEP6Account : WalletAccount + sealed class NEP6Account : WalletAccount { private readonly NEP6Wallet wallet; private string nep2key; @@ -16,21 +16,21 @@ internal class NEP6Account : WalletAccount public override bool HasKey => nep2key != null; public NEP6Account(NEP6Wallet wallet, UInt160 scriptHash, string nep2key = null) - : base(scriptHash) + : base(scriptHash, wallet.ProtocolSettings) { this.wallet = wallet; this.nep2key = nep2key; } public NEP6Account(NEP6Wallet wallet, UInt160 scriptHash, KeyPair key, string password) - : this(wallet, scriptHash, key.Export(password, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P)) + : this(wallet, scriptHash, key.Export(password, wallet.ProtocolSettings.AddressVersion, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P)) { this.key = key; } public static NEP6Account FromJson(JObject json, NEP6Wallet wallet) { - return new NEP6Account(wallet, json["address"].AsString().ToScriptHash(), json["key"]?.AsString()) + return new NEP6Account(wallet, json["address"].AsString().ToScriptHash(wallet.ProtocolSettings.AddressVersion), json["key"]?.AsString()) { Label = json["label"]?.AsString(), IsDefault = json["isdefault"].AsBoolean(), @@ -55,7 +55,7 @@ public KeyPair GetKey(string password) if (nep2key == null) return null; if (key == null) { - key = new KeyPair(Wallet.GetPrivateKeyFromNEP2(nep2key, password, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P)); + key = new KeyPair(Wallet.GetPrivateKeyFromNEP2(nep2key, password, ProtocolSettings.AddressVersion, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P)); } return key; } @@ -63,7 +63,7 @@ public KeyPair GetKey(string password) public JObject ToJson() { JObject account = new JObject(); - account["address"] = ScriptHash.ToAddress(); + account["address"] = ScriptHash.ToAddress(ProtocolSettings.AddressVersion); account["label"] = Label; account["isdefault"] = IsDefault; account["lock"] = Lock; @@ -77,7 +77,7 @@ public bool VerifyPassword(string password) { try { - Wallet.GetPrivateKeyFromNEP2(nep2key, password, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P); + Wallet.GetPrivateKeyFromNEP2(nep2key, password, ProtocolSettings.AddressVersion, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P); return true; } catch (FormatException) @@ -104,14 +104,14 @@ internal bool ChangePasswordPrepare(string password_old, string password_new) { try { - keyTemplate = new KeyPair(Wallet.GetPrivateKeyFromNEP2(nep2key, password_old, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P)); + keyTemplate = new KeyPair(Wallet.GetPrivateKeyFromNEP2(nep2key, password_old, ProtocolSettings.AddressVersion, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P)); } catch { return false; } } - nep2KeyNew = keyTemplate.Export(password_new, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P); + nep2KeyNew = keyTemplate.Export(password_new, ProtocolSettings.AddressVersion, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P); return true; } diff --git a/src/neo/Wallets/NEP6/NEP6Wallet.cs b/src/neo/Wallets/NEP6/NEP6Wallet.cs index 3a5af6d7af..bd0961d390 100644 --- a/src/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/neo/Wallets/NEP6/NEP6Wallet.cs @@ -23,7 +23,7 @@ public class NEP6Wallet : Wallet public override string Name => name; public override Version Version => version; - public NEP6Wallet(string path, string name = null) : base(path) + public NEP6Wallet(string path, ProtocolSettings settings, string name = null) : base(path, settings) { if (File.Exists(path)) { @@ -40,9 +40,9 @@ public NEP6Wallet(string path, string name = null) : base(path) } } - internal NEP6Wallet(JObject wallet) : base(null) + public NEP6Wallet(string path, ProtocolSettings settings, JObject json) : base(path, settings) { - LoadFromJson(wallet, out Scrypt, out accounts, out extra); + LoadFromJson(json, out Scrypt, out accounts, out extra); } private void LoadFromJson(JObject wallet, out ScryptParameters scrypt, out Dictionary accounts, out JObject extra) @@ -143,7 +143,7 @@ public override WalletAccount CreateAccount(UInt160 scriptHash) public KeyPair DecryptKey(string nep2key) { - return new KeyPair(GetPrivateKeyFromNEP2(nep2key, password, Scrypt.N, Scrypt.R, Scrypt.P)); + return new KeyPair(GetPrivateKeyFromNEP2(nep2key, password, ProtocolSettings.AddressVersion, Scrypt.N, Scrypt.R, Scrypt.P)); } public override bool DeleteAccount(UInt160 scriptHash) @@ -214,7 +214,7 @@ public override WalletAccount Import(string wif) public override WalletAccount Import(string nep2, string passphrase, int N = 16384, int r = 8, int p = 8) { - KeyPair key = new KeyPair(GetPrivateKeyFromNEP2(nep2, passphrase, N, r, p)); + KeyPair key = new KeyPair(GetPrivateKeyFromNEP2(nep2, passphrase, ProtocolSettings.AddressVersion, N, r, p)); NEP6Contract contract = new NEP6Contract { Script = Contract.CreateSignatureRedeemScript(key.PublicKey), @@ -237,10 +237,10 @@ internal void Lock() password = null; } - public static NEP6Wallet Migrate(string path, string db3path, string password) + public static NEP6Wallet Migrate(string path, string db3path, string password, ProtocolSettings settings) { - UserWallet wallet_old = UserWallet.Open(db3path, password); - NEP6Wallet wallet_new = new NEP6Wallet(path, wallet_old.Name); + UserWallet wallet_old = UserWallet.Open(db3path, password, settings); + NEP6Wallet wallet_new = new NEP6Wallet(path, settings, wallet_old.Name); using (wallet_new.Unlock(password)) { foreach (WalletAccount account in wallet_old.GetAccounts()) diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs index 8d69d941c4..2d331dccfc 100644 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ b/src/neo/Wallets/SQLite/UserWallet.cs @@ -45,7 +45,7 @@ public override Version Version ///
/// Path /// Password Key - private UserWallet(string path, byte[] passwordKey) : base(path) + private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings) : base(path, settings) { this.salt = LoadStoredData("Salt"); byte[] passwordHash = LoadStoredData("PasswordHash"); @@ -68,7 +68,7 @@ private UserWallet(string path, byte[] passwordKey) : base(path) /// Path /// Password Key /// Scrypt initialization value - private UserWallet(string path, byte[] passwordKey, ScryptParameters scrypt) : base(path) + private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings, ScryptParameters scrypt) : base(path, settings) { this.iv = new byte[16]; this.salt = new byte[20]; @@ -117,13 +117,13 @@ private void AddAccount(UserWalletAccount account) { db_account = ctx.Accounts.Add(new Account { - Nep2key = account.Key.Export(passphrase, scrypt.N, scrypt.R, scrypt.P), + Nep2key = account.Key.Export(passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P), PublicKeyHash = account.Key.PublicKeyHash.ToArray() }).Entity; } else { - db_account.Nep2key = account.Key.Export(passphrase, scrypt.N, scrypt.R, scrypt.P); + db_account.Nep2key = account.Key.Export(passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P); } } if (account.Contract != null) @@ -191,14 +191,14 @@ public override bool Contains(UInt160 scriptHash) } } - public static UserWallet Create(string path, string password, ScryptParameters scrypt = null) + public static UserWallet Create(string path, string password, ProtocolSettings settings, ScryptParameters scrypt = null) { - return new UserWallet(path, password.ToAesKey(), scrypt ?? ScryptParameters.Default); + return new UserWallet(path, password.ToAesKey(), settings, scrypt ?? ScryptParameters.Default); } - public static UserWallet Create(string path, SecureString password, ScryptParameters scrypt = null) + public static UserWallet Create(string path, SecureString password, ProtocolSettings settings, ScryptParameters scrypt = null) { - return new UserWallet(path, password.ToAesKey(), scrypt ?? ScryptParameters.Default); + return new UserWallet(path, password.ToAesKey(), settings, scrypt ?? ScryptParameters.Default); } public override WalletAccount CreateAccount(byte[] privateKey) @@ -209,7 +209,7 @@ public override WalletAccount CreateAccount(byte[] privateKey) Script = SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), ParameterList = new[] { ContractParameterType.Signature } }; - UserWalletAccount account = new UserWalletAccount(contract.ScriptHash) + UserWalletAccount account = new UserWalletAccount(contract.ScriptHash, ProtocolSettings) { Key = key, Contract = contract @@ -229,7 +229,7 @@ public override WalletAccount CreateAccount(SmartContract.Contract contract, Key ParameterList = contract.ParameterList }; } - UserWalletAccount account = new UserWalletAccount(verification_contract.ScriptHash) + UserWalletAccount account = new UserWalletAccount(verification_contract.ScriptHash, ProtocolSettings) { Key = key, Contract = verification_contract @@ -240,7 +240,7 @@ public override WalletAccount CreateAccount(SmartContract.Contract contract, Key public override WalletAccount CreateAccount(UInt160 scriptHash) { - UserWalletAccount account = new UserWalletAccount(scriptHash); + UserWalletAccount account = new UserWalletAccount(scriptHash, ProtocolSettings); AddAccount(account); return account; } @@ -303,13 +303,13 @@ private Dictionary LoadAccounts() using (WalletDataContext ctx = new WalletDataContext(Path)) { string passphrase = Encoding.UTF8.GetString(masterKey); - Dictionary accounts = ctx.Addresses.Select(p => p.ScriptHash).AsEnumerable().Select(p => new UserWalletAccount(new UInt160(p))).ToDictionary(p => p.ScriptHash); + Dictionary accounts = ctx.Addresses.Select(p => p.ScriptHash).AsEnumerable().Select(p => new UserWalletAccount(new UInt160(p), ProtocolSettings)).ToDictionary(p => p.ScriptHash); foreach (Contract db_contract in ctx.Contracts.Include(p => p.Account)) { VerificationContract contract = db_contract.RawData.AsSerializable(); UserWalletAccount account = accounts[contract.ScriptHash]; account.Contract = contract; - account.Key = new KeyPair(GetPrivateKeyFromNEP2(db_contract.Account.Nep2key, passphrase, scrypt.N, scrypt.R, scrypt.P)); + account.Key = new KeyPair(GetPrivateKeyFromNEP2(db_contract.Account.Nep2key, passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P)); } return accounts; } @@ -323,14 +323,14 @@ private byte[] LoadStoredData(string name) } } - public static UserWallet Open(string path, string password) + public static UserWallet Open(string path, string password, ProtocolSettings settings) { - return new UserWallet(path, password.ToAesKey()); + return new UserWallet(path, password.ToAesKey(), settings); } - public static UserWallet Open(string path, SecureString password) + public static UserWallet Open(string path, SecureString password, ProtocolSettings settings) { - return new UserWallet(path, password.ToAesKey()); + return new UserWallet(path, password.ToAesKey(), settings); } private void SaveStoredData(string name, byte[] value) diff --git a/src/neo/Wallets/SQLite/UserWalletAccount.cs b/src/neo/Wallets/SQLite/UserWalletAccount.cs index d586f59d36..adfba104e6 100644 --- a/src/neo/Wallets/SQLite/UserWalletAccount.cs +++ b/src/neo/Wallets/SQLite/UserWalletAccount.cs @@ -1,13 +1,13 @@ namespace Neo.Wallets.SQLite { - internal class UserWalletAccount : WalletAccount + sealed class UserWalletAccount : WalletAccount { public KeyPair Key; public override bool HasKey => Key != null; - public UserWalletAccount(UInt160 scriptHash) - : base(scriptHash) + public UserWalletAccount(UInt160 scriptHash, ProtocolSettings settings) + : base(scriptHash, settings) { } diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 5d5b0ea501..05086e9581 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -21,6 +21,7 @@ namespace Neo.Wallets { public abstract class Wallet { + public ProtocolSettings ProtocolSettings { get; } public abstract string Name { get; } public string Path { get; } public abstract Version Version { get; } @@ -34,12 +35,9 @@ public abstract class Wallet public abstract WalletAccount GetAccount(UInt160 scriptHash); public abstract IEnumerable GetAccounts(); - internal Wallet() - { - } - - protected Wallet(string path) + protected Wallet(string path, ProtocolSettings settings) { + this.ProtocolSettings = settings; this.Path = path; } @@ -144,7 +142,7 @@ public BigDecimal GetBalance(DataCache snapshot, UInt160 asset_id, params UInt16 sb.EmitDynamicCall(asset_id, "decimals"); script = sb.ToArray(); } - using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, gas: 20000000L * accounts.Length); + using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, settings: ProtocolSettings, gas: 20000000L * accounts.Length); if (engine.State == VMState.FAULT) return new BigDecimal(BigInteger.Zero, 0); byte decimals = (byte)engine.ResultStack.Pop().GetInteger(); @@ -152,7 +150,7 @@ public BigDecimal GetBalance(DataCache snapshot, UInt160 asset_id, params UInt16 return new BigDecimal(amount, decimals); } - public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, int N = 16384, int r = 8, int p = 8) + public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, byte version, int N = 16384, int r = 8, int p = 8) { if (nep2 == null) throw new ArgumentNullException(nameof(nep2)); if (passphrase == null) throw new ArgumentNullException(nameof(passphrase)); @@ -175,7 +173,7 @@ public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, int N Array.Clear(derivedhalf2, 0, derivedhalf2.Length); ECPoint pubkey = Cryptography.ECC.ECCurve.Secp256r1.G * prikey; UInt160 script_hash = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); - string address = script_hash.ToAddress(); + string address = script_hash.ToAddress(version); if (!Encoding.ASCII.GetBytes(address).Sha256().Sha256().AsSpan(0, 4).SequenceEqual(addresshash)) throw new FormatException(); return prikey; @@ -235,7 +233,7 @@ public virtual WalletAccount Import(string wif) public virtual WalletAccount Import(string nep2, string passphrase, int N = 16384, int r = 8, int p = 8) { - byte[] privateKey = GetPrivateKeyFromNEP2(nep2, passphrase, N, r, p); + byte[] privateKey = GetPrivateKeyFromNEP2(nep2, passphrase, ProtocolSettings.AddressVersion, N, r, p); WalletAccount account = CreateAccount(privateKey); Array.Clear(privateKey, 0, privateKey.Length); return account; @@ -264,7 +262,7 @@ public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, using (ScriptBuilder sb2 = new ScriptBuilder()) { sb2.EmitDynamicCall(assetId, "balanceOf", account); - using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot)) + using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, settings: ProtocolSettings)) { if (engine.State.HasFlag(VMState.FAULT)) throw new InvalidOperationException($"Execution for {assetId}.balanceOf('{account}' fault"); @@ -340,7 +338,7 @@ private Transaction MakeTransaction(DataCache snapshot, byte[] script, Signer[] }; // will try to execute 'transfer' script to check if it works - using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.CreateSnapshot(), tx, gas: maxGas)) + using (ApplicationEngine engine = ApplicationEngine.Run(script, snapshot.CreateSnapshot(), tx, settings: ProtocolSettings, gas: maxGas)) { if (engine.State == VMState.FAULT) { @@ -395,7 +393,7 @@ public long CalculateNetworkFee(DataCache snapshot, Transaction tx) size += Array.Empty().GetVarSize() * 2; // Check verify cost - using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CreateSnapshot()); + using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CreateSnapshot(), settings: ProtocolSettings); engine.LoadContract(contract, md, CallFlags.None); if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); if (!engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.Hash} returns false."); @@ -443,7 +441,7 @@ public bool Sign(ContractParametersContext context) account = GetAccount(point); if (account?.HasKey != true) continue; KeyPair key = account.GetKey(); - byte[] signature = context.Verifiable.Sign(key); + byte[] signature = context.Verifiable.Sign(key, ProtocolSettings.Magic); fSuccess |= context.AddSignature(multiSigContract, key.PublicKey, signature); if (fSuccess) m--; if (context.Completed || m <= 0) break; @@ -454,7 +452,7 @@ public bool Sign(ContractParametersContext context) { // Try to sign with regular accounts KeyPair key = account.GetKey(); - byte[] signature = context.Verifiable.Sign(key); + byte[] signature = context.Verifiable.Sign(key, ProtocolSettings.Magic); fSuccess |= context.AddSignature(account.Contract, key.PublicKey, signature); continue; } diff --git a/src/neo/Wallets/WalletAccount.cs b/src/neo/Wallets/WalletAccount.cs index 5e2408a1d0..a62a7c1fd1 100644 --- a/src/neo/Wallets/WalletAccount.cs +++ b/src/neo/Wallets/WalletAccount.cs @@ -4,20 +4,22 @@ namespace Neo.Wallets { public abstract class WalletAccount { + protected readonly ProtocolSettings ProtocolSettings; public readonly UInt160 ScriptHash; public string Label; public bool IsDefault; public bool Lock; public Contract Contract; - public string Address => ScriptHash.ToAddress(); + public string Address => ScriptHash.ToAddress(ProtocolSettings.AddressVersion); public abstract bool HasKey { get; } public bool WatchOnly => Contract == null; public abstract KeyPair GetKey(); - protected WalletAccount(UInt160 scriptHash) + protected WalletAccount(UInt160 scriptHash, ProtocolSettings settings) { + this.ProtocolSettings = settings; this.ScriptHash = scriptHash; } } diff --git a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs index 92ce52bbaa..3bef6f9bb5 100644 --- a/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs +++ b/tests/neo.UnitTests/Ledger/UT_TrimmedBlock.cs @@ -105,7 +105,7 @@ public void TestDeserialize() newBlock.Deserialize(reader); } tblock.Hashes.Length.Should().Be(newBlock.Hashes.Length); - tblock.Header.ToJson().ToString().Should().Be(newBlock.Header.ToJson().ToString()); + tblock.Header.ToJson(ProtocolSettings.Default).ToString().Should().Be(newBlock.Header.ToJson(ProtocolSettings.Default).ToString()); } } } diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs index 29503277f7..c6d457859b 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Block.cs @@ -84,7 +84,7 @@ public void Serialize() UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(uut, val256, out var _, out var _, out var _, out var _, out var _, out var _, 1); - var hex = "0000000000000000000000000000000000000000000000000000000000000000000000003c92d2d37b91caa73ab056d0676d9bd78752a5e2e79e3ae27936e9b3a330e80ee913ff854c0000000000000000000000000000000000000000000000000000000001000111010000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000006c23be5d32679baa9c5c2aa0d329fd2a2441d7875d0f34d42f58f70428fbbbb9e913ff854c0000000000000000000000000000000000000000000000000000000001000111010000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; uut.ToArray().ToHexString().Should().Be(hex); } @@ -94,7 +94,7 @@ public void Deserialize() UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(new Block(), val256, out var merkRoot, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 1); - var hex = "0000000000000000000000000000000000000000000000000000000000000000000000003c92d2d37b91caa73ab056d0676d9bd78752a5e2e79e3ae27936e9b3a330e80ee913ff854c0000000000000000000000000000000000000000000000000000000001000111010000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; + var hex = "0000000000000000000000000000000000000000000000000000000000000000000000006c23be5d32679baa9c5c2aa0d329fd2a2441d7875d0f34d42f58f70428fbbbb9e913ff854c0000000000000000000000000000000000000000000000000000000001000111010000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000001000112010000"; using (MemoryStream ms = new MemoryStream(hex.HexToBytes(), false)) using (BinaryReader reader = new BinaryReader(ms)) @@ -164,13 +164,13 @@ public void ToJson() UInt256 val256 = UInt256.Zero; TestUtils.SetupBlockWithValues(uut, val256, out var merkRoot, out var val160, out var timestampVal, out var indexVal, out var scriptVal, out var transactionsVal, 1); - JObject jObj = uut.ToJson(); + JObject jObj = uut.ToJson(ProtocolSettings.Default); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0x0ffaf8fc7eeb34959e7220d316e3294290e84b6a183b4bdb5c7c14a8f80239ee"); + jObj["hash"].AsString().Should().Be("0x54b3a829333d9bb352eef69942317fe011251370c2212ad0d20d7c2a3974b26e"); jObj["size"].AsNumber().Should().Be(159); jObj["version"].AsNumber().Should().Be(0); jObj["previousblockhash"].AsString().Should().Be("0x0000000000000000000000000000000000000000000000000000000000000000"); - jObj["merkleroot"].AsString().Should().Be("0x0ee830a3b3e93679e23a9ee7e2a55287d79b6d67d056b03aa7ca917bd3d2923c"); + jObj["merkleroot"].AsString().Should().Be("0xb9bbfb2804f7582fd4340f5d87d741242afd29d3a02a5c9caa9b67325dbe236c"); jObj["time"].AsNumber().Should().Be(328665601001); jObj["index"].AsNumber().Should().Be(0); jObj["nextconsensus"].AsString().Should().Be("NKuyBkoGdZZSLyPbJEetheRhMjeznFZszf"); @@ -181,7 +181,7 @@ public void ToJson() jObj["tx"].Should().NotBeNull(); JArray txObj = (JArray)jObj["tx"]; - txObj[0]["hash"].AsString().Should().Be("0x0ee830a3b3e93679e23a9ee7e2a55287d79b6d67d056b03aa7ca917bd3d2923c"); + txObj[0]["hash"].AsString().Should().Be("0xb9bbfb2804f7582fd4340f5d87d741242afd29d3a02a5c9caa9b67325dbe236c"); txObj[0]["size"].AsNumber().Should().Be(53); txObj[0]["version"].AsNumber().Should().Be(0); ((JArray)txObj[0]["attributes"]).Count.Should().Be(0); diff --git a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs index b58c94110e..a8821acdd3 100644 --- a/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs +++ b/tests/neo.UnitTests/Network/P2P/Payloads/UT_Transaction.cs @@ -1059,9 +1059,9 @@ public void ToJson() } }; - JObject jObj = uut.ToJson(); + JObject jObj = uut.ToJson(ProtocolSettings.Default); jObj.Should().NotBeNull(); - jObj["hash"].AsString().Should().Be("0xe17382d26702bde77b00a9f23ea156b77c418764cbc45b2692088b5fde0336e3"); + jObj["hash"].AsString().Should().Be("0x0ab073429086d9e48fc87386122917989705d1c81fe4a60bf90e2fc228de3146"); jObj["size"].AsNumber().Should().Be(84); jObj["version"].AsNumber().Should().Be(0); ((JArray)jObj["attributes"]).Count.Should().Be(0); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 22d8361ac1..b4b7960e77 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -122,14 +122,6 @@ public void TestTrigger() ApplicationEngine engine2 = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); testNativeContract.TestTrigger(engine2); } - - [TestMethod] - public void TestTestCall() - { - var snapshot = TestBlockchain.GetTestSnapshot(); - ApplicationEngine engine = testNativeContract.TestCall(snapshot, "System.Blockchain.GetHeight", 0); - engine.ResultStack.Should().BeEmpty(); - } } public class TestNativeContract : NativeContract diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index e952bb61aa..60c42e0720 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -520,19 +520,15 @@ public void TestCalculateBonus() public void TestGetNextBlockValidators1() { var snapshot = TestBlockchain.GetTestSnapshot(); - using (ApplicationEngine engine = NativeContract.NEO.TestCall(snapshot, "getNextBlockValidators")) - { - var result = engine.ResultStack.Peek(); - result.GetType().Should().Be(typeof(VM.Types.Array)); - ((VM.Types.Array)result).Count.Should().Be(7); - ((VM.Types.Array)result)[0].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); - ((VM.Types.Array)result)[1].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - ((VM.Types.Array)result)[2].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - ((VM.Types.Array)result)[3].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - ((VM.Types.Array)result)[4].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - ((VM.Types.Array)result)[5].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - ((VM.Types.Array)result)[6].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - } + var result = (VM.Types.Array)NativeContract.NEO.Call(snapshot, "getNextBlockValidators"); + result.Count.Should().Be(7); + result[0].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + result[1].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + result[2].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + result[3].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + result[4].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + result[5].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + result[6].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); } [TestMethod] @@ -554,8 +550,7 @@ public void TestGetNextBlockValidators2() public void TestGetCandidates1() { var snapshot = TestBlockchain.GetTestSnapshot(); - using ApplicationEngine engine = NativeContract.NEO.TestCall(snapshot, "getCandidates"); - var array = engine.ResultStack.Pop(); + var array = (VM.Types.Array)NativeContract.NEO.Call(snapshot, "getCandidates"); array.Count.Should().Be(0); } @@ -627,33 +622,29 @@ public void TestCheckCandidate() public void TestGetCommittee() { var snapshot = TestBlockchain.GetTestSnapshot(); - using (ApplicationEngine engine = NativeContract.NEO.TestCall(snapshot, "getCommittee")) - { - var result = engine.ResultStack.Peek(); - result.GetType().Should().Be(typeof(VM.Types.Array)); - ((VM.Types.Array)result).Count.Should().Be(21); - ((VM.Types.Array)result)[0].GetSpan().ToHexString().Should().Be("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639"); - ((VM.Types.Array)result)[1].GetSpan().ToHexString().Should().Be("03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0"); - ((VM.Types.Array)result)[2].GetSpan().ToHexString().Should().Be("0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30"); - ((VM.Types.Array)result)[3].GetSpan().ToHexString().Should().Be("0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d"); - ((VM.Types.Array)result)[4].GetSpan().ToHexString().Should().Be("023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe"); - ((VM.Types.Array)result)[5].GetSpan().ToHexString().Should().Be("03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0"); - ((VM.Types.Array)result)[6].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); - ((VM.Types.Array)result)[7].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); - ((VM.Types.Array)result)[8].GetSpan().ToHexString().Should().Be("02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad"); - ((VM.Types.Array)result)[9].GetSpan().ToHexString().Should().Be("03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379"); - ((VM.Types.Array)result)[10].GetSpan().ToHexString().Should().Be("0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654"); - ((VM.Types.Array)result)[11].GetSpan().ToHexString().Should().Be("02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62"); - ((VM.Types.Array)result)[12].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); - ((VM.Types.Array)result)[13].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); - ((VM.Types.Array)result)[14].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); - ((VM.Types.Array)result)[15].GetSpan().ToHexString().Should().Be("03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050"); - ((VM.Types.Array)result)[16].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); - ((VM.Types.Array)result)[17].GetSpan().ToHexString().Should().Be("02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a"); - ((VM.Types.Array)result)[18].GetSpan().ToHexString().Should().Be("03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc"); - ((VM.Types.Array)result)[19].GetSpan().ToHexString().Should().Be("03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde"); - ((VM.Types.Array)result)[20].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); - } + var result = (VM.Types.Array)NativeContract.NEO.Call(snapshot, "getCommittee"); + result.Count.Should().Be(21); + result[0].GetSpan().ToHexString().Should().Be("020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639"); + result[1].GetSpan().ToHexString().Should().Be("03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0"); + result[2].GetSpan().ToHexString().Should().Be("0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30"); + result[3].GetSpan().ToHexString().Should().Be("0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d"); + result[4].GetSpan().ToHexString().Should().Be("023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe"); + result[5].GetSpan().ToHexString().Should().Be("03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0"); + result[6].GetSpan().ToHexString().Should().Be("02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70"); + result[7].GetSpan().ToHexString().Should().Be("024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d"); + result[8].GetSpan().ToHexString().Should().Be("02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad"); + result[9].GetSpan().ToHexString().Should().Be("03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379"); + result[10].GetSpan().ToHexString().Should().Be("0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654"); + result[11].GetSpan().ToHexString().Should().Be("02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62"); + result[12].GetSpan().ToHexString().Should().Be("02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e"); + result[13].GetSpan().ToHexString().Should().Be("03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"); + result[14].GetSpan().ToHexString().Should().Be("03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a"); + result[15].GetSpan().ToHexString().Should().Be("03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050"); + result[16].GetSpan().ToHexString().Should().Be("02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554"); + result[17].GetSpan().ToHexString().Should().Be("02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a"); + result[18].GetSpan().ToHexString().Should().Be("03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc"); + result[19].GetSpan().ToHexString().Should().Be("03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde"); + result[20].GetSpan().ToHexString().Should().Be("02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093"); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_Contract.cs b/tests/neo.UnitTests/SmartContract/UT_Contract.cs index 94f929e0ac..a1e905edbc 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Contract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Contract.cs @@ -14,23 +14,6 @@ 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[] expectedArray = new byte[40]; - expectedArray[0] = (byte)OpCode.PUSHDATA1; - expectedArray[1] = 0x21; - Array.Copy(key.PublicKey.EncodePoint(true), 0, expectedArray, 2, 33); - expectedArray[35] = (byte)OpCode.SYSCALL; - Array.Copy(BitConverter.GetBytes(ApplicationEngine.Neo_Crypto_CheckSig), 0, expectedArray, 36, 4); - Assert.AreEqual(expectedArray.ToScriptHash().ToAddress(), contract.Address); - } - [TestMethod] public void TestGetScriptHash() { diff --git a/tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs b/tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs index 96ae48d17b..d09da0cdf9 100644 --- a/tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs +++ b/tests/neo.UnitTests/SmartContract/UT_DeployedContract.cs @@ -8,7 +8,7 @@ namespace Neo.UnitTests.SmartContract public class UT_DeployedContract { [TestMethod] - public void TestGetAddress() + public void TestGetScriptHash() { var contract = new DeployedContract(new ContractState() { @@ -31,7 +31,6 @@ public void TestGetAddress() }); Assert.AreEqual("0xb2e3fe334830b4741fa5d762f2ab36b90b86c49b", contract.ScriptHash.ToString()); - Assert.AreEqual("Na7bMBy8KWZKSFBWTxeSKth1Je9AcWTpQM", contract.Address); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index ebd197d62e..d820dc4c4e 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -26,7 +26,7 @@ public void TestCheckSig() { var engine = GetEngine(true); IVerifiable iv = engine.ScriptContainer; - byte[] message = iv.GetHashData(); + byte[] message = iv.GetSignData(ProtocolSettings.Default.Magic); 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); @@ -41,7 +41,7 @@ public void TestCrypto_CheckMultiSig() { var engine = GetEngine(true); IVerifiable iv = engine.ScriptContainer; - byte[] message = iv.GetHashData(); + byte[] message = iv.GetSignData(ProtocolSettings.Default.Magic); 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}; diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 57cca0834a..3aff416fe2 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -292,7 +292,7 @@ public void TestCrypto_Verify() { var engine = GetEngine(true); IVerifiable iv = engine.ScriptContainer; - byte[] message = iv.GetHashData(); + byte[] message = iv.GetSignData(ProtocolSettings.Default.Magic); 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); diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index 8bf1ff8d5b..2fa51ecf58 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -97,7 +97,7 @@ public static NEP6Wallet GenerateTestWallet() wallet["accounts"] = new JArray(); wallet["extra"] = null; wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"3.0\",\"scrypt\":{\"n\":2,\"r\":1,\"p\":1},\"accounts\":[],\"extra\":null}"); - return new NEP6Wallet(wallet); + return new NEP6Wallet(null, ProtocolSettings.Default, wallet); } public static Transaction GetTransaction(UInt160 sender) diff --git a/tests/neo.UnitTests/TestWalletAccount.cs b/tests/neo.UnitTests/TestWalletAccount.cs index b8a242c578..fdb986194a 100644 --- a/tests/neo.UnitTests/TestWalletAccount.cs +++ b/tests/neo.UnitTests/TestWalletAccount.cs @@ -13,7 +13,7 @@ class TestWalletAccount : WalletAccount public override KeyPair GetKey() => key; public TestWalletAccount(UInt160 hash) - : base(hash) + : base(hash, ProtocolSettings.Default) { var mock = new Mock(); mock.SetupGet(p => p.ScriptHash).Returns(hash); diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index 9151d50c13..e117036565 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -17,18 +17,18 @@ namespace Neo.UnitTests public class UT_Helper { [TestMethod] - public void GetHashData() + public void GetSignData() { TestVerifiable verifiable = new TestVerifiable(); - byte[] res = verifiable.GetHashData(); - res.ToHexString().Should().Be("4e454f000774657374537472"); + byte[] res = verifiable.GetSignData(ProtocolSettings.Default.Magic); + res.ToHexString().Should().Be("4e454f0050b51da6bb366be3ea50140cda45ba7df575287c0371000b2037ed3898ff8bf5"); } [TestMethod] public void Sign() { TestVerifiable verifiable = new TestVerifiable(); - byte[] res = verifiable.Sign(new KeyPair(TestUtils.GetByteArray(32, 0x42))); + byte[] res = verifiable.Sign(new KeyPair(TestUtils.GetByteArray(32, 0x42)), ProtocolSettings.Default.Magic); res.Length.Should().Be(64); } diff --git a/tests/neo.UnitTests/UT_ProtocolSettings.cs b/tests/neo.UnitTests/UT_ProtocolSettings.cs index bce97cd5d8..71bb145057 100644 --- a/tests/neo.UnitTests/UT_ProtocolSettings.cs +++ b/tests/neo.UnitTests/UT_ProtocolSettings.cs @@ -1,44 +1,19 @@ using FluentAssertions; -using Microsoft.Extensions.Configuration; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Wallets; -using System.Collections.Generic; -using System.IO; -using System.Reflection; namespace Neo.UnitTests { [TestClass] public class UT_ProtocolSettings { - // since ProtocolSettings.Default is designed to be writable only once, use reflection to - // reset the underlying _default field to null before and after running tests in this class. - static void ResetProtocolSettings() - { - var defaultField = typeof(ProtocolSettings) - .GetField("_default", BindingFlags.Static | BindingFlags.NonPublic); - defaultField.SetValue(null, null); - } - - [TestInitialize] - public void Initialize() - { - ResetProtocolSettings(); - } - - [TestCleanup] - public void Cleanup() - { - ResetProtocolSettings(); - } - [TestMethod] public void CheckFirstLetterOfAddresses() { UInt160 min = UInt160.Parse("0x0000000000000000000000000000000000000000"); - min.ToAddress()[0].Should().Be('N'); + min.ToAddress(ProtocolSettings.Default.AddressVersion)[0].Should().Be('N'); UInt160 max = UInt160.Parse("0xffffffffffffffffffffffffffffffffffffffff"); - max.ToAddress()[0].Should().Be('N'); + max.ToAddress(ProtocolSettings.Default.AddressVersion)[0].Should().Be('N'); } [TestMethod] @@ -48,80 +23,6 @@ public void Default_Magic_should_be_mainnet_Magic_value() ProtocolSettings.Default.Magic.Should().Be(mainNetMagic); } - [TestMethod] - public void Can_initialize_ProtocolSettings() - { - var expectedMagic = 12345u; - - var dict = new Dictionary() - { - { "ProtocolConfiguration:Magic", $"{expectedMagic}" } - }; - - var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build(); - ProtocolSettings.Initialize(config).Should().BeTrue(); - ProtocolSettings.Default.Magic.Should().Be(expectedMagic); - } - - [TestMethod] - public void Initialize_ProtocolSettings_NativeBlockIndex() - { - var tempFile = Path.GetTempFileName(); - File.WriteAllText(tempFile, @" -{ - ""ProtocolConfiguration"": - { - ""NativeActivations"":{ ""test"":123 } - } -} -"); - var config = new ConfigurationBuilder().AddJsonFile(tempFile).Build(); - File.Delete(tempFile); - - ProtocolSettings.Initialize(config).Should().BeTrue(); - ProtocolSettings.Default.NativeActivations.Count.Should().Be(1); - ProtocolSettings.Default.NativeActivations["test"].Should().Be(123); - } - - [TestMethod] - public void Cant_initialize_ProtocolSettings_after_default_settings_used() - { - var mainNetMagic = 0x4F454Eu; - ProtocolSettings.Default.Magic.Should().Be(mainNetMagic); - - var updatedMagic = 54321u; - var dict = new Dictionary() - { - { "ProtocolConfiguration:Magic", $"{updatedMagic}" } - }; - - var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build(); - ProtocolSettings.Initialize(config).Should().BeFalse(); - ProtocolSettings.Default.Magic.Should().Be(mainNetMagic); - } - - [TestMethod] - public void Cant_initialize_ProtocolSettings_twice() - { - var expectedMagic = 12345u; - var dict = new Dictionary() - { - { "ProtocolConfiguration:Magic", $"{expectedMagic}" } - }; - - var config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build(); - ProtocolSettings.Initialize(config).Should().BeTrue(); - - var updatedMagic = 54321u; - dict = new Dictionary() - { - { "ProtocolConfiguration:Magic", $"{updatedMagic}" } - }; - config = new ConfigurationBuilder().AddInMemoryCollection(dict).Build(); - ProtocolSettings.Initialize(config).Should().BeFalse(); - ProtocolSettings.Default.Magic.Should().Be(expectedMagic); - } - [TestMethod] public void TestGetMemoryPoolMaxTransactions() { @@ -131,7 +32,7 @@ public void TestGetMemoryPoolMaxTransactions() [TestMethod] public void TestGetMillisecondsPerBlock() { - ProtocolSettings.Default.MillisecondsPerBlock.Should().Be(200); + ProtocolSettings.Default.MillisecondsPerBlock.Should().Be(15000); } [TestMethod] diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs index a4f36fa5d5..fb11039d39 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs +++ b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Account.cs @@ -23,7 +23,7 @@ public static void ClassSetup(TestContext context) byte[] privateKey = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01}; _keyPair = new KeyPair(privateKey); - _nep2 = _keyPair.Export("Satoshi", 2, 1, 1); + _nep2 = _keyPair.Export("Satoshi", ProtocolSettings.Default.AddressVersion, 2, 1, 1); } [TestInitialize] @@ -88,7 +88,7 @@ public void TestFromJson() json["contract"] = null; json["extra"] = null; NEP6Account account = NEP6Account.FromJson(json, _wallet); - account.ScriptHash.Should().Be("NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash()); + account.ScriptHash.Should().Be("NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash(ProtocolSettings.Default.AddressVersion)); account.Label.Should().BeNull(); account.IsDefault.Should().BeTrue(); account.Lock.Should().BeFalse(); diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index 1af95ce082..19c605c64d 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.IO.Json; +using Neo.SmartContract; using Neo.Wallets; using Neo.Wallets.NEP6; using Neo.Wallets.SQLite; @@ -10,6 +11,7 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; +using Contract = Neo.SmartContract.Contract; namespace Neo.UnitTests.Wallets.NEP6 { @@ -39,7 +41,7 @@ public static void ClassInit(TestContext context) } keyPair = new KeyPair(privateKey); testScriptHash = Neo.SmartContract.Contract.CreateSignatureContract(keyPair.PublicKey).ScriptHash; - nep2key = keyPair.Export("123", 2, 1, 1); + nep2key = keyPair.Export("123", ProtocolSettings.Default.AddressVersion, 2, 1, 1); } private NEP6Wallet CreateWallet() @@ -80,7 +82,7 @@ public void TestChangePassword() wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); File.WriteAllText(wPath, wallet.ToString()); - uut = new NEP6Wallet(wPath); + uut = new NEP6Wallet(wPath, ProtocolSettings.Default); uut.Unlock("123"); uut.CreateAccount(keyPair.PrivateKey); uut.ChangePassword("456", "123").Should().BeFalse(); @@ -93,11 +95,11 @@ public void TestChangePassword() [TestMethod] public void TestConstructorWithPathAndName() { - NEP6Wallet wallet = new NEP6Wallet(wPath); + NEP6Wallet wallet = new NEP6Wallet(wPath, ProtocolSettings.Default); Assert.AreEqual("name", wallet.Name); Assert.AreEqual(new ScryptParameters(2, 1, 1).ToJson().ToString(), wallet.Scrypt.ToJson().ToString()); Assert.AreEqual(new Version("3.0").ToString(), wallet.Version.ToString()); - wallet = new NEP6Wallet("", "test"); + wallet = new NEP6Wallet("", ProtocolSettings.Default, "test"); Assert.AreEqual("test", wallet.Name); Assert.AreEqual(ScryptParameters.Default.ToJson().ToString(), wallet.Scrypt.ToJson().ToString()); Assert.AreEqual(Version.Parse("3.0"), wallet.Version); @@ -113,7 +115,7 @@ public void TestConstructorWithJObject() wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); wallet.ToString().Should().Be("{\"name\":\"test\",\"version\":\"3.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":{}}"); - NEP6Wallet w = new NEP6Wallet(wallet); + NEP6Wallet w = new NEP6Wallet(null, ProtocolSettings.Default, wallet); Assert.AreEqual("test", w.Name); Assert.AreEqual(Version.Parse("3.0").ToString(), w.Version.ToString()); } @@ -205,7 +207,7 @@ public void TestCreateAccountWithScriptHash() [TestMethod] public void TestDecryptKey() { - string nep2key = keyPair.Export("123", 2, 1, 1); + string nep2key = keyPair.Export("123", ProtocolSettings.Default.AddressVersion, 2, 1, 1); uut.Unlock("123"); KeyPair key1 = uut.DecryptKey(nep2key); bool result = key1.Equals(keyPair); @@ -235,13 +237,13 @@ public void TestGetAccount() result = uut.Contains(testScriptHash); Assert.AreEqual(true, result); WalletAccount account = uut.GetAccount(testScriptHash); - Assert.AreEqual(Neo.SmartContract.Contract.CreateSignatureContract(keyPair.PublicKey).Address, account.Address); + Assert.AreEqual(Contract.CreateSignatureRedeemScript(keyPair.PublicKey).ToScriptHash().ToAddress(ProtocolSettings.Default.AddressVersion), account.Address); } [TestMethod] public void TestGetAccounts() { - Dictionary keys = new Dictionary(); + Dictionary keys = new Dictionary(); uut.Unlock("123"); byte[] privateKey = new byte[32]; using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) @@ -249,14 +251,13 @@ public void TestGetAccounts() rng.GetBytes(privateKey); } KeyPair key = new KeyPair(privateKey); - Neo.SmartContract.Contract contract = Neo.SmartContract.Contract.CreateSignatureContract(key.PublicKey); - keys.Add(contract.Address, key); - keys.Add(Neo.SmartContract.Contract.CreateSignatureContract(keyPair.PublicKey).Address, keyPair); + keys.Add(Contract.CreateSignatureRedeemScript(key.PublicKey).ToScriptHash(), key); + keys.Add(Contract.CreateSignatureRedeemScript(keyPair.PublicKey).ToScriptHash(), keyPair); uut.CreateAccount(key.PrivateKey); uut.CreateAccount(keyPair.PrivateKey); foreach (var account in uut.GetAccounts()) { - if (!keys.TryGetValue(account.Address, out KeyPair k)) + if (!keys.ContainsKey(account.ScriptHash)) { Assert.Fail(); } @@ -319,7 +320,7 @@ public void TestImportNep2() wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); - uut = new NEP6Wallet(wallet); + uut = new NEP6Wallet(null, ProtocolSettings.Default, wallet); result = uut.Contains(testScriptHash); Assert.AreEqual(false, result); uut.Import(nep2key, "123", 2, 1, 1); @@ -344,10 +345,10 @@ public void TestLock() public void TestMigrate() { string path = GetRandomPath(); - UserWallet uw = UserWallet.Create(path, "123"); + UserWallet uw = UserWallet.Create(path, "123", ProtocolSettings.Default); uw.CreateAccount(keyPair.PrivateKey); string npath = CreateWalletFile(); // Scrypt test values - NEP6Wallet nw = NEP6Wallet.Migrate(npath, path, "123"); + NEP6Wallet nw = NEP6Wallet.Migrate(npath, path, "123", ProtocolSettings.Default); bool result = nw.Contains(testScriptHash); Assert.AreEqual(true, result); if (File.Exists(path)) File.Delete(path); @@ -364,7 +365,7 @@ public void TestSave() wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); File.WriteAllText(wPath, wallet.ToString()); - uut = new NEP6Wallet(wPath); + uut = new NEP6Wallet(wPath, ProtocolSettings.Default); uut.Unlock("123"); uut.CreateAccount(keyPair.PrivateKey); bool result = uut.Contains(testScriptHash); @@ -405,8 +406,8 @@ public void TestVerifyPassword() wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); - uut = new NEP6Wallet(wallet); - nep2key = keyPair.Export("123", 2, 1, 1); + uut = new NEP6Wallet(null, ProtocolSettings.Default, wallet); + nep2key = keyPair.Export("123", ProtocolSettings.Default.AddressVersion, 2, 1, 1); uut.Import(nep2key, "123", 2, 1, 1); Assert.IsFalse(uut.VerifyPassword("1")); Assert.IsTrue(uut.VerifyPassword("123")); diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs index e6cb2e821e..b56dbede0a 100644 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs +++ b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWallet.cs @@ -29,7 +29,7 @@ public static string GetRandomPath() public static void Setup(TestContext ctx) { path = GetRandomPath(); - wallet = UserWallet.Create(path, "123456", new ScryptParameters(2, 1, 1)); + wallet = UserWallet.Create(path, "123456", ProtocolSettings.Default, new ScryptParameters(2, 1, 1)); byte[] privateKey = new byte[32]; using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) { @@ -131,14 +131,14 @@ public void TestCreateAndOpenSecureString() ss.AppendChar('b'); ss.AppendChar('c'); - var w1 = UserWallet.Create(myPath, ss, new ScryptParameters(0, 0, 0)); + var w1 = UserWallet.Create(myPath, ss, ProtocolSettings.Default, new ScryptParameters(0, 0, 0)); w1.Should().NotBeNull(); - var w2 = UserWallet.Open(myPath, ss); + var w2 = UserWallet.Open(myPath, ss, ProtocolSettings.Default); w2.Should().NotBeNull(); ss.AppendChar('d'); - Action action = () => UserWallet.Open(myPath, ss); + Action action = () => UserWallet.Open(myPath, ss, ProtocolSettings.Default); action.Should().Throw(); TestUtils.DeleteFile(myPath); @@ -171,10 +171,10 @@ public void TestGetVersion() [TestMethod] public void TestOpen() { - var w1 = UserWallet.Open(path, "123456"); + var w1 = UserWallet.Open(path, "123456", ProtocolSettings.Default); w1.Should().NotBeNull(); - Action action = () => UserWallet.Open(path, "123"); + Action action = () => UserWallet.Open(path, "123", ProtocolSettings.Default); action.Should().Throw(); } diff --git a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs index fe4fd48cb3..be3d756b19 100644 --- a/tests/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs +++ b/tests/neo.UnitTests/Wallets/SQLite/UT_UserWalletAccount.cs @@ -9,21 +9,21 @@ public class UT_UserWalletAccount [TestMethod] public void TestGenerator() { - UserWalletAccount account = new UserWalletAccount(UInt160.Zero); + UserWalletAccount account = new UserWalletAccount(UInt160.Zero, ProtocolSettings.Default); Assert.IsNotNull(account); } [TestMethod] public void TestGetHasKey() { - UserWalletAccount account = new UserWalletAccount(UInt160.Zero); + UserWalletAccount account = new UserWalletAccount(UInt160.Zero, ProtocolSettings.Default); Assert.AreEqual(false, account.HasKey); } [TestMethod] public void TestGetKey() { - UserWalletAccount account = new UserWalletAccount(UInt160.Zero); + UserWalletAccount account = new UserWalletAccount(UInt160.Zero, ProtocolSettings.Default); Assert.AreEqual(null, account.GetKey()); } } diff --git a/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs b/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs index f50c9065f8..d87811cf7f 100644 --- a/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs +++ b/tests/neo.UnitTests/Wallets/UT_AssetDescriptor.cs @@ -14,7 +14,7 @@ public void TestConstructorWithNonexistAssetId() var snapshot = TestBlockchain.GetTestSnapshot(); Action action = () => { - var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4")); + var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, ProtocolSettings.Default, UInt160.Parse("01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4")); }; action.Should().Throw(); } @@ -23,7 +23,7 @@ public void TestConstructorWithNonexistAssetId() public void Check_GAS() { var snapshot = TestBlockchain.GetTestSnapshot(); - var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, NativeContract.GAS.Hash); + var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, ProtocolSettings.Default, NativeContract.GAS.Hash); descriptor.AssetId.Should().Be(NativeContract.GAS.Hash); descriptor.AssetName.Should().Be(nameof(GasToken)); descriptor.ToString().Should().Be(nameof(GasToken)); @@ -35,7 +35,7 @@ public void Check_GAS() public void Check_NEO() { var snapshot = TestBlockchain.GetTestSnapshot(); - var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, NativeContract.NEO.Hash); + var descriptor = new Neo.Wallets.AssetDescriptor(snapshot, ProtocolSettings.Default, NativeContract.NEO.Hash); descriptor.AssetId.Should().Be(NativeContract.NEO.Hash); descriptor.AssetName.Should().Be(nameof(NeoToken)); descriptor.ToString().Should().Be(nameof(NeoToken)); diff --git a/tests/neo.UnitTests/Wallets/UT_Wallet.cs b/tests/neo.UnitTests/Wallets/UT_Wallet.cs index 2802d68e0a..2bc76c0007 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallet.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallet.cs @@ -20,6 +20,10 @@ internal class MyWallet : Wallet Dictionary accounts = new Dictionary(); + public MyWallet() : base(null, ProtocolSettings.Default) + { + } + public override bool ChangePassword(string oldPassword, string newPassword) { throw new NotImplementedException(); @@ -100,7 +104,7 @@ public class UT_Wallet public static void ClassInit(TestContext context) { glkey = UT_Crypto.generateCertainKey(32); - nep2Key = glkey.Export("pwd", 2, 1, 1); + nep2Key = glkey.Export("pwd", ProtocolSettings.Default.AddressVersion, 2, 1, 1); } [TestMethod] @@ -233,19 +237,19 @@ public void TestGetBalance() [TestMethod] public void TestGetPrivateKeyFromNEP2() { - Action action = () => Wallet.GetPrivateKeyFromNEP2(null, null, 2, 1, 1); + Action action = () => Wallet.GetPrivateKeyFromNEP2(null, null, ProtocolSettings.Default.AddressVersion, 2, 1, 1); action.Should().Throw(); - action = () => Wallet.GetPrivateKeyFromNEP2("TestGetPrivateKeyFromNEP2", null, 2, 1, 1); + action = () => Wallet.GetPrivateKeyFromNEP2("TestGetPrivateKeyFromNEP2", null, ProtocolSettings.Default.AddressVersion, 2, 1, 1); action.Should().Throw(); - action = () => Wallet.GetPrivateKeyFromNEP2("3vQB7B6MrGQZaxCuFg4oh", "TestGetPrivateKeyFromNEP2", 2, 1, 1); + action = () => Wallet.GetPrivateKeyFromNEP2("3vQB7B6MrGQZaxCuFg4oh", "TestGetPrivateKeyFromNEP2", ProtocolSettings.Default.AddressVersion, 2, 1, 1); action.Should().Throw(); - action = () => Wallet.GetPrivateKeyFromNEP2(nep2Key, "Test", 2, 1, 1); + action = () => Wallet.GetPrivateKeyFromNEP2(nep2Key, "Test", ProtocolSettings.Default.AddressVersion, 2, 1, 1); action.Should().Throw(); - Wallet.GetPrivateKeyFromNEP2(nep2Key, "pwd", 2, 1, 1).Should().BeEquivalentTo(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }); + Wallet.GetPrivateKeyFromNEP2(nep2Key, "pwd", ProtocolSettings.Default.AddressVersion, 2, 1, 1).Should().BeEquivalentTo(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }); } [TestMethod] diff --git a/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs b/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs index 42ebf6bb63..13ba4d113f 100644 --- a/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs +++ b/tests/neo.UnitTests/Wallets/UT_WalletAccount.cs @@ -11,7 +11,7 @@ public class MyWalletAccount : WalletAccount public override bool HasKey => key != null; public MyWalletAccount(UInt160 scriptHash) - : base(scriptHash) + : base(scriptHash, ProtocolSettings.Default) { } diff --git a/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs b/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs index 9f0167c55a..8c5f23f5a4 100644 --- a/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs +++ b/tests/neo.UnitTests/Wallets/UT_Wallets_Helper.cs @@ -15,18 +15,18 @@ public void TestToScriptHash() { byte[] array = { 0x01 }; UInt160 scriptHash = new UInt160(Crypto.Hash160(array)); - "NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash().Should().Be(scriptHash); + "NdtB8RXRmJ7Nhw1FPTm7E6HoDZGnDw37nf".ToScriptHash(ProtocolSettings.Default.AddressVersion).Should().Be(scriptHash); - Action action = () => "3vQB7B6MrGQZaxCuFg4oh".ToScriptHash(); + Action action = () => "3vQB7B6MrGQZaxCuFg4oh".ToScriptHash(ProtocolSettings.Default.AddressVersion); action.Should().Throw(); - var address = scriptHash.ToAddress(); + var address = scriptHash.ToAddress(ProtocolSettings.Default.AddressVersion); Span data = stackalloc byte[21]; // NEO version is 0x17 data[0] = 0x01; scriptHash.ToArray().CopyTo(data[1..]); address = Base58.Base58CheckEncode(data); - action = () => address.ToScriptHash(); + action = () => address.ToScriptHash(ProtocolSettings.Default.AddressVersion); action.Should().Throw(); } } diff --git a/tests/neo.UnitTests/neo.UnitTests.csproj b/tests/neo.UnitTests/neo.UnitTests.csproj index 9809eb5cbf..dfa7212163 100644 --- a/tests/neo.UnitTests/neo.UnitTests.csproj +++ b/tests/neo.UnitTests/neo.UnitTests.csproj @@ -8,12 +8,6 @@ true - - - PreserveNewest - - - diff --git a/tests/neo.UnitTests/protocol.json b/tests/neo.UnitTests/protocol.json deleted file mode 100644 index 50f802e7cf..0000000000 --- a/tests/neo.UnitTests/protocol.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "ProtocolConfiguration": { - "MillisecondsPerBlock": 200 - } -} From d73d190c87eb999c36934c1fe93aaee8c818701a Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 10 Feb 2021 13:51:14 +0100 Subject: [PATCH 17/63] Clean EmitDynamicCall code (#2321) * Clean EmitDynamicCall code * Add Safe callflag * Remove Safe * Update AssetDescriptor.cs Co-authored-by: Erik Zhang --- src/neo/VM/Helper.cs | 41 +++++++------------ src/neo/Wallets/AssetDescriptor.cs | 8 ++-- src/neo/Wallets/Wallet.cs | 8 ++-- .../SmartContract/UT_InteropService.cs | 4 +- .../SmartContract/UT_Syscalls.cs | 14 +++---- 5 files changed, 32 insertions(+), 43 deletions(-) diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 4eed45e1d2..7a6165b397 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -46,36 +46,25 @@ public static ScriptBuilder Emit(this ScriptBuilder sb, params OpCode[] ops) return sb; } - public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation) - { - sb.Emit(OpCode.NEWARRAY0); - sb.EmitPush(CallFlags.All); - sb.EmitPush(operation); - sb.EmitPush(scriptHash); - sb.EmitSysCall(ApplicationEngine.System_Contract_Call); - return sb; - } - - public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params ContractParameter[] args) + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params object[] args) { - for (int i = args.Length - 1; i >= 0; i--) - sb.EmitPush(args[i]); - sb.EmitPush(args.Length); - sb.Emit(OpCode.PACK); - sb.EmitPush(CallFlags.All); - sb.EmitPush(operation); - sb.EmitPush(scriptHash); - sb.EmitSysCall(ApplicationEngine.System_Contract_Call); - return sb; + return EmitDynamicCall(sb, scriptHash, operation, CallFlags.All, args); } - public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params object[] args) + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, CallFlags flags, params object[] args) { - for (int i = args.Length - 1; i >= 0; i--) - sb.EmitPush(args[i]); - sb.EmitPush(args.Length); - sb.Emit(OpCode.PACK); - sb.EmitPush(CallFlags.All); + if (args.Length == 0) + { + sb.Emit(OpCode.NEWARRAY0); + } + else + { + for (int i = args.Length - 1; i >= 0; i--) + sb.EmitPush(args[i]); + sb.EmitPush(args.Length); + sb.Emit(OpCode.PACK); + } + sb.EmitPush(flags); sb.EmitPush(operation); sb.EmitPush(scriptHash); sb.EmitSysCall(ApplicationEngine.System_Contract_Call); diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index a85b4cc874..fef54a2a15 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -16,17 +16,17 @@ public class AssetDescriptor public AssetDescriptor(DataCache snapshot, ProtocolSettings settings, UInt160 asset_id) { var contract = NativeContract.ContractManagement.GetContract(snapshot, asset_id); - if (contract is null) throw new ArgumentException(); + if (contract is null) throw new ArgumentException(null, nameof(asset_id)); byte[] script; using (ScriptBuilder sb = new ScriptBuilder()) { - sb.EmitDynamicCall(asset_id, "decimals"); - sb.EmitDynamicCall(asset_id, "symbol"); + sb.EmitDynamicCall(asset_id, "decimals", CallFlags.ReadOnly); + sb.EmitDynamicCall(asset_id, "symbol", CallFlags.ReadOnly); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, settings: settings, gas: 0_10000000); - if (engine.State == VMState.FAULT) throw new ArgumentException(null, nameof(asset_id)); + if (engine.State != VMState.HALT) throw new ArgumentException(null, nameof(asset_id)); this.AssetId = asset_id; this.AssetName = contract.Manifest.Name; this.Symbol = engine.ResultStack.Pop().GetString(); diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 05086e9581..a2c91495fb 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -136,10 +136,10 @@ public BigDecimal GetBalance(DataCache snapshot, UInt160 asset_id, params UInt16 sb.EmitPush(0); foreach (UInt160 account in accounts) { - sb.EmitDynamicCall(asset_id, "balanceOf", account); + sb.EmitDynamicCall(asset_id, "balanceOf", CallFlags.ReadOnly, account); sb.Emit(OpCode.ADD); } - sb.EmitDynamicCall(asset_id, "decimals"); + sb.EmitDynamicCall(asset_id, "decimals", CallFlags.ReadOnly); script = sb.ToArray(); } using ApplicationEngine engine = ApplicationEngine.Run(script, snapshot, settings: ProtocolSettings, gas: 20000000L * accounts.Length); @@ -261,10 +261,10 @@ public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, foreach (UInt160 account in accounts) using (ScriptBuilder sb2 = new ScriptBuilder()) { - sb2.EmitDynamicCall(assetId, "balanceOf", account); + sb2.EmitDynamicCall(assetId, "balanceOf", CallFlags.ReadOnly, account); using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, settings: ProtocolSettings)) { - if (engine.State.HasFlag(VMState.FAULT)) + if (engine.State != VMState.HALT) throw new InvalidOperationException($"Execution for {assetId}.balanceOf('{account}' fault"); BigInteger value = engine.ResultStack.Pop().GetInteger(); if (value.Sign > 0) balances.Add((account, value)); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 3aff416fe2..538bac3597 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -211,7 +211,7 @@ public void TestExecutionEngine_GetCallingScriptHash() var contract = new ContractState() { - Manifest = TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer), + Manifest = TestUtils.CreateManifest("test", ContractParameterType.Any, ContractParameterType.String, ContractParameterType.Integer), Nef = new NefFile { Script = scriptA.ToArray() }, Hash = scriptA.ToArray().ToScriptHash() }; @@ -219,7 +219,7 @@ public void TestExecutionEngine_GetCallingScriptHash() engine.Snapshot.AddContract(contract.Hash, contract); using ScriptBuilder scriptB = new ScriptBuilder(); - scriptB.EmitDynamicCall(contract.Hash, "test", 0, 1); + scriptB.EmitDynamicCall(contract.Hash, "test", "0", 1); engine.LoadScript(scriptB.ToArray()); Assert.AreEqual(VMState.HALT, engine.Execute()); diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index f5dc93aafb..f1b94e27fb 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -220,9 +220,9 @@ public void System_Runtime_GetInvocationCounter() snapshot.DeleteContract(contractA.Hash); snapshot.DeleteContract(contractB.Hash); snapshot.DeleteContract(contractC.Hash); - contractA.Manifest = TestUtils.CreateManifest("dummyMain", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer); - contractB.Manifest = TestUtils.CreateManifest("dummyMain", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer); - contractC.Manifest = TestUtils.CreateManifest("dummyMain", ContractParameterType.Any, ContractParameterType.Integer, ContractParameterType.Integer); + contractA.Manifest = TestUtils.CreateManifest("dummyMain", ContractParameterType.Any, ContractParameterType.String, ContractParameterType.Integer); + contractB.Manifest = TestUtils.CreateManifest("dummyMain", ContractParameterType.Any, ContractParameterType.String, ContractParameterType.Integer); + contractC.Manifest = TestUtils.CreateManifest("dummyMain", ContractParameterType.Any, ContractParameterType.String, ContractParameterType.Integer); snapshot.AddContract(contractA.Hash, contractA); snapshot.AddContract(contractB.Hash, contractB); snapshot.AddContract(contractC.Hash, contractC); @@ -232,10 +232,10 @@ public void System_Runtime_GetInvocationCounter() using (var script = new ScriptBuilder()) { - script.EmitDynamicCall(contractA.Hash, "dummyMain", 0, 1); - script.EmitDynamicCall(contractB.Hash, "dummyMain", 0, 1); - script.EmitDynamicCall(contractB.Hash, "dummyMain", 0, 1); - script.EmitDynamicCall(contractC.Hash, "dummyMain", 0, 1); + script.EmitDynamicCall(contractA.Hash, "dummyMain", "0", 1); + script.EmitDynamicCall(contractB.Hash, "dummyMain", "0", 1); + script.EmitDynamicCall(contractB.Hash, "dummyMain", "0", 1); + script.EmitDynamicCall(contractC.Hash, "dummyMain", "0", 1); // Execute From b58f652dcd0c87d978f11d0f1cbf7ef37f0c1063 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 10 Feb 2021 20:37:41 +0100 Subject: [PATCH 18/63] Set max verification gas for state dependant signatures (#2315) * Set max verification for state dependant * Update Transaction.cs * Prevent VerifyStateDependent if not enough gas in StaticIndependent * Check gas in wallet * Fix wallet condition * Update Transaction.cs * Remove wallet condition Co-authored-by: Erik Zhang --- src/neo/Network/P2P/Payloads/Transaction.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 050e75d2d6..ddcdb485c3 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using static Neo.SmartContract.Helper; using Array = Neo.VM.Types.Array; namespace Neo.Network.P2P.Payloads @@ -296,13 +297,14 @@ public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, Data long net_fee = NetworkFee - Size * NativeContract.Policy.GetFeePerByte(snapshot); if (net_fee < 0) return VerifyResult.InsufficientFunds; + if (net_fee > MaxVerificationGas) net_fee = MaxVerificationGas; uint execFeeFactor = NativeContract.Policy.GetExecFeeFactor(snapshot); for (int i = 0; i < hashes.Length; i++) { if (witnesses[i].VerificationScript.IsSignatureContract()) - net_fee -= execFeeFactor * SmartContract.Helper.SignatureContractCost(); + net_fee -= execFeeFactor * SignatureContractCost(); else if (witnesses[i].VerificationScript.IsMultiSigContract(out int m, out int n)) - net_fee -= execFeeFactor * SmartContract.Helper.MultiSignatureContractCost(m, n); + net_fee -= execFeeFactor * MultiSignatureContractCost(m, n); else { if (!this.VerifyWitness(settings, snapshot, hashes[i], witnesses[i], net_fee, out long fee)) @@ -325,11 +327,17 @@ public virtual VerifyResult VerifyStateIndependent(ProtocolSettings settings) { return VerifyResult.Invalid; } + long net_fee = Math.Min(NetworkFee, MaxVerificationGas); UInt160[] hashes = GetScriptHashesForVerifying(null); for (int i = 0; i < hashes.Length; i++) if (witnesses[i].VerificationScript.IsStandardContract()) - if (!this.VerifyWitness(settings, null, hashes[i], witnesses[i], SmartContract.Helper.MaxVerificationGas, out _)) + if (!this.VerifyWitness(settings, null, hashes[i], witnesses[i], net_fee, out long fee)) return VerifyResult.Invalid; + else + { + net_fee -= fee; + if (net_fee < 0) return VerifyResult.InsufficientFunds; + } return VerifyResult.Succeed; } From 8b9f1d2132d19199b04dc034cab3b3b53e9101b5 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 11 Feb 2021 10:49:44 +0800 Subject: [PATCH 19/63] Allow reverify inventories (#2326) --- src/neo/Ledger/Blockchain.cs | 14 +++++++++----- src/neo/Ledger/TransactionRouter.cs | 11 +++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 982b28f193..e87a6cbcec 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -25,7 +25,7 @@ public class Import { public IEnumerable Blocks; public bool Verify = tru public class ImportCompleted { } public class FillMemoryPool { public IEnumerable Transactions; } public class FillCompleted { } - internal class PreverifyCompleted { public Transaction Transaction; public VerifyResult Result; } + public class Reverify { public IInventory[] Inventories; } public class RelayResult { public IInventory Inventory; public VerifyResult Result; } private class UnverifiedBlocksList { public LinkedList Blocks = new LinkedList(); public HashSet Nodes = new HashSet(); } @@ -245,10 +245,10 @@ private VerifyResult OnNewTransaction(Transaction transaction) return system.MemPool.TryAdd(transaction, system.StoreView); } - private void OnPreverifyCompleted(PreverifyCompleted task) + private void OnPreverifyCompleted(TransactionRouter.PreverifyCompleted task) { if (task.Result == VerifyResult.Succeed) - OnInventory(task.Transaction, true); + OnInventory(task.Transaction, task.Relay); else SendRelayResult(task.Transaction, task.Result); } @@ -275,9 +275,13 @@ protected override void OnReceive(object message) case IInventory inventory: OnInventory(inventory); break; - case PreverifyCompleted task: + case TransactionRouter.PreverifyCompleted task: OnPreverifyCompleted(task); break; + case Reverify reverify: + foreach (IInventory inventory in reverify.Inventories) + OnInventory(inventory, false); + break; case Idle _: if (system.MemPool.ReVerifyTopUnverifiedTransactionsIfNeeded(MaxTxToReverifyPerIdle, system.StoreView)) Self.Tell(Idle.Instance, ActorRefs.NoSender); @@ -290,7 +294,7 @@ private void OnTransaction(Transaction tx) if (ContainsTransaction(tx.Hash)) SendRelayResult(tx, VerifyResult.AlreadyExists); else - txrouter.Tell(tx, Sender); + txrouter.Forward(new TransactionRouter.Preverify(tx, true)); } private void Persist(Block block) diff --git a/src/neo/Ledger/TransactionRouter.cs b/src/neo/Ledger/TransactionRouter.cs index 2d1e38f114..4ab5536874 100644 --- a/src/neo/Ledger/TransactionRouter.cs +++ b/src/neo/Ledger/TransactionRouter.cs @@ -7,6 +7,9 @@ namespace Neo.Ledger { internal class TransactionRouter : UntypedActor { + public record Preverify(Transaction Transaction, bool Relay); + public record PreverifyCompleted(Transaction Transaction, bool Relay, VerifyResult Result); + private readonly NeoSystem system; public TransactionRouter(NeoSystem system) @@ -16,12 +19,8 @@ public TransactionRouter(NeoSystem system) protected override void OnReceive(object message) { - if (message is not Transaction tx) return; - system.Blockchain.Tell(new Blockchain.PreverifyCompleted - { - Transaction = tx, - Result = tx.VerifyStateIndependent(system.Settings) - }, Sender); + if (message is not Preverify preverify) return; + system.Blockchain.Tell(new PreverifyCompleted(preverify.Transaction, preverify.Relay, preverify.Transaction.VerifyStateIndependent(system.Settings)), Sender); } internal static Props Props(NeoSystem system) From f92772a57c4b7113ad3ad20e8f9ba6e483963bed Mon Sep 17 00:00:00 2001 From: Shargon Date: Thu, 11 Feb 2021 09:04:44 +0100 Subject: [PATCH 20/63] Fix compile (#2328) Co-authored-by: Erik Zhang --- src/neo/SmartContract/Native/NativeContract.cs | 4 ++-- src/neo/neo.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index cb808759df..388fc8d204 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -50,9 +50,9 @@ protected NativeContract() { foreach (ContractMethodMetadata method in descriptors) { - method.Descriptor.Offset = sb.Offset; + method.Descriptor.Offset = sb.Length; sb.EmitPush(0); //version - methods.Add(sb.Offset, method); + methods.Add(sb.Length, method); sb.EmitSysCall(ApplicationEngine.System_Contract_CallNative); sb.Emit(OpCode.RET); } diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 73789a7ad7..f3e6698599 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -29,7 +29,7 @@ - + From d517a85e26381c4c1c005bbdbaba799bd35f6311 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 11 Feb 2021 16:11:58 +0800 Subject: [PATCH 21/63] Remove Utility.LoadConfig (#2330) Co-authored-by: Shargon --- src/neo/ProtocolSettings.cs | 5 +++-- src/neo/Utility.cs | 37 ------------------------------- tests/neo.UnitTests/UT_Utility.cs | 22 ------------------ 3 files changed, 3 insertions(+), 61 deletions(-) delete mode 100644 tests/neo.UnitTests/UT_Utility.cs diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index 3ca2723f10..7576c4b5a6 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -69,9 +69,10 @@ public record ProtocolSettings NativeActivations = ImmutableDictionary.Empty }; - public static ProtocolSettings Load(string config) + public static ProtocolSettings Load(string path, bool optional = true) { - IConfigurationSection section = Utility.LoadConfig(config).GetSection("ProtocolConfiguration"); + IConfigurationRoot config = new ConfigurationBuilder().AddJsonFile(path, optional).Build(); + IConfigurationSection section = config.GetSection("ProtocolConfiguration"); return new ProtocolSettings { Magic = section.GetValue("Magic", Default.Magic), diff --git a/src/neo/Utility.cs b/src/neo/Utility.cs index 7ec4a23d43..7570b93680 100644 --- a/src/neo/Utility.cs +++ b/src/neo/Utility.cs @@ -1,10 +1,6 @@ using Akka.Actor; using Akka.Event; -using Microsoft.Extensions.Configuration; using Neo.Plugins; -using System; -using System.IO; -using System.Reflection; using System.Text; namespace Neo @@ -29,39 +25,6 @@ static Utility() StrictUTF8.EncoderFallback = EncoderFallback.ExceptionFallback; } - /// - /// Load configuration with different Environment Variable - /// - /// Configuration - /// IConfigurationRoot - public static IConfigurationRoot LoadConfig(string config) - { - var env = Environment.GetEnvironmentVariable("NEO_NETWORK"); - var configFile = string.IsNullOrWhiteSpace(env) ? $"{config}.json" : $"{config}.{env}.json"; - - // Working directory - var file = Path.Combine(Environment.CurrentDirectory, configFile); - if (!File.Exists(file)) - { - // EntryPoint folder - file = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), configFile); - if (!File.Exists(file)) - { - // neo.dll folder - file = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), configFile); - if (!File.Exists(file)) - { - // default config - return new ConfigurationBuilder().Build(); - } - } - } - - return new ConfigurationBuilder() - .AddJsonFile(file, true) - .Build(); - } - public static void Log(string source, LogLevel level, object message) { foreach (ILogPlugin plugin in Plugin.Loggers) diff --git a/tests/neo.UnitTests/UT_Utility.cs b/tests/neo.UnitTests/UT_Utility.cs deleted file mode 100644 index 85f4c00c97..0000000000 --- a/tests/neo.UnitTests/UT_Utility.cs +++ /dev/null @@ -1,22 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.IO; - -namespace Neo.UnitTests -{ - [TestClass] - public class UT_Utility - { - [TestMethod] - public void LoadConfig() - { - Assert.IsFalse(File.Exists("test.json")); - Utility.LoadConfig("test").GetSection("test").Value.Should().BeNull(); - - File.WriteAllText("test.json", @"{""test"":1}"); - Assert.IsTrue(File.Exists("test.json")); - Utility.LoadConfig("test").GetSection("test").Value.Should().Be("1"); - File.Delete("test.json"); - } - } -} From e2fee7069909ba7af4fb34c4c9efbb57c9fb8921 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 11 Feb 2021 16:27:19 +0800 Subject: [PATCH 22/63] Allow plugins work with multipe NeoSystems (#2323) --- src/neo/Ledger/Blockchain.cs | 4 +- src/neo/Ledger/MemoryPool.cs | 6 +-- src/neo/NeoSystem.cs | 39 ++++++++++++---- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 2 +- src/neo/Plugins/IConsensusProvider.cs | 9 ---- .../Plugins/IMemoryPoolTxObserverPlugin.cs | 4 +- src/neo/Plugins/IP2PPlugin.cs | 2 +- src/neo/Plugins/IPersistencePlugin.cs | 4 +- src/neo/Plugins/IWalletProvider.cs | 2 +- src/neo/Plugins/Plugin.cs | 45 +------------------ src/neo/Plugins/RequiredServicesAttribute.cs | 15 ------- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 4 +- tests/neo.UnitTests/Plugins/TestLogPlugin.cs | 15 ------- tests/neo.UnitTests/Plugins/UT_Plugin.cs | 16 ++----- 14 files changed, 49 insertions(+), 118 deletions(-) delete mode 100644 src/neo/Plugins/IConsensusProvider.cs delete mode 100644 src/neo/Plugins/RequiredServicesAttribute.cs diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index e87a6cbcec..a1679f70ad 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -339,14 +339,14 @@ private void Persist(Block block) all_application_executed.Add(application_executed); } foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) - plugin.OnPersist(block, snapshot, all_application_executed); + plugin.OnPersist(system, block, snapshot, all_application_executed); snapshot.Commit(); List commitExceptions = null; foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins) { try { - plugin.OnCommit(block, snapshot); + plugin.OnCommit(system, block, snapshot); } catch (Exception ex) { diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index 842f7fcf64..dd19860368 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -277,9 +277,9 @@ internal VerifyResult TryAdd(Transaction tx, DataCache snapshot) foreach (IMemoryPoolTxObserverPlugin plugin in Plugin.TxObserverPlugins) { - plugin.TransactionAdded(poolItem.Tx); + plugin.TransactionAdded(_system, poolItem.Tx); if (removedTransactions != null) - plugin.TransactionsRemoved(MemoryPoolTxRemovalReason.CapacityExceeded, removedTransactions); + plugin.TransactionsRemoved(_system, MemoryPoolTxRemovalReason.CapacityExceeded, removedTransactions); } if (!_unsortedTransactions.ContainsKey(tx.Hash)) return VerifyResult.OutOfMemory; @@ -447,7 +447,7 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, var invalidTransactions = invalidItems.Select(p => p.Tx).ToArray(); foreach (IMemoryPoolTxObserverPlugin plugin in Plugin.TxObserverPlugins) - plugin.TransactionsRemoved(MemoryPoolTxRemovalReason.NoLongerValid, invalidTransactions); + plugin.TransactionsRemoved(_system, MemoryPoolTxRemovalReason.NoLongerValid, invalidTransactions); return reverifiedItems.Count; } diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index ceded56843..18e3b4ce21 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -8,7 +8,10 @@ using Neo.SmartContract; using Neo.VM; using System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; +using System.Threading; using static Neo.Ledger.Blockchain; namespace Neo @@ -36,15 +39,18 @@ public class NeoSystem : IDisposable public HeaderCache HeaderCache { get; } = new HeaderCache(); internal RelayCache RelayCache { get; } = new RelayCache(100); + private ImmutableList services = ImmutableList.Empty; private readonly string storage_engine; private readonly IStore store; private ChannelsConfig start_message = null; - private bool suspend = false; + private int suspend = 0; static NeoSystem() { // Unify unhandled exceptions AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + + Plugin.LoadPlugins(); } public NeoSystem(ProtocolSettings settings, string storageEngine = null, string storagePath = null) @@ -68,16 +74,15 @@ public NeoSystem(ProtocolSettings settings, string storageEngine = null, string }, Transactions = Array.Empty() }; - Plugin.LoadPlugins(this); this.storage_engine = storageEngine; this.store = LoadStore(storagePath); this.MemPool = new MemoryPool(this); this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); - foreach (var plugin in Plugin.Plugins) - plugin.OnPluginsLoaded(); Blockchain.Ask(new FillMemoryPool { Transactions = Enumerable.Empty() }).Wait(); + foreach (var plugin in Plugin.Plugins) + plugin.OnSystemLoaded(this); } private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) @@ -97,6 +102,20 @@ public void Dispose() store.Dispose(); } + public void AddService(object service) + { + ImmutableInterlocked.Update(ref services, p => p.Add(service)); + } + + public T GetService(Func filter = null) + { + IEnumerable result = services.OfType(); + if (filter is null) + return result.FirstOrDefault(); + else + return result.FirstOrDefault(filter); + } + public void EnsureStoped(IActorRef actor) { using Inbox inbox = Inbox.Create(ActorSystem); @@ -112,30 +131,32 @@ public IStore LoadStore(string path) : Plugin.Storages[storage_engine].GetStore(path); } - internal void ResumeNodeStartup() + public bool ResumeNodeStartup() { - suspend = false; + if (Interlocked.Decrement(ref suspend) != 0) + return false; if (start_message != null) { LocalNode.Tell(start_message); start_message = null; } + return true; } public void StartNode(ChannelsConfig config) { start_message = config; - if (!suspend) + if (suspend == 0) { LocalNode.Tell(start_message); start_message = null; } } - internal void SuspendNodeStartup() + public void SuspendNodeStartup() { - suspend = true; + Interlocked.Increment(ref suspend); } public SnapshotCache GetSnapshot() diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 2c39f0465e..de08038d16 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -38,7 +38,7 @@ protected override UInt256 GetKeyForItem((UInt256, DateTime) item) private void OnMessage(Message msg) { foreach (IP2PPlugin plugin in Plugin.P2PPlugins) - if (!plugin.OnP2PMessage(msg)) + if (!plugin.OnP2PMessage(system, msg)) return; if (Version == null) { diff --git a/src/neo/Plugins/IConsensusProvider.cs b/src/neo/Plugins/IConsensusProvider.cs deleted file mode 100644 index a7e021863a..0000000000 --- a/src/neo/Plugins/IConsensusProvider.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Neo.Wallets; - -namespace Neo.Plugins -{ - public interface IConsensusProvider - { - void Start(Wallet wallet); - } -} diff --git a/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs b/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs index 2d7e0b2d63..54b7c2d50d 100644 --- a/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs +++ b/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs @@ -5,7 +5,7 @@ namespace Neo.Plugins { public interface IMemoryPoolTxObserverPlugin { - void TransactionAdded(Transaction tx); - void TransactionsRemoved(MemoryPoolTxRemovalReason reason, IEnumerable transactions); + void TransactionAdded(NeoSystem system, Transaction tx); + void TransactionsRemoved(NeoSystem system, MemoryPoolTxRemovalReason reason, IEnumerable transactions); } } diff --git a/src/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs index 5479373ad4..7d8d7db8cd 100644 --- a/src/neo/Plugins/IP2PPlugin.cs +++ b/src/neo/Plugins/IP2PPlugin.cs @@ -4,6 +4,6 @@ namespace Neo.Plugins { public interface IP2PPlugin { - bool OnP2PMessage(Message message) => true; + bool OnP2PMessage(NeoSystem system, Message message) => true; } } diff --git a/src/neo/Plugins/IPersistencePlugin.cs b/src/neo/Plugins/IPersistencePlugin.cs index 76bb126cc5..17b5f1b319 100644 --- a/src/neo/Plugins/IPersistencePlugin.cs +++ b/src/neo/Plugins/IPersistencePlugin.cs @@ -8,8 +8,8 @@ namespace Neo.Plugins { public interface IPersistencePlugin { - void OnPersist(Block block, DataCache snapshot, IReadOnlyList applicationExecutedList) { } - void OnCommit(Block block, DataCache snapshot) { } + void OnPersist(NeoSystem system, Block block, DataCache snapshot, IReadOnlyList applicationExecutedList) { } + void OnCommit(NeoSystem system, Block block, DataCache snapshot) { } bool ShouldThrowExceptionFromCommit(Exception ex) => false; } } diff --git a/src/neo/Plugins/IWalletProvider.cs b/src/neo/Plugins/IWalletProvider.cs index 361ba3d9eb..0fd1e86e8a 100644 --- a/src/neo/Plugins/IWalletProvider.cs +++ b/src/neo/Plugins/IWalletProvider.cs @@ -5,7 +5,7 @@ namespace Neo.Plugins { public interface IWalletProvider { - event EventHandler WalletOpened; + event EventHandler WalletChanged; Wallet GetWallet(); } diff --git a/src/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs index ddcdd54036..1f8e4475b3 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -2,11 +2,9 @@ using Neo.SmartContract; using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; -using System.Threading; using static System.IO.Path; namespace Neo.Plugins @@ -20,16 +18,13 @@ public abstract class Plugin : IDisposable internal static readonly List P2PPlugins = new List(); internal static readonly List TxObserverPlugins = new List(); - private static ImmutableList services = ImmutableList.Empty; public static readonly string PluginsDirectory = Combine(GetDirectoryName(Assembly.GetEntryAssembly().Location), "Plugins"); private static readonly FileSystemWatcher configWatcher; - private static int suspend = 0; public virtual string ConfigFile => Combine(PluginsDirectory, GetType().Assembly.GetName().Name, "config.json"); public virtual string Name => GetType().Name; public virtual string Description => ""; public virtual string Path => Combine(PluginsDirectory, GetType().Assembly.ManifestModule.ScopeName); - protected static NeoSystem System { get; private set; } public virtual Version Version => GetType().Assembly.GetName().Version; static Plugin() @@ -62,21 +57,6 @@ protected Plugin() Configure(); } - public static void AddService(object service) - { - ImmutableInterlocked.Update(ref services, p => p.Add(service)); - } - - private static bool CheckRequiredServices(Type type) - { - RequiredServicesAttribute attribute = type.GetCustomAttribute(); - if (attribute is null) return true; - foreach (Type rt in attribute.RequiredServices) - if (services.All(p => !rt.IsAssignableFrom(p.GetType()))) - return false; - return true; - } - protected virtual void Configure() { } @@ -143,18 +123,12 @@ protected IConfigurationSection GetConfiguration() return new ConfigurationBuilder().AddJsonFile(ConfigFile, optional: true).Build().GetSection("PluginConfiguration"); } - protected static T GetService() - { - return services.OfType().FirstOrDefault(); - } - private static void LoadPlugin(Assembly assembly) { foreach (Type type in assembly.ExportedTypes) { if (!type.IsSubclassOf(typeof(Plugin))) continue; if (type.IsAbstract) continue; - if (!CheckRequiredServices(type)) continue; ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes); try @@ -168,9 +142,8 @@ private static void LoadPlugin(Assembly assembly) } } - internal static void LoadPlugins(NeoSystem system) + internal static void LoadPlugins() { - System = system; if (!Directory.Exists(PluginsDirectory)) return; List assemblies = new List(); foreach (string filename in Directory.EnumerateFiles(PluginsDirectory, "*.dll", SearchOption.TopDirectoryOnly)) @@ -197,16 +170,8 @@ protected virtual bool OnMessage(object message) return false; } - internal protected virtual void OnPluginsLoaded() - { - } - - protected static bool ResumeNodeStartup() + internal protected virtual void OnSystemLoaded(NeoSystem system) { - if (Interlocked.Decrement(ref suspend) != 0) - return false; - System.ResumeNodeStartup(); - return true; } public static bool SendMessage(object message) @@ -216,11 +181,5 @@ public static bool SendMessage(object message) return true; return false; } - - protected static void SuspendNodeStartup() - { - Interlocked.Increment(ref suspend); - System.SuspendNodeStartup(); - } } } diff --git a/src/neo/Plugins/RequiredServicesAttribute.cs b/src/neo/Plugins/RequiredServicesAttribute.cs deleted file mode 100644 index ae0f227115..0000000000 --- a/src/neo/Plugins/RequiredServicesAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Neo.Plugins -{ - [AttributeUsage(AttributeTargets.Class)] - public class RequiredServicesAttribute : Attribute - { - public Type[] RequiredServices { get; } - - public RequiredServicesAttribute(params Type[] requiredServices) - { - this.RequiredServices = requiredServices; - } - } -} diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index c3bca08016..bb6f82d387 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -21,8 +21,8 @@ namespace Neo.UnitTests.Ledger internal class TestIMemoryPoolTxObserverPlugin : Plugin, IMemoryPoolTxObserverPlugin { protected override void Configure() { } - public void TransactionAdded(Transaction tx) { } - public void TransactionsRemoved(MemoryPoolTxRemovalReason reason, IEnumerable transactions) { } + public void TransactionAdded(NeoSystem system, Transaction tx) { } + public void TransactionsRemoved(NeoSystem system, MemoryPoolTxRemovalReason reason, IEnumerable transactions) { } } [TestClass] diff --git a/tests/neo.UnitTests/Plugins/TestLogPlugin.cs b/tests/neo.UnitTests/Plugins/TestLogPlugin.cs index 90d9d2c4df..cb73ce4106 100644 --- a/tests/neo.UnitTests/Plugins/TestLogPlugin.cs +++ b/tests/neo.UnitTests/Plugins/TestLogPlugin.cs @@ -32,20 +32,5 @@ public IConfigurationSection TestGetConfiguration() } 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/tests/neo.UnitTests/Plugins/UT_Plugin.cs b/tests/neo.UnitTests/Plugins/UT_Plugin.cs index 97085860b5..17758c92ca 100644 --- a/tests/neo.UnitTests/Plugins/UT_Plugin.cs +++ b/tests/neo.UnitTests/Plugins/UT_Plugin.cs @@ -21,7 +21,7 @@ private class dummyPersistencePlugin : IPersistencePlugin { } public void TestIP2PPlugin() { var pp = new DummyP2PPlugin() as IP2PPlugin; - Assert.IsTrue(pp.OnP2PMessage(null)); + Assert.IsTrue(pp.OnP2PMessage(null, null)); } [TestMethod] @@ -33,8 +33,8 @@ public void TestIPersistencePlugin() // With empty default implementation - pp.OnCommit(null, null); - pp.OnPersist(null, null, null); + pp.OnCommit(null, null, null); + pp.OnPersist(null, null, null, null); } [TestMethod] @@ -81,16 +81,6 @@ public void TestSendMessage() } } - [TestMethod] - public void TestResumeNodeStartupAndSuspendNodeStartup() - { - TestLogPlugin.TestLoadPlugins(TestBlockchain.TheNeoSystem); - TestLogPlugin.TestSuspendNodeStartup(); - TestLogPlugin.TestSuspendNodeStartup(); - TestLogPlugin.TestResumeNodeStartup().Should().BeFalse(); - TestLogPlugin.TestResumeNodeStartup().Should().BeTrue(); - } - [TestMethod] public void TestGetConfiguration() { From 1dcda66308b06cd50ba0cd1ba5e9cea67213a8ef Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 12 Feb 2021 22:30:03 +0800 Subject: [PATCH 23/63] Remove some settings from policy contract (#2300) --- src/neo/Ledger/MemoryPool.cs | 7 +- src/neo/Network/P2P/Payloads/Block.cs | 4 +- .../P2P/Payloads/MerkleBlockPayload.cs | 2 +- src/neo/Network/P2P/Payloads/Transaction.cs | 2 - src/neo/ProtocolSettings.cs | 3 + .../SmartContract/Native/PolicyContract.cs | 56 +------ src/neo/SmartContract/Native/TrimmedBlock.cs | 2 +- .../SmartContract/Native/UT_PolicyContract.cs | 157 +----------------- 8 files changed, 12 insertions(+), 221 deletions(-) diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index dd19860368..07cb1f5fde 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -3,7 +3,6 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.Plugins; -using Neo.SmartContract.Native; using System; using System.Collections; using System.Collections.Generic; @@ -368,8 +367,7 @@ internal void UpdatePoolForBlockPersisted(Block block, DataCache snapshot) if (block.Index > 0 && _system.HeaderCache.Count > 0) return; - uint _maxTxPerBlock = NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); - ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, (int)_maxTxPerBlock, MaxMillisecondsToReverifyTx, snapshot); + ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, (int)ProtocolSettings.Default.MaxTransactionsPerBlock, MaxMillisecondsToReverifyTx, snapshot); } internal void InvalidateAllTransactions() @@ -468,8 +466,7 @@ internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, DataCac if (_unverifiedSortedTransactions.Count > 0) { - uint _maxTxPerBlock = NativeContract.Policy.GetMaxTransactionsPerBlock(snapshot); - int verifyCount = _sortedTransactions.Count > _maxTxPerBlock ? 1 : maxToVerify; + int verifyCount = _sortedTransactions.Count > ProtocolSettings.Default.MaxTransactionsPerBlock ? 1 : maxToVerify; ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, verifyCount, MaxMillisecondsToReverifyTxPerIdle, snapshot); } diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index 5901c7988e..1f55dfefce 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -11,8 +11,6 @@ namespace Neo.Network.P2P.Payloads { public sealed class Block : IEquatable, IInventory { - public const int MaxTransactionsPerBlock = ushort.MaxValue; - public Header Header; public Transaction[] Transactions; @@ -33,7 +31,7 @@ public sealed class Block : IEquatable, IInventory public void Deserialize(BinaryReader reader) { Header = reader.ReadSerializable
(); - Transactions = reader.ReadSerializableArray(MaxTransactionsPerBlock); + Transactions = reader.ReadSerializableArray((int)ProtocolSettings.Default.MaxTransactionsPerBlock); if (Transactions.Distinct().Count() != Transactions.Length) throw new FormatException(); if (MerkleTree.ComputeRoot(Transactions.Select(p => p.Hash).ToArray()) != Header.MerkleRoot) diff --git a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs index 034f6cf5a3..98ec7050cd 100644 --- a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs +++ b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs @@ -34,7 +34,7 @@ public static MerkleBlockPayload Create(Block block, BitArray flags) public void Deserialize(BinaryReader reader) { Header = reader.ReadSerializable
(); - TxCount = (int)reader.ReadVarInt(Block.MaxTransactionsPerBlock); + TxCount = (int)reader.ReadVarInt(ProtocolSettings.Default.MaxTransactionsPerBlock); Hashes = reader.ReadSerializableArray(TxCount); Flags = reader.ReadVarBytes((Math.Max(TxCount, 1) + 7) / 8); } diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index ddcdb485c3..c675047a17 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -288,8 +288,6 @@ public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, Data foreach (UInt160 hash in hashes) if (NativeContract.Policy.IsBlocked(snapshot, hash)) return VerifyResult.PolicyFail; - if (NativeContract.Policy.GetMaxBlockSystemFee(snapshot) < SystemFee) - return VerifyResult.PolicyFail; if (!(context?.CheckTransaction(this, snapshot) ?? true)) return VerifyResult.InsufficientFunds; foreach (TransactionAttribute attribute in Attributes) if (!attribute.Verify(snapshot, this)) diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index 7576c4b5a6..20db3460c6 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -17,6 +17,7 @@ public record ProtocolSettings public string[] SeedList { get; init; } public uint MillisecondsPerBlock { get; init; } public TimeSpan TimePerBlock => TimeSpan.FromMilliseconds(MillisecondsPerBlock); + public uint MaxTransactionsPerBlock { get; init; } public int MemoryPoolMaxTransactions { get; init; } public uint MaxTraceableBlocks { get; init; } public IReadOnlyDictionary NativeActivations { get; init; } @@ -64,6 +65,7 @@ public record ProtocolSettings "seed5.neo.org:10333" }, MillisecondsPerBlock = 15000, + MaxTransactionsPerBlock = 512, MemoryPoolMaxTransactions = 50_000, MaxTraceableBlocks = 2_102_400, NativeActivations = ImmutableDictionary.Empty @@ -85,6 +87,7 @@ public static ProtocolSettings Load(string path, bool optional = true) ? section.GetSection("SeedList").GetChildren().Select(p => p.Get()).ToArray() : Default.SeedList, MillisecondsPerBlock = section.GetValue("MillisecondsPerBlock", Default.MillisecondsPerBlock), + MaxTransactionsPerBlock = section.GetValue("MaxTransactionsPerBlock", Default.MaxTransactionsPerBlock), MemoryPoolMaxTransactions = section.GetValue("MemoryPoolMaxTransactions", Default.MemoryPoolMaxTransactions), MaxTraceableBlocks = section.GetValue("MaxTraceableBlocks", Default.MaxTraceableBlocks), NativeActivations = section.GetSection("NativeActivations").Exists() diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index eb60511042..5bee55656b 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -1,7 +1,5 @@ #pragma warning disable IDE0051 -using Neo.Network.P2P; -using Neo.Network.P2P.Payloads; using Neo.Persistence; using System; using System.Numerics; @@ -12,14 +10,11 @@ public sealed class PolicyContract : NativeContract { public const uint DefaultExecFeeFactor = 30; public const uint DefaultStoragePrice = 100000; - private const uint MaxExecFeeFactor = 1000; - private const uint MaxStoragePrice = 10000000; + public const uint MaxExecFeeFactor = 1000; + public const uint MaxStoragePrice = 10000000; - private const byte Prefix_MaxTransactionsPerBlock = 23; - private const byte Prefix_FeePerByte = 10; private const byte Prefix_BlockedAccount = 15; - private const byte Prefix_MaxBlockSize = 12; - private const byte Prefix_MaxBlockSystemFee = 17; + private const byte Prefix_FeePerByte = 10; private const byte Prefix_ExecFeeFactor = 18; private const byte Prefix_StoragePrice = 19; @@ -29,32 +24,11 @@ internal PolicyContract() internal override void Initialize(ApplicationEngine engine) { - engine.Snapshot.Add(CreateStorageKey(Prefix_MaxTransactionsPerBlock), new StorageItem(512)); engine.Snapshot.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem(1000)); - engine.Snapshot.Add(CreateStorageKey(Prefix_MaxBlockSize), new StorageItem(1024 * 256)); - engine.Snapshot.Add(CreateStorageKey(Prefix_MaxBlockSystemFee), new StorageItem(9000 * GAS.Factor)); // For the transfer method of NEP5, the maximum persisting time is about three seconds. engine.Snapshot.Add(CreateStorageKey(Prefix_ExecFeeFactor), new StorageItem(DefaultExecFeeFactor)); engine.Snapshot.Add(CreateStorageKey(Prefix_StoragePrice), new StorageItem(DefaultStoragePrice)); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] - public uint GetMaxTransactionsPerBlock(DataCache snapshot) - { - return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_MaxTransactionsPerBlock)]; - } - - [ContractMethod(0_01000000, CallFlags.ReadStates)] - public uint GetMaxBlockSize(DataCache snapshot) - { - return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_MaxBlockSize)]; - } - - [ContractMethod(0_01000000, CallFlags.ReadStates)] - public long GetMaxBlockSystemFee(DataCache snapshot) - { - return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_MaxBlockSystemFee)]; - } - [ContractMethod(0_01000000, CallFlags.ReadStates)] public long GetFeePerByte(DataCache snapshot) { @@ -79,30 +53,6 @@ public bool IsBlocked(DataCache snapshot, UInt160 account) return snapshot.Contains(CreateStorageKey(Prefix_BlockedAccount).Add(account)); } - [ContractMethod(0_03000000, CallFlags.WriteStates)] - private void SetMaxBlockSize(ApplicationEngine engine, uint value) - { - if (value > Message.PayloadMaxSize) throw new ArgumentOutOfRangeException(nameof(value)); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); - engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize)).Set(value); - } - - [ContractMethod(0_03000000, CallFlags.WriteStates)] - private void SetMaxTransactionsPerBlock(ApplicationEngine engine, uint value) - { - if (value > Block.MaxTransactionsPerBlock) throw new ArgumentOutOfRangeException(nameof(value)); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); - engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock)).Set(value); - } - - [ContractMethod(0_03000000, CallFlags.WriteStates)] - private void SetMaxBlockSystemFee(ApplicationEngine engine, long value) - { - if (value <= 4007600) throw new ArgumentOutOfRangeException(nameof(value)); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); - engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MaxBlockSystemFee)).Set(value); - } - [ContractMethod(0_03000000, CallFlags.WriteStates)] private void SetFeePerByte(ApplicationEngine engine, long value) { diff --git a/src/neo/SmartContract/Native/TrimmedBlock.cs b/src/neo/SmartContract/Native/TrimmedBlock.cs index 07d02c3a41..d7a9078b62 100644 --- a/src/neo/SmartContract/Native/TrimmedBlock.cs +++ b/src/neo/SmartContract/Native/TrimmedBlock.cs @@ -21,7 +21,7 @@ public class TrimmedBlock : IInteroperable, ISerializable public void Deserialize(BinaryReader reader) { Header = reader.ReadSerializable
(); - Hashes = reader.ReadSerializableArray(Block.MaxTransactionsPerBlock); + Hashes = reader.ReadSerializableArray((int)ProtocolSettings.Default.MaxTransactionsPerBlock); } public void Serialize(BinaryWriter writer) diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs index 8f52063e82..370f647bb3 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_PolicyContract.cs @@ -30,166 +30,11 @@ public void Check_Default() { var snapshot = _snapshot.CreateSnapshot(); - var ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(512); - - ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(1024 * 256); - - ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(9000 * 100000000L); - - ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); + var ret = NativeContract.Policy.Call(snapshot, "getFeePerByte"); ret.Should().BeOfType(); ret.GetInteger().Should().Be(1000); } - [TestMethod] - public void Check_SetMaxBlockSize() - { - var snapshot = _snapshot.CreateSnapshot(); - - // Fake blockchain - - Block block = new Block() - { - Header = new Header - { - Index = 1000, - PrevHash = UInt256.Zero - } - }; - UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); - - // Without signature - - Assert.ThrowsException(() => - { - NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(null), block, - "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); - }); - - var ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(1024 * 256); - - // More than expected - - Assert.ThrowsException(() => - { - NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, - "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = Neo.Network.P2P.Message.PayloadMaxSize + 1 }); - }); - - ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(1024 * 256); - - // With signature - - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, - "setMaxBlockSize", new ContractParameter(ContractParameterType.Integer) { Value = 1024 }); - ret.IsNull.Should().BeTrue(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSize"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(1024); - } - - [TestMethod] - public void Check_SetMaxBlockSystemFee() - { - var snapshot = _snapshot.CreateSnapshot(); - - // Fake blockchain - - Block block = new Block() - { - Header = new Header - { - Index = 1000, - PrevHash = UInt256.Zero - } - }; - UInt160 committeeMultiSigAddr = NativeContract.NEO.GetCommitteeAddress(snapshot); - - // Without signature - - Assert.ThrowsException(() => - { - NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(null), block, - "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = 1024 * (long)NativeContract.GAS.Factor }); - }); - - var ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(9000 * (long)NativeContract.GAS.Factor); - - // Less than expected - - Assert.ThrowsException(() => - { - NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, - "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = -1000 }); - }); - - ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(9000 * (long)NativeContract.GAS.Factor); - - // With signature - - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(committeeMultiSigAddr), block, - "setMaxBlockSystemFee", new ContractParameter(ContractParameterType.Integer) { Value = 1024 * (long)NativeContract.GAS.Factor }); - ret.IsNull.Should().BeTrue(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxBlockSystemFee"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(1024 * (long)NativeContract.GAS.Factor); - } - - [TestMethod] - public void Check_SetMaxTransactionsPerBlock() - { - var snapshot = _snapshot.CreateSnapshot(); - - // Fake blockchain - - Block block = new Block() - { - Header = new Header - { - Index = 1000, - PrevHash = UInt256.Zero - } - }; - - // Without signature - - Assert.ThrowsException(() => - { - NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(), block, - "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - }); - - var ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(512); - - // With signature - - ret = NativeContract.Policy.Call(snapshot, new Nep17NativeContractExtensions.ManualWitness(NativeContract.NEO.GetCommitteeAddress(snapshot)), block, - "setMaxTransactionsPerBlock", new ContractParameter(ContractParameterType.Integer) { Value = 1 }); - ret.IsNull.Should().BeTrue(); - - ret = NativeContract.Policy.Call(snapshot, "getMaxTransactionsPerBlock"); - ret.Should().BeOfType(); - ret.GetInteger().Should().Be(1); - } - [TestMethod] public void Check_SetFeePerByte() { From 0d4632821f3f049e065c3b97324b165d9eab9927 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 13 Feb 2021 12:38:08 +0800 Subject: [PATCH 24/63] Remove NFTState.Description (#2333) --- src/neo/SmartContract/Native/NFTState.cs | 10 ++-------- src/neo/SmartContract/Native/NameService.cs | 5 ++--- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/neo/SmartContract/Native/NFTState.cs b/src/neo/SmartContract/Native/NFTState.cs index 8bcb85948c..e9f464ea95 100644 --- a/src/neo/SmartContract/Native/NFTState.cs +++ b/src/neo/SmartContract/Native/NFTState.cs @@ -9,17 +9,12 @@ public abstract class NFTState : IInteroperable { public UInt160 Owner; public string Name; - public string Description; public abstract byte[] Id { get; } public virtual Map ToMap(ReferenceCounter referenceCounter) { - return new Map(referenceCounter) - { - ["name"] = Name, - ["description"] = Description - }; + return new Map(referenceCounter) { ["name"] = Name }; } public virtual void FromStackItem(StackItem stackItem) @@ -27,12 +22,11 @@ public virtual void FromStackItem(StackItem stackItem) Struct @struct = (Struct)stackItem; Owner = new UInt160(@struct[0].GetSpan()); Name = @struct[1].GetString(); - Description = @struct[2].GetString(); } public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) { - return new Struct(referenceCounter) { Owner.ToArray(), Name, Description }; + return new Struct(referenceCounter) { Owner.ToArray(), Name }; } } } diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index 9457536450..11610b734a 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -124,7 +124,6 @@ private bool Register(ApplicationEngine engine, string name, UInt160 owner) { Owner = owner, Name = name, - Description = "", Expiration = (uint)(engine.PersistingBlock.Timestamp / 1000) + OneYear }; Mint(engine, state); @@ -263,8 +262,8 @@ public override void FromStackItem(StackItem stackItem) { base.FromStackItem(stackItem); Struct @struct = (Struct)stackItem; - Expiration = (uint)@struct[3].GetInteger(); - Admin = @struct[4].IsNull ? null : new UInt160(@struct[4].GetSpan()); + Expiration = (uint)@struct[2].GetInteger(); + Admin = @struct[3].IsNull ? null : new UInt160(@struct[3].GetSpan()); } public override StackItem ToStackItem(ReferenceCounter referenceCounter) From cddf6e8326f4ec3b295bda128c23b67b57f10b87 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 14 Feb 2021 02:03:39 +0800 Subject: [PATCH 25/63] Fix CreateDummyBlock (#2334) --- src/neo/SmartContract/ApplicationEngine.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 54337815b5..39066917a9 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -318,10 +318,10 @@ internal void StepOut() throw new InvalidOperationException("StepOut failed.", FaultException); } - private static Block CreateDummyBlock(DataCache snapshot) + private static Block CreateDummyBlock(DataCache snapshot, ProtocolSettings settings) { UInt256 hash = NativeContract.Ledger.CurrentHash(snapshot); - var currentBlock = NativeContract.Ledger.GetBlock(snapshot, hash); + Block currentBlock = NativeContract.Ledger.GetBlock(snapshot, hash); return new Block { Header = new Header @@ -329,7 +329,7 @@ private static Block CreateDummyBlock(DataCache snapshot) Version = 0, PrevHash = hash, MerkleRoot = new UInt256(), - Timestamp = currentBlock.Timestamp + 1, + Timestamp = currentBlock.Timestamp + settings.MillisecondsPerBlock, Index = currentBlock.Index + 1, NextConsensus = currentBlock.NextConsensus, Witness = new Witness @@ -338,7 +338,7 @@ private static Block CreateDummyBlock(DataCache snapshot) VerificationScript = Array.Empty() }, }, - Transactions = new Transaction[0] + Transactions = Array.Empty() }; } @@ -359,7 +359,7 @@ internal static void ResetApplicationEngineProvider() public static ApplicationEngine Run(byte[] script, DataCache snapshot, IVerifiable container = null, Block persistingBlock = null, ProtocolSettings settings = null, int offset = 0, long gas = TestModeGas) { - persistingBlock ??= CreateDummyBlock(snapshot); + persistingBlock ??= CreateDummyBlock(snapshot, settings ?? ProtocolSettings.Default); ApplicationEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock, settings, gas); engine.LoadScript(script, initialPosition: offset); engine.Execute(); From ba6eef4d37ff95f392c27d697a119fd081bb43d6 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 14 Feb 2021 17:46:34 +0800 Subject: [PATCH 26/63] Fix some merge errors (#2337) --- src/neo/Ledger/MemoryPool.cs | 4 ++-- src/neo/Network/P2P/Payloads/Block.cs | 2 +- src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs | 2 +- src/neo/SmartContract/Native/TrimmedBlock.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index 07cb1f5fde..11f54173c0 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -367,7 +367,7 @@ internal void UpdatePoolForBlockPersisted(Block block, DataCache snapshot) if (block.Index > 0 && _system.HeaderCache.Count > 0) return; - ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, (int)ProtocolSettings.Default.MaxTransactionsPerBlock, MaxMillisecondsToReverifyTx, snapshot); + ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, (int)_system.Settings.MaxTransactionsPerBlock, MaxMillisecondsToReverifyTx, snapshot); } internal void InvalidateAllTransactions() @@ -466,7 +466,7 @@ internal bool ReVerifyTopUnverifiedTransactionsIfNeeded(int maxToVerify, DataCac if (_unverifiedSortedTransactions.Count > 0) { - int verifyCount = _sortedTransactions.Count > ProtocolSettings.Default.MaxTransactionsPerBlock ? 1 : maxToVerify; + int verifyCount = _sortedTransactions.Count > _system.Settings.MaxTransactionsPerBlock ? 1 : maxToVerify; ReverifyTransactions(_sortedTransactions, _unverifiedSortedTransactions, verifyCount, MaxMillisecondsToReverifyTxPerIdle, snapshot); } diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index 1f55dfefce..83827ac5ed 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -31,7 +31,7 @@ public sealed class Block : IEquatable, IInventory public void Deserialize(BinaryReader reader) { Header = reader.ReadSerializable
(); - Transactions = reader.ReadSerializableArray((int)ProtocolSettings.Default.MaxTransactionsPerBlock); + Transactions = reader.ReadSerializableArray(ushort.MaxValue); if (Transactions.Distinct().Count() != Transactions.Length) throw new FormatException(); if (MerkleTree.ComputeRoot(Transactions.Select(p => p.Hash).ToArray()) != Header.MerkleRoot) diff --git a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs index 98ec7050cd..84a3162ff2 100644 --- a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs +++ b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs @@ -34,7 +34,7 @@ public static MerkleBlockPayload Create(Block block, BitArray flags) public void Deserialize(BinaryReader reader) { Header = reader.ReadSerializable
(); - TxCount = (int)reader.ReadVarInt(ProtocolSettings.Default.MaxTransactionsPerBlock); + TxCount = (int)reader.ReadVarInt(ushort.MaxValue); Hashes = reader.ReadSerializableArray(TxCount); Flags = reader.ReadVarBytes((Math.Max(TxCount, 1) + 7) / 8); } diff --git a/src/neo/SmartContract/Native/TrimmedBlock.cs b/src/neo/SmartContract/Native/TrimmedBlock.cs index d7a9078b62..d0d5de8aec 100644 --- a/src/neo/SmartContract/Native/TrimmedBlock.cs +++ b/src/neo/SmartContract/Native/TrimmedBlock.cs @@ -21,7 +21,7 @@ public class TrimmedBlock : IInteroperable, ISerializable public void Deserialize(BinaryReader reader) { Header = reader.ReadSerializable
(); - Hashes = reader.ReadSerializableArray((int)ProtocolSettings.Default.MaxTransactionsPerBlock); + Hashes = reader.ReadSerializableArray(ushort.MaxValue); } public void Serialize(BinaryWriter writer) From 4fd5ce0498e5e7a80255867ae47bf20964940019 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 15 Feb 2021 16:09:26 +0800 Subject: [PATCH 27/63] Adjust native contract fee (#2331) * Adjust native contract fee * Add default Oracle price * Charge the committee Co-authored-by: Shargon --- .../Native/ContractManagement.cs | 16 ++++---- .../Native/ContractMethodAttribute.cs | 13 ++----- .../Native/ContractMethodMetadata.cs | 6 ++- src/neo/SmartContract/Native/CryptoLib.cs | 6 +-- src/neo/SmartContract/Native/FungibleToken.cs | 10 ++--- .../SmartContract/Native/LedgerContract.cs | 12 +++--- src/neo/SmartContract/Native/NameService.cs | 22 +++++------ .../SmartContract/Native/NativeContract.cs | 2 +- src/neo/SmartContract/Native/NeoToken.cs | 39 +++++++++++++------ .../SmartContract/Native/NonfungibleToken.cs | 18 ++++----- .../SmartContract/Native/OracleContract.cs | 29 +++++++++++--- .../SmartContract/Native/PolicyContract.cs | 18 ++++----- .../SmartContract/Native/RoleManagement.cs | 4 +- src/neo/SmartContract/Native/StdLib.cs | 20 +++++----- .../SmartContract/Native/UT_NativeContract.cs | 6 +-- 15 files changed, 125 insertions(+), 96 deletions(-) diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 886e93ea9d..544d57ffba 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -104,13 +104,13 @@ internal override void OnPersist(ApplicationEngine engine) } } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] private long GetMinimumDeploymentFee(DataCache snapshot) { return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_MinimumDeploymentFee)]; } - [ContractMethod(0_03000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private void SetMinimumDeploymentFee(ApplicationEngine engine, BigInteger value) { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); @@ -118,7 +118,7 @@ private void SetMinimumDeploymentFee(ApplicationEngine engine, BigInteger value) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MinimumDeploymentFee)).Set(value); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public ContractState GetContract(DataCache snapshot, UInt160 hash) { return snapshot.TryGet(CreateStorageKey(Prefix_Contract).Add(hash))?.GetInteroperable(); @@ -130,13 +130,13 @@ public IEnumerable ListContracts(DataCache snapshot) return snapshot.Find(listContractsPrefix).Select(kvp => kvp.Value.GetInteroperable()); } - [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest) { return Deploy(engine, nefFile, manifest, StackItem.Null); } - [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { if (engine.ScriptContainer is not Transaction tx) @@ -182,13 +182,13 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma return contract; } - [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest) { Update(engine, nefFile, manifest, StackItem.Null); } - [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { if (nefFile is null && manifest is null) throw new ArgumentException(); @@ -228,7 +228,7 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, S engine.SendNotification(Hash, "Update", new VM.Types.Array { contract.Hash.ToArray() }); } - [ContractMethod(0_01000000, CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] private void Destroy(ApplicationEngine engine) { UInt160 hash = engine.CallingScriptHash; diff --git a/src/neo/SmartContract/Native/ContractMethodAttribute.cs b/src/neo/SmartContract/Native/ContractMethodAttribute.cs index 4952fc2781..393a8f187f 100644 --- a/src/neo/SmartContract/Native/ContractMethodAttribute.cs +++ b/src/neo/SmartContract/Native/ContractMethodAttribute.cs @@ -5,14 +5,9 @@ namespace Neo.SmartContract.Native [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)] internal class ContractMethodAttribute : Attribute { - public string Name { get; set; } - public long Price { get; } - public CallFlags RequiredCallFlags { get; } - - public ContractMethodAttribute(long price, CallFlags requiredCallFlags) - { - this.Price = price; - this.RequiredCallFlags = requiredCallFlags; - } + public string Name { get; init; } + public CallFlags RequiredCallFlags { get; init; } + public long CpuFee { get; init; } + public long StorageFee { get; init; } } } diff --git a/src/neo/SmartContract/Native/ContractMethodMetadata.cs b/src/neo/SmartContract/Native/ContractMethodMetadata.cs index eca5f2abc1..d4e26594bc 100644 --- a/src/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -17,7 +17,8 @@ internal class ContractMethodMetadata public InteropParameterDescriptor[] Parameters { get; } public bool NeedApplicationEngine { get; } public bool NeedSnapshot { get; } - public long Price { get; } + public long CpuFee { get; } + public long StorageFee { get; } public CallFlags RequiredCallFlags { get; } public ContractMethodDescriptor Descriptor { get; } @@ -40,7 +41,8 @@ public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribu this.Parameters = parameterInfos.Skip(1).Select(p => new InteropParameterDescriptor(p)).ToArray(); else this.Parameters = parameterInfos.Select(p => new InteropParameterDescriptor(p)).ToArray(); - this.Price = attribute.Price; + this.CpuFee = attribute.CpuFee; + this.StorageFee = attribute.StorageFee; this.RequiredCallFlags = attribute.RequiredCallFlags; this.Descriptor = new ContractMethodDescriptor { diff --git a/src/neo/SmartContract/Native/CryptoLib.cs b/src/neo/SmartContract/Native/CryptoLib.cs index 9295104da3..0d16095c0b 100644 --- a/src/neo/SmartContract/Native/CryptoLib.cs +++ b/src/neo/SmartContract/Native/CryptoLib.cs @@ -15,19 +15,19 @@ public sealed class CryptoLib : NativeContract internal CryptoLib() { } - [ContractMethod(1 << 15, CallFlags.None, Name = "ripemd160")] + [ContractMethod(CpuFee = 1 << 15, Name = "ripemd160")] public static byte[] RIPEMD160(byte[] data) { return data.RIPEMD160(); } - [ContractMethod(1 << 15, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 15)] public static byte[] Sha256(byte[] data) { return data.Sha256(); } - [ContractMethod(1 << 15, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 15)] public static bool VerifyWithECDsa(byte[] message, byte[] pubkey, byte[] signature, NamedCurve curve) { try diff --git a/src/neo/SmartContract/Native/FungibleToken.cs b/src/neo/SmartContract/Native/FungibleToken.cs index 30f931c2c1..bcd1239ced 100644 --- a/src/neo/SmartContract/Native/FungibleToken.cs +++ b/src/neo/SmartContract/Native/FungibleToken.cs @@ -12,9 +12,9 @@ namespace Neo.SmartContract.Native public abstract class FungibleToken : NativeContract where TState : AccountState, new() { - [ContractMethod(0, CallFlags.None)] + [ContractMethod] public abstract string Symbol { get; } - [ContractMethod(0, CallFlags.None)] + [ContractMethod] public abstract byte Decimals { get; } public BigInteger Factor { get; } @@ -87,7 +87,7 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, PostTransfer(engine, account, null, amount, StackItem.Null, false); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public virtual BigInteger TotalSupply(DataCache snapshot) { StorageItem storage = snapshot.TryGet(CreateStorageKey(Prefix_TotalSupply)); @@ -95,7 +95,7 @@ public virtual BigInteger TotalSupply(DataCache snapshot) return storage; } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public virtual BigInteger BalanceOf(DataCache snapshot, UInt160 account) { StorageItem storage = snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(account)); @@ -103,7 +103,7 @@ public virtual BigInteger BalanceOf(DataCache snapshot, UInt160 account) return storage.GetInteroperable().Balance; } - [ContractMethod(0_09000000, CallFlags.WriteStates | CallFlags.AllowCall | CallFlags.AllowNotify)] + [ContractMethod(CpuFee = 1 << 17, StorageFee = 50, RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowCall | CallFlags.AllowNotify)] protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount, StackItem data) { if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); diff --git a/src/neo/SmartContract/Native/LedgerContract.cs b/src/neo/SmartContract/Native/LedgerContract.cs index 7d723e26c3..0329cabab9 100644 --- a/src/neo/SmartContract/Native/LedgerContract.cs +++ b/src/neo/SmartContract/Native/LedgerContract.cs @@ -60,13 +60,13 @@ public UInt256 GetBlockHash(DataCache snapshot, uint index) return new UInt256(item.Value); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public UInt256 CurrentHash(DataCache snapshot) { return snapshot[CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable().Hash; } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public uint CurrentIndex(DataCache snapshot) { return snapshot[CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable().Index; @@ -89,7 +89,7 @@ public TrimmedBlock GetTrimmedBlock(DataCache snapshot, UInt256 hash) return item.Value.AsSerializable(); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] private TrimmedBlock GetBlock(ApplicationEngine engine, byte[] indexOrHash) { UInt256 hash; @@ -145,7 +145,7 @@ public Transaction GetTransaction(DataCache snapshot, UInt256 hash) return GetTransactionState(snapshot, hash)?.Transaction; } - [ContractMethod(0_01000000, CallFlags.ReadStates, Name = "getTransaction")] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates, Name = "getTransaction")] private Transaction GetTransactionForContract(ApplicationEngine engine, UInt256 hash) { TransactionState state = GetTransactionState(engine.Snapshot, hash); @@ -153,7 +153,7 @@ private Transaction GetTransactionForContract(ApplicationEngine engine, UInt256 return state.Transaction; } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] private int GetTransactionHeight(ApplicationEngine engine, UInt256 hash) { TransactionState state = GetTransactionState(engine.Snapshot, hash); @@ -161,7 +161,7 @@ private int GetTransactionHeight(ApplicationEngine engine, UInt256 hash) return (int)state.BlockIndex; } - [ContractMethod(0_02000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.ReadStates)] private Transaction GetTransactionFromBlock(ApplicationEngine engine, byte[] blockIndexOrHash, int txIndex) { UInt256 hash; diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index 11610b734a..ebd3793260 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -65,7 +65,7 @@ protected override byte[] GetKey(byte[] tokenId) return Crypto.Hash160(tokenId); } - [ContractMethod(0_03000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private void AddRoot(ApplicationEngine engine, string root) { if (!rootRegex.IsMatch(root)) throw new ArgumentException(null, nameof(root)); @@ -81,7 +81,7 @@ public IEnumerable GetRoots(DataCache snapshot) return snapshot[CreateStorageKey(Prefix_Roots)].GetInteroperable(); } - [ContractMethod(0_03000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private void SetPrice(ApplicationEngine engine, long price) { if (price <= 0 || price > 10000_00000000) throw new ArgumentOutOfRangeException(nameof(price)); @@ -89,13 +89,13 @@ private void SetPrice(ApplicationEngine engine, long price) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_DomainPrice)).Set(price); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public long GetPrice(DataCache snapshot) { return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_DomainPrice)]; } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public bool IsAvailable(DataCache snapshot, string name) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -108,7 +108,7 @@ public bool IsAvailable(DataCache snapshot, string name) return true; } - [ContractMethod(0_01000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private bool Register(ApplicationEngine engine, string name, UInt160 owner) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -131,7 +131,7 @@ private bool Register(ApplicationEngine engine, string name, UInt160 owner) return true; } - [ContractMethod(0, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private uint Renew(ApplicationEngine engine, string name) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -146,7 +146,7 @@ private uint Renew(ApplicationEngine engine, string name) return state.Expiration; } - [ContractMethod(0_03000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, StorageFee = 20, RequiredCallFlags = CallFlags.WriteStates)] private void SetAdmin(ApplicationEngine engine, string name, UInt160 admin) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -165,7 +165,7 @@ private static bool CheckAdmin(ApplicationEngine engine, NameState state) return engine.CheckWitnessInternal(state.Admin); } - [ContractMethod(0_30000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, StorageFee = 200, RequiredCallFlags = CallFlags.WriteStates)] private void SetRecord(ApplicationEngine engine, string name, RecordType type, string data) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -198,7 +198,7 @@ private void SetRecord(ApplicationEngine engine, string name, RecordType type, s item.Value = Utility.StrictUTF8.GetBytes(data); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public string GetRecord(DataCache snapshot, string name, RecordType type) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -218,7 +218,7 @@ public string GetRecord(DataCache snapshot, string name, RecordType type) yield return ((RecordType)key.Key[^1], Utility.StrictUTF8.GetString(value.Value)); } - [ContractMethod(0_01000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private void DeleteRecord(ApplicationEngine engine, string name, RecordType type) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -229,7 +229,7 @@ private void DeleteRecord(ApplicationEngine engine, string name, RecordType type engine.Snapshot.Delete(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type)); } - [ContractMethod(0_03000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 17, RequiredCallFlags = CallFlags.ReadStates)] public string Resolve(DataCache snapshot, string name, RecordType type) { return Resolve(snapshot, name, type, 2); diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 388fc8d204..15311ac3f1 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -112,7 +112,7 @@ internal void Invoke(ApplicationEngine engine, byte version) ExecutionContextState state = context.GetState(); if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}."); - engine.AddGas(method.Price); + engine.AddGas(method.CpuFee * Policy.GetExecFeeFactor(engine.Snapshot) + method.StorageFee * Policy.GetStoragePrice(engine.Snapshot)); List parameters = new List(); if (method.NeedApplicationEngine) parameters.Add(engine); if (method.NeedSnapshot) parameters.Add(engine.Snapshot); diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index d69e4c1d08..737a8c2281 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -25,6 +25,7 @@ public sealed class NeoToken : FungibleToken private const byte Prefix_Candidate = 33; private const byte Prefix_Committee = 14; private const byte Prefix_GasPerBlock = 29; + private const byte Prefix_RegisterPrice = 13; private const byte Prefix_VoterRewardPerCommittee = 23; private const byte NeoHolderRewardRatio = 10; @@ -119,10 +120,8 @@ internal override void Initialize(ApplicationEngine engine) var cachedCommittee = new CachedCommittee(engine.ProtocolSettings.StandbyCommittee.Select(p => (p, BigInteger.Zero))); engine.Snapshot.Add(CreateStorageKey(Prefix_Committee), new StorageItem(cachedCommittee)); engine.Snapshot.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(new byte[0])); - - // Initialize economic parameters - engine.Snapshot.Add(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(0u), new StorageItem(5 * GAS.Factor)); + engine.Snapshot.Add(CreateStorageKey(Prefix_RegisterPrice), new StorageItem(1000 * GAS.Factor)); Mint(engine, Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators), TotalAmount, false); } @@ -173,7 +172,7 @@ internal override void PostPersist(ApplicationEngine engine) } } - [ContractMethod(0_05000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private void SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock) { if (gasPerBlock < 0 || gasPerBlock > 10 * GAS.Factor) @@ -185,12 +184,27 @@ private void SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock) entry.Set(gasPerBlock); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public BigInteger GetGasPerBlock(DataCache snapshot) { return GetSortedGasRecords(snapshot, Ledger.CurrentIndex(snapshot) + 1).First().GasPerBlock; } + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + private void SetRegisterPrice(ApplicationEngine engine, long registerPrice) + { + if (registerPrice <= 0) + throw new ArgumentOutOfRangeException(nameof(registerPrice)); + if (!CheckCommittee(engine)) throw new InvalidOperationException(); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_RegisterPrice)).Set(registerPrice); + } + + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] + public long GetRegisterPrice(DataCache snapshot) + { + return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_RegisterPrice)]; + } + private IEnumerable<(uint Index, BigInteger GasPerBlock)> GetSortedGasRecords(DataCache snapshot, uint end) { byte[] key = CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(end).ToArray(); @@ -199,7 +213,7 @@ public BigInteger GetGasPerBlock(DataCache snapshot) .Select(u => (BinaryPrimitives.ReadUInt32BigEndian(u.Key.Key.AsSpan(^sizeof(uint))), (BigInteger)u.Value)); } - [ContractMethod(0_03000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 17, RequiredCallFlags = CallFlags.ReadStates)] public BigInteger UnclaimedGas(DataCache snapshot, UInt160 account, uint end) { StorageItem storage = snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(account)); @@ -208,11 +222,12 @@ public BigInteger UnclaimedGas(DataCache snapshot, UInt160 account, uint end) return CalculateBonus(snapshot, state.VoteTo, state.Balance, state.BalanceHeight, end); } - [ContractMethod(1000_00000000, CallFlags.WriteStates)] + [ContractMethod(RequiredCallFlags = CallFlags.WriteStates)] private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) { if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) return false; + engine.AddGas(GetRegisterPrice(engine.Snapshot)); StorageKey key = CreateStorageKey(Prefix_Candidate).Add(pubkey); StorageItem item = engine.Snapshot.GetAndChange(key, () => new StorageItem(new CandidateState())); CandidateState state = item.GetInteroperable(); @@ -220,7 +235,7 @@ private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) return true; } - [ContractMethod(0_05000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.WriteStates)] private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) { if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) @@ -234,7 +249,7 @@ private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) return true; } - [ContractMethod(0_05000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.WriteStates)] private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) { if (!engine.CheckWitnessInternal(account)) return false; @@ -272,7 +287,7 @@ private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) return true; } - [ContractMethod(1_00000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 22, RequiredCallFlags = CallFlags.ReadStates)] public (ECPoint PublicKey, BigInteger Votes)[] GetCandidates(DataCache snapshot) { byte[] prefix_key = CreateStorageKey(Prefix_Candidate).ToArray(); @@ -283,7 +298,7 @@ private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) )).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes)).ToArray(); } - [ContractMethod(1_00000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 22, RequiredCallFlags = CallFlags.ReadStates)] public ECPoint[] GetCommittee(DataCache snapshot) { return GetCommitteeFromCache(snapshot).Select(p => p.PublicKey).OrderBy(p => p).ToArray(); @@ -315,7 +330,7 @@ public ECPoint[] ComputeNextBlockValidators(DataCache snapshot, ProtocolSettings return candidates.OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Take(settings.CommitteeMembersCount); } - [ContractMethod(1_00000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 22, RequiredCallFlags = CallFlags.ReadStates)] private ECPoint[] GetNextBlockValidators(ApplicationEngine engine) { return GetNextBlockValidators(engine.Snapshot, engine.ProtocolSettings.ValidatorsCount); diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index 33f039cf1b..cd0355c3ab 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -17,9 +17,9 @@ namespace Neo.SmartContract.Native public abstract class NonfungibleToken : NativeContract where TokenState : NFTState, new() { - [ContractMethod(0, CallFlags.None)] + [ContractMethod] public abstract string Symbol { get; } - [ContractMethod(0, CallFlags.None)] + [ContractMethod] public byte Decimals => 0; private const byte Prefix_TotalSupply = 11; @@ -97,39 +97,39 @@ private protected void Burn(ApplicationEngine engine, StorageKey key) PostTransfer(engine, token.Owner, null, token.Id); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public BigInteger TotalSupply(DataCache snapshot) { return snapshot[CreateStorageKey(Prefix_TotalSupply)]; } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public UInt160 OwnerOf(DataCache snapshot, byte[] tokenId) { return snapshot[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().Owner; } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public Map Properties(ApplicationEngine engine, byte[] tokenId) { return engine.Snapshot[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().ToMap(engine.ReferenceCounter); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public BigInteger BalanceOf(DataCache snapshot, UInt160 owner) { if (owner is null) throw new ArgumentNullException(nameof(owner)); return snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable().Balance ?? BigInteger.Zero; } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] protected IIterator Tokens(DataCache snapshot) { var results = snapshot.Find(CreateStorageKey(Prefix_Token).ToArray()).GetEnumerator(); return new StorageIterator(results, FindOptions.ValuesOnly | FindOptions.DeserializeValues | FindOptions.PickField1, null); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] protected IIterator TokensOf(DataCache snapshot, UInt160 owner) { NFTAccountState account = snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable(); @@ -137,7 +137,7 @@ protected IIterator TokensOf(DataCache snapshot, UInt160 owner) return new ArrayWrapper(tokens.Select(p => (StackItem)p).ToArray()); } - [ContractMethod(0_09000000, CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(CpuFee = 1 << 17, StorageFee = 50, RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] protected bool Transfer(ApplicationEngine engine, UInt160 to, byte[] tokenId) { if (to is null) throw new ArgumentNullException(nameof(to)); diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index 9ec83975e6..9b2330dad4 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -22,12 +22,11 @@ public sealed class OracleContract : NativeContract private const int MaxCallbackLength = 32; private const int MaxUserDataLength = 512; + private const byte Prefix_Price = 5; private const byte Prefix_RequestId = 9; private const byte Prefix_Request = 7; private const byte Prefix_IdList = 6; - private const long OracleRequestPrice = 0_50000000; - internal OracleContract() { var events = new List(Manifest.Abi.Events) @@ -81,7 +80,22 @@ internal OracleContract() Manifest.Abi.Events = events.ToArray(); } - [ContractMethod(0, CallFlags.WriteStates | CallFlags.AllowCall | CallFlags.AllowNotify)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + private void SetPrice(ApplicationEngine engine, long price) + { + if (price <= 0) + throw new ArgumentOutOfRangeException(nameof(price)); + if (!CheckCommittee(engine)) throw new InvalidOperationException(); + engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Price)).Set(price); + } + + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] + public long GetPrice(DataCache snapshot) + { + return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_Price)]; + } + + [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowCall | CallFlags.AllowNotify)] private void Finish(ApplicationEngine engine) { Transaction tx = (Transaction)engine.ScriptContainer; @@ -129,6 +143,7 @@ private static byte[] GetUrlHash(string url) internal override void Initialize(ApplicationEngine engine) { engine.Snapshot.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BigInteger.Zero)); + engine.Snapshot.Add(CreateStorageKey(Prefix_Price), new StorageItem(0_50000000)); } internal override void PostPersist(ApplicationEngine engine) @@ -157,7 +172,7 @@ internal override void PostPersist(ApplicationEngine engine) if (nodes.Length > 0) { int index = (int)(response.Id % (ulong)nodes.Length); - nodes[index].GAS += OracleRequestPrice; + nodes[index].GAS += GetPrice(engine.Snapshot); } } if (nodes != null) @@ -169,7 +184,7 @@ internal override void PostPersist(ApplicationEngine engine) } } - [ContractMethod(OracleRequestPrice, CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] private void Request(ApplicationEngine engine, string url, string filter, string callback, StackItem userData, long gasForResponse) { //Check the arguments @@ -179,6 +194,8 @@ private void Request(ApplicationEngine engine, string url, string filter, string || gasForResponse < 0_10000000) throw new ArgumentException(); + engine.AddGas(GetPrice(engine.Snapshot)); + //Mint gas for the response engine.AddGas(gasForResponse); GAS.Mint(engine, Hash, gasForResponse, false); @@ -211,7 +228,7 @@ private void Request(ApplicationEngine engine, string url, string filter, string engine.SendNotification(Hash, "OracleRequest", new VM.Types.Array { id, engine.CallingScriptHash.ToArray(), url, filter ?? StackItem.Null }); } - [ContractMethod(0_01000000, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.None)] private bool Verify(ApplicationEngine engine) { Transaction tx = (Transaction)engine.ScriptContainer; diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 5bee55656b..af03a8e5da 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -29,31 +29,31 @@ internal override void Initialize(ApplicationEngine engine) engine.Snapshot.Add(CreateStorageKey(Prefix_StoragePrice), new StorageItem(DefaultStoragePrice)); } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public long GetFeePerByte(DataCache snapshot) { return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_FeePerByte)]; } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public uint GetExecFeeFactor(DataCache snapshot) { return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_ExecFeeFactor)]; } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public uint GetStoragePrice(DataCache snapshot) { return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_StoragePrice)]; } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public bool IsBlocked(DataCache snapshot, UInt160 account) { return snapshot.Contains(CreateStorageKey(Prefix_BlockedAccount).Add(account)); } - [ContractMethod(0_03000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private void SetFeePerByte(ApplicationEngine engine, long value) { if (value < 0 || value > 1_00000000) throw new ArgumentOutOfRangeException(nameof(value)); @@ -61,7 +61,7 @@ private void SetFeePerByte(ApplicationEngine engine, long value) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_FeePerByte)).Set(value); } - [ContractMethod(0_03000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private void SetExecFeeFactor(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxExecFeeFactor) throw new ArgumentOutOfRangeException(nameof(value)); @@ -69,7 +69,7 @@ private void SetExecFeeFactor(ApplicationEngine engine, uint value) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_ExecFeeFactor)).Set(value); } - [ContractMethod(0_03000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private void SetStoragePrice(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxStoragePrice) throw new ArgumentOutOfRangeException(nameof(value)); @@ -77,7 +77,7 @@ private void SetStoragePrice(ApplicationEngine engine, uint value) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_StoragePrice)).Set(value); } - [ContractMethod(0_03000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private bool BlockAccount(ApplicationEngine engine, UInt160 account) { if (!CheckCommittee(engine)) throw new InvalidOperationException(); @@ -89,7 +89,7 @@ private bool BlockAccount(ApplicationEngine engine, UInt160 account) return true; } - [ContractMethod(0_03000000, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private bool UnblockAccount(ApplicationEngine engine, UInt160 account) { if (!CheckCommittee(engine)) throw new InvalidOperationException(); diff --git a/src/neo/SmartContract/Native/RoleManagement.cs b/src/neo/SmartContract/Native/RoleManagement.cs index 6c79093139..3ec8578621 100644 --- a/src/neo/SmartContract/Native/RoleManagement.cs +++ b/src/neo/SmartContract/Native/RoleManagement.cs @@ -16,7 +16,7 @@ internal RoleManagement() { } - [ContractMethod(0_01000000, CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public ECPoint[] GetDesignatedByRole(DataCache snapshot, Role role, uint index) { if (!Enum.IsDefined(typeof(Role), role)) @@ -30,7 +30,7 @@ public ECPoint[] GetDesignatedByRole(DataCache snapshot, Role role, uint index) .FirstOrDefault() ?? System.Array.Empty(); } - [ContractMethod(0, CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] nodes) { if (nodes.Length == 0 || nodes.Length > 32) diff --git a/src/neo/SmartContract/Native/StdLib.cs b/src/neo/SmartContract/Native/StdLib.cs index 505a132601..96d270c6a2 100644 --- a/src/neo/SmartContract/Native/StdLib.cs +++ b/src/neo/SmartContract/Native/StdLib.cs @@ -13,31 +13,31 @@ public sealed class StdLib : NativeContract { internal StdLib() { } - [ContractMethod(1 << 12, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 12)] private static byte[] Serialize(ApplicationEngine engine, StackItem item) { return BinarySerializer.Serialize(item, engine.Limits.MaxItemSize); } - [ContractMethod(1 << 14, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 14)] private static StackItem Deserialize(ApplicationEngine engine, byte[] data) { return BinarySerializer.Deserialize(data, engine.Limits.MaxStackSize, engine.ReferenceCounter); } - [ContractMethod(1 << 12, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 12)] private static byte[] JsonSerialize(ApplicationEngine engine, StackItem item) { return JsonSerializer.SerializeToByteArray(item, engine.Limits.MaxItemSize); } - [ContractMethod(1 << 14, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 14)] private static StackItem JsonDeserialize(ApplicationEngine engine, byte[] json) { return JsonSerializer.Deserialize(JObject.Parse(json, 10), engine.ReferenceCounter); } - [ContractMethod(1 << 12, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 12)] public static string Itoa(BigInteger value, int @base) { return @base switch @@ -48,7 +48,7 @@ public static string Itoa(BigInteger value, int @base) }; } - [ContractMethod(1 << 12, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 12)] public static BigInteger Atoi(string value, int @base) { return @base switch @@ -59,25 +59,25 @@ public static BigInteger Atoi(string value, int @base) }; } - [ContractMethod(1 << 12, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 12)] public static string Base64Encode(byte[] data) { return Convert.ToBase64String(data); } - [ContractMethod(1 << 12, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 12)] public static byte[] Base64Decode(string s) { return Convert.FromBase64String(s); } - [ContractMethod(1 << 12, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 12)] public static string Base58Encode(byte[] data) { return Base58.Encode(data); } - [ContractMethod(1 << 12, CallFlags.None)] + [ContractMethod(CpuFee = 1 << 12)] public static byte[] Base58Decode(string s) { return Base58.Decode(s); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index b4b7960e77..e627830569 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -23,14 +23,14 @@ public void TestInitialize() private class DummyNative : NativeContract { - [ContractMethod(0, CallFlags.None)] + [ContractMethod] public void NetTypes( bool p1, sbyte p2, byte p3, short p4, ushort p5, int p6, uint p7, long p8, ulong p9, BigInteger p10, byte[] p11, string p12, IInteroperable p13, ISerializable p14, int[] p15, ContractParameterType p16, object p17) { } - [ContractMethod(0, CallFlags.None)] + [ContractMethod] public void VMTypes( VM.Types.Boolean p1, VM.Types.Integer p2, VM.Types.ByteString p3, VM.Types.Buffer p4, VM.Types.Array p5, VM.Types.Struct p6, VM.Types.Map p7, VM.Types.StackItem p8 @@ -126,7 +126,7 @@ public void TestTrigger() public class TestNativeContract : NativeContract { - [ContractMethod(0, CallFlags.None)] + [ContractMethod] public string HelloWorld => "hello world"; public void TestTrigger(ApplicationEngine engine) From 7b24f8efaf752e0eb64eaa258a0b29f6359194be Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 15 Feb 2021 16:12:02 +0800 Subject: [PATCH 28/63] Add Transaction.Verify() (#2338) Co-authored-by: Shargon --- src/neo/Network/P2P/Payloads/Transaction.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index c675047a17..c919160019 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -279,6 +279,14 @@ public JObject ToJson(ProtocolSettings settings) return json; } + public bool Verify(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) + { + VerifyResult result = VerifyStateIndependent(settings); + if (result != VerifyResult.Succeed) return false; + result = VerifyStateDependent(settings, snapshot, context); + return result == VerifyResult.Succeed; + } + public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) { uint height = NativeContract.Ledger.CurrentIndex(snapshot); From fcc0a45fc77d863cdf10c959fad7e80c62879acc Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 15 Feb 2021 21:46:07 +0800 Subject: [PATCH 29/63] Add NativeUpdateHistory to ProtocolSettings (#2332) --- src/neo/ProtocolSettings.cs | 31 +++-- .../ApplicationEngine.Contract.cs | 13 +- .../Native/ContractManagement.cs | 3 +- .../SmartContract/Native/NativeContract.cs | 2 - .../SmartContract/Native/UT_FungibleToken.cs | 63 +-------- .../SmartContract/Native/UT_NativeContract.cs | 122 ------------------ tests/neo.UnitTests/UT_ProtocolSettings.cs | 5 +- 7 files changed, 35 insertions(+), 204 deletions(-) diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index 20db3460c6..ee0e990ce5 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -1,8 +1,8 @@ using Microsoft.Extensions.Configuration; using Neo.Cryptography.ECC; +using Neo.SmartContract.Native; using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; namespace Neo @@ -20,7 +20,7 @@ public record ProtocolSettings public uint MaxTransactionsPerBlock { get; init; } public int MemoryPoolMaxTransactions { get; init; } public uint MaxTraceableBlocks { get; init; } - public IReadOnlyDictionary NativeActivations { get; init; } + public IReadOnlyDictionary NativeUpdateHistory { get; init; } private IReadOnlyList _standbyValidators; public IReadOnlyList StandbyValidators => _standbyValidators ??= StandbyCommittee.Take(ValidatorsCount).ToArray(); @@ -68,7 +68,19 @@ public record ProtocolSettings MaxTransactionsPerBlock = 512, MemoryPoolMaxTransactions = 50_000, MaxTraceableBlocks = 2_102_400, - NativeActivations = ImmutableDictionary.Empty + NativeUpdateHistory = new Dictionary + { + [nameof(ContractManagement)] = new[] { 0u }, + [nameof(StdLib)] = new[] { 0u }, + [nameof(CryptoLib)] = new[] { 0u }, + [nameof(LedgerContract)] = new[] { 0u }, + [nameof(NeoToken)] = new[] { 0u }, + [nameof(GasToken)] = new[] { 0u }, + [nameof(PolicyContract)] = new[] { 0u }, + [nameof(RoleManagement)] = new[] { 0u }, + [nameof(OracleContract)] = new[] { 0u }, + [nameof(NameService)] = new[] { 0u } + } }; public static ProtocolSettings Load(string path, bool optional = true) @@ -90,17 +102,10 @@ public static ProtocolSettings Load(string path, bool optional = true) MaxTransactionsPerBlock = section.GetValue("MaxTransactionsPerBlock", Default.MaxTransactionsPerBlock), MemoryPoolMaxTransactions = section.GetValue("MemoryPoolMaxTransactions", Default.MemoryPoolMaxTransactions), MaxTraceableBlocks = section.GetValue("MaxTraceableBlocks", Default.MaxTraceableBlocks), - NativeActivations = section.GetSection("NativeActivations").Exists() - ? section.GetSection("NativeActivations").GetChildren().ToDictionary((a) => a.Key, b => uint.Parse(b.Value)) - : Default.NativeActivations + NativeUpdateHistory = section.GetSection("NativeUpdateHistory").Exists() + ? section.GetSection("NativeUpdateHistory").GetChildren().ToDictionary(p => p.Key, p => p.GetChildren().Select(q => uint.Parse(q.Value)).ToArray()) + : Default.NativeUpdateHistory }; } - - public uint GetNativeActivation(string name) - { - if (NativeActivations.TryGetValue(name, out uint index)) - return index; - return 0; - } } } diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 76aa7474b0..f75eb3f188 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -43,6 +43,9 @@ protected internal void CallNativeContract(byte version) NativeContract contract = NativeContract.GetContract(CurrentScriptHash); if (contract is null) throw new InvalidOperationException("It is not allowed to use \"System.Contract.CallNative\" directly."); + uint activeIndex = ProtocolSettings.NativeUpdateHistory[contract.Name][0]; + if (activeIndex > NativeContract.Ledger.CurrentIndex(Snapshot)) + throw new InvalidOperationException($"The native contract {contract.Name} is not active."); contract.Invoke(this, version); } @@ -88,8 +91,11 @@ protected internal void NativeOnPersist() if (Trigger != TriggerType.OnPersist) throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) - if (ProtocolSettings.GetNativeActivation(contract.Name) <= PersistingBlock.Index) + { + uint activeIndex = ProtocolSettings.NativeUpdateHistory[contract.Name][0]; + if (activeIndex <= PersistingBlock.Index) contract.OnPersist(this); + } } protected internal void NativePostPersist() @@ -97,8 +103,11 @@ protected internal void NativePostPersist() if (Trigger != TriggerType.PostPersist) throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) - if (ProtocolSettings.GetNativeActivation(contract.Name) <= PersistingBlock.Index) + { + uint activeIndex = ProtocolSettings.NativeUpdateHistory[contract.Name][0]; + if (activeIndex <= PersistingBlock.Index) contract.PostPersist(this); + } } } } diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 544d57ffba..da70ec082c 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -91,7 +91,8 @@ internal override void OnPersist(ApplicationEngine engine) { foreach (NativeContract contract in Contracts) { - if (engine.ProtocolSettings.GetNativeActivation(contract.Name) != engine.PersistingBlock.Index) + uint activeIndex = engine.ProtocolSettings.NativeUpdateHistory[contract.Name][0]; + if (activeIndex != engine.PersistingBlock.Index) continue; engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState { diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 15311ac3f1..bd4a964ca3 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -103,8 +103,6 @@ public static NativeContract GetContract(UInt160 hash) internal void Invoke(ApplicationEngine engine, byte version) { - if (engine.ProtocolSettings.GetNativeActivation(Name) > Ledger.CurrentIndex(engine.Snapshot)) - throw new InvalidOperationException($"The native contract {Name} is not active."); if (version != 0) throw new InvalidOperationException($"The native contract of version {version} is not active."); ExecutionContext context = engine.CurrentContext; diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs index 74309efc1f..2a10a91a76 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_FungibleToken.cs @@ -1,79 +1,18 @@ using Akka.TestKit.Xunit2; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.SmartContract; using Neo.SmartContract.Native; -using System; -using System.Numerics; namespace Neo.UnitTests.SmartContract.Native { [TestClass] public class UT_FungibleToken : TestKit { - protected const byte Prefix_TotalSupply = 11; - private static readonly TestNep17Token test = new TestNep17Token(); - - [TestMethod] - public void TestName() - { - Assert.AreEqual(test.Name, test.Manifest.Name); - } - [TestMethod] public void TestTotalSupply() { var snapshot = TestBlockchain.GetTestSnapshot(); - - StorageItem item = new StorageItem - { - Value = new byte[] { 0x01 } - }; - var key = CreateStorageKey(Prefix_TotalSupply); - - key.Id = test.Id; - - snapshot.Add(key, item); - test.TotalSupply(snapshot).Should().Be(1); - } - - [TestMethod] - public void TestTotalSupplyDecimal() - { - var snapshot = TestBlockchain.GetTestSnapshot(); - - BigInteger totalSupply = 100_000_000; - totalSupply *= test.Factor; - - StorageItem item = new StorageItem - { - Value = totalSupply.ToByteArrayStandard() - }; - var key = CreateStorageKey(Prefix_TotalSupply); - - key.Id = test.Id; - - snapshot.Add(key, item); - - test.TotalSupply(snapshot).Should().Be(10_000_000_000_000_000); + NativeContract.GAS.TotalSupply(snapshot).Should().Be(3000000050000000); } - - public StorageKey CreateStorageKey(byte prefix, byte[] key = null) - { - StorageKey storageKey = new StorageKey - { - Id = 0, - Key = new byte[sizeof(byte) + (key?.Length ?? 0)] - }; - storageKey.Key[0] = prefix; - key?.CopyTo(storageKey.Key.AsSpan(1)); - return storageKey; - } - } - - public class TestNep17Token : FungibleToken - { - public override string Symbol => throw new NotImplementedException(); - public override byte Decimals => 8; } } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index e627830569..710502bef5 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -1,137 +1,15 @@ -using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.IO; -using Neo.SmartContract; using Neo.SmartContract.Native; -using Neo.VM; -using System; -using System.Numerics; namespace Neo.UnitTests.SmartContract.Native { [TestClass] public class UT_NativeContract { - private static readonly TestNativeContract testNativeContract = new TestNativeContract(); - - [TestMethod] - public void TestInitialize() - { - ApplicationEngine ae = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); - testNativeContract.Initialize(ae); - } - - private class DummyNative : NativeContract - { - [ContractMethod] - public void NetTypes( - bool p1, sbyte p2, byte p3, short p4, ushort p5, int p6, uint p7, long p8, ulong p9, BigInteger p10, - byte[] p11, string p12, IInteroperable p13, ISerializable p14, int[] p15, ContractParameterType p16, - object p17) - { } - - [ContractMethod] - public void VMTypes( - VM.Types.Boolean p1, VM.Types.Integer p2, VM.Types.ByteString p3, VM.Types.Buffer p4, - VM.Types.Array p5, VM.Types.Struct p6, VM.Types.Map p7, VM.Types.StackItem p8 - ) - { } - } - - [TestMethod] - public void TestToParameter() - { - var manifest = new DummyNative().Manifest; - var netTypes = manifest.Abi.GetMethod("netTypes", 17); - - Assert.AreEqual(netTypes.ReturnType, ContractParameterType.Void); - Assert.AreEqual(netTypes.Parameters[0].Type, ContractParameterType.Boolean); - Assert.AreEqual(netTypes.Parameters[1].Type, ContractParameterType.Integer); - Assert.AreEqual(netTypes.Parameters[2].Type, ContractParameterType.Integer); - Assert.AreEqual(netTypes.Parameters[3].Type, ContractParameterType.Integer); - Assert.AreEqual(netTypes.Parameters[4].Type, ContractParameterType.Integer); - Assert.AreEqual(netTypes.Parameters[5].Type, ContractParameterType.Integer); - Assert.AreEqual(netTypes.Parameters[6].Type, ContractParameterType.Integer); - Assert.AreEqual(netTypes.Parameters[7].Type, ContractParameterType.Integer); - Assert.AreEqual(netTypes.Parameters[8].Type, ContractParameterType.Integer); - Assert.AreEqual(netTypes.Parameters[9].Type, ContractParameterType.Integer); - Assert.AreEqual(netTypes.Parameters[10].Type, ContractParameterType.ByteArray); - Assert.AreEqual(netTypes.Parameters[11].Type, ContractParameterType.String); - Assert.AreEqual(netTypes.Parameters[12].Type, ContractParameterType.Array); - Assert.AreEqual(netTypes.Parameters[13].Type, ContractParameterType.ByteArray); - Assert.AreEqual(netTypes.Parameters[14].Type, ContractParameterType.Array); - Assert.AreEqual(netTypes.Parameters[15].Type, ContractParameterType.Integer); - Assert.AreEqual(netTypes.Parameters[16].Type, ContractParameterType.Any); - - var vmTypes = manifest.Abi.GetMethod("vMTypes", 8); - - Assert.AreEqual(vmTypes.ReturnType, ContractParameterType.Void); - Assert.AreEqual(vmTypes.Parameters[0].Type, ContractParameterType.Boolean); - Assert.AreEqual(vmTypes.Parameters[1].Type, ContractParameterType.Integer); - Assert.AreEqual(vmTypes.Parameters[2].Type, ContractParameterType.ByteArray); - Assert.AreEqual(vmTypes.Parameters[3].Type, ContractParameterType.ByteArray); - Assert.AreEqual(vmTypes.Parameters[4].Type, ContractParameterType.Array); - Assert.AreEqual(vmTypes.Parameters[5].Type, ContractParameterType.Array); - Assert.AreEqual(vmTypes.Parameters[6].Type, ContractParameterType.Map); - Assert.AreEqual(vmTypes.Parameters[7].Type, ContractParameterType.Any); - } - [TestMethod] public void TestGetContract() { Assert.IsTrue(NativeContract.NEO == NativeContract.GetContract(NativeContract.NEO.Hash)); } - - [TestMethod] - public void TestInvoke() - { - var snapshot = TestBlockchain.GetTestSnapshot(); - snapshot.Add(NativeContract.ContractManagement.CreateStorageKey(8, testNativeContract.Hash), new StorageItem(new ContractState - { - Id = 0, - Nef = testNativeContract.Nef, - Hash = testNativeContract.Hash, - Manifest = testNativeContract.Manifest - })); - - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: 20_00000000)) - { - using ScriptBuilder script = new ScriptBuilder(); - script.EmitDynamicCall(testNativeContract.Hash, "wrongMethod"); - engine.LoadScript(script.ToArray()); - engine.Execute().Should().Be(VMState.FAULT); - } - - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: 20_00000000)) - { - using ScriptBuilder script = new ScriptBuilder(); - script.EmitDynamicCall(testNativeContract.Hash, "helloWorld"); - engine.LoadScript(script.ToArray()); - engine.Execute().Should().Be(VMState.HALT); - } - } - - [TestMethod] - public void TestTrigger() - { - var snapshot = TestBlockchain.GetTestSnapshot(); - - ApplicationEngine engine1 = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); - Assert.ThrowsException(() => testNativeContract.TestTrigger(engine1)); - - ApplicationEngine engine2 = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); - testNativeContract.TestTrigger(engine2); - } - } - - public class TestNativeContract : NativeContract - { - [ContractMethod] - public string HelloWorld => "hello world"; - - public void TestTrigger(ApplicationEngine engine) - { - if (engine.Trigger != TriggerType.OnPersist) throw new InvalidOperationException(); - } } } diff --git a/tests/neo.UnitTests/UT_ProtocolSettings.cs b/tests/neo.UnitTests/UT_ProtocolSettings.cs index 71bb145057..77cd48c0f6 100644 --- a/tests/neo.UnitTests/UT_ProtocolSettings.cs +++ b/tests/neo.UnitTests/UT_ProtocolSettings.cs @@ -1,5 +1,6 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract.Native; using Neo.Wallets; namespace Neo.UnitTests @@ -42,9 +43,9 @@ public void TestGetSeedList() } [TestMethod] - public void TestNativeActivations() + public void TestNativeUpdateHistory() { - ProtocolSettings.Default.NativeActivations.Count.Should().Be(0); + ProtocolSettings.Default.NativeUpdateHistory.Count.Should().Be(NativeContract.Contracts.Count); } } } From 32307177b08e618b2ddd27e47474dd948e140af6 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 16 Feb 2021 14:21:22 +0100 Subject: [PATCH 30/63] Remove BitConverter (#2341) * Remove BitConverter * Optimize Co-authored-by: Erik Zhang --- src/neo/Cryptography/Murmur3.cs | 5 +++- src/neo/SmartContract/Helper.cs | 4 +-- src/neo/SmartContract/InteropDescriptor.cs | 4 +-- .../Native/ContractManagement.cs | 2 +- src/neo/SmartContract/NefFile.cs | 3 ++- src/neo/Wallets/SQLite/UserWallet.cs | 26 ++++++++++++++----- 6 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/neo/Cryptography/Murmur3.cs b/src/neo/Cryptography/Murmur3.cs index 82642baf7f..671177a63a 100644 --- a/src/neo/Cryptography/Murmur3.cs +++ b/src/neo/Cryptography/Murmur3.cs @@ -65,7 +65,10 @@ protected override byte[] HashFinal() hash ^= hash >> 13; hash *= 0xc2b2ae35; hash ^= hash >> 16; - return BitConverter.GetBytes(hash); + + byte[] buffer = new byte[sizeof(uint)]; + BinaryPrimitives.WriteUInt32LittleEndian(buffer, hash); + return buffer; } public override void Initialize() diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 8f282adaee..13c6e901ae 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -125,7 +125,7 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List } if (script.Length != i + 5) return false; if (script[i++] != (byte)OpCode.SYSCALL) return false; - if (BitConverter.ToUInt32(script, i) != ApplicationEngine.Neo_Crypto_CheckMultisig) + if (BinaryPrimitives.ReadUInt32LittleEndian(script.AsSpan(i)) != ApplicationEngine.Neo_Crypto_CheckMultisig) return false; return true; } @@ -136,7 +136,7 @@ public static bool IsSignatureContract(this byte[] script) if (script[0] != (byte)OpCode.PUSHDATA1 || script[1] != 33 || script[35] != (byte)OpCode.SYSCALL - || BitConverter.ToUInt32(script, 36) != ApplicationEngine.Neo_Crypto_CheckSig) + || BinaryPrimitives.ReadUInt32LittleEndian(script.AsSpan(36)) != ApplicationEngine.Neo_Crypto_CheckSig) return false; return true; } diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index ea7acc2144..7b3a20d1d4 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -1,5 +1,5 @@ using Neo.Cryptography; -using System; +using System.Buffers.Binary; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -19,7 +19,7 @@ public class InteropDescriptor internal InteropDescriptor(string name, MethodInfo handler, long fixedPrice, CallFlags requiredCallFlags) { this.Name = name; - this.Hash = BitConverter.ToUInt32(Encoding.ASCII.GetBytes(name).Sha256(), 0); + this.Hash = BinaryPrimitives.ReadUInt32LittleEndian(Encoding.ASCII.GetBytes(name).Sha256()); this.Handler = handler; this.Parameters = handler.GetParameters().Select(p => new InteropParameterDescriptor(p)).ToList().AsReadOnly(); this.FixedPrice = fixedPrice; diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index da70ec082c..9b2e8ebd96 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -237,7 +237,7 @@ private void Destroy(ApplicationEngine engine) ContractState contract = engine.Snapshot.TryGet(ckey)?.GetInteroperable(); if (contract is null) return; engine.Snapshot.Delete(ckey); - foreach (var (key, _) in engine.Snapshot.Find(BitConverter.GetBytes(contract.Id))) + foreach (var (key, _) in engine.Snapshot.Find(StorageKey.CreateSearchPrefix(contract.Id, ReadOnlySpan.Empty))) engine.Snapshot.Delete(key); engine.SendNotification(Hash, "Destroy", new VM.Types.Array { hash.ToArray() }); } diff --git a/src/neo/SmartContract/NefFile.cs b/src/neo/SmartContract/NefFile.cs index e43bf67433..7a4ee2e850 100644 --- a/src/neo/SmartContract/NefFile.cs +++ b/src/neo/SmartContract/NefFile.cs @@ -2,6 +2,7 @@ using Neo.IO; using Neo.IO.Json; using System; +using System.Buffers.Binary; using System.IO; using System.Linq; @@ -107,7 +108,7 @@ public void Deserialize(BinaryReader reader) /// Return checksum public static uint ComputeChecksum(NefFile file) { - return BitConverter.ToUInt32(Crypto.Hash256(file.ToArray().AsSpan(..^sizeof(int)))); + return BinaryPrimitives.ReadUInt32LittleEndian(Crypto.Hash256(file.ToArray().AsSpan(..^sizeof(uint)))); } public JObject ToJson() diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs index 2d331dccfc..e14afb5e12 100644 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ b/src/neo/Wallets/SQLite/UserWallet.cs @@ -55,9 +55,9 @@ private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings) : this.masterKey = LoadStoredData("MasterKey").AesDecrypt(passwordKey, iv); this.scrypt = new ScryptParameters ( - BitConverter.ToInt32(LoadStoredData("ScryptN")), - BitConverter.ToInt32(LoadStoredData("ScryptR")), - BitConverter.ToInt32(LoadStoredData("ScryptP")) + BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptN")), + BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptR")), + BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptP")) ); this.accounts = LoadAccounts(); } @@ -82,15 +82,20 @@ private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings, S rng.GetBytes(masterKey); } Version version = Assembly.GetExecutingAssembly().GetName().Version; + byte[] versionBuffer = new byte[sizeof(int) * 4]; + BinaryPrimitives.WriteInt32LittleEndian(versionBuffer, version.Major); + BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(4), version.Minor); + BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(8), version.Build); + BinaryPrimitives.WriteInt32LittleEndian(versionBuffer.AsSpan(12), version.Revision); BuildDatabase(); SaveStoredData("IV", iv); SaveStoredData("Salt", salt); SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); SaveStoredData("MasterKey", masterKey.AesEncrypt(passwordKey, iv)); - SaveStoredData("Version", new[] { version.Major, version.Minor, version.Build, version.Revision }.Select(p => BitConverter.GetBytes(p)).SelectMany(p => p).ToArray()); - SaveStoredData("ScryptN", BitConverter.GetBytes(this.scrypt.N)); - SaveStoredData("ScryptR", BitConverter.GetBytes(this.scrypt.R)); - SaveStoredData("ScryptP", BitConverter.GetBytes(this.scrypt.P)); + SaveStoredData("Version", versionBuffer); + SaveStoredData("ScryptN", this.scrypt.N); + SaveStoredData("ScryptR", this.scrypt.R); + SaveStoredData("ScryptP", this.scrypt.P); } private void AddAccount(UserWalletAccount account) @@ -333,6 +338,13 @@ public static UserWallet Open(string path, SecureString password, ProtocolSettin return new UserWallet(path, password.ToAesKey(), settings); } + private void SaveStoredData(string name, int value) + { + byte[] data = new byte[sizeof(int)]; + BinaryPrimitives.WriteInt32LittleEndian(data, value); + SaveStoredData(name, data); + } + private void SaveStoredData(string name, byte[] value) { lock (db_lock) From b828b4353380380e6c4f0384c6a145998a003e82 Mon Sep 17 00:00:00 2001 From: bettybao1209 Date: Wed, 17 Feb 2021 02:49:52 +0800 Subject: [PATCH 31/63] Add ServiceAdded event (#2344) * add service * revert others Co-authored-by: Shargon --- src/neo/NeoSystem.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 18e3b4ce21..5db9964e8b 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -18,6 +18,7 @@ namespace Neo { public class NeoSystem : IDisposable { + public event EventHandler ServiceAdded; public ProtocolSettings Settings { get; } public ActorSystem ActorSystem { get; } = ActorSystem.Create(nameof(NeoSystem), $"akka {{ log-dead-letters = off , loglevel = warning, loggers = [ \"{typeof(Utility.Logger).AssemblyQualifiedName}\" ] }}" + @@ -105,6 +106,7 @@ public void Dispose() public void AddService(object service) { ImmutableInterlocked.Update(ref services, p => p.Add(service)); + ServiceAdded?.Invoke(this, service); } public T GetService(Func filter = null) From b11d7d2eaa542242850d396fe1e874f3753200d9 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 17 Feb 2021 12:32:39 +0800 Subject: [PATCH 32/63] Use system ECDsa (#2340) --- src/neo/Cryptography/Crypto.cs | 75 +++---------- src/neo/Cryptography/ECC/ECDsa.cs | 105 ------------------ src/neo/Helper.cs | 16 --- .../Cryptography/ECC/UT_ECDSA.cs | 36 ------ tests/neo.UnitTests/Cryptography/UT_Crypto.cs | 7 +- .../SmartContract/UT_InteropService.NEO.cs | 5 +- .../SmartContract/UT_InteropService.cs | 2 +- tests/neo.UnitTests/UT_Helper.cs | 13 --- 8 files changed, 21 insertions(+), 238 deletions(-) delete mode 100644 src/neo/Cryptography/ECC/ECDsa.cs delete mode 100644 tests/neo.UnitTests/Cryptography/ECC/UT_ECDSA.cs diff --git a/src/neo/Cryptography/Crypto.cs b/src/neo/Cryptography/Crypto.cs index ddd010cc63..ae5adf6aed 100644 --- a/src/neo/Cryptography/Crypto.cs +++ b/src/neo/Cryptography/Crypto.cs @@ -1,5 +1,4 @@ using System; -using System.Numerics; using System.Security.Cryptography; namespace Neo.Cryptography @@ -35,74 +34,26 @@ public static byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey) public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ECC.ECPoint pubkey) { - if (pubkey.Curve == ECC.ECCurve.Secp256r1) + ECCurve curve = + pubkey.Curve == ECC.ECCurve.Secp256r1 ? ECCurve.NamedCurves.nistP256 : + pubkey.Curve == ECC.ECCurve.Secp256k1 ? ECCurve.CreateFromFriendlyName("secP256k1") : + throw new NotSupportedException(); + byte[] buffer = pubkey.EncodePoint(false); + using var ecdsa = ECDsa.Create(new ECParameters { - byte[] buffer = pubkey.EncodePoint(false); - using (var ecdsa = ECDsa.Create(new ECParameters - { - Curve = ECCurve.NamedCurves.nistP256, - Q = new ECPoint - { - X = buffer[1..33], - Y = buffer[33..] - } - })) + Curve = curve, + Q = new ECPoint { - return ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256); + X = buffer[1..33], + Y = buffer[33..] } - } - else - { - var ecdsa = new ECC.ECDsa(pubkey); - var r = new BigInteger(signature[..32], true, true); - var s = new BigInteger(signature[32..], true, true); - return ecdsa.VerifySignature(message.Sha256(), r, s); - } + }); + return ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256); } public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ReadOnlySpan pubkey, ECC.ECCurve curve) { - if (curve == ECC.ECCurve.Secp256r1) - { - if (pubkey.Length == 33 && (pubkey[0] == 0x02 || pubkey[0] == 0x03)) - { - try - { - pubkey = ECC.ECPoint.DecodePoint(pubkey, curve).EncodePoint(false).AsSpan(1); - } - catch - { - return false; - } - } - else if (pubkey.Length == 65 && pubkey[0] == 0x04) - { - pubkey = pubkey[1..]; - } - else - { - throw new ArgumentException(); - } - using (var ecdsa = ECDsa.Create(new ECParameters - { - Curve = ECCurve.NamedCurves.nistP256, - Q = new ECPoint - { - X = pubkey[..32].ToArray(), - Y = pubkey[32..].ToArray() - } - })) - { - return ecdsa.VerifyData(message, signature, HashAlgorithmName.SHA256); - } - } - else - { - var ecdsa = new ECC.ECDsa(ECC.ECPoint.DecodePoint(pubkey, curve)); - var r = new BigInteger(signature[..32], true, true); - var s = new BigInteger(signature[32..], true, true); - return ecdsa.VerifySignature(message.Sha256(), r, s); - } + return VerifySignature(message, signature, ECC.ECPoint.DecodePoint(pubkey, curve)); } } } diff --git a/src/neo/Cryptography/ECC/ECDsa.cs b/src/neo/Cryptography/ECC/ECDsa.cs deleted file mode 100644 index 1c0a17d7a9..0000000000 --- a/src/neo/Cryptography/ECC/ECDsa.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Numerics; -using System.Security.Cryptography; - -namespace Neo.Cryptography.ECC -{ - public class ECDsa - { - private readonly byte[] privateKey; - private readonly ECPoint publicKey; - private readonly ECCurve curve; - - public ECDsa(byte[] privateKey, ECCurve curve) - : this(curve.G * privateKey) - { - this.privateKey = privateKey; - } - - public ECDsa(ECPoint publicKey) - { - this.publicKey = publicKey; - this.curve = publicKey.Curve; - } - - private BigInteger CalculateE(BigInteger n, ReadOnlySpan message) - { - if (n.GetBitLength() != (message.Length * 8)) - { - throw new ArgumentException($"Message must be {n.GetBitLength()} bit length"); - } - return new BigInteger(message, isUnsigned: true, isBigEndian: true); - } - - public BigInteger[] GenerateSignature(ReadOnlySpan message) - { - if (privateKey == null) throw new InvalidOperationException(); - BigInteger e = CalculateE(curve.N, message); - BigInteger d = new BigInteger(privateKey, isUnsigned: true, isBigEndian: true); - BigInteger r, s; - using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) - { - do - { - BigInteger k; - do - { - do - { - k = rng.NextBigInteger((int)curve.N.GetBitLength()); - } - while (k.Sign == 0 || k.CompareTo(curve.N) >= 0); - ECPoint p = ECPoint.Multiply(curve.G, k); - BigInteger x = p.X.Value; - r = x.Mod(curve.N); - } - while (r.Sign == 0); - s = (k.ModInverse(curve.N) * (e + d * r)).Mod(curve.N); - if (s > curve.N / 2) - { - s = curve.N - s; - } - } - while (s.Sign == 0); - } - return new BigInteger[] { r, s }; - } - - private static ECPoint SumOfTwoMultiplies(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) - { - int m = (int)Math.Max(k.GetBitLength(), l.GetBitLength()); - ECPoint Z = P + Q; - ECPoint R = P.Curve.Infinity; - for (int i = m - 1; i >= 0; --i) - { - R = R.Twice(); - if (k.TestBit(i)) - { - if (l.TestBit(i)) - R = R + Z; - else - R = R + P; - } - else - { - if (l.TestBit(i)) - R = R + Q; - } - } - return R; - } - - public bool VerifySignature(ReadOnlySpan message, BigInteger r, BigInteger s) - { - if (r.Sign < 1 || s.Sign < 1 || r.CompareTo(curve.N) >= 0 || s.CompareTo(curve.N) >= 0) - return false; - BigInteger e = CalculateE(curve.N, message); - BigInteger c = s.ModInverse(curve.N); - BigInteger u1 = (e * c).Mod(curve.N); - BigInteger u2 = (r * c).Mod(curve.N); - ECPoint point = SumOfTwoMultiplies(curve.G, u1, publicKey, u2); - BigInteger v = point.X.Value.Mod(curve.N); - return v.Equals(r); - } - } -} diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index 69fd7ddba4..0001d75438 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -7,7 +7,6 @@ using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; -using System.Security.Cryptography; using System.Text; namespace Neo @@ -169,21 +168,6 @@ internal static BigInteger NextBigInteger(this Random rand, int sizeInBits) return new BigInteger(b); } - internal static BigInteger NextBigInteger(this RandomNumberGenerator rng, int sizeInBits) - { - if (sizeInBits < 0) - throw new ArgumentException("sizeInBits must be non-negative"); - if (sizeInBits == 0) - return 0; - Span b = stackalloc byte[sizeInBits / 8 + 1]; - rng.GetBytes(b); - if (sizeInBits % 8 == 0) - b[b.Length - 1] = 0; - else - b[b.Length - 1] &= (byte)((1 << sizeInBits % 8) - 1); - return new BigInteger(b); - } - public static BigInteger Sum(this IEnumerable source) { var sum = BigInteger.Zero; diff --git a/tests/neo.UnitTests/Cryptography/ECC/UT_ECDSA.cs b/tests/neo.UnitTests/Cryptography/ECC/UT_ECDSA.cs deleted file mode 100644 index b776fd4eff..0000000000 --- a/tests/neo.UnitTests/Cryptography/ECC/UT_ECDSA.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Neo.Cryptography.ECC; -using Neo.IO; -using System; -using ECCurve = Neo.Cryptography.ECC.ECCurve; - -namespace Neo.UnitTests.Cryptography.ECC -{ - [TestClass] - public class UT_ECDSA - { - [TestMethod] - public void GenerateSignature() - { - var ecdsa = new ECDsa(ECCurve.Secp256k1.Infinity); - Assert.ThrowsException(() => ecdsa.GenerateSignature(UInt256.Zero.ToArray())); - Assert.ThrowsException(() => ecdsa.VerifySignature(new byte[0], 1, 2)); - - var pk = new byte[32]; - for (int x = 0; x < pk.Length; x++) pk[x] = (byte)x; - - ecdsa = new ECDsa(pk, ECCurve.Secp256k1); - - var zero = UInt256.Zero.ToArray(); - var one = UInt256.Parse("0100000000000000000000000000000000000000000000000000000000000000").ToArray(); - var two = UInt256.Parse("0200000000000000000000000000000000000000000000000000000000000000").ToArray(); - var sig = ecdsa.GenerateSignature(one); - - Assert.IsTrue(ecdsa.VerifySignature(one, sig[0], sig[1])); - Assert.IsFalse(ecdsa.VerifySignature(two, sig[0], sig[1])); - Assert.IsFalse(ecdsa.VerifySignature(one, sig[0] + 1, sig[1])); - Assert.IsFalse(ecdsa.VerifySignature(one, sig[0], sig[1] + 1)); - Assert.IsFalse(ecdsa.VerifySignature(zero, sig[0], sig[1])); - } - } -} diff --git a/tests/neo.UnitTests/Cryptography/UT_Crypto.cs b/tests/neo.UnitTests/Cryptography/UT_Crypto.cs index 0154fea65a..b4f0862b8d 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Crypto.cs +++ b/tests/neo.UnitTests/Cryptography/UT_Crypto.cs @@ -52,11 +52,12 @@ public void TestVerifySignature() wrongKey[0] = 0x03; for (int i = 1; i < 33; i++) wrongKey[i] = byte.MaxValue; - Crypto.VerifySignature(message, signature, wrongKey, Neo.Cryptography.ECC.ECCurve.Secp256r1).Should().BeFalse(); - - wrongKey = new byte[36]; Action action = () => Crypto.VerifySignature(message, signature, wrongKey, Neo.Cryptography.ECC.ECCurve.Secp256r1); action.Should().Throw(); + + wrongKey = new byte[36]; + action = () => Crypto.VerifySignature(message, signature, wrongKey, Neo.Cryptography.ECC.ECCurve.Secp256r1); + action.Should().Throw(); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index d820dc4c4e..3947723a59 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -33,7 +33,8 @@ public void TestCheckSig() ECPoint pubkey = keyPair.PublicKey; byte[] signature = Crypto.Sign(message, privateKey, pubkey.EncodePoint(false).Skip(1).ToArray()); engine.CheckSig(pubkey.EncodePoint(false), signature).Should().BeTrue(); - engine.CheckSig(new byte[70], signature).Should().BeFalse(); + Action action = () => engine.CheckSig(new byte[70], signature); + action.Should().Throw(); } [TestMethod] @@ -100,7 +101,7 @@ public void TestCrypto_CheckMultiSig() signature1, signature2 }; - engine.CheckMultisig(pubkeys, signatures).Should().BeFalse(); + Assert.ThrowsException(() => engine.CheckMultisig(pubkeys, signatures)); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index 538bac3597..d473618414 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -302,7 +302,7 @@ public void TestCrypto_Verify() byte[] wrongkey = pubkey.EncodePoint(false); wrongkey[0] = 5; - engine.CheckSig(wrongkey, signature).Should().BeFalse(); + Assert.ThrowsException(() => engine.CheckSig(wrongkey, signature)); } [TestMethod] diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index e117036565..dde8ccbd5e 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Net; using System.Numerics; -using System.Security.Cryptography; namespace Neo.UnitTests { @@ -205,18 +204,6 @@ public void TestNextBigIntegerForRandom() 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 TestUnmapForIPAddress() { From 116d5a88460e8bb10b3aba88d950eef3bf036f2c Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 17 Feb 2021 08:45:22 +0100 Subject: [PATCH 33/63] Add CreateStandardMultisigAccount (#2343) * Add CreateStandardMultisigAccount * plural * dotnet-format * Rename * Use CreateArray() * Fix UT Co-authored-by: Erik Zhang --- .../ApplicationEngine.Contract.cs | 6 +++ src/neo/VM/Helper.cs | 12 +---- .../UT_ApplicationEngine.Contract.cs | 50 +++++++++++++++++++ .../SmartContract/UT_ApplicationEngine.cs | 2 +- 4 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index f75eb3f188..2ea536c059 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -19,6 +19,7 @@ partial class ApplicationEngine /// Warning: check first that input public key is valid, before creating the script. /// public static readonly InteropDescriptor System_Contract_CreateStandardAccount = Register("System.Contract.CreateStandardAccount", nameof(CreateStandardAccount), 1 << 8, CallFlags.None); + public static readonly InteropDescriptor System_Contract_CreateMultisigAccount = Register("System.Contract.CreateMultisigAccount", nameof(CreateMultisigAccount), 1 << 8, CallFlags.None); public static readonly InteropDescriptor System_Contract_NativeOnPersist = Register("System.Contract.NativeOnPersist", nameof(NativeOnPersist), 0, CallFlags.WriteStates); public static readonly InteropDescriptor System_Contract_NativePostPersist = Register("System.Contract.NativePostPersist", nameof(NativePostPersist), 0, CallFlags.WriteStates); @@ -86,6 +87,11 @@ protected internal UInt160 CreateStandardAccount(ECPoint pubKey) return Contract.CreateSignatureRedeemScript(pubKey).ToScriptHash(); } + protected internal UInt160 CreateMultisigAccount(int m, ECPoint[] pubKeys) + { + return Contract.CreateMultiSigRedeemScript(m, pubKeys).ToScriptHash(); + } + protected internal void NativeOnPersist() { if (Trigger != TriggerType.OnPersist) diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 7a6165b397..001e044f8e 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -53,17 +53,7 @@ public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scrip public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, CallFlags flags, params object[] args) { - if (args.Length == 0) - { - sb.Emit(OpCode.NEWARRAY0); - } - else - { - for (int i = args.Length - 1; i >= 0; i--) - sb.EmitPush(args[i]); - sb.EmitPush(args.Length); - sb.Emit(OpCode.PACK); - } + sb.CreateArray(args); sb.EmitPush(flags); sb.EmitPush(operation); sb.EmitPush(scriptHash); diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs new file mode 100644 index 0000000000..939cc282a2 --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Contract.cs @@ -0,0 +1,50 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.SmartContract; +using Neo.VM; +using System.Linq; + +namespace Neo.UnitTests.SmartContract +{ + public partial class UT_ApplicationEngine + { + [TestMethod] + public void TestCreateStandardAccount() + { + var settings = ProtocolSettings.Default; + using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + + using var script = new ScriptBuilder(); + script.EmitSysCall(ApplicationEngine.System_Contract_CreateStandardAccount, settings.StandbyCommittee[0].EncodePoint(true)); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(VMState.HALT, engine.Execute()); + + var result = engine.ResultStack.Pop(); + new UInt160(result.GetSpan()).Should().Be(Contract.CreateSignatureRedeemScript(settings.StandbyCommittee[0]).ToScriptHash()); + } + + [TestMethod] + public void TestCreateStandardMultisigAccount() + { + var settings = ProtocolSettings.Default; + using var engine = ApplicationEngine.Create(TriggerType.Application, null, null, settings: TestBlockchain.TheNeoSystem.Settings, gas: 1100_00000000); + + using var script = new ScriptBuilder(); + script.EmitSysCall(ApplicationEngine.System_Contract_CreateMultisigAccount, new object[] + { + 2, + 3, + settings.StandbyCommittee[0].EncodePoint(true), + settings.StandbyCommittee[1].EncodePoint(true), + settings.StandbyCommittee[2].EncodePoint(true) + }); + engine.LoadScript(script.ToArray()); + + Assert.AreEqual(VMState.HALT, engine.Execute()); + + var result = engine.ResultStack.Pop(); + new UInt160(result.GetSpan()).Should().Be(Contract.CreateMultiSigRedeemScript(2, settings.StandbyCommittee.Take(3).ToArray()).ToScriptHash()); + } + } +} diff --git a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs index 9b808a907c..66f4f87e64 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.cs @@ -6,7 +6,7 @@ namespace Neo.UnitTests.SmartContract { [TestClass] - public class UT_ApplicationEngine + public partial class UT_ApplicationEngine { private string eventName = null; From 9f21f3cf059193bffa098c64771769d862b11a03 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 17 Feb 2021 16:01:54 +0800 Subject: [PATCH 34/63] Transaction.Verify returns VerifyResult (#2348) Co-authored-by: Shargon --- src/neo/Network/P2P/Payloads/Transaction.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index c919160019..b114c15f73 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -279,12 +279,11 @@ public JObject ToJson(ProtocolSettings settings) return json; } - public bool Verify(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) + public VerifyResult Verify(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) { VerifyResult result = VerifyStateIndependent(settings); - if (result != VerifyResult.Succeed) return false; - result = VerifyStateDependent(settings, snapshot, context); - return result == VerifyResult.Succeed; + if (result != VerifyResult.Succeed) return result; + return VerifyStateDependent(settings, snapshot, context); } public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) From 79d462eb4a478ae82c181ef723033457c9fcd1d6 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 17 Feb 2021 22:51:40 +0800 Subject: [PATCH 35/63] Fix CallFlags for native contracts and syscalls (#2339) --- .../SmartContract/ApplicationEngine.Contract.cs | 4 ++-- src/neo/SmartContract/ApplicationEngine.Storage.cs | 6 +++--- src/neo/SmartContract/ApplicationEngine.cs | 2 +- src/neo/SmartContract/Native/ContractManagement.cs | 12 ++++++------ src/neo/SmartContract/Native/FungibleToken.cs | 2 +- src/neo/SmartContract/Native/NameService.cs | 14 +++++++------- src/neo/SmartContract/Native/NeoToken.cs | 10 +++++----- src/neo/SmartContract/Native/NonfungibleToken.cs | 2 +- src/neo/SmartContract/Native/OracleContract.cs | 6 +++--- src/neo/SmartContract/Native/PolicyContract.cs | 10 +++++----- src/neo/SmartContract/Native/RoleManagement.cs | 2 +- 11 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 2ea536c059..64ecea9182 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -20,8 +20,8 @@ partial class ApplicationEngine /// public static readonly InteropDescriptor System_Contract_CreateStandardAccount = Register("System.Contract.CreateStandardAccount", nameof(CreateStandardAccount), 1 << 8, CallFlags.None); public static readonly InteropDescriptor System_Contract_CreateMultisigAccount = Register("System.Contract.CreateMultisigAccount", nameof(CreateMultisigAccount), 1 << 8, CallFlags.None); - public static readonly InteropDescriptor System_Contract_NativeOnPersist = Register("System.Contract.NativeOnPersist", nameof(NativeOnPersist), 0, CallFlags.WriteStates); - public static readonly InteropDescriptor System_Contract_NativePostPersist = Register("System.Contract.NativePostPersist", nameof(NativePostPersist), 0, CallFlags.WriteStates); + public static readonly InteropDescriptor System_Contract_NativeOnPersist = Register("System.Contract.NativeOnPersist", nameof(NativeOnPersist), 0, CallFlags.States); + public static readonly InteropDescriptor System_Contract_NativePostPersist = Register("System.Contract.NativePostPersist", nameof(NativePostPersist), 0, CallFlags.States); protected internal void CallContract(UInt160 contractHash, string method, CallFlags callFlags, Array args) { diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index 17b62fba63..e593f1d0f2 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -15,9 +15,9 @@ partial class ApplicationEngine public static readonly InteropDescriptor System_Storage_AsReadOnly = Register("System.Storage.AsReadOnly", nameof(AsReadOnly), 1 << 4, CallFlags.ReadStates); public static readonly InteropDescriptor System_Storage_Get = Register("System.Storage.Get", nameof(Get), 1 << 15, CallFlags.ReadStates); public static readonly InteropDescriptor System_Storage_Find = Register("System.Storage.Find", nameof(Find), 1 << 15, CallFlags.ReadStates); - public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 0, CallFlags.WriteStates); - public static readonly InteropDescriptor System_Storage_PutEx = Register("System.Storage.PutEx", nameof(PutEx), 0, CallFlags.WriteStates); - public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 0, CallFlags.WriteStates); + public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 0, CallFlags.States); + public static readonly InteropDescriptor System_Storage_PutEx = Register("System.Storage.PutEx", nameof(PutEx), 0, CallFlags.States); + public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 0, CallFlags.States); protected internal StorageContext GetStorageContext() { diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 39066917a9..26b2d049a4 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -90,7 +90,7 @@ private ExecutionContext CallContractInternal(ContractState contract, ContractMe { if (method.Safe) { - flags &= ~CallFlags.WriteStates; + flags &= ~(CallFlags.WriteStates | CallFlags.AllowNotify); } else { diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 9b2e8ebd96..52fd8f61a9 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -111,7 +111,7 @@ private long GetMinimumDeploymentFee(DataCache snapshot) return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_MinimumDeploymentFee)]; } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetMinimumDeploymentFee(ApplicationEngine engine, BigInteger value) { if (value < 0) throw new ArgumentOutOfRangeException(nameof(value)); @@ -131,13 +131,13 @@ public IEnumerable ListContracts(DataCache snapshot) return snapshot.Find(listContractsPrefix).Select(kvp => kvp.Value.GetInteroperable()); } - [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest) { return Deploy(engine, nefFile, manifest, StackItem.Null); } - [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { if (engine.ScriptContainer is not Transaction tx) @@ -183,13 +183,13 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma return contract; } - [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest) { Update(engine, nefFile, manifest, StackItem.Null); } - [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { if (nefFile is null && manifest is null) throw new ArgumentException(); @@ -229,7 +229,7 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, S engine.SendNotification(Hash, "Update", new VM.Types.Array { contract.Hash.ToArray() }); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] private void Destroy(ApplicationEngine engine) { UInt160 hash = engine.CallingScriptHash; diff --git a/src/neo/SmartContract/Native/FungibleToken.cs b/src/neo/SmartContract/Native/FungibleToken.cs index bcd1239ced..d1d96dd3f7 100644 --- a/src/neo/SmartContract/Native/FungibleToken.cs +++ b/src/neo/SmartContract/Native/FungibleToken.cs @@ -103,7 +103,7 @@ public virtual BigInteger BalanceOf(DataCache snapshot, UInt160 account) return storage.GetInteroperable().Balance; } - [ContractMethod(CpuFee = 1 << 17, StorageFee = 50, RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowCall | CallFlags.AllowNotify)] + [ContractMethod(CpuFee = 1 << 17, StorageFee = 50, RequiredCallFlags = CallFlags.States | CallFlags.AllowCall | CallFlags.AllowNotify)] protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount, StackItem data) { if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index ebd3793260..8490e5f12d 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -65,7 +65,7 @@ protected override byte[] GetKey(byte[] tokenId) return Crypto.Hash160(tokenId); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void AddRoot(ApplicationEngine engine, string root) { if (!rootRegex.IsMatch(root)) throw new ArgumentException(null, nameof(root)); @@ -81,7 +81,7 @@ public IEnumerable GetRoots(DataCache snapshot) return snapshot[CreateStorageKey(Prefix_Roots)].GetInteroperable(); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetPrice(ApplicationEngine engine, long price) { if (price <= 0 || price > 10000_00000000) throw new ArgumentOutOfRangeException(nameof(price)); @@ -108,7 +108,7 @@ public bool IsAvailable(DataCache snapshot, string name) return true; } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private bool Register(ApplicationEngine engine, string name, UInt160 owner) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -131,7 +131,7 @@ private bool Register(ApplicationEngine engine, string name, UInt160 owner) return true; } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private uint Renew(ApplicationEngine engine, string name) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -146,7 +146,7 @@ private uint Renew(ApplicationEngine engine, string name) return state.Expiration; } - [ContractMethod(CpuFee = 1 << 15, StorageFee = 20, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, StorageFee = 20, RequiredCallFlags = CallFlags.States)] private void SetAdmin(ApplicationEngine engine, string name, UInt160 admin) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -165,7 +165,7 @@ private static bool CheckAdmin(ApplicationEngine engine, NameState state) return engine.CheckWitnessInternal(state.Admin); } - [ContractMethod(CpuFee = 1 << 15, StorageFee = 200, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, StorageFee = 200, RequiredCallFlags = CallFlags.States)] private void SetRecord(ApplicationEngine engine, string name, RecordType type, string data) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -218,7 +218,7 @@ public string GetRecord(DataCache snapshot, string name, RecordType type) yield return ((RecordType)key.Key[^1], Utility.StrictUTF8.GetString(value.Value)); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void DeleteRecord(ApplicationEngine engine, string name, RecordType type) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index 737a8c2281..6ec3ee1685 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -172,7 +172,7 @@ internal override void PostPersist(ApplicationEngine engine) } } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock) { if (gasPerBlock < 0 || gasPerBlock > 10 * GAS.Factor) @@ -190,7 +190,7 @@ public BigInteger GetGasPerBlock(DataCache snapshot) return GetSortedGasRecords(snapshot, Ledger.CurrentIndex(snapshot) + 1).First().GasPerBlock; } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetRegisterPrice(ApplicationEngine engine, long registerPrice) { if (registerPrice <= 0) @@ -222,7 +222,7 @@ public BigInteger UnclaimedGas(DataCache snapshot, UInt160 account, uint end) return CalculateBonus(snapshot, state.VoteTo, state.Balance, state.BalanceHeight, end); } - [ContractMethod(RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(RequiredCallFlags = CallFlags.States)] private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) { if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) @@ -235,7 +235,7 @@ private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) return true; } - [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.States)] private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) { if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) @@ -249,7 +249,7 @@ private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) return true; } - [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.States)] private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) { if (!engine.CheckWitnessInternal(account)) return false; diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index cd0355c3ab..bb2f63bb9b 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -137,7 +137,7 @@ protected IIterator TokensOf(DataCache snapshot, UInt160 owner) return new ArrayWrapper(tokens.Select(p => (StackItem)p).ToArray()); } - [ContractMethod(CpuFee = 1 << 17, StorageFee = 50, RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(CpuFee = 1 << 17, StorageFee = 50, RequiredCallFlags = CallFlags.States | CallFlags.AllowCall | CallFlags.AllowNotify)] protected bool Transfer(ApplicationEngine engine, UInt160 to, byte[] tokenId) { if (to is null) throw new ArgumentNullException(nameof(to)); diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index 9b2330dad4..09f512678e 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -80,7 +80,7 @@ internal OracleContract() Manifest.Abi.Events = events.ToArray(); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetPrice(ApplicationEngine engine, long price) { if (price <= 0) @@ -95,7 +95,7 @@ public long GetPrice(DataCache snapshot) return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_Price)]; } - [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowCall | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowCall | CallFlags.AllowNotify)] private void Finish(ApplicationEngine engine) { Transaction tx = (Transaction)engine.ScriptContainer; @@ -184,7 +184,7 @@ internal override void PostPersist(ApplicationEngine engine) } } - [ContractMethod(RequiredCallFlags = CallFlags.WriteStates | CallFlags.AllowNotify)] + [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] private void Request(ApplicationEngine engine, string url, string filter, string callback, StackItem userData, long gasForResponse) { //Check the arguments diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index af03a8e5da..31e0d7b834 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -53,7 +53,7 @@ public bool IsBlocked(DataCache snapshot, UInt160 account) return snapshot.Contains(CreateStorageKey(Prefix_BlockedAccount).Add(account)); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetFeePerByte(ApplicationEngine engine, long value) { if (value < 0 || value > 1_00000000) throw new ArgumentOutOfRangeException(nameof(value)); @@ -61,7 +61,7 @@ private void SetFeePerByte(ApplicationEngine engine, long value) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_FeePerByte)).Set(value); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetExecFeeFactor(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxExecFeeFactor) throw new ArgumentOutOfRangeException(nameof(value)); @@ -69,7 +69,7 @@ private void SetExecFeeFactor(ApplicationEngine engine, uint value) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_ExecFeeFactor)).Set(value); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void SetStoragePrice(ApplicationEngine engine, uint value) { if (value == 0 || value > MaxStoragePrice) throw new ArgumentOutOfRangeException(nameof(value)); @@ -77,7 +77,7 @@ private void SetStoragePrice(ApplicationEngine engine, uint value) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_StoragePrice)).Set(value); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private bool BlockAccount(ApplicationEngine engine, UInt160 account) { if (!CheckCommittee(engine)) throw new InvalidOperationException(); @@ -89,7 +89,7 @@ private bool BlockAccount(ApplicationEngine engine, UInt160 account) return true; } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private bool UnblockAccount(ApplicationEngine engine, UInt160 account) { if (!CheckCommittee(engine)) throw new InvalidOperationException(); diff --git a/src/neo/SmartContract/Native/RoleManagement.cs b/src/neo/SmartContract/Native/RoleManagement.cs index 3ec8578621..0ca1bbef44 100644 --- a/src/neo/SmartContract/Native/RoleManagement.cs +++ b/src/neo/SmartContract/Native/RoleManagement.cs @@ -30,7 +30,7 @@ public ECPoint[] GetDesignatedByRole(DataCache snapshot, Role role, uint index) .FirstOrDefault() ?? System.Array.Empty(); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.WriteStates)] + [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] nodes) { if (nodes.Length == 0 || nodes.Length > 32) From da622ed5329c34ca3be71708309d21e8a4f41a8f Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 17 Feb 2021 22:57:56 +0800 Subject: [PATCH 36/63] Fix FromJson (#2350) --- src/neo/IO/Json/JArray.cs | 2 + src/neo/IO/Json/JBoolean.cs | 2 + src/neo/IO/Json/JNumber.cs | 2 + src/neo/IO/Json/JObject.cs | 37 +++++++++++++------ src/neo/IO/Json/JString.cs | 2 + src/neo/SmartContract/ContractParameter.cs | 2 +- .../Manifest/ContractEventDescriptor.cs | 2 +- .../SmartContract/Manifest/ContractGroup.cs | 4 +- .../Manifest/ContractManifest.cs | 6 +-- .../Manifest/ContractMethodDescriptor.cs | 8 ++-- .../Manifest/ContractParameterDefinition.cs | 4 +- .../Manifest/ContractPermission.cs | 2 +- .../Manifest/ContractPermissionDescriptor.cs | 2 +- src/neo/Wallets/NEP6/NEP6Account.cs | 8 ++-- 14 files changed, 53 insertions(+), 30 deletions(-) diff --git a/src/neo/IO/Json/JArray.cs b/src/neo/IO/Json/JArray.cs index 9519c98b3e..4da0659f68 100644 --- a/src/neo/IO/Json/JArray.cs +++ b/src/neo/IO/Json/JArray.cs @@ -71,6 +71,8 @@ public void CopyTo(JObject[] array, int arrayIndex) items.CopyTo(array, arrayIndex); } + public override JArray GetArray() => this; + public IEnumerator GetEnumerator() { return items.GetEnumerator(); diff --git a/src/neo/IO/Json/JBoolean.cs b/src/neo/IO/Json/JBoolean.cs index 7cfd7ee025..71e4c5dc3f 100644 --- a/src/neo/IO/Json/JBoolean.cs +++ b/src/neo/IO/Json/JBoolean.cs @@ -26,6 +26,8 @@ public override string AsString() return Value.ToString().ToLowerInvariant(); } + public override bool GetBoolean() => Value; + public override string ToString() { return AsString(); diff --git a/src/neo/IO/Json/JNumber.cs b/src/neo/IO/Json/JNumber.cs index 165d8138ec..356b024522 100644 --- a/src/neo/IO/Json/JNumber.cs +++ b/src/neo/IO/Json/JNumber.cs @@ -32,6 +32,8 @@ public override string AsString() return Value.ToString(CultureInfo.InvariantCulture); } + public override double GetNumber() => Value; + public override string ToString() { return AsString(); diff --git a/src/neo/IO/Json/JObject.cs b/src/neo/IO/Json/JObject.cs index 9b725381af..8aae4bf76f 100644 --- a/src/neo/IO/Json/JObject.cs +++ b/src/neo/IO/Json/JObject.cs @@ -45,18 +45,21 @@ public bool ContainsProperty(string key) return Properties.ContainsKey(key); } - private static string GetString(ref Utf8JsonReader reader) + public virtual JArray GetArray() => throw new InvalidCastException(); + + public virtual bool GetBoolean() => throw new InvalidCastException(); + + public int GetInt32() { - try - { - return reader.GetString(); - } - catch (InvalidOperationException ex) - { - throw new FormatException(ex.Message, ex); - } + double d = GetNumber(); + if (d % 1 != 0) throw new InvalidCastException(); + return checked((int)d); } + public virtual double GetNumber() => throw new InvalidCastException(); + + public virtual string GetString() => throw new InvalidCastException(); + public static JObject Parse(ReadOnlySpan value, int max_nest = 100) { Utf8JsonReader reader = new Utf8JsonReader(value, new JsonReaderOptions @@ -92,7 +95,7 @@ private static JObject Read(ref Utf8JsonReader reader, bool skipReading = false) JsonTokenType.Number => reader.GetDouble(), JsonTokenType.StartArray => ReadArray(ref reader), JsonTokenType.StartObject => ReadObject(ref reader), - JsonTokenType.String => GetString(ref reader), + JsonTokenType.String => ReadString(ref reader), JsonTokenType.True => true, _ => throw new FormatException(), }; @@ -125,7 +128,7 @@ private static JObject ReadObject(ref Utf8JsonReader reader) case JsonTokenType.EndObject: return obj; case JsonTokenType.PropertyName: - string name = GetString(ref reader); + string name = ReadString(ref reader); if (obj.Properties.ContainsKey(name)) throw new FormatException(); JObject value = Read(ref reader); obj.Properties.Add(name, value); @@ -137,6 +140,18 @@ private static JObject ReadObject(ref Utf8JsonReader reader) throw new FormatException(); } + private static string ReadString(ref Utf8JsonReader reader) + { + try + { + return reader.GetString(); + } + catch (InvalidOperationException ex) + { + throw new FormatException(ex.Message, ex); + } + } + public byte[] ToByteArray(bool indented) { using MemoryStream ms = new MemoryStream(); diff --git a/src/neo/IO/Json/JString.cs b/src/neo/IO/Json/JString.cs index 7c625875b3..c057c0cb34 100644 --- a/src/neo/IO/Json/JString.cs +++ b/src/neo/IO/Json/JString.cs @@ -29,6 +29,8 @@ public override string AsString() return Value; } + public override string GetString() => Value; + public override T TryGetEnum(T defaultValue = default, bool ignoreCase = false) { try diff --git a/src/neo/SmartContract/ContractParameter.cs b/src/neo/SmartContract/ContractParameter.cs index 21318be7da..1bae945d05 100644 --- a/src/neo/SmartContract/ContractParameter.cs +++ b/src/neo/SmartContract/ContractParameter.cs @@ -39,7 +39,7 @@ public static ContractParameter FromJson(JObject json) { ContractParameter parameter = new ContractParameter { - Type = json["type"].TryGetEnum() + Type = Enum.Parse(json["type"].GetString()) }; if (json["value"] != null) switch (parameter.Type) diff --git a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs index 7d8afd1121..69570011d0 100644 --- a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs @@ -44,7 +44,7 @@ public static ContractEventDescriptor FromJson(JObject json) { ContractEventDescriptor descriptor = new ContractEventDescriptor { - Name = json["name"].AsString(), + Name = json["name"].GetString(), Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), }; if (string.IsNullOrEmpty(descriptor.Name)) throw new FormatException(); diff --git a/src/neo/SmartContract/Manifest/ContractGroup.cs b/src/neo/SmartContract/Manifest/ContractGroup.cs index b39c44c0dd..a756e56f35 100644 --- a/src/neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/neo/SmartContract/Manifest/ContractGroup.cs @@ -45,8 +45,8 @@ public static ContractGroup FromJson(JObject json) { ContractGroup group = new ContractGroup { - PubKey = ECPoint.Parse(json["pubkey"].AsString(), ECCurve.Secp256r1), - Signature = Convert.FromBase64String(json["signature"].AsString()), + PubKey = ECPoint.Parse(json["pubkey"].GetString(), ECCurve.Secp256r1), + Signature = Convert.FromBase64String(json["signature"].GetString()), }; if (group.Signature.Length != 64) throw new FormatException(); return group; diff --git a/src/neo/SmartContract/Manifest/ContractManifest.cs b/src/neo/SmartContract/Manifest/ContractManifest.cs index 71d93020f2..7c95c278e5 100644 --- a/src/neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/neo/SmartContract/Manifest/ContractManifest.cs @@ -95,12 +95,12 @@ public static ContractManifest FromJson(JObject json) { ContractManifest manifest = new ContractManifest { - Name = json["name"].AsString(), + Name = json["name"].GetString(), Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson(u)).ToArray(), - SupportedStandards = ((JArray)json["supportedstandards"]).Select(u => u.AsString()).ToArray(), + SupportedStandards = ((JArray)json["supportedstandards"]).Select(u => u.GetString()).ToArray(), Abi = ContractAbi.FromJson(json["abi"]), Permissions = ((JArray)json["permissions"]).Select(u => ContractPermission.FromJson(u)).ToArray(), - Trusts = WildcardContainer.FromJson(json["trusts"], u => UInt160.Parse(u.AsString())), + Trusts = WildcardContainer.FromJson(json["trusts"], u => UInt160.Parse(u.GetString())), Extra = json["extra"] }; if (string.IsNullOrEmpty(manifest.Name)) diff --git a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs index b9374d98e2..94029e5a08 100644 --- a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -49,11 +49,11 @@ public override StackItem ToStackItem(ReferenceCounter referenceCounter) { ContractMethodDescriptor descriptor = new ContractMethodDescriptor { - Name = json["name"].AsString(), + Name = json["name"].GetString(), Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), - ReturnType = (ContractParameterType)Enum.Parse(typeof(ContractParameterType), json["returntype"].AsString()), - Offset = (int)json["offset"].AsNumber(), - Safe = json["safe"].AsBoolean(), + ReturnType = Enum.Parse(json["returntype"].GetString()), + Offset = json["offset"].GetInt32(), + Safe = json["safe"].GetBoolean() }; if (string.IsNullOrEmpty(descriptor.Name)) throw new FormatException(); _ = descriptor.Parameters.ToDictionary(p => p.Name); diff --git a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs index 4bfed05262..e18a04b199 100644 --- a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -39,8 +39,8 @@ public static ContractParameterDefinition FromJson(JObject json) { ContractParameterDefinition parameter = new ContractParameterDefinition { - Name = json["name"].AsString(), - Type = (ContractParameterType)Enum.Parse(typeof(ContractParameterType), json["type"].AsString()), + Name = json["name"].GetString(), + Type = Enum.Parse(json["type"].GetString()) }; if (string.IsNullOrEmpty(parameter.Name)) throw new FormatException(); diff --git a/src/neo/SmartContract/Manifest/ContractPermission.cs b/src/neo/SmartContract/Manifest/ContractPermission.cs index 14c2608349..2fbeeabdae 100644 --- a/src/neo/SmartContract/Manifest/ContractPermission.cs +++ b/src/neo/SmartContract/Manifest/ContractPermission.cs @@ -66,7 +66,7 @@ public static ContractPermission FromJson(JObject json) ContractPermission permission = new ContractPermission { Contract = ContractPermissionDescriptor.FromJson(json["contract"]), - Methods = WildcardContainer.FromJson(json["methods"], u => u.AsString()), + Methods = WildcardContainer.FromJson(json["methods"], u => u.GetString()), }; if (permission.Methods.Any(p => string.IsNullOrEmpty(p))) throw new FormatException(); diff --git a/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs b/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs index 3ac79727d3..83e0ddbeb0 100644 --- a/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs @@ -72,7 +72,7 @@ public override int GetHashCode() public static ContractPermissionDescriptor FromJson(JObject json) { - string str = json.AsString(); + string str = json.GetString(); if (str.Length == 42) return Create(UInt160.Parse(str)); if (str.Length == 66) diff --git a/src/neo/Wallets/NEP6/NEP6Account.cs b/src/neo/Wallets/NEP6/NEP6Account.cs index 1be45b604b..7d68aea23a 100644 --- a/src/neo/Wallets/NEP6/NEP6Account.cs +++ b/src/neo/Wallets/NEP6/NEP6Account.cs @@ -30,11 +30,11 @@ public NEP6Account(NEP6Wallet wallet, UInt160 scriptHash, KeyPair key, string pa public static NEP6Account FromJson(JObject json, NEP6Wallet wallet) { - return new NEP6Account(wallet, json["address"].AsString().ToScriptHash(wallet.ProtocolSettings.AddressVersion), json["key"]?.AsString()) + return new NEP6Account(wallet, json["address"].GetString().ToScriptHash(wallet.ProtocolSettings.AddressVersion), json["key"]?.GetString()) { - Label = json["label"]?.AsString(), - IsDefault = json["isdefault"].AsBoolean(), - Lock = json["lock"].AsBoolean(), + Label = json["label"]?.GetString(), + IsDefault = json["isdefault"].GetBoolean(), + Lock = json["lock"].GetBoolean(), Contract = NEP6Contract.FromJson(json["contract"]), Extra = json["extra"] }; From b1a6ed0abe4bda9a0e5b28d7868e12c769293d7e Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 19 Feb 2021 18:58:01 +0800 Subject: [PATCH 37/63] Remove StorageItem.IsConstant (#2351) --- .../ApplicationEngine.Storage.cs | 29 +++--------- .../SmartContract/Native/LedgerContract.cs | 6 +-- src/neo/SmartContract/StorageFlags.cs | 11 ----- src/neo/SmartContract/StorageItem.cs | 18 +++----- .../Extensions/NativeContractExtensions.cs | 2 +- tests/neo.UnitTests/Ledger/UT_StorageItem.cs | 8 ++-- .../SmartContract/Native/UT_NeoToken.cs | 2 - .../SmartContract/UT_InteropPrices.cs | 8 ++-- .../SmartContract/UT_InteropService.NEO.cs | 6 +-- .../SmartContract/UT_InteropService.cs | 45 ++----------------- .../SmartContract/UT_SmartContractHelper.cs | 6 +-- .../SmartContract/UT_Syscalls.cs | 2 +- tests/neo.UnitTests/UT_DataCache.cs | 22 ++++----- 13 files changed, 44 insertions(+), 121 deletions(-) delete mode 100644 src/neo/SmartContract/StorageFlags.cs diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index e593f1d0f2..f92cb57ad7 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -15,9 +15,8 @@ partial class ApplicationEngine public static readonly InteropDescriptor System_Storage_AsReadOnly = Register("System.Storage.AsReadOnly", nameof(AsReadOnly), 1 << 4, CallFlags.ReadStates); public static readonly InteropDescriptor System_Storage_Get = Register("System.Storage.Get", nameof(Get), 1 << 15, CallFlags.ReadStates); public static readonly InteropDescriptor System_Storage_Find = Register("System.Storage.Find", nameof(Find), 1 << 15, CallFlags.ReadStates); - public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 0, CallFlags.States); - public static readonly InteropDescriptor System_Storage_PutEx = Register("System.Storage.PutEx", nameof(PutEx), 0, CallFlags.States); - public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 0, CallFlags.States); + public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 1 << 15, CallFlags.WriteStates); + public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 1 << 15, CallFlags.WriteStates); protected internal StorageContext GetStorageContext() { @@ -76,16 +75,6 @@ protected internal IIterator Find(StorageContext context, byte[] prefix, FindOpt } protected internal void Put(StorageContext context, byte[] key, byte[] value) - { - PutExInternal(context, key, value, StorageFlags.None); - } - - protected internal void PutEx(StorageContext context, byte[] key, byte[] value, StorageFlags flags) - { - PutExInternal(context, key, value, flags); - } - - private void PutExInternal(StorageContext context, byte[] key, byte[] value, StorageFlags flags) { if (key.Length > MaxStorageKeySize || value.Length > MaxStorageValueSize || context.IsReadOnly) throw new ArgumentException(); @@ -104,32 +93,28 @@ private void PutExInternal(StorageContext context, byte[] key, byte[] value, Sto } else { - if (item.IsConstant) throw new InvalidOperationException(); if (value.Length == 0) - newDataSize = 1; + newDataSize = 0; else if (value.Length <= item.Value.Length) newDataSize = (value.Length - 1) / 4 + 1; + else if (item.Value.Length == 0) + newDataSize = value.Length; else newDataSize = (item.Value.Length - 1) / 4 + 1 + value.Length - item.Value.Length; } AddGas(newDataSize * StoragePrice); item.Value = value; - item.IsConstant = flags.HasFlag(StorageFlags.Constant); } protected internal void Delete(StorageContext context, byte[] key) { if (context.IsReadOnly) throw new ArgumentException(); - AddGas(StoragePrice); - StorageKey skey = new StorageKey + Snapshot.Delete(new StorageKey { Id = context.Id, Key = key - }; - if (Snapshot.TryGet(skey)?.IsConstant == true) - throw new InvalidOperationException(); - Snapshot.Delete(skey); + }); } } } diff --git a/src/neo/SmartContract/Native/LedgerContract.cs b/src/neo/SmartContract/Native/LedgerContract.cs index 0329cabab9..5a7db239d6 100644 --- a/src/neo/SmartContract/Native/LedgerContract.cs +++ b/src/neo/SmartContract/Native/LedgerContract.cs @@ -22,15 +22,15 @@ internal LedgerContract() internal override void OnPersist(ApplicationEngine engine) { - engine.Snapshot.Add(CreateStorageKey(Prefix_BlockHash).AddBigEndian(engine.PersistingBlock.Index), new StorageItem(engine.PersistingBlock.Hash.ToArray(), true)); - engine.Snapshot.Add(CreateStorageKey(Prefix_Block).Add(engine.PersistingBlock.Hash), new StorageItem(Trim(engine.PersistingBlock).ToArray(), true)); + engine.Snapshot.Add(CreateStorageKey(Prefix_BlockHash).AddBigEndian(engine.PersistingBlock.Index), new StorageItem(engine.PersistingBlock.Hash.ToArray())); + engine.Snapshot.Add(CreateStorageKey(Prefix_Block).Add(engine.PersistingBlock.Hash), new StorageItem(Trim(engine.PersistingBlock).ToArray())); foreach (Transaction tx in engine.PersistingBlock.Transactions) { engine.Snapshot.Add(CreateStorageKey(Prefix_Transaction).Add(tx.Hash), new StorageItem(new TransactionState { BlockIndex = engine.PersistingBlock.Index, Transaction = tx - }, true)); + })); } } diff --git a/src/neo/SmartContract/StorageFlags.cs b/src/neo/SmartContract/StorageFlags.cs deleted file mode 100644 index 5888426e66..0000000000 --- a/src/neo/SmartContract/StorageFlags.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Neo.SmartContract -{ - [Flags] - public enum StorageFlags : byte - { - None = 0, - Constant = 0x01 - } -} diff --git a/src/neo/SmartContract/StorageItem.cs b/src/neo/SmartContract/StorageItem.cs index 9da764cd9a..b76c6583f3 100644 --- a/src/neo/SmartContract/StorageItem.cs +++ b/src/neo/SmartContract/StorageItem.cs @@ -11,9 +11,8 @@ public class StorageItem : ISerializable { private byte[] value; private object cache; - public bool IsConstant; - public int Size => Value.GetVarSize() + sizeof(bool); + public int Size => Value.GetVarSize(); public byte[] Value { @@ -37,22 +36,19 @@ public byte[] Value public StorageItem() { } - public StorageItem(byte[] value, bool isConstant = false) + public StorageItem(byte[] value) { this.value = value; - this.IsConstant = isConstant; } - public StorageItem(BigInteger value, bool isConstant = false) + public StorageItem(BigInteger value) { this.cache = value; - this.IsConstant = isConstant; } - public StorageItem(IInteroperable interoperable, bool isConstant = false) + public StorageItem(IInteroperable interoperable) { this.cache = interoperable; - this.IsConstant = isConstant; } public void Add(BigInteger integer) @@ -64,21 +60,18 @@ public StorageItem Clone() { return new StorageItem { - Value = Value, - IsConstant = IsConstant + Value = Value }; } public void Deserialize(BinaryReader reader) { Value = reader.ReadVarBytes(); - IsConstant = reader.ReadBoolean(); } public void FromReplica(StorageItem replica) { Value = replica.Value; - IsConstant = replica.IsConstant; } public T GetInteroperable() where T : IInteroperable, new() @@ -103,7 +96,6 @@ public void FromReplica(StorageItem replica) public void Serialize(BinaryWriter writer) { writer.WriteVarBytes(Value); - writer.Write(IsConstant); } public void Set(BigInteger integer) diff --git a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs index 697b9f76b0..bf5f84070c 100644 --- a/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs +++ b/tests/neo.UnitTests/Extensions/NativeContractExtensions.cs @@ -80,7 +80,7 @@ public static void DestroyContract(this DataCache snapshot, UInt160 callingScrip public static void AddContract(this DataCache snapshot, UInt160 hash, ContractState state) { var key = new KeyBuilder(NativeContract.ContractManagement.Id, 8).Add(hash); - snapshot.Add(key, new StorageItem(state, false)); + snapshot.Add(key, new StorageItem(state)); } public static void DeleteContract(this DataCache snapshot, UInt160 hash) diff --git a/tests/neo.UnitTests/Ledger/UT_StorageItem.cs b/tests/neo.UnitTests/Ledger/UT_StorageItem.cs index f9ced186ec..84c8ff7d42 100644 --- a/tests/neo.UnitTests/Ledger/UT_StorageItem.cs +++ b/tests/neo.UnitTests/Ledger/UT_StorageItem.cs @@ -37,14 +37,14 @@ public void Value_Set() public void Size_Get() { uut.Value = TestUtils.GetByteArray(10, 0x42); - uut.Size.Should().Be(12); // 2 + 10 + uut.Size.Should().Be(11); // 1 + 10 } [TestMethod] public void Size_Get_Larger() { uut.Value = TestUtils.GetByteArray(88, 0x42); - uut.Size.Should().Be(90); // 2 + 88 + uut.Size.Should().Be(89); // 1 + 88 } [TestMethod] @@ -96,7 +96,7 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 10, 66, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0 }; + byte[] requiredData = new byte[] { 10, 66, 32, 32, 32, 32, 32, 32, 32, 32, 32 }; data.Length.Should().Be(requiredData.Length); for (int i = 0; i < requiredData.Length; i++) @@ -109,11 +109,9 @@ public void Serialize() public void TestFromReplica() { uut.Value = TestUtils.GetByteArray(10, 0x42); - uut.IsConstant = true; StorageItem dest = new StorageItem(); dest.FromReplica(uut); dest.Value.Should().BeEquivalentTo(uut.Value); - dest.IsConstant.Should().Be(uut.IsConstant); } } } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs index 60c42e0720..a2831cfcc4 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NeoToken.cs @@ -1030,7 +1030,6 @@ internal static void CheckValidator(ECPoint eCPoint, DataCache.Trackable trackab st.Should().Be(0); trackable.Key.Key.Should().BeEquivalentTo(new byte[] { 33 }.Concat(eCPoint.EncodePoint(true))); - trackable.Item.IsConstant.Should().Be(false); } internal static void CheckBalance(byte[] account, DataCache.Trackable trackable, BigInteger balance, BigInteger height, ECPoint voteTo) @@ -1045,7 +1044,6 @@ internal static void CheckBalance(byte[] account, DataCache.Trackable trackable, st[2].GetSpan().AsSerializable().Should().BeEquivalentTo(voteTo); // Votes trackable.Key.Key.Should().BeEquivalentTo(new byte[] { 20 }.Concat(account)); - trackable.Item.IsConstant.Should().Be(false); } internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs index 3c0a997157..99aa739052 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropPrices.cs @@ -66,7 +66,7 @@ public void ApplicationEngineRegularPut() debugger.StepInto(); var setupPrice = ae.GasConsumed; debugger.Execute(); - (ae.GasConsumed - setupPrice).Should().Be(ae.StoragePrice * (1 + value.Length)); + (ae.GasConsumed - setupPrice).Should().Be(ae.StoragePrice * value.Length + (1 << 15) * 30); } } @@ -99,7 +99,7 @@ public void ApplicationEngineReusedStorage_FullReuse() debugger.StepInto(); var setupPrice = applicationEngine.GasConsumed; debugger.Execute(); - (applicationEngine.GasConsumed - setupPrice).Should().Be(1 * applicationEngine.StoragePrice); + (applicationEngine.GasConsumed - setupPrice).Should().Be(1 * applicationEngine.StoragePrice + (1 << 15) * 30); } } @@ -135,7 +135,7 @@ public void ApplicationEngineReusedStorage_PartialReuse() var setupPrice = ae.GasConsumed; debugger.StepInto(); debugger.StepInto(); - (ae.GasConsumed - setupPrice).Should().Be((1 + (oldValue.Length / 4) + value.Length - oldValue.Length) * ae.StoragePrice); + (ae.GasConsumed - setupPrice).Should().Be((1 + (oldValue.Length / 4) + value.Length - oldValue.Length) * ae.StoragePrice + (1 << 15) * 30); } } @@ -174,7 +174,7 @@ public void ApplicationEngineReusedStorage_PartialReuseTwice() debugger.StepInto(); //syscall Storage.GetContext var setupPrice = ae.GasConsumed; debugger.StepInto(); //syscall Storage.Put - (ae.GasConsumed - setupPrice).Should().Be((sItem.Value.Length / 4 + 1) * ae.StoragePrice); // = PUT basic fee + (ae.GasConsumed - setupPrice).Should().Be((sItem.Value.Length / 4 + 1) * ae.StoragePrice + (1 << 15) * 30); // = PUT basic fee } } diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 3947723a59..59f7acdf4c 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -201,8 +201,7 @@ public void TestContract_Update() var storageItem = new StorageItem { - Value = new byte[] { 0x01 }, - IsConstant = false + Value = new byte[] { 0x01 } }; var storageKey = new StorageKey @@ -259,8 +258,7 @@ public void TestStorage_Find() var storageItem = new StorageItem { - Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, - IsConstant = true + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 } }; var storageKey = new StorageKey { diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index d473618414..d4a2e69197 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -414,8 +414,7 @@ public void TestStorage_Get() var storageItem = new StorageItem { - Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, - IsConstant = true + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 } }; snapshot.AddContract(state.Hash, state); snapshot.Add(storageKey, storageItem); @@ -471,8 +470,7 @@ public void TestStorage_Put() }; var storageItem = new StorageItem { - Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, - IsConstant = true + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 } }; snapshot.AddContract(state.Hash, state); snapshot.Add(storageKey, storageItem); @@ -481,10 +479,6 @@ public void TestStorage_Put() key = new byte[] { 0x01 }; value = new byte[] { 0x02 }; storageContext.IsReadOnly = false; - Assert.ThrowsException(() => engine.Put(storageContext, key, value)); - - //success - storageItem.IsConstant = false; engine.Put(storageContext, key, value); //value length == 0 @@ -493,35 +487,6 @@ public void TestStorage_Put() engine.Put(storageContext, key, value); } - [TestMethod] - public void TestStorage_PutEx() - { - var snapshot = TestBlockchain.GetTestSnapshot(); - var state = TestUtils.GetContract(); - var storageKey = new StorageKey - { - Id = 0x42000000, - Key = new byte[] { 0x01 } - }; - var storageItem = new StorageItem - { - Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, - IsConstant = false - }; - snapshot.AddContract(state.Hash, state); - snapshot.Add(storageKey, storageItem); - var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(new byte[] { 0x01 }); - var key = new byte[] { 0x01 }; - var value = new byte[] { 0x02 }; - var storageContext = new StorageContext - { - Id = state.Id, - IsReadOnly = false - }; - engine.PutEx(storageContext, key, value, StorageFlags.None); - } - [TestMethod] public void TestStorage_Delete() { @@ -535,8 +500,7 @@ public void TestStorage_Delete() }; var storageItem = new StorageItem { - Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, - IsConstant = false + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 } }; snapshot.AddContract(state.Hash, state); snapshot.Add(storageKey, storageItem); @@ -601,8 +565,7 @@ public void TestContract_Destroy() var scriptHash = UInt160.Parse("0xcb9f3b7c6fb1cf2c13a40637c189bdd066a272b4"); var storageItem = new StorageItem { - Value = new byte[] { 0x01, 0x02, 0x03, 0x04 }, - IsConstant = false + Value = new byte[] { 0x01, 0x02, 0x03, 0x04 } }; var storageKey = new StorageKey diff --git a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs index d6bba55e6b..811292f35a 100644 --- a/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_SmartContractHelper.cs @@ -213,14 +213,14 @@ public static void TransactionAdd(DataCache snapshot, params TransactionState[] { foreach (TransactionState tx in txs) { - snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Transaction, tx.Transaction.Hash), new StorageItem(tx, true)); + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Transaction, tx.Transaction.Hash), new StorageItem(tx)); } } public static void BlocksAdd(DataCache snapshot, UInt256 hash, TrimmedBlock block) { - snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_BlockHash, block.Index), new StorageItem(hash.ToArray(), true)); - snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Block, hash), new StorageItem(block.ToArray(), true)); + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_BlockHash, block.Index), new StorageItem(hash.ToArray())); + snapshot.Add(NativeContract.Ledger.CreateStorageKey(Prefix_Block, hash), new StorageItem(block.ToArray())); } } } diff --git a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs index f1b94e27fb..f960ae9a46 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Syscalls.cs @@ -78,7 +78,7 @@ public void System_Blockchain_GetBlock() { BlockIndex = block.Index, Transaction = tx - }, true)); + })); engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings); engine.LoadScript(script.ToArray()); diff --git a/tests/neo.UnitTests/UT_DataCache.cs b/tests/neo.UnitTests/UT_DataCache.cs index 6f458da991..951c2275d1 100644 --- a/tests/neo.UnitTests/UT_DataCache.cs +++ b/tests/neo.UnitTests/UT_DataCache.cs @@ -18,27 +18,27 @@ public void TestCachedFind_Between() storages.Add ( new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); storages.Add ( new StorageKey() { Key = new byte[] { 0x00, 0x01 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); storages.Add ( new StorageKey() { Key = new byte[] { 0x00, 0x03 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); cache.Add ( new StorageKey() { Key = new byte[] { 0x01, 0x02 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); cache.Add ( new StorageKey() { Key = new byte[] { 0x00, 0x02 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); CollectionAssert.AreEqual( @@ -57,22 +57,22 @@ public void TestCachedFind_Last() storages.Add ( new StorageKey() { Key = new byte[] { 0x00, 0x01 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); storages.Add ( new StorageKey() { Key = new byte[] { 0x01, 0x01 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); cache.Add ( new StorageKey() { Key = new byte[] { 0x00, 0x02 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); cache.Add ( new StorageKey() { Key = new byte[] { 0x01, 0x02 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); CollectionAssert.AreEqual(cache.Find(new byte[5]).Select(u => u.Key.Key[1]).ToArray(), new byte[] { 0x01, 0x02 } @@ -89,12 +89,12 @@ public void TestCachedFind_Empty() cache.Add ( new StorageKey() { Key = new byte[] { 0x00, 0x02 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); cache.Add ( new StorageKey() { Key = new byte[] { 0x01, 0x02 }, Id = 0 }, - new StorageItem() { IsConstant = false, Value = new byte[] { } } + new StorageItem() { Value = new byte[] { } } ); CollectionAssert.AreEqual( From 0c83d454a3c6b11aa02763a3bfee1ef4f886d3b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Naz=C3=A1rio=20Coelho?= Date: Fri, 19 Feb 2021 09:36:31 -0300 Subject: [PATCH 38/63] Set DefaultFeePerByte as a variable (#2353) Co-authored-by: Erik Zhang --- src/neo/SmartContract/Native/PolicyContract.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 31e0d7b834..66b3d4adb9 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -10,6 +10,7 @@ public sealed class PolicyContract : NativeContract { public const uint DefaultExecFeeFactor = 30; public const uint DefaultStoragePrice = 100000; + public const uint DefaultFeePerByte = 1000; public const uint MaxExecFeeFactor = 1000; public const uint MaxStoragePrice = 10000000; @@ -24,7 +25,7 @@ internal PolicyContract() internal override void Initialize(ApplicationEngine engine) { - engine.Snapshot.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem(1000)); + engine.Snapshot.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem(DefaultFeePerByte)); engine.Snapshot.Add(CreateStorageKey(Prefix_ExecFeeFactor), new StorageItem(DefaultExecFeeFactor)); engine.Snapshot.Add(CreateStorageKey(Prefix_StoragePrice), new StorageItem(DefaultStoragePrice)); } From ee898bf41667cdbe3b836b3bd08c2d3199046c2e Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 21 Feb 2021 18:54:50 +0800 Subject: [PATCH 39/63] Fix types in native contracts (#2356) * Fix types in native contracts * format Co-authored-by: Shargon --- src/neo/SmartContract/Native/ContractMethodMetadata.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/neo/SmartContract/Native/ContractMethodMetadata.cs b/src/neo/SmartContract/Native/ContractMethodMetadata.cs index d4e26594bc..d22e5332f5 100644 --- a/src/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -1,3 +1,4 @@ +using Neo.Cryptography.ECC; using Neo.IO; using Neo.Persistence; using Neo.SmartContract.Manifest; @@ -70,6 +71,7 @@ private static ContractParameterType ToParameterType(Type type) if (type == typeof(string)) return ContractParameterType.String; if (type == typeof(UInt160)) return ContractParameterType.Hash160; if (type == typeof(UInt256)) return ContractParameterType.Hash256; + if (type == typeof(ECPoint)) return ContractParameterType.PublicKey; if (type == typeof(VM.Types.Boolean)) return ContractParameterType.Boolean; if (type == typeof(Integer)) return ContractParameterType.Integer; if (type == typeof(ByteString)) return ContractParameterType.ByteArray; @@ -77,11 +79,14 @@ private static ContractParameterType ToParameterType(Type type) if (type == typeof(Array)) return ContractParameterType.Array; if (type == typeof(Struct)) return ContractParameterType.Array; if (type == typeof(Map)) return ContractParameterType.Map; + if (type == typeof(StackItem)) return ContractParameterType.Any; + if (type == typeof(object)) return ContractParameterType.Any; if (typeof(IInteroperable).IsAssignableFrom(type)) return ContractParameterType.Array; if (typeof(ISerializable).IsAssignableFrom(type)) return ContractParameterType.ByteArray; if (type.IsArray) return ContractParameterType.Array; if (type.IsEnum) return ContractParameterType.Integer; - return ContractParameterType.Any; + if (type.IsValueType) return ContractParameterType.Array; + return ContractParameterType.InteropInterface; } } } From b60a8d3a0441bb93386fe9a7c106b3e521568031 Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 24 Feb 2021 08:56:15 +0100 Subject: [PATCH 40/63] Update NeoVM (#2359) * Update VM * Update Sqlite * Update prices * Update price Co-authored-by: Erik Zhang --- src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs | 2 ++ src/neo/neo.csproj | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index a31edaa6b5..bb79c74bc4 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -156,6 +156,8 @@ partial class ApplicationEngine [OpCode.MUL] = 1 << 3, [OpCode.DIV] = 1 << 3, [OpCode.MOD] = 1 << 3, + [OpCode.POW] = 1 << 6, + [OpCode.SQRT] = 1 << 11, [OpCode.SHL] = 1 << 3, [OpCode.SHR] = 1 << 3, [OpCode.NOT] = 1 << 2, diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index f3e6698599..36686d28cf 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -28,8 +28,8 @@ - - + + From b77690f2bc509a609c480b7cd36e399914a88d4c Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 24 Feb 2021 17:24:10 +0800 Subject: [PATCH 41/63] Allow native contracts to be disabled on private net (#2361) --- .../SmartContract/ApplicationEngine.Contract.cs | 15 +++++++++------ .../SmartContract/Native/ContractManagement.cs | 5 +++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 64ecea9182..53970e2009 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -44,8 +44,9 @@ protected internal void CallNativeContract(byte version) NativeContract contract = NativeContract.GetContract(CurrentScriptHash); if (contract is null) throw new InvalidOperationException("It is not allowed to use \"System.Contract.CallNative\" directly."); - uint activeIndex = ProtocolSettings.NativeUpdateHistory[contract.Name][0]; - if (activeIndex > NativeContract.Ledger.CurrentIndex(Snapshot)) + if (!ProtocolSettings.NativeUpdateHistory.TryGetValue(contract.Name, out uint[] updates)) + throw new InvalidOperationException($"The native contract {contract.Name} is not active."); + if (updates[0] > NativeContract.Ledger.CurrentIndex(Snapshot)) throw new InvalidOperationException($"The native contract {contract.Name} is not active."); contract.Invoke(this, version); } @@ -98,8 +99,9 @@ protected internal void NativeOnPersist() throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) { - uint activeIndex = ProtocolSettings.NativeUpdateHistory[contract.Name][0]; - if (activeIndex <= PersistingBlock.Index) + if (!ProtocolSettings.NativeUpdateHistory.TryGetValue(contract.Name, out uint[] updates)) + continue; + if (updates[0] <= PersistingBlock.Index) contract.OnPersist(this); } } @@ -110,8 +112,9 @@ protected internal void NativePostPersist() throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) { - uint activeIndex = ProtocolSettings.NativeUpdateHistory[contract.Name][0]; - if (activeIndex <= PersistingBlock.Index) + if (!ProtocolSettings.NativeUpdateHistory.TryGetValue(contract.Name, out uint[] updates)) + continue; + if (updates[0] <= PersistingBlock.Index) contract.PostPersist(this); } } diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 52fd8f61a9..dc187632f7 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -91,8 +91,9 @@ internal override void OnPersist(ApplicationEngine engine) { foreach (NativeContract contract in Contracts) { - uint activeIndex = engine.ProtocolSettings.NativeUpdateHistory[contract.Name][0]; - if (activeIndex != engine.PersistingBlock.Index) + if (!engine.ProtocolSettings.NativeUpdateHistory.TryGetValue(contract.Name, out uint[] updates)) + continue; + if (updates[0] != engine.PersistingBlock.Index) continue; engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState { From 48835b454f1dc41a6c5b807ce91b43d7d35c7bc8 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sat, 27 Feb 2021 21:29:45 +0800 Subject: [PATCH 42/63] Fix Header's hash (#2367) --- src/neo/Network/P2P/Payloads/Header.cs | 125 +++++++++++++++++-------- 1 file changed, 84 insertions(+), 41 deletions(-) diff --git a/src/neo/Network/P2P/Payloads/Header.cs b/src/neo/Network/P2P/Payloads/Header.cs index 8d3fb31194..5b4c7671aa 100644 --- a/src/neo/Network/P2P/Payloads/Header.cs +++ b/src/neo/Network/P2P/Payloads/Header.cs @@ -12,15 +12,57 @@ namespace Neo.Network.P2P.Payloads { public sealed class Header : IEquatable
, IVerifiable { - public uint Version; - public UInt256 PrevHash; - public UInt256 MerkleRoot; - public ulong Timestamp; - public uint Index; - public byte PrimaryIndex; - public UInt160 NextConsensus; + private uint version; + private UInt256 prevHash; + private UInt256 merkleRoot; + private ulong timestamp; + private uint index; + private byte primaryIndex; + private UInt160 nextConsensus; public Witness Witness; + public uint Version + { + get => version; + set { version = value; _hash = null; } + } + + public UInt256 PrevHash + { + get => prevHash; + set { prevHash = value; _hash = null; } + } + + public UInt256 MerkleRoot + { + get => merkleRoot; + set { merkleRoot = value; _hash = null; } + } + + public ulong Timestamp + { + get => timestamp; + set { timestamp = value; _hash = null; } + } + + public uint Index + { + get => index; + set { index = value; _hash = null; } + } + + public byte PrimaryIndex + { + get => primaryIndex; + set { primaryIndex = value; _hash = null; } + } + + public UInt160 NextConsensus + { + get => nextConsensus; + set { nextConsensus = value; _hash = null; } + } + private UInt256 _hash = null; public UInt256 Hash { @@ -67,14 +109,15 @@ public void Deserialize(BinaryReader reader) void IVerifiable.DeserializeUnsigned(BinaryReader reader) { - Version = reader.ReadUInt32(); - if (Version > 0) throw new FormatException(); - PrevHash = reader.ReadSerializable(); - MerkleRoot = reader.ReadSerializable(); - Timestamp = reader.ReadUInt64(); - Index = reader.ReadUInt32(); - PrimaryIndex = reader.ReadByte(); - NextConsensus = reader.ReadSerializable(); + _hash = null; + version = reader.ReadUInt32(); + if (version > 0) throw new FormatException(); + prevHash = reader.ReadSerializable(); + merkleRoot = reader.ReadSerializable(); + timestamp = reader.ReadUInt64(); + index = reader.ReadUInt32(); + primaryIndex = reader.ReadByte(); + nextConsensus = reader.ReadSerializable(); } public bool Equals(Header other) @@ -96,10 +139,10 @@ public override int GetHashCode() UInt160[] IVerifiable.GetScriptHashesForVerifying(DataCache snapshot) { - if (PrevHash == UInt256.Zero) return new[] { Witness.ScriptHash }; - TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); + if (prevHash == UInt256.Zero) return new[] { Witness.ScriptHash }; + TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, prevHash); if (prev is null) throw new InvalidOperationException(); - return new[] { prev.Header.NextConsensus }; + return new[] { prev.Header.nextConsensus }; } public void Serialize(BinaryWriter writer) @@ -110,13 +153,13 @@ public void Serialize(BinaryWriter writer) void IVerifiable.SerializeUnsigned(BinaryWriter writer) { - writer.Write(Version); - writer.Write(PrevHash); - writer.Write(MerkleRoot); - writer.Write(Timestamp); - writer.Write(Index); - writer.Write(PrimaryIndex); - writer.Write(NextConsensus); + writer.Write(version); + writer.Write(prevHash); + writer.Write(merkleRoot); + writer.Write(timestamp); + writer.Write(index); + writer.Write(primaryIndex); + writer.Write(nextConsensus); } public JObject ToJson(ProtocolSettings settings) @@ -124,25 +167,25 @@ public JObject ToJson(ProtocolSettings settings) JObject json = new JObject(); json["hash"] = Hash.ToString(); json["size"] = Size; - json["version"] = Version; - json["previousblockhash"] = PrevHash.ToString(); - json["merkleroot"] = MerkleRoot.ToString(); - json["time"] = Timestamp; - json["index"] = Index; - json["primary"] = PrimaryIndex; - json["nextconsensus"] = NextConsensus.ToAddress(settings.AddressVersion); + json["version"] = version; + json["previousblockhash"] = prevHash.ToString(); + json["merkleroot"] = merkleRoot.ToString(); + json["time"] = timestamp; + json["index"] = index; + json["primary"] = primaryIndex; + json["nextconsensus"] = nextConsensus.ToAddress(settings.AddressVersion); json["witnesses"] = new JArray(Witness.ToJson()); return json; } internal bool Verify(ProtocolSettings settings, DataCache snapshot) { - if (PrimaryIndex >= settings.ValidatorsCount) + if (primaryIndex >= settings.ValidatorsCount) return false; - TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, PrevHash); + TrimmedBlock prev = NativeContract.Ledger.GetTrimmedBlock(snapshot, prevHash); if (prev is null) return false; - if (prev.Index + 1 != Index) return false; - if (prev.Header.Timestamp >= Timestamp) return false; + if (prev.Index + 1 != index) return false; + if (prev.Header.timestamp >= timestamp) return false; if (!this.VerifyWitnesses(settings, snapshot, 1_00000000)) return false; return true; } @@ -151,12 +194,12 @@ internal bool Verify(ProtocolSettings settings, DataCache snapshot, HeaderCache { Header prev = headerCache.Last; if (prev is null) return Verify(settings, snapshot); - if (PrimaryIndex >= settings.ValidatorsCount) + if (primaryIndex >= settings.ValidatorsCount) return false; - if (prev.Hash != PrevHash) return false; - if (prev.Index + 1 != Index) return false; - if (prev.Timestamp >= Timestamp) return false; - return this.VerifyWitness(settings, snapshot, prev.NextConsensus, Witness, 1_00000000, out _); + if (prev.Hash != prevHash) return false; + if (prev.index + 1 != index) return false; + if (prev.timestamp >= timestamp) return false; + return this.VerifyWitness(settings, snapshot, prev.nextConsensus, Witness, 1_00000000, out _); } } } From 865de26e8081208708426ca886fbfb82890778b0 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Sun, 28 Feb 2021 15:25:07 +0800 Subject: [PATCH 43/63] Stricter settings --- src/neo/SmartContract/ApplicationEngine.Contract.cs | 11 ++++++----- src/neo/SmartContract/Native/ContractManagement.cs | 5 ++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 53970e2009..49e6fd09c3 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -44,7 +44,8 @@ protected internal void CallNativeContract(byte version) NativeContract contract = NativeContract.GetContract(CurrentScriptHash); if (contract is null) throw new InvalidOperationException("It is not allowed to use \"System.Contract.CallNative\" directly."); - if (!ProtocolSettings.NativeUpdateHistory.TryGetValue(contract.Name, out uint[] updates)) + uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; + if (updates.Length == 0) throw new InvalidOperationException($"The native contract {contract.Name} is not active."); if (updates[0] > NativeContract.Ledger.CurrentIndex(Snapshot)) throw new InvalidOperationException($"The native contract {contract.Name} is not active."); @@ -99,8 +100,8 @@ protected internal void NativeOnPersist() throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) { - if (!ProtocolSettings.NativeUpdateHistory.TryGetValue(contract.Name, out uint[] updates)) - continue; + uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; + if (updates.Length == 0) continue; if (updates[0] <= PersistingBlock.Index) contract.OnPersist(this); } @@ -112,8 +113,8 @@ protected internal void NativePostPersist() throw new InvalidOperationException(); foreach (NativeContract contract in NativeContract.Contracts) { - if (!ProtocolSettings.NativeUpdateHistory.TryGetValue(contract.Name, out uint[] updates)) - continue; + uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; + if (updates.Length == 0) continue; if (updates[0] <= PersistingBlock.Index) contract.PostPersist(this); } diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index dc187632f7..3fdd8f3240 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -91,9 +91,8 @@ internal override void OnPersist(ApplicationEngine engine) { foreach (NativeContract contract in Contracts) { - if (!engine.ProtocolSettings.NativeUpdateHistory.TryGetValue(contract.Name, out uint[] updates)) - continue; - if (updates[0] != engine.PersistingBlock.Index) + uint[] updates = engine.ProtocolSettings.NativeUpdateHistory[contract.Name]; + if (updates.Length == 0 || updates[0] != engine.PersistingBlock.Index) continue; engine.Snapshot.Add(CreateStorageKey(Prefix_Contract).Add(contract.Hash), new StorageItem(new ContractState { From 49b38741eec5ce7da9420bfff52abb24e752261a Mon Sep 17 00:00:00 2001 From: ZhangTao Date: Mon, 1 Mar 2021 06:14:08 -0600 Subject: [PATCH 44/63] fix dead lock with state service (#2368) --- src/neo/NeoSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 5db9964e8b..75f7be56e7 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -81,9 +81,9 @@ public NeoSystem(ProtocolSettings settings, string storageEngine = null, string this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); - Blockchain.Ask(new FillMemoryPool { Transactions = Enumerable.Empty() }).Wait(); foreach (var plugin in Plugin.Plugins) plugin.OnSystemLoaded(this); + Blockchain.Ask(new FillMemoryPool { Transactions = Enumerable.Empty() }).Wait(); } private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) From b6a769e0b056a977131b88c978d09e61f6ec1c72 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 4 Mar 2021 12:24:22 +0800 Subject: [PATCH 45/63] Make CallFromNativeContract debuggable (#2358) --- .../ApplicationEngine.Contract.cs | 46 ++++++---- src/neo/SmartContract/ApplicationEngine.cs | 47 +++++----- src/neo/SmartContract/ContractTask.cs | 39 ++++++++ src/neo/SmartContract/ContractTaskAwaiter.cs | 63 +++++++++++++ .../ContractTaskMethodBuilder.cs | 89 +++++++++++++++++++ .../Native/ContractManagement.cs | 41 ++++----- .../Native/ContractMethodMetadata.cs | 2 + src/neo/SmartContract/Native/FungibleToken.cs | 31 +++---- src/neo/SmartContract/Native/GasToken.cs | 10 +-- .../SmartContract/Native/LedgerContract.cs | 6 +- src/neo/SmartContract/Native/NameService.cs | 12 +-- .../SmartContract/Native/NativeContract.cs | 57 +++++++----- src/neo/SmartContract/Native/NeoToken.cs | 23 ++--- .../SmartContract/Native/NonfungibleToken.cs | 23 ++--- .../SmartContract/Native/OracleContract.cs | 16 ++-- .../SmartContract/Native/PolicyContract.cs | 3 +- tests/neo.UnitTests/Ledger/UT_MemoryPool.cs | 6 +- .../UT_TransactionVerificationContext.cs | 4 +- .../SmartContract/Native/UT_GasToken.cs | 15 ++-- 19 files changed, 384 insertions(+), 149 deletions(-) create mode 100644 src/neo/SmartContract/ContractTask.cs create mode 100644 src/neo/SmartContract/ContractTaskAwaiter.cs create mode 100644 src/neo/SmartContract/ContractTaskMethodBuilder.cs diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 49e6fd09c3..0b62df4dcb 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -94,29 +94,43 @@ protected internal UInt160 CreateMultisigAccount(int m, ECPoint[] pubKeys) return Contract.CreateMultiSigRedeemScript(m, pubKeys).ToScriptHash(); } - protected internal void NativeOnPersist() + protected internal async void NativeOnPersist() { - if (Trigger != TriggerType.OnPersist) - throw new InvalidOperationException(); - foreach (NativeContract contract in NativeContract.Contracts) + try { - uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; - if (updates.Length == 0) continue; - if (updates[0] <= PersistingBlock.Index) - contract.OnPersist(this); + if (Trigger != TriggerType.OnPersist) + throw new InvalidOperationException(); + foreach (NativeContract contract in NativeContract.Contracts) + { + uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; + if (updates.Length == 0) continue; + if (updates[0] <= PersistingBlock.Index) + await contract.OnPersist(this); + } + } + catch (Exception ex) + { + Throw(ex); } } - protected internal void NativePostPersist() + protected internal async void NativePostPersist() { - if (Trigger != TriggerType.PostPersist) - throw new InvalidOperationException(); - foreach (NativeContract contract in NativeContract.Contracts) + try + { + if (Trigger != TriggerType.PostPersist) + throw new InvalidOperationException(); + foreach (NativeContract contract in NativeContract.Contracts) + { + uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; + if (updates.Length == 0) continue; + if (updates[0] <= PersistingBlock.Index) + await contract.PostPersist(this); + } + } + catch (Exception ex) { - uint[] updates = ProtocolSettings.NativeUpdateHistory[contract.Name]; - if (updates.Length == 0) continue; - if (updates[0] <= PersistingBlock.Index) - contract.PostPersist(this); + Throw(ex); } } } diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 26b2d049a4..f3aa9741f7 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -34,6 +34,7 @@ public partial class ApplicationEngine : ExecutionEngine private List notifications; private List disposables; private readonly Dictionary invocationCounter = new Dictionary(); + private readonly Dictionary contractTasks = new Dictionary(); private readonly uint exec_fee_factor; internal readonly uint StoragePrice; @@ -71,10 +72,15 @@ protected internal void AddGas(long gas) throw new InvalidOperationException("Insufficient GAS."); } - protected override void OnFault(Exception e) + protected override void OnFault(Exception ex) { - FaultException = e; - base.OnFault(e); + FaultException = ex; + base.OnFault(ex); + } + + internal void Throw(Exception ex) + { + OnFault(ex); } private ExecutionContext CallContractInternal(UInt160 contractHash, string method, CallFlags flags, bool hasReturnValue, StackItem[] args) @@ -124,25 +130,33 @@ private ExecutionContext CallContractInternal(ContractState contract, ContractMe return context_new; } - internal void CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, string method, params StackItem[] args) + internal ContractTask CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, string method, params StackItem[] args) { - ExecutionContext context_current = CurrentContext; ExecutionContext context_new = CallContractInternal(hash, method, CallFlags.All, false, args); ExecutionContextState state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; - while (CurrentContext != context_current) - StepOut(); + ContractTask task = new ContractTask(); + contractTasks.Add(context_new, task.GetAwaiter()); + return task; } - internal T CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, string method, params StackItem[] args) + internal ContractTask CallFromNativeContract(UInt160 callingScriptHash, UInt160 hash, string method, params StackItem[] args) { - ExecutionContext context_current = CurrentContext; ExecutionContext context_new = CallContractInternal(hash, method, CallFlags.All, true, args); ExecutionContextState state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; - while (CurrentContext != context_current) - StepOut(); - return (T)Convert(Pop(), new InteropParameterDescriptor(typeof(T))); + ContractTask task = new ContractTask(); + contractTasks.Add(context_new, task.GetAwaiter()); + return task; + } + + protected override void ContextUnloaded(ExecutionContext context) + { + base.ContextUnloaded(context); + if (!contractTasks.Remove(context, out var awaiter)) return; + if (UncaughtException is not null) + throw new VMUnhandledException(UncaughtException); + awaiter.SetResult(this); } public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, ProtocolSettings settings = null, long gas = TestModeGas) @@ -309,15 +323,6 @@ protected override void PreExecuteInstruction() AddGas(exec_fee_factor * OpCodePrices[CurrentContext.CurrentInstruction.OpCode]); } - internal void StepOut() - { - int c = InvocationStack.Count; - while (State != VMState.HALT && State != VMState.FAULT && InvocationStack.Count >= c) - ExecuteNext(); - if (State == VMState.FAULT) - throw new InvalidOperationException("StepOut failed.", FaultException); - } - private static Block CreateDummyBlock(DataCache snapshot, ProtocolSettings settings) { UInt256 hash = NativeContract.Ledger.CurrentHash(snapshot); diff --git a/src/neo/SmartContract/ContractTask.cs b/src/neo/SmartContract/ContractTask.cs new file mode 100644 index 0000000000..c93c0cc7bd --- /dev/null +++ b/src/neo/SmartContract/ContractTask.cs @@ -0,0 +1,39 @@ +using System.Runtime.CompilerServices; + +namespace Neo.SmartContract +{ + [AsyncMethodBuilder(typeof(ContractTaskMethodBuilder))] + class ContractTask + { + private readonly ContractTaskAwaiter awaiter; + + public static ContractTask CompletedTask { get; } + + static ContractTask() + { + CompletedTask = new ContractTask(); + CompletedTask.GetAwaiter().SetResult(); + } + + public ContractTask() + { + awaiter = CreateAwaiter(); + } + + protected virtual ContractTaskAwaiter CreateAwaiter() => new ContractTaskAwaiter(); + + public virtual ContractTaskAwaiter GetAwaiter() => awaiter; + + public virtual object GetResult() => null; + } + + [AsyncMethodBuilder(typeof(ContractTaskMethodBuilder<>))] + class ContractTask : ContractTask + { + protected override ContractTaskAwaiter CreateAwaiter() => new ContractTaskAwaiter(); + + public override ContractTaskAwaiter GetAwaiter() => (ContractTaskAwaiter)base.GetAwaiter(); + + public override object GetResult() => GetAwaiter().GetResult(); + } +} diff --git a/src/neo/SmartContract/ContractTaskAwaiter.cs b/src/neo/SmartContract/ContractTaskAwaiter.cs new file mode 100644 index 0000000000..2590798c05 --- /dev/null +++ b/src/neo/SmartContract/ContractTaskAwaiter.cs @@ -0,0 +1,63 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Neo.SmartContract +{ + class ContractTaskAwaiter : INotifyCompletion + { + private Action continuation; + private Exception exception; + + public bool IsCompleted { get; private set; } + + public void GetResult() + { + if (exception is not null) + throw exception; + } + + public void SetResult() => RunContinuation(); + + public virtual void SetResult(ApplicationEngine engine) => SetResult(); + + public void SetException(Exception exception) + { + this.exception = exception; + RunContinuation(); + } + + public void OnCompleted(Action continuation) + { + Interlocked.CompareExchange(ref this.continuation, continuation, null); + } + + protected void RunContinuation() + { + IsCompleted = true; + continuation?.Invoke(); + } + } + + class ContractTaskAwaiter : ContractTaskAwaiter + { + private T result; + + public new T GetResult() + { + base.GetResult(); + return result; + } + + public void SetResult(T result) + { + this.result = result; + RunContinuation(); + } + + public override void SetResult(ApplicationEngine engine) + { + SetResult((T)engine.Convert(engine.Pop(), new InteropParameterDescriptor(typeof(T)))); + } + } +} diff --git a/src/neo/SmartContract/ContractTaskMethodBuilder.cs b/src/neo/SmartContract/ContractTaskMethodBuilder.cs new file mode 100644 index 0000000000..b5f8128fac --- /dev/null +++ b/src/neo/SmartContract/ContractTaskMethodBuilder.cs @@ -0,0 +1,89 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Neo.SmartContract +{ + sealed class ContractTaskMethodBuilder + { + private ContractTask task; + + public ContractTask Task => task ??= new ContractTask(); + + public static ContractTaskMethodBuilder Create() => new ContractTaskMethodBuilder(); + + public void SetException(Exception exception) + { + Task.GetAwaiter().SetException(exception); + } + + public void SetResult() + { + Task.GetAwaiter().SetResult(); + } + + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + awaiter.OnCompleted(stateMachine.MoveNext); + } + + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + awaiter.OnCompleted(stateMachine.MoveNext); + } + + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + stateMachine.MoveNext(); + } + + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + } + } + + sealed class ContractTaskMethodBuilder + { + private ContractTask task; + + public ContractTask Task => task ??= new ContractTask(); + + public static ContractTaskMethodBuilder Create() => new ContractTaskMethodBuilder(); + + public void SetException(Exception exception) + { + Task.GetAwaiter().SetException(exception); + } + + public void SetResult(T result) + { + Task.GetAwaiter().SetResult(result); + } + + public void AwaitOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : INotifyCompletion + where TStateMachine : IAsyncStateMachine + { + awaiter.OnCompleted(stateMachine.MoveNext); + } + + public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, ref TStateMachine stateMachine) + where TAwaiter : ICriticalNotifyCompletion + where TStateMachine : IAsyncStateMachine + { + awaiter.OnCompleted(stateMachine.MoveNext); + } + + public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine + { + stateMachine.MoveNext(); + } + + public void SetStateMachine(IAsyncStateMachine stateMachine) + { + } + } +} diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 3fdd8f3240..1d4dea1505 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -81,13 +81,22 @@ private int GetNextAvailableId(DataCache snapshot) return value; } - internal override void Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine) { engine.Snapshot.Add(CreateStorageKey(Prefix_MinimumDeploymentFee), new StorageItem(10_00000000)); engine.Snapshot.Add(CreateStorageKey(Prefix_NextAvailableId), new StorageItem(1)); + return ContractTask.CompletedTask; } - internal override void OnPersist(ApplicationEngine engine) + private async ContractTask OnDeploy(ApplicationEngine engine, ContractState contract, StackItem data, bool update) + { + ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy", 2); + if (md is not null) + await engine.CallFromNativeContract(Hash, contract.Hash, md.Name, data, update); + engine.SendNotification(Hash, update ? "Update" : "Deploy", new VM.Types.Array { contract.Hash.ToArray() }); + } + + internal override async ContractTask OnPersist(ApplicationEngine engine) { foreach (NativeContract contract in Contracts) { @@ -101,7 +110,7 @@ internal override void OnPersist(ApplicationEngine engine) Hash = contract.Hash, Manifest = contract.Manifest })); - contract.Initialize(engine); + await contract.Initialize(engine); } } @@ -132,13 +141,13 @@ public IEnumerable ListContracts(DataCache snapshot) } [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] - private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest) + private ContractTask Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest) { return Deploy(engine, nefFile, manifest, StackItem.Null); } [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] - private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) + private async ContractTask Deploy(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { if (engine.ScriptContainer is not Transaction tx) throw new InvalidOperationException(); @@ -172,25 +181,19 @@ private ContractState Deploy(ApplicationEngine engine, byte[] nefFile, byte[] ma engine.Snapshot.Add(key, new StorageItem(contract)); - // Execute _deploy - - ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy", 2); - if (md != null) - engine.CallFromNativeContract(Hash, hash, md.Name, data, false); - - engine.SendNotification(Hash, "Deploy", new VM.Types.Array { contract.Hash.ToArray() }); + await OnDeploy(engine, contract, data, false); return contract; } [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] - private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest) + private ContractTask Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest) { - Update(engine, nefFile, manifest, StackItem.Null); + return Update(engine, nefFile, manifest, StackItem.Null); } [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] - private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) + private ContractTask Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, StackItem data) { if (nefFile is null && manifest is null) throw new ArgumentException(); @@ -220,13 +223,7 @@ private void Update(ApplicationEngine engine, byte[] nefFile, byte[] manifest, S } Check(contract.Nef.Script, contract.Manifest.Abi); contract.UpdateCounter++; // Increase update counter - if (nefFile != null) - { - ContractMethodDescriptor md = contract.Manifest.Abi.GetMethod("_deploy", 2); - if (md != null) - engine.CallFromNativeContract(Hash, contract.Hash, md.Name, data, true); - } - engine.SendNotification(Hash, "Update", new VM.Types.Array { contract.Hash.ToArray() }); + return OnDeploy(engine, contract, data, true); } [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] diff --git a/src/neo/SmartContract/Native/ContractMethodMetadata.cs b/src/neo/SmartContract/Native/ContractMethodMetadata.cs index d22e5332f5..4f4071c6cc 100644 --- a/src/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -56,6 +56,8 @@ public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribu private static ContractParameterType ToParameterType(Type type) { + if (type.BaseType == typeof(ContractTask)) return ToParameterType(type.GenericTypeArguments[0]); + if (type == typeof(ContractTask)) return ContractParameterType.Void; if (type == typeof(void)) return ContractParameterType.Void; if (type == typeof(bool)) return ContractParameterType.Boolean; if (type == typeof(sbyte)) return ContractParameterType.Integer; diff --git a/src/neo/SmartContract/Native/FungibleToken.cs b/src/neo/SmartContract/Native/FungibleToken.cs index d1d96dd3f7..1308bd76e4 100644 --- a/src/neo/SmartContract/Native/FungibleToken.cs +++ b/src/neo/SmartContract/Native/FungibleToken.cs @@ -56,20 +56,20 @@ protected FungibleToken() Manifest.Abi.Events = events.ToArray(); } - internal protected virtual void Mint(ApplicationEngine engine, UInt160 account, BigInteger amount, bool callOnPayment) + internal async ContractTask Mint(ApplicationEngine engine, UInt160 account, BigInteger amount, bool callOnPayment) { if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); if (amount.IsZero) return; StorageItem storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Account).Add(account), () => new StorageItem(new TState())); TState state = storage.GetInteroperable(); - OnBalanceChanging(engine, account, state, amount); + await OnBalanceChanging(engine, account, state, amount); state.Balance += amount; storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply), () => new StorageItem(BigInteger.Zero)); storage.Add(amount); - PostTransfer(engine, null, account, amount, StackItem.Null, callOnPayment); + await PostTransfer(engine, null, account, amount, StackItem.Null, callOnPayment); } - internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, BigInteger amount) + internal async ContractTask Burn(ApplicationEngine engine, UInt160 account, BigInteger amount) { if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); if (amount.IsZero) return; @@ -77,14 +77,14 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, StorageItem storage = engine.Snapshot.GetAndChange(key); TState state = storage.GetInteroperable(); if (state.Balance < amount) throw new InvalidOperationException(); - OnBalanceChanging(engine, account, state, -amount); + await OnBalanceChanging(engine, account, state, -amount); if (state.Balance == amount) engine.Snapshot.Delete(key); else state.Balance -= amount; storage = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply)); storage.Add(-amount); - PostTransfer(engine, account, null, amount, StackItem.Null, false); + await PostTransfer(engine, account, null, amount, StackItem.Null, false); } [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] @@ -104,7 +104,7 @@ public virtual BigInteger BalanceOf(DataCache snapshot, UInt160 account) } [ContractMethod(CpuFee = 1 << 17, StorageFee = 50, RequiredCallFlags = CallFlags.States | CallFlags.AllowCall | CallFlags.AllowNotify)] - protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount, StackItem data) + private protected async ContractTask Transfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount, StackItem data) { if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); if (!from.Equals(engine.CallingScriptHash) && !engine.CheckWitnessInternal(from)) @@ -116,7 +116,7 @@ protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 if (storage_from != null) { TState state_from = storage_from.GetInteroperable(); - OnBalanceChanging(engine, from, state_from, amount); + await OnBalanceChanging(engine, from, state_from, amount); } } else @@ -126,11 +126,11 @@ protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 if (state_from.Balance < amount) return false; if (from.Equals(to)) { - OnBalanceChanging(engine, from, state_from, BigInteger.Zero); + await OnBalanceChanging(engine, from, state_from, BigInteger.Zero); } else { - OnBalanceChanging(engine, from, state_from, -amount); + await OnBalanceChanging(engine, from, state_from, -amount); if (state_from.Balance == amount) engine.Snapshot.Delete(key_from); else @@ -138,19 +138,20 @@ protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 StorageKey key_to = CreateStorageKey(Prefix_Account).Add(to); StorageItem storage_to = engine.Snapshot.GetAndChange(key_to, () => new StorageItem(new TState())); TState state_to = storage_to.GetInteroperable(); - OnBalanceChanging(engine, to, state_to, amount); + await OnBalanceChanging(engine, to, state_to, amount); state_to.Balance += amount; } } - PostTransfer(engine, from, to, amount, data, true); + await PostTransfer(engine, from, to, amount, data, true); return true; } - protected virtual void OnBalanceChanging(ApplicationEngine engine, UInt160 account, TState state, BigInteger amount) + internal virtual ContractTask OnBalanceChanging(ApplicationEngine engine, UInt160 account, TState state, BigInteger amount) { + return ContractTask.CompletedTask; } - private void PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount, StackItem data, bool callOnPayment) + private async ContractTask PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount, StackItem data, bool callOnPayment) { // Send notification @@ -163,7 +164,7 @@ private void PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, Bi // Call onNEP17Payment method - engine.CallFromNativeContract(Hash, to, "onNEP17Payment", from?.ToArray() ?? StackItem.Null, amount, data); + await engine.CallFromNativeContract(Hash, to, "onNEP17Payment", from?.ToArray() ?? StackItem.Null, amount, data); } } } diff --git a/src/neo/SmartContract/Native/GasToken.cs b/src/neo/SmartContract/Native/GasToken.cs index 48eb9affc1..228b79c8fd 100644 --- a/src/neo/SmartContract/Native/GasToken.cs +++ b/src/neo/SmartContract/Native/GasToken.cs @@ -12,23 +12,23 @@ internal GasToken() { } - internal override void Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine) { UInt160 account = Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators); - Mint(engine, account, 30_000_000 * Factor, false); + return Mint(engine, account, 30_000_000 * Factor, false); } - internal override void OnPersist(ApplicationEngine engine) + internal override async ContractTask OnPersist(ApplicationEngine engine) { long totalNetworkFee = 0; foreach (Transaction tx in engine.PersistingBlock.Transactions) { - Burn(engine, tx.Sender, tx.SystemFee + tx.NetworkFee); + await Burn(engine, tx.Sender, tx.SystemFee + tx.NetworkFee); totalNetworkFee += tx.NetworkFee; } ECPoint[] validators = NEO.GetNextBlockValidators(engine.Snapshot, engine.ProtocolSettings.ValidatorsCount); UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.PersistingBlock.PrimaryIndex]).ToScriptHash(); - Mint(engine, primary, totalNetworkFee, false); + await Mint(engine, primary, totalNetworkFee, false); } } } diff --git a/src/neo/SmartContract/Native/LedgerContract.cs b/src/neo/SmartContract/Native/LedgerContract.cs index 5a7db239d6..6488f3e91b 100644 --- a/src/neo/SmartContract/Native/LedgerContract.cs +++ b/src/neo/SmartContract/Native/LedgerContract.cs @@ -20,7 +20,7 @@ internal LedgerContract() { } - internal override void OnPersist(ApplicationEngine engine) + internal override ContractTask OnPersist(ApplicationEngine engine) { engine.Snapshot.Add(CreateStorageKey(Prefix_BlockHash).AddBigEndian(engine.PersistingBlock.Index), new StorageItem(engine.PersistingBlock.Hash.ToArray())); engine.Snapshot.Add(CreateStorageKey(Prefix_Block).Add(engine.PersistingBlock.Hash), new StorageItem(Trim(engine.PersistingBlock).ToArray())); @@ -32,13 +32,15 @@ internal override void OnPersist(ApplicationEngine engine) Transaction = tx })); } + return ContractTask.CompletedTask; } - internal override void PostPersist(ApplicationEngine engine) + internal override ContractTask PostPersist(ApplicationEngine engine) { HashIndexState state = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_CurrentBlock), () => new StorageItem(new HashIndexState())).GetInteroperable(); state.Hash = engine.PersistingBlock.Hash; state.Index = engine.PersistingBlock.Index; + return ContractTask.CompletedTask; } internal bool Initialized(DataCache snapshot) diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index 8490e5f12d..799125d7a7 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -34,14 +34,14 @@ internal NameService() { } - internal override void Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine) { - base.Initialize(engine); engine.Snapshot.Add(CreateStorageKey(Prefix_Roots), new StorageItem(new StringList())); engine.Snapshot.Add(CreateStorageKey(Prefix_DomainPrice), new StorageItem(10_00000000)); + return base.Initialize(engine); } - internal override void OnPersist(ApplicationEngine engine) + internal override async ContractTask OnPersist(ApplicationEngine engine) { uint now = (uint)(engine.PersistingBlock.Timestamp / 1000) + 1; byte[] start = CreateStorageKey(Prefix_Expiration).AddBigEndian(0).ToArray(); @@ -51,7 +51,7 @@ internal override void OnPersist(ApplicationEngine engine) engine.Snapshot.Delete(key); foreach (var (key2, _) in engine.Snapshot.Find(CreateStorageKey(Prefix_Record).Add(key.Key.AsSpan(5)).ToArray())) engine.Snapshot.Delete(key2); - Burn(engine, CreateStorageKey(Prefix_Token).Add(key.Key.AsSpan(5))); + await Burn(engine, CreateStorageKey(Prefix_Token).Add(key.Key.AsSpan(5))); } } @@ -109,7 +109,7 @@ public bool IsAvailable(DataCache snapshot, string name) } [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.States)] - private bool Register(ApplicationEngine engine, string name, UInt160 owner) + private async ContractTask Register(ApplicationEngine engine, string name, UInt160 owner) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); string[] names = name.Split('.'); @@ -126,7 +126,7 @@ private bool Register(ApplicationEngine engine, string name, UInt160 owner) Name = name, Expiration = (uint)(engine.PersistingBlock.Timestamp / 1000) + OneYear }; - Mint(engine, state); + await Mint(engine, state); engine.Snapshot.Add(CreateStorageKey(Prefix_Expiration).AddBigEndian(state.Expiration).Add(hash), new StorageItem(new byte[] { 0 })); return true; } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index bd4a964ca3..2004b76845 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -101,24 +101,38 @@ public static NativeContract GetContract(UInt160 hash) return contract; } - internal void Invoke(ApplicationEngine engine, byte version) + internal async void Invoke(ApplicationEngine engine, byte version) { - if (version != 0) - throw new InvalidOperationException($"The native contract of version {version} is not active."); - ExecutionContext context = engine.CurrentContext; - ContractMethodMetadata method = methods[context.InstructionPointer]; - ExecutionContextState state = context.GetState(); - if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) - throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}."); - engine.AddGas(method.CpuFee * Policy.GetExecFeeFactor(engine.Snapshot) + method.StorageFee * Policy.GetStoragePrice(engine.Snapshot)); - List parameters = new List(); - if (method.NeedApplicationEngine) parameters.Add(engine); - if (method.NeedSnapshot) parameters.Add(engine.Snapshot); - for (int i = 0; i < method.Parameters.Length; i++) - parameters.Add(engine.Convert(context.EvaluationStack.Pop(), method.Parameters[i])); - object returnValue = method.Handler.Invoke(this, parameters.ToArray()); - if (method.Handler.ReturnType != typeof(void)) - context.EvaluationStack.Push(engine.Convert(returnValue)); + try + { + if (version != 0) + throw new InvalidOperationException($"The native contract of version {version} is not active."); + ExecutionContext context = engine.CurrentContext; + ContractMethodMetadata method = methods[context.InstructionPointer]; + ExecutionContextState state = context.GetState(); + if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) + throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}."); + engine.AddGas(method.CpuFee * Policy.GetExecFeeFactor(engine.Snapshot) + method.StorageFee * Policy.GetStoragePrice(engine.Snapshot)); + List parameters = new List(); + if (method.NeedApplicationEngine) parameters.Add(engine); + if (method.NeedSnapshot) parameters.Add(engine.Snapshot); + for (int i = 0; i < method.Parameters.Length; i++) + parameters.Add(engine.Convert(context.EvaluationStack.Pop(), method.Parameters[i])); + object returnValue = method.Handler.Invoke(this, parameters.ToArray()); + if (returnValue is ContractTask task) + { + await task; + returnValue = task.GetResult(); + } + if (method.Handler.ReturnType != typeof(void) && method.Handler.ReturnType != typeof(ContractTask)) + { + context.EvaluationStack.Push(engine.Convert(returnValue)); + } + } + catch (Exception ex) + { + engine.Throw(ex); + } } public static bool IsNative(UInt160 hash) @@ -126,16 +140,19 @@ public static bool IsNative(UInt160 hash) return contractsDictionary.ContainsKey(hash); } - internal virtual void Initialize(ApplicationEngine engine) + internal virtual ContractTask Initialize(ApplicationEngine engine) { + return ContractTask.CompletedTask; } - internal virtual void OnPersist(ApplicationEngine engine) + internal virtual ContractTask OnPersist(ApplicationEngine engine) { + return ContractTask.CompletedTask; } - internal virtual void PostPersist(ApplicationEngine engine) + internal virtual ContractTask PostPersist(ApplicationEngine engine) { + return ContractTask.CompletedTask; } } } diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index 6ec3ee1685..c4ff13918d 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -42,9 +42,9 @@ public override BigInteger TotalSupply(DataCache snapshot) return TotalAmount; } - protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 account, NeoAccountState state, BigInteger amount) + internal override async ContractTask OnBalanceChanging(ApplicationEngine engine, UInt160 account, NeoAccountState state, BigInteger amount) { - DistributeGas(engine, account, state); + await DistributeGas(engine, account, state); if (amount.IsZero) return; if (state.VoteTo is null) return; engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_VotersCount)).Add(amount); @@ -54,14 +54,14 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco CheckCandidate(engine.Snapshot, state.VoteTo, candidate); } - private void DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccountState state) + private async ContractTask DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccountState state) { // PersistingBlock is null when running under the debugger if (engine.PersistingBlock is null) return; BigInteger gas = CalculateBonus(engine.Snapshot, state.VoteTo, state.Balance, state.BalanceHeight, engine.PersistingBlock.Index); state.BalanceHeight = engine.PersistingBlock.Index; - GAS.Mint(engine, account, gas, true); + await GAS.Mint(engine, account, gas, true); } private BigInteger CalculateBonus(DataCache snapshot, ECPoint vote, BigInteger value, uint start, uint end) @@ -115,17 +115,17 @@ private void CheckCandidate(DataCache snapshot, ECPoint pubkey, CandidateState c public static bool ShouldRefreshCommittee(uint height, int committeeMembersCount) => height % committeeMembersCount == 0; - internal override void Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine) { var cachedCommittee = new CachedCommittee(engine.ProtocolSettings.StandbyCommittee.Select(p => (p, BigInteger.Zero))); engine.Snapshot.Add(CreateStorageKey(Prefix_Committee), new StorageItem(cachedCommittee)); engine.Snapshot.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(new byte[0])); engine.Snapshot.Add(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(0u), new StorageItem(5 * GAS.Factor)); engine.Snapshot.Add(CreateStorageKey(Prefix_RegisterPrice), new StorageItem(1000 * GAS.Factor)); - Mint(engine, Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators), TotalAmount, false); + return Mint(engine, Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators), TotalAmount, false); } - internal override void OnPersist(ApplicationEngine engine) + internal override ContractTask OnPersist(ApplicationEngine engine) { // Set next committee if (ShouldRefreshCommittee(engine.PersistingBlock.Index, engine.ProtocolSettings.CommitteeMembersCount)) @@ -135,9 +135,10 @@ internal override void OnPersist(ApplicationEngine engine) cachedCommittee.Clear(); cachedCommittee.AddRange(ComputeCommitteeMembers(engine.Snapshot, engine.ProtocolSettings)); } + return ContractTask.CompletedTask; } - internal override void PostPersist(ApplicationEngine engine) + internal override async ContractTask PostPersist(ApplicationEngine engine) { // Distribute GAS for committee @@ -148,7 +149,7 @@ internal override void PostPersist(ApplicationEngine engine) var committee = GetCommitteeFromCache(engine.Snapshot); var pubkey = committee.ElementAt(index).PublicKey; var account = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); - GAS.Mint(engine, account, gasPerBlock * CommitteeRewardRatio / 100, false); + await GAS.Mint(engine, account, gasPerBlock * CommitteeRewardRatio / 100, false); // Record the cumulative reward of the voters of committee @@ -250,7 +251,7 @@ private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) } [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.States)] - private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) + private async ContractTask Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) { if (!engine.CheckWitnessInternal(account)) return false; NeoAccountState state_account = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Account).Add(account))?.GetInteroperable(); @@ -270,7 +271,7 @@ private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) else item.Add(-state_account.Balance); } - DistributeGas(engine, account, state_account); + await DistributeGas(engine, account, state_account); if (state_account.VoteTo != null) { StorageKey key = CreateStorageKey(Prefix_Candidate).Add(state_account.VoteTo); diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index bb2f63bb9b..b6d7d2b55c 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -64,26 +64,27 @@ protected NonfungibleToken() protected virtual byte[] GetKey(byte[] tokenId) => tokenId; - internal override void Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine) { engine.Snapshot.Add(CreateStorageKey(Prefix_TotalSupply), new StorageItem(BigInteger.Zero)); + return ContractTask.CompletedTask; } - protected void Mint(ApplicationEngine engine, TokenState token) + private protected ContractTask Mint(ApplicationEngine engine, TokenState token) { engine.Snapshot.Add(CreateStorageKey(Prefix_Token).Add(GetKey(token.Id)), new StorageItem(token)); NFTAccountState account = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Account).Add(token.Owner), () => new StorageItem(new NFTAccountState())).GetInteroperable(); account.Add(token.Id); engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply)).Add(1); - PostTransfer(engine, null, token.Owner, token.Id); + return PostTransfer(engine, null, token.Owner, token.Id); } - protected void Burn(ApplicationEngine engine, byte[] tokenId) + private protected ContractTask Burn(ApplicationEngine engine, byte[] tokenId) { - Burn(engine, CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))); + return Burn(engine, CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))); } - private protected void Burn(ApplicationEngine engine, StorageKey key) + private protected ContractTask Burn(ApplicationEngine engine, StorageKey key) { TokenState token = engine.Snapshot.TryGet(key)?.GetInteroperable(); if (token is null) throw new InvalidOperationException(); @@ -94,7 +95,7 @@ private protected void Burn(ApplicationEngine engine, StorageKey key) if (account.Balance.IsZero) engine.Snapshot.Delete(key_account); engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_TotalSupply)).Add(-1); - PostTransfer(engine, token.Owner, null, token.Id); + return PostTransfer(engine, token.Owner, null, token.Id); } [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] @@ -138,7 +139,7 @@ protected IIterator TokensOf(DataCache snapshot, UInt160 owner) } [ContractMethod(CpuFee = 1 << 17, StorageFee = 50, RequiredCallFlags = CallFlags.States | CallFlags.AllowCall | CallFlags.AllowNotify)] - protected bool Transfer(ApplicationEngine engine, UInt160 to, byte[] tokenId) + private protected async ContractTask Transfer(ApplicationEngine engine, UInt160 to, byte[] tokenId) { if (to is null) throw new ArgumentNullException(nameof(to)); StorageKey key_token = CreateStorageKey(Prefix_Token).Add(GetKey(tokenId)); @@ -160,7 +161,7 @@ protected bool Transfer(ApplicationEngine engine, UInt160 to, byte[] tokenId) account.Add(tokenId); OnTransferred(engine, from, token); } - PostTransfer(engine, from, to, tokenId); + await PostTransfer(engine, from, to, tokenId); return true; } @@ -168,13 +169,13 @@ protected virtual void OnTransferred(ApplicationEngine engine, UInt160 from, Tok { } - private void PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, byte[] tokenId) + private async ContractTask PostTransfer(ApplicationEngine engine, UInt160 from, UInt160 to, byte[] tokenId) { engine.SendNotification(Hash, "Transfer", new Array { from?.ToArray() ?? StackItem.Null, to?.ToArray() ?? StackItem.Null, 1, tokenId }); if (to is not null && ContractManagement.GetContract(engine.Snapshot, to) is not null) - engine.CallFromNativeContract(Hash, to, "onNEP11Payment", from?.ToArray() ?? StackItem.Null, 1, tokenId); + await engine.CallFromNativeContract(Hash, to, "onNEP11Payment", from?.ToArray() ?? StackItem.Null, 1, tokenId); } class NFTAccountState : AccountState diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index 09f512678e..0c93a26a0b 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -96,7 +96,7 @@ public long GetPrice(DataCache snapshot) } [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowCall | CallFlags.AllowNotify)] - private void Finish(ApplicationEngine engine) + private ContractTask Finish(ApplicationEngine engine) { Transaction tx = (Transaction)engine.ScriptContainer; OracleResponse response = tx.GetAttribute(); @@ -105,7 +105,7 @@ private void Finish(ApplicationEngine engine) if (request == null) throw new ArgumentException("Oracle request was not found"); engine.SendNotification(Hash, "OracleResponse", new VM.Types.Array { response.Id, request.OriginalTxid.ToArray() }); StackItem userData = BinarySerializer.Deserialize(request.UserData, engine.Limits.MaxStackSize, engine.ReferenceCounter); - engine.CallFromNativeContract(Hash, request.CallbackContract, request.CallbackMethod, request.Url, userData, (int)response.Code, response.Result); + return engine.CallFromNativeContract(Hash, request.CallbackContract, request.CallbackMethod, request.Url, userData, (int)response.Code, response.Result); } private UInt256 GetOriginalTxid(ApplicationEngine engine) @@ -140,13 +140,14 @@ private static byte[] GetUrlHash(string url) return Crypto.Hash160(Utility.StrictUTF8.GetBytes(url)); } - internal override void Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine) { engine.Snapshot.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BigInteger.Zero)); engine.Snapshot.Add(CreateStorageKey(Prefix_Price), new StorageItem(0_50000000)); + return ContractTask.CompletedTask; } - internal override void PostPersist(ApplicationEngine engine) + internal override async ContractTask PostPersist(ApplicationEngine engine) { (UInt160 Account, BigInteger GAS)[] nodes = null; foreach (Transaction tx in engine.PersistingBlock.Transactions) @@ -179,13 +180,14 @@ internal override void PostPersist(ApplicationEngine engine) { foreach (var (account, gas) in nodes) { - if (gas.Sign > 0) GAS.Mint(engine, account, gas, false); + if (gas.Sign > 0) + await GAS.Mint(engine, account, gas, false); } } } [ContractMethod(RequiredCallFlags = CallFlags.States | CallFlags.AllowNotify)] - private void Request(ApplicationEngine engine, string url, string filter, string callback, StackItem userData, long gasForResponse) + private async ContractTask Request(ApplicationEngine engine, string url, string filter, string callback, StackItem userData, long gasForResponse) { //Check the arguments if (Utility.StrictUTF8.GetByteCount(url) > MaxUrlLength @@ -198,7 +200,7 @@ private void Request(ApplicationEngine engine, string url, string filter, string //Mint gas for the response engine.AddGas(gasForResponse); - GAS.Mint(engine, Hash, gasForResponse, false); + await GAS.Mint(engine, Hash, gasForResponse, false); //Increase the request id StorageItem item_id = engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_RequestId)); diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 66b3d4adb9..2de9e54591 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -23,11 +23,12 @@ internal PolicyContract() { } - internal override void Initialize(ApplicationEngine engine) + internal override ContractTask Initialize(ApplicationEngine engine) { engine.Snapshot.Add(CreateStorageKey(Prefix_FeePerByte), new StorageItem(DefaultFeePerByte)); engine.Snapshot.Add(CreateStorageKey(Prefix_ExecFeeFactor), new StorageItem(DefaultExecFeeFactor)); engine.Snapshot.Add(CreateStorageKey(Prefix_StoragePrice), new StorageItem(DefaultStoragePrice)); + return ContractTask.CompletedTask; } [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] diff --git a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs index bb6f82d387..b600db7afb 100644 --- a/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs +++ b/tests/neo.UnitTests/Ledger/UT_MemoryPool.cs @@ -227,7 +227,7 @@ public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, senderAccount); ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); - NativeContract.GAS.Mint(engine, UInt160.Zero, 70, true); + _ = NativeContract.GAS.Mint(engine, UInt160.Zero, 70, true); long txFee = 1; AddTransactionsWithBalanceVerify(70, txFee, snapshot); @@ -245,7 +245,7 @@ public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() ApplicationEngine applicationEngine = ApplicationEngine.Create(TriggerType.All, block, snapshot, block, settings: TestBlockchain.TheNeoSystem.Settings, gas: (long)balance); NativeContract.GAS.Burn(applicationEngine, sender, NativeContract.GAS.BalanceOf(snapshot, sender)); - NativeContract.GAS.Mint(applicationEngine, sender, txFee * 30, true); // Set the balance to meet 30 txs only + _ = NativeContract.GAS.Mint(applicationEngine, sender, txFee * 30, true); // Set the balance to meet 30 txs only // Persist block and reverify all the txs in mempool, but half of the txs will be discarded _unit.UpdatePoolForBlockPersisted(block, snapshot); @@ -254,7 +254,7 @@ public void BlockPersistAndReverificationWillAbandonTxAsBalanceTransfered() // Revert the balance NativeContract.GAS.Burn(applicationEngine, sender, txFee * 30); - NativeContract.GAS.Mint(applicationEngine, sender, balance, true); + _ = NativeContract.GAS.Mint(applicationEngine, sender, balance, true); } private void VerifyTransactionsSortedDescending(IEnumerable transactions) diff --git a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs b/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs index b393fab8c2..b0a150a154 100644 --- a/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs +++ b/tests/neo.UnitTests/Ledger/UT_TransactionVerificationContext.cs @@ -55,7 +55,7 @@ public void TestDuplicateOracle() ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, UInt160.Zero); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); - NativeContract.GAS.Mint(engine, UInt160.Zero, 8, false); + _ = NativeContract.GAS.Mint(engine, UInt160.Zero, 8, false); // Test TransactionVerificationContext verificationContext = new TransactionVerificationContext(); @@ -76,7 +76,7 @@ public void TestTransactionSenderFee() ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, settings: TestBlockchain.TheNeoSystem.Settings, gas: long.MaxValue); BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, UInt160.Zero); NativeContract.GAS.Burn(engine, UInt160.Zero, balance); - NativeContract.GAS.Mint(engine, UInt160.Zero, 8, true); + _ = NativeContract.GAS.Mint(engine, UInt160.Zero, 8, true); TransactionVerificationContext verificationContext = new TransactionVerificationContext(); var tx = CreateTransactionWithFee(1, 2); diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs index d415ca5fb5..dd3b036bd8 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_GasToken.cs @@ -9,6 +9,7 @@ using System; using System.Linq; using System.Numerics; +using System.Threading.Tasks; namespace Neo.UnitTests.SmartContract.Native { @@ -35,7 +36,7 @@ public void TestSetup() public void Check_Decimals() => NativeContract.GAS.Decimals(_snapshot).Should().Be(8); [TestMethod] - public void Check_BalanceOfTransferAndBurn() + public async Task Check_BalanceOfTransferAndBurn() { var snapshot = _snapshot.CreateSnapshot(); var persistingBlock = new Block { Header = new Header { Index = 1000 } }; @@ -91,17 +92,17 @@ public void Check_BalanceOfTransferAndBurn() using var engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot, persistingBlock, settings: TestBlockchain.TheNeoSystem.Settings, gas: 0); keyCount = snapshot.GetChangeSet().Count(); - Assert.ThrowsException(() => - NativeContract.GAS.Burn(engine, new UInt160(to), BigInteger.MinusOne)); + await Assert.ThrowsExceptionAsync(async () => + await NativeContract.GAS.Burn(engine, new UInt160(to), BigInteger.MinusOne)); // Burn more than expected - Assert.ThrowsException(() => - NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(30000500_00000001))); + await Assert.ThrowsExceptionAsync(async () => + await NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(30000500_00000001))); // Real burn - NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(1)); + await NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(1)); NativeContract.GAS.BalanceOf(snapshot, to).Should().Be(3000049999999999); @@ -109,7 +110,7 @@ public void Check_BalanceOfTransferAndBurn() // Burn all - NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000049999999999)); + await NativeContract.GAS.Burn(engine, new UInt160(to), new BigInteger(3000049999999999)); (keyCount - 1).Should().Be(snapshot.GetChangeSet().Count()); From 3d6b5f785ae4b3ce7074c1b6a0259c0673ba7990 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 4 Mar 2021 12:51:10 +0800 Subject: [PATCH 46/63] Fix CalculateNetworkFee (#2375) --- src/neo/SmartContract/Native/OracleContract.cs | 2 +- src/neo/Wallets/Wallet.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index 0c93a26a0b..e63a406b99 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -230,7 +230,7 @@ private async ContractTask Request(ApplicationEngine engine, string url, string engine.SendNotification(Hash, "OracleRequest", new VM.Types.Array { id, engine.CallingScriptHash.ToArray(), url, filter ?? StackItem.Null }); } - [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.None)] + [ContractMethod(CpuFee = 1 << 15)] private bool Verify(ApplicationEngine engine) { Transaction tx = (Transaction)engine.ScriptContainer; diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index a2c91495fb..cfd779f382 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -394,7 +394,7 @@ public long CalculateNetworkFee(DataCache snapshot, Transaction tx) // Check verify cost using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Verification, tx, snapshot.CreateSnapshot(), settings: ProtocolSettings); - engine.LoadContract(contract, md, CallFlags.None); + engine.LoadContract(contract, md, CallFlags.ReadOnly); if (engine.Execute() == VMState.FAULT) throw new ArgumentException($"Smart contract {contract.Hash} verification fault."); if (!engine.ResultStack.Pop().GetBoolean()) throw new ArgumentException($"Smart contract {contract.Hash} returns false."); From bdb612bebbe0510acde51c1e2c16c2978c1d8e5e Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Thu, 4 Mar 2021 14:33:43 +0800 Subject: [PATCH 47/63] Load settings from IConfigurationSection (#2376) --- src/neo/ProtocolSettings.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index ee0e990ce5..bba0eb4fd6 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -87,6 +87,11 @@ public static ProtocolSettings Load(string path, bool optional = true) { IConfigurationRoot config = new ConfigurationBuilder().AddJsonFile(path, optional).Build(); IConfigurationSection section = config.GetSection("ProtocolConfiguration"); + return Load(section); + } + + public static ProtocolSettings Load(IConfigurationSection section) + { return new ProtocolSettings { Magic = section.GetValue("Magic", Default.Magic), From 0ea094c5eeb72c1678551e27ee70da76338d9bd6 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 5 Mar 2021 13:25:35 +0800 Subject: [PATCH 48/63] Move AES methods and mark them as private (#2360) --- src/neo/Cryptography/Helper.cs | 56 ----------- src/neo/Wallets/KeyPair.cs | 13 ++- src/neo/Wallets/SQLite/UserWallet.cs | 26 ++++- src/neo/Wallets/Wallet.cs | 12 ++- .../Cryptography/UT_Cryptography_Helper.cs | 95 ------------------- 5 files changed, 46 insertions(+), 156 deletions(-) diff --git a/src/neo/Cryptography/Helper.cs b/src/neo/Cryptography/Helper.cs index d39236c33d..31ba0cd6ec 100644 --- a/src/neo/Cryptography/Helper.cs +++ b/src/neo/Cryptography/Helper.cs @@ -13,62 +13,6 @@ namespace Neo.Cryptography { public static class Helper { - internal static byte[] AES256Decrypt(this byte[] block, byte[] key) - { - using (Aes aes = Aes.Create()) - { - aes.Key = key; - aes.Mode = CipherMode.ECB; - aes.Padding = PaddingMode.None; - using (ICryptoTransform decryptor = aes.CreateDecryptor()) - { - return decryptor.TransformFinalBlock(block, 0, block.Length); - } - } - } - - internal static byte[] AES256Encrypt(this byte[] block, byte[] key) - { - using (Aes aes = Aes.Create()) - { - aes.Key = key; - aes.Mode = CipherMode.ECB; - aes.Padding = PaddingMode.None; - using (ICryptoTransform encryptor = aes.CreateEncryptor()) - { - return encryptor.TransformFinalBlock(block, 0, block.Length); - } - } - } - - internal static byte[] AesDecrypt(this byte[] data, byte[] key, byte[] iv) - { - if (data == null || key == null || iv == null) throw new ArgumentNullException(); - if (data.Length % 16 != 0 || key.Length != 32 || iv.Length != 16) throw new ArgumentException(); - using (Aes aes = Aes.Create()) - { - aes.Padding = PaddingMode.None; - using (ICryptoTransform decryptor = aes.CreateDecryptor(key, iv)) - { - return decryptor.TransformFinalBlock(data, 0, data.Length); - } - } - } - - internal static byte[] AesEncrypt(this byte[] data, byte[] key, byte[] iv) - { - if (data == null || key == null || iv == null) throw new ArgumentNullException(); - if (data.Length % 16 != 0 || key.Length != 32 || iv.Length != 16) throw new ArgumentException(); - using (Aes aes = Aes.Create()) - { - aes.Padding = PaddingMode.None; - using (ICryptoTransform encryptor = aes.CreateEncryptor(key, iv)) - { - return encryptor.TransformFinalBlock(data, 0, data.Length); - } - } - } - public static byte[] RIPEMD160(this byte[] value) { using var ripemd160 = new RIPEMD160Managed(); diff --git a/src/neo/Wallets/KeyPair.cs b/src/neo/Wallets/KeyPair.cs index 3730bbf9e6..875aac79c1 100644 --- a/src/neo/Wallets/KeyPair.cs +++ b/src/neo/Wallets/KeyPair.cs @@ -2,6 +2,7 @@ using Neo.SmartContract; using Org.BouncyCastle.Crypto.Generators; using System; +using System.Security.Cryptography; using System.Text; using static Neo.Wallets.Helper; @@ -60,7 +61,7 @@ public string Export(string passphrase, byte version, int N = 16384, int r = 8, byte[] derivedkey = SCrypt.Generate(Encoding.UTF8.GetBytes(passphrase), addresshash, N, r, p, 64); byte[] derivedhalf1 = derivedkey[..32]; byte[] derivedhalf2 = derivedkey[32..]; - byte[] encryptedkey = XOR(PrivateKey, derivedhalf1).AES256Encrypt(derivedhalf2); + byte[] encryptedkey = Encrypt(XOR(PrivateKey, derivedhalf1), derivedhalf2); Span buffer = stackalloc byte[39]; buffer[0] = 0x01; buffer[1] = 0x42; @@ -70,6 +71,16 @@ public string Export(string passphrase, byte version, int N = 16384, int r = 8, return Base58.Base58CheckEncode(buffer); } + private static byte[] Encrypt(byte[] data, byte[] key) + { + using Aes aes = Aes.Create(); + aes.Key = key; + aes.Mode = CipherMode.ECB; + aes.Padding = PaddingMode.None; + using ICryptoTransform encryptor = aes.CreateEncryptor(); + return encryptor.TransformFinalBlock(data, 0, data.Length); + } + public override int GetHashCode() { return PublicKey.GetHashCode(); diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs index e14afb5e12..12aee5e039 100644 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ b/src/neo/Wallets/SQLite/UserWallet.cs @@ -52,7 +52,7 @@ private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings) : if (passwordHash != null && !passwordHash.SequenceEqual(passwordKey.Concat(salt).ToArray().Sha256())) throw new CryptographicException(); this.iv = LoadStoredData("IV"); - this.masterKey = LoadStoredData("MasterKey").AesDecrypt(passwordKey, iv); + this.masterKey = Decrypt(LoadStoredData("MasterKey"), passwordKey, iv); this.scrypt = new ScryptParameters ( BinaryPrimitives.ReadInt32LittleEndian(LoadStoredData("ScryptN")), @@ -91,7 +91,7 @@ private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings, S SaveStoredData("IV", iv); SaveStoredData("Salt", salt); SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); - SaveStoredData("MasterKey", masterKey.AesEncrypt(passwordKey, iv)); + SaveStoredData("MasterKey", Encrypt(masterKey, passwordKey, iv)); SaveStoredData("Version", versionBuffer); SaveStoredData("ScryptN", this.scrypt.N); SaveStoredData("ScryptR", this.scrypt.R); @@ -179,7 +179,7 @@ public override bool ChangePassword(string oldPassword, string newPassword) try { SaveStoredData("PasswordHash", passwordKey.Concat(salt).ToArray().Sha256()); - SaveStoredData("MasterKey", masterKey.AesEncrypt(passwordKey, iv)); + SaveStoredData("MasterKey", Encrypt(masterKey, passwordKey, iv)); return true; } finally @@ -376,5 +376,25 @@ public override bool VerifyPassword(string password) { return password.ToAesKey().Concat(salt).ToArray().Sha256().SequenceEqual(LoadStoredData("PasswordHash")); } + + private static byte[] Encrypt(byte[] data, byte[] key, byte[] iv) + { + if (data == null || key == null || iv == null) throw new ArgumentNullException(); + if (data.Length % 16 != 0 || key.Length != 32 || iv.Length != 16) throw new ArgumentException(); + using Aes aes = Aes.Create(); + aes.Padding = PaddingMode.None; + using ICryptoTransform encryptor = aes.CreateEncryptor(key, iv); + return encryptor.TransformFinalBlock(data, 0, data.Length); + } + + private static byte[] Decrypt(byte[] data, byte[] key, byte[] iv) + { + if (data == null || key == null || iv == null) throw new ArgumentNullException(); + if (data.Length % 16 != 0 || key.Length != 32 || iv.Length != 16) throw new ArgumentException(); + using Aes aes = Aes.Create(); + aes.Padding = PaddingMode.None; + using ICryptoTransform decryptor = aes.CreateDecryptor(key, iv); + return decryptor.TransformFinalBlock(data, 0, data.Length); + } } } diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index cfd779f382..6700b99f84 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -150,6 +150,16 @@ public BigDecimal GetBalance(DataCache snapshot, UInt160 asset_id, params UInt16 return new BigDecimal(amount, decimals); } + private static byte[] Decrypt(byte[] data, byte[] key) + { + using Aes aes = Aes.Create(); + aes.Key = key; + aes.Mode = CipherMode.ECB; + aes.Padding = PaddingMode.None; + using ICryptoTransform decryptor = aes.CreateDecryptor(); + return decryptor.TransformFinalBlock(data, 0, data.Length); + } + public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, byte version, int N = 16384, int r = 8, int p = 8) { if (nep2 == null) throw new ArgumentNullException(nameof(nep2)); @@ -168,7 +178,7 @@ public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, byte byte[] encryptedkey = new byte[32]; Buffer.BlockCopy(data, 7, encryptedkey, 0, 32); Array.Clear(data, 0, data.Length); - byte[] prikey = XOR(encryptedkey.AES256Decrypt(derivedhalf2), derivedhalf1); + byte[] prikey = XOR(Decrypt(encryptedkey, derivedhalf2), derivedhalf1); Array.Clear(derivedhalf1, 0, derivedhalf1.Length); Array.Clear(derivedhalf2, 0, derivedhalf2.Length); ECPoint pubkey = Cryptography.ECC.ECCurve.Secp256r1.G * prikey; diff --git a/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs b/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs index 1b69ed11a2..5689dce488 100644 --- a/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs +++ b/tests/neo.UnitTests/Cryptography/UT_Cryptography_Helper.cs @@ -13,101 +13,6 @@ namespace Neo.UnitTests.Cryptography [TestClass] public class UT_Cryptography_Helper { - [TestMethod] - public void TestAES256Encrypt() - { - byte[] block = Encoding.ASCII.GetBytes("00000000000000000000000000000000"); - byte[] key = Encoding.ASCII.GetBytes("1234567812345678"); - byte[] result = block.AES256Encrypt(key); - string encryptString = result.ToHexString(); - encryptString.Should().Be("f69e0923d8247eef417d6a78944a4b39f69e0923d8247eef417d6a78944a4b39"); - } - - [TestMethod] - public void TestAES256Decrypt() - { - byte[] block = new byte[32]; - byte[] key = Encoding.ASCII.GetBytes("1234567812345678"); - string decryptString = "f69e0923d8247eef417d6a78944a4b39f69e0923d8247eef417d6a78944a4b399ae8fd02b340288a0e7bbff0f0ba54d6"; - for (int i = 0; i < 32; i++) - block[i] = Convert.ToByte(decryptString.Substring(i * 2, 2), 16); - string str = System.Text.Encoding.Default.GetString(block.AES256Decrypt(key)); - str.Should().Be("00000000000000000000000000000000"); - } - - [TestMethod] - public void TestAesEncrypt() - { - byte[] data = Encoding.ASCII.GetBytes("00000000000000000000000000000000"); - byte[] key = Encoding.ASCII.GetBytes("12345678123456781234567812345678"); - byte[] iv = Encoding.ASCII.GetBytes("1234567812345678"); - byte[] result = data.AesEncrypt(key, iv); - - string encryptString = result.ToHexString(); - encryptString.Should().Be("07c748cf7d326782f82e60ebe60e2fac289e84e9ce91c1bc41565d14ecb53640"); - - byte[] nullData = null; - Action action = () => nullData.AesEncrypt(key, iv); - action.Should().Throw(); - - byte[] nullKey = null; - action = () => data.AesEncrypt(nullKey, iv); - action.Should().Throw(); - - byte[] nullIv = null; - action = () => data.AesEncrypt(key, nullIv); - action.Should().Throw(); - - byte[] wrongData = Encoding.ASCII.GetBytes("000000000000000000000000000000001"); ; - action = () => wrongData.AesEncrypt(key, iv); - action.Should().Throw(); - - byte[] wrongKey = Encoding.ASCII.GetBytes("123456781234567812345678123456780"); ; - action = () => data.AesEncrypt(wrongKey, iv); - action.Should().Throw(); - - byte[] wrongIv = Encoding.ASCII.GetBytes("12345678123456780"); ; - action = () => data.AesEncrypt(key, wrongIv); - action.Should().Throw(); - } - - [TestMethod] - public void TestAesDecrypt() - { - byte[] data = new byte[32]; - byte[] key = Encoding.ASCII.GetBytes("12345678123456781234567812345678"); - byte[] iv = Encoding.ASCII.GetBytes("1234567812345678"); - string decryptString = "07c748cf7d326782f82e60ebe60e2fac289e84e9ce91c1bc41565d14ecb5364073f28c9aa7bd6b069e44d8a97beb2b58"; - for (int i = 0; i < 32; i++) - data[i] = Convert.ToByte(decryptString.Substring(i * 2, 2), 16); - string str = System.Text.Encoding.Default.GetString(data.AesDecrypt(key, iv)); - str.Should().Be("00000000000000000000000000000000"); - - byte[] nullData = null; - Action action = () => nullData.AesDecrypt(key, iv); - action.Should().Throw(); - - byte[] nullKey = null; - action = () => data.AesDecrypt(nullKey, iv); - action.Should().Throw(); - - byte[] nullIv = null; - action = () => data.AesDecrypt(key, nullIv); - action.Should().Throw(); - - byte[] wrongData = Encoding.ASCII.GetBytes("00000000000000001"); ; - action = () => wrongData.AesDecrypt(key, iv); - action.Should().Throw(); - - byte[] wrongKey = Encoding.ASCII.GetBytes("123456781234567812345678123456780"); ; - action = () => data.AesDecrypt(wrongKey, iv); - action.Should().Throw(); - - byte[] wrongIv = Encoding.ASCII.GetBytes("12345678123456780"); ; - action = () => data.AesDecrypt(key, wrongIv); - action.Should().Throw(); - } - [TestMethod] public void TestBase58CheckDecode() { From d1c3cd1ae4e15eaec1691f6ff3e890fc0617506f Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 5 Mar 2021 10:10:09 +0100 Subject: [PATCH 49/63] Add GetSignatures to ContractParametersContext (#2370) * Add GetPendingWitnesses * GetSignatures * Remove clean signatures * Store single signatures in ContextItem * Clean code * readonly * Optimize * Change return type * Fix UT Co-authored-by: Erik Zhang --- .../ContractParametersContext.cs | 64 +++++++++---------- tests/neo.UnitTests/Ledger/UT_Blockchain.cs | 3 + .../UT_ContractParameterContext.cs | 2 +- 3 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index b64527ffd1..66cfac2e8d 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -15,44 +15,36 @@ public class ContractParametersContext { private class ContextItem { - public byte[] Script; - public ContractParameter[] Parameters; - public Dictionary Signatures; - - private ContextItem() { } + public readonly byte[] Script; + public readonly ContractParameter[] Parameters; + public readonly Dictionary Signatures; public ContextItem(Contract contract) { this.Script = contract.Script; this.Parameters = contract.ParameterList.Select(p => new ContractParameter { Type = p }).ToArray(); + this.Signatures = new Dictionary(); } - public static ContextItem FromJson(JObject json) + public ContextItem(JObject json) { - return new ContextItem + this.Script = Convert.FromBase64String(json["script"].AsString()); + this.Parameters = ((JArray)json["parameters"]).Select(p => ContractParameter.FromJson(p)).ToArray(); + this.Signatures = json["signatures"].Properties.Select(p => new { - Script = Convert.FromBase64String(json["script"]?.AsString()), - Parameters = ((JArray)json["parameters"]).Select(p => ContractParameter.FromJson(p)).ToArray(), - Signatures = json["signatures"]?.Properties.Select(p => new - { - PublicKey = ECPoint.Parse(p.Key, ECCurve.Secp256r1), - Signature = Convert.FromBase64String(p.Value.AsString()) - }).ToDictionary(p => p.PublicKey, p => p.Signature) - }; + PublicKey = ECPoint.Parse(p.Key, ECCurve.Secp256r1), + Signature = Convert.FromBase64String(p.Value.AsString()) + }).ToDictionary(p => p.PublicKey, p => p.Signature); } public JObject ToJson() { - JObject json = new JObject(); - if (Script != null) - json["script"] = Convert.ToBase64String(Script); + JObject json = new(); + 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()] = Convert.ToBase64String(signature.Value); - } + json["signatures"] = new JObject(); + foreach (var signature in Signatures) + json["signatures"][signature.Key.ToString()] = Convert.ToBase64String(signature.Value); return json; } } @@ -112,15 +104,12 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) { if (contract.Script.IsMultiSigContract(out _, out ECPoint[] points)) { + if (!points.Contains(pubkey)) return false; ContextItem item = CreateItem(contract); if (item == null) return false; if (item.Parameters.All(p => p.Value != null)) return false; - if (item.Signatures == null) - item.Signatures = new Dictionary(); - else if (item.Signatures.ContainsKey(pubkey)) + if (!item.Signatures.TryAdd(pubkey, signature)) return false; - if (!points.Contains(pubkey)) return false; - item.Signatures.Add(pubkey, signature); if (item.Signatures.Count == contract.ParameterList.Length) { Dictionary dic = points.Select((p, i) => new @@ -136,7 +125,6 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) for (int i = 0; i < sigs.Length; i++) if (!Add(contract, i, sigs[i])) throw new InvalidOperationException(); - item.Signatures = null; } return true; } @@ -156,7 +144,12 @@ public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) // return now to prevent array index out of bounds exception return false; } - return Add(contract, index, signature); + ContextItem item = CreateItem(contract); + if (item == null) return false; + if (!item.Signatures.TryAdd(pubkey, signature)) + return false; + item.Parameters[index].Value = signature; + return true; } } @@ -185,7 +178,7 @@ public static ContractParametersContext FromJson(JObject json, DataCache snapsho ContractParametersContext context = new ContractParametersContext(snapshot, verifiable); foreach (var property in json["items"].Properties) { - context.ContextItems.Add(UInt160.Parse(property.Key), ContextItem.FromJson(property.Value)); + context.ContextItems.Add(UInt160.Parse(property.Key), new ContextItem(property.Value)); } return context; } @@ -202,6 +195,13 @@ public IReadOnlyList GetParameters(UInt160 scriptHash) return item.Parameters; } + public IReadOnlyDictionary GetSignatures(UInt160 scriptHash) + { + if (!ContextItems.TryGetValue(scriptHash, out ContextItem item)) + return null; + return item.Signatures; + } + public byte[] GetScript(UInt160 scriptHash) { if (!ContextItems.TryGetValue(scriptHash, out ContextItem item)) diff --git a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs index 9cba972afe..afcebd2013 100644 --- a/tests/neo.UnitTests/Ledger/UT_Blockchain.cs +++ b/tests/neo.UnitTests/Ledger/UT_Blockchain.cs @@ -9,6 +9,7 @@ using Neo.Wallets; using Neo.Wallets.NEP6; using System; +using System.Linq; using System.Numerics; namespace Neo.UnitTests.Ledger @@ -90,8 +91,10 @@ private static Transaction CreateValidTx(DataCache snapshot, NEP6Wallet wallet, tx.Nonce = nonce; var data = new ContractParametersContext(snapshot, tx); + Assert.IsNull(data.GetSignatures(tx.Sender)); Assert.IsTrue(wallet.Sign(data)); Assert.IsTrue(data.Completed); + Assert.AreEqual(1, data.GetSignatures(tx.Sender).Count()); tx.Witnesses = data.GetWitnesses(); return tx; diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index e36fc38790..c4a8243d2c 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -53,7 +53,7 @@ public void TestToString() public void TestParse() { var snapshot = TestBlockchain.GetTestSnapshot(); - var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI=\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}]}}}", snapshot); + var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI=\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}],\"signatures\":{\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\":\"AQ==\"}}}}", snapshot); ret.ScriptHashes[0].ToString().Should().Be("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); ((Transaction)ret.Verifiable).Script.ToHexString().Should().Be(new byte[] { 18 }.ToHexString()); } From e6dadb276e17b5245a3a2d6520826022413c8082 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 5 Mar 2021 13:39:18 +0300 Subject: [PATCH 50/63] Role: add NeoFS alphabet node role (#2377) --- src/neo/SmartContract/Native/Role.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/neo/SmartContract/Native/Role.cs b/src/neo/SmartContract/Native/Role.cs index c3f38b7cc6..59a927e8a0 100644 --- a/src/neo/SmartContract/Native/Role.cs +++ b/src/neo/SmartContract/Native/Role.cs @@ -3,6 +3,7 @@ namespace Neo.SmartContract.Native public enum Role : byte { StateValidator = 4, - Oracle = 8 + Oracle = 8, + NeoFSAlphabetNode = 16 } } From 26c3adf812c3d3584718101f9ab79d7f5dc49385 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 8 Mar 2021 12:03:13 +0300 Subject: [PATCH 51/63] StorageItem: optimize binary representation (#2379) * StorageItem: optimize binary representation After #2351 StorageItem is just a thin wrapper for some byte array in its essence so we can Use the same trick as was used for storage keys in #1566 and avoid additional wrapping/unwrapping with VarBytes encoding. * StorageItem: optimize out reader.BaseStream.Position It's always 0. --- src/neo/SmartContract/StorageItem.cs | 4 ++-- tests/neo.UnitTests/Ledger/UT_StorageItem.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/neo/SmartContract/StorageItem.cs b/src/neo/SmartContract/StorageItem.cs index b76c6583f3..add08f943a 100644 --- a/src/neo/SmartContract/StorageItem.cs +++ b/src/neo/SmartContract/StorageItem.cs @@ -66,7 +66,7 @@ public StorageItem Clone() public void Deserialize(BinaryReader reader) { - Value = reader.ReadVarBytes(); + Value = reader.ReadBytes((int)(reader.BaseStream.Length)); } public void FromReplica(StorageItem replica) @@ -95,7 +95,7 @@ public void FromReplica(StorageItem replica) public void Serialize(BinaryWriter writer) { - writer.WriteVarBytes(Value); + writer.Write(Value); } public void Set(BigInteger integer) diff --git a/tests/neo.UnitTests/Ledger/UT_StorageItem.cs b/tests/neo.UnitTests/Ledger/UT_StorageItem.cs index 84c8ff7d42..e750c18782 100644 --- a/tests/neo.UnitTests/Ledger/UT_StorageItem.cs +++ b/tests/neo.UnitTests/Ledger/UT_StorageItem.cs @@ -64,7 +64,7 @@ public void Clone() [TestMethod] public void Deserialize() { - byte[] data = new byte[] { 10, 66, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0 }; + byte[] data = new byte[] { 66, 32, 32, 32, 32, 32, 32, 32, 32, 32 }; int index = 0; using (MemoryStream ms = new MemoryStream(data, index, data.Length - index, false)) { @@ -96,7 +96,7 @@ public void Serialize() } } - byte[] requiredData = new byte[] { 10, 66, 32, 32, 32, 32, 32, 32, 32, 32, 32 }; + byte[] requiredData = new byte[] { 66, 32, 32, 32, 32, 32, 32, 32, 32, 32 }; data.Length.Should().Be(requiredData.Length); for (int i = 0; i < requiredData.Length; i++) From 9ef9541a753d7e46e817379f43731518f0d82236 Mon Sep 17 00:00:00 2001 From: Harry Pierson Date: Mon, 8 Mar 2021 21:33:58 -0800 Subject: [PATCH 52/63] factor genesis block creation logic into separate CreateGenesisBlock method for use by blockchain toolkit (#2381) --- src/neo/NeoSystem.cs | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 75f7be56e7..7ac736d7a0 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -57,24 +57,7 @@ static NeoSystem() public NeoSystem(ProtocolSettings settings, string storageEngine = null, string storagePath = null) { this.Settings = settings; - this.GenesisBlock = new Block - { - Header = new Header - { - PrevHash = UInt256.Zero, - MerkleRoot = UInt256.Zero, - Timestamp = (new DateTime(2016, 7, 15, 15, 8, 21, DateTimeKind.Utc)).ToTimestampMS(), - Index = 0, - PrimaryIndex = 0, - NextConsensus = Contract.GetBFTAddress(settings.StandbyValidators), - Witness = new Witness - { - InvocationScript = Array.Empty(), - VerificationScript = new[] { (byte)OpCode.PUSH1 } - }, - }, - Transactions = Array.Empty() - }; + this.GenesisBlock = CreateGenesisBlock(settings); this.storage_engine = storageEngine; this.store = LoadStore(storagePath); this.MemPool = new MemoryPool(this); @@ -86,6 +69,25 @@ public NeoSystem(ProtocolSettings settings, string storageEngine = null, string Blockchain.Ask(new FillMemoryPool { Transactions = Enumerable.Empty() }).Wait(); } + public static Block CreateGenesisBlock(ProtocolSettings settings) => new Block + { + Header = new Header + { + PrevHash = UInt256.Zero, + MerkleRoot = UInt256.Zero, + Timestamp = (new DateTime(2016, 7, 15, 15, 8, 21, DateTimeKind.Utc)).ToTimestampMS(), + Index = 0, + PrimaryIndex = 0, + NextConsensus = Contract.GetBFTAddress(settings.StandbyValidators), + Witness = new Witness + { + InvocationScript = Array.Empty(), + VerificationScript = new[] { (byte)OpCode.PUSH1 } + }, + }, + Transactions = Array.Empty() + }; + private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { Utility.Log("UnhandledException", LogLevel.Fatal, e.ExceptionObject); From 3a89b8d534937c704b8c82113163bd3a4214abfd Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 10 Mar 2021 03:00:40 +0800 Subject: [PATCH 53/63] Turn InteropDescriptor into record (#2382) --- src/neo/SmartContract/ApplicationEngine.cs | 26 +++++++++++------ src/neo/SmartContract/InteropDescriptor.cs | 33 +++++++++++++--------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index f3aa9741f7..2ab06af570 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -304,15 +304,19 @@ protected void ValidateCallFlags(CallFlags requiredCallFlags) protected override void OnSysCall(uint method) { - InteropDescriptor descriptor = services[method]; + OnSysCall(services[method]); + } + + protected virtual void OnSysCall(InteropDescriptor descriptor) + { ValidateCallFlags(descriptor.RequiredCallFlags); AddGas(descriptor.FixedPrice * exec_fee_factor); - List parameters = descriptor.Parameters.Count > 0 - ? new List() - : null; - foreach (var pd in descriptor.Parameters) - parameters.Add(Convert(Pop(), pd)); - object returnValue = descriptor.Handler.Invoke(this, parameters?.ToArray()); + + object[] parameters = new object[descriptor.Parameters.Count]; + for (int i = 0; i < parameters.Length; i++) + parameters[i] = Convert(Pop(), descriptor.Parameters[i]); + + object returnValue = descriptor.Handler.Invoke(this, parameters); if (descriptor.Handler.ReturnType != typeof(void)) Push(Convert(returnValue)); } @@ -351,7 +355,13 @@ private static InteropDescriptor Register(string name, string handler, long fixe { MethodInfo method = typeof(ApplicationEngine).GetMethod(handler, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) ?? typeof(ApplicationEngine).GetProperty(handler, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetMethod; - InteropDescriptor descriptor = new InteropDescriptor(name, method, fixedPrice, requiredCallFlags); + InteropDescriptor descriptor = new() + { + Name = name, + Handler = method, + FixedPrice = fixedPrice, + RequiredCallFlags = requiredCallFlags + }; services ??= new Dictionary(); services.Add(descriptor.Hash, descriptor); return descriptor; diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index 7b3a20d1d4..d8b2798d1c 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -7,25 +7,30 @@ namespace Neo.SmartContract { - public class InteropDescriptor + public record InteropDescriptor { - public string Name { get; } - public uint Hash { get; } - public MethodInfo Handler { get; } - public IReadOnlyList Parameters { get; } - public long FixedPrice { get; } - public CallFlags RequiredCallFlags { get; } + public string Name { get; init; } - internal InteropDescriptor(string name, MethodInfo handler, long fixedPrice, CallFlags requiredCallFlags) + private uint _hash; + public uint Hash { - this.Name = name; - this.Hash = BinaryPrimitives.ReadUInt32LittleEndian(Encoding.ASCII.GetBytes(name).Sha256()); - this.Handler = handler; - this.Parameters = handler.GetParameters().Select(p => new InteropParameterDescriptor(p)).ToList().AsReadOnly(); - this.FixedPrice = fixedPrice; - this.RequiredCallFlags = requiredCallFlags; + get + { + if (_hash == 0) + _hash = BinaryPrimitives.ReadUInt32LittleEndian(Encoding.ASCII.GetBytes(Name).Sha256()); + return _hash; + } } + public MethodInfo Handler { get; init; } + + private IReadOnlyList _parameters; + public IReadOnlyList Parameters => _parameters ??= Handler.GetParameters().Select(p => new InteropParameterDescriptor(p)).ToList().AsReadOnly(); + + public long FixedPrice { get; init; } + + public CallFlags RequiredCallFlags { get; init; } + public static implicit operator uint(InteropDescriptor descriptor) { return descriptor.Hash; From 4454e542f8de315ffe34605290828052339193bf Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 10 Mar 2021 10:22:05 +0100 Subject: [PATCH 54/63] Redefine prices (#2384) --- src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index bb79c74bc4..1d1c1c07c0 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -173,8 +173,8 @@ partial class ApplicationEngine [OpCode.MIN] = 1 << 3, [OpCode.MAX] = 1 << 3, [OpCode.WITHIN] = 1 << 3, - [OpCode.PACK] = 1 << 9, - [OpCode.UNPACK] = 1 << 9, + [OpCode.PACK] = 1 << 11, + [OpCode.UNPACK] = 1 << 11, [OpCode.NEWARRAY0] = 1 << 4, [OpCode.NEWARRAY] = 1 << 9, [OpCode.NEWARRAY_T] = 1 << 9, @@ -194,7 +194,7 @@ partial class ApplicationEngine [OpCode.POPITEM] = 1 << 4, [OpCode.ISNULL] = 1 << 1, [OpCode.ISTYPE] = 1 << 1, - [OpCode.CONVERT] = 1 << 11, + [OpCode.CONVERT] = 1 << 13, }; } } From c6f4c0011c86df68993b7a6b469b26109b17a04a Mon Sep 17 00:00:00 2001 From: Shargon Date: Fri, 12 Mar 2021 09:26:25 +0100 Subject: [PATCH 55/63] Change get validator prices (#2392) * change prices * Revert change * Revert GetNextBlockValidators price * fix --- src/neo/SmartContract/Native/NeoToken.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index c4ff13918d..197c58ea1e 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -299,7 +299,7 @@ private async ContractTask Vote(ApplicationEngine engine, UInt160 account, )).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes)).ToArray(); } - [ContractMethod(CpuFee = 1 << 22, RequiredCallFlags = CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.ReadStates)] public ECPoint[] GetCommittee(DataCache snapshot) { return GetCommitteeFromCache(snapshot).Select(p => p.PublicKey).OrderBy(p => p).ToArray(); @@ -331,7 +331,7 @@ public ECPoint[] ComputeNextBlockValidators(DataCache snapshot, ProtocolSettings return candidates.OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Take(settings.CommitteeMembersCount); } - [ContractMethod(CpuFee = 1 << 22, RequiredCallFlags = CallFlags.ReadStates)] + [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.ReadStates)] private ECPoint[] GetNextBlockValidators(ApplicationEngine engine) { return GetNextBlockValidators(engine.Snapshot, engine.ProtocolSettings.ValidatorsCount); From 8365f710a7936769902186758c45082f22446c5c Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Fri, 12 Mar 2021 11:38:47 +0300 Subject: [PATCH 56/63] SmartContract: rename hex in ContractParametersContext (#2394) * SmartContract: rename hex in ContractParametersContext It's not really hex since #1199 and even before that it could've been named a bit differently. `item` is not an option because we already have `items` there, but we're signing things so probably it's OK to call this signed thing a `document`. * SmartContract: rename 'document' field to 'data' Seems to be better for this, thanks @shargon. Co-authored-by: Erik Zhang --- src/neo/SmartContract/ContractParametersContext.cs | 4 ++-- .../SmartContract/UT_ContractParameterContext.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index 66cfac2e8d..ee32c8f5d2 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -170,7 +170,7 @@ public static ContractParametersContext FromJson(JObject json, DataCache snapsho if (!typeof(IVerifiable).IsAssignableFrom(type)) throw new FormatException(); var verifiable = (IVerifiable)Activator.CreateInstance(type); - using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(json["hex"].AsString()), false)) + using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(json["data"].AsString()), false)) using (BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8)) { verifiable.DeserializeUnsigned(reader); @@ -246,7 +246,7 @@ public JObject ToJson() { Verifiable.SerializeUnsigned(writer); writer.Flush(); - json["hex"] = Convert.ToBase64String(ms.ToArray()); + json["data"] = Convert.ToBase64String(ms.ToArray()); } json["items"] = new JObject(); foreach (var item in ContextItems) diff --git a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs index c4a8243d2c..c47a38befe 100644 --- a/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs +++ b/tests/neo.UnitTests/SmartContract/UT_ContractParameterContext.cs @@ -46,14 +46,14 @@ public void TestToString() var context = new ContractParametersContext(snapshot, tx); context.Add(contract, 0, new byte[] { 0x01 }); string str = context.ToString(); - str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""hex"":""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI="",""items"":{}}"); + str.Should().Be(@"{""type"":""Neo.Network.P2P.Payloads.Transaction"",""data"":""AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI="",""items"":{}}"); } [TestMethod] public void TestParse() { var snapshot = TestBlockchain.GetTestSnapshot(); - var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"hex\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI=\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}],\"signatures\":{\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\":\"AQ==\"}}}}", snapshot); + var ret = ContractParametersContext.Parse("{\"type\":\"Neo.Network.P2P.Payloads.Transaction\",\"data\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFmUJDLobcPtqo9vZKIdjXsd8fVGwEAARI=\",\"items\":{\"0xbecaad15c0ea585211faf99738a4354014f177f2\":{\"script\":\"IQJv8DuUkkHOHa3UNRnmlg4KhbQaaaBcMoEDqivOFZTKFmh0dHaq\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"AQ==\"}],\"signatures\":{\"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c\":\"AQ==\"}}}}", snapshot); ret.ScriptHashes[0].ToString().Should().Be("0x1bd5c777ec35768892bd3daab60fb7a1cb905066"); ((Transaction)ret.Verifiable).Script.ToHexString().Should().Be(new byte[] { 18 }.ToHexString()); } @@ -62,7 +62,7 @@ public void TestParse() public void TestFromJson() { var snapshot = TestBlockchain.GetTestSnapshot(); - Action action = () => ContractParametersContext.Parse("{\"type\":\"wrongType\",\"hex\":\"00000000007c97764845172d827d3c863743293931a691271a0000000000000000000000000000000000000000000100\",\"items\":{\"0x1bd5c777ec35768892bd3daab60fb7a1cb905066\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1650680a906ad4\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}", snapshot); + Action action = () => ContractParametersContext.Parse("{\"type\":\"wrongType\",\"data\":\"00000000007c97764845172d827d3c863743293931a691271a0000000000000000000000000000000000000000000100\",\"items\":{\"0x1bd5c777ec35768892bd3daab60fb7a1cb905066\":{\"script\":\"21026ff03b949241ce1dadd43519e6960e0a85b41a69a05c328103aa2bce1594ca1650680a906ad4\",\"parameters\":[{\"type\":\"Signature\",\"value\":\"01\"}]}}}", snapshot); action.Should().Throw(); } From 3626b59b5b4288a986ee63d9f8f0dded37581238 Mon Sep 17 00:00:00 2001 From: Harry Pierson Date: Sat, 13 Mar 2021 09:15:55 -0800 Subject: [PATCH 57/63] Make ContractManagement.Check available for blockchain toolkit (#2396) * Make ContractManagement.Check available for blockchain toolkit * Move to Helper Co-authored-by: Erik Zhang --- src/neo/SmartContract/Helper.cs | 14 ++++++++++++++ src/neo/SmartContract/Native/ContractManagement.cs | 14 ++------------ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index 13c6e901ae..bbcd88fa19 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -9,6 +9,7 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; +using System.Linq; namespace Neo.SmartContract { @@ -33,6 +34,19 @@ public static long MultiSignatureContractCost(int m, int n) return fee; } + public static void Check(byte[] script, ContractAbi abi) + { + Check(new Script(script, true), abi); + } + + public static void Check(this Script script, ContractAbi abi) + { + foreach (ContractMethodDescriptor method in abi.Methods) + script.GetInstruction(method.Offset); + abi.GetMethod(string.Empty, 0); // Trigger the construction of ContractAbi.methodDictionary to check the uniqueness of the method names. + _ = abi.Events.ToDictionary(p => p.Name); // Check the uniqueness of the event names. + } + public static UInt160 GetContractHash(UInt160 sender, uint nefCheckSum, string name) { using var sb = new ScriptBuilder(); diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 1d4dea1505..817c8c4961 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -4,7 +4,6 @@ using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; -using Neo.VM; using Neo.VM.Types; using System; using System.Collections.Generic; @@ -64,15 +63,6 @@ internal ContractManagement() Manifest.Abi.Events = events.ToArray(); } - private static void Check(byte[] script, ContractAbi abi) - { - Script s = new Script(script, true); - foreach (ContractMethodDescriptor method in abi.Methods) - s.GetInstruction(method.Offset); - abi.GetMethod(string.Empty, 0); // Trigger the construction of ContractAbi.methodDictionary to check the uniqueness of the method names. - _ = abi.Events.ToDictionary(p => p.Name); // Check the uniqueness of the event names. - } - private int GetNextAvailableId(DataCache snapshot) { StorageItem item = snapshot.GetAndChange(CreateStorageKey(Prefix_NextAvailableId)); @@ -163,7 +153,7 @@ private async ContractTask Deploy(ApplicationEngine engine, byte[ NefFile nef = nefFile.AsSerializable(); ContractManifest parsedManifest = ContractManifest.Parse(manifest); - Check(nef.Script, parsedManifest.Abi); + Helper.Check(nef.Script, parsedManifest.Abi); UInt160 hash = Helper.GetContractHash(tx.Sender, nef.CheckSum, parsedManifest.Name); StorageKey key = CreateStorageKey(Prefix_Contract).Add(hash); if (engine.Snapshot.Contains(key)) @@ -221,7 +211,7 @@ private ContractTask Update(ApplicationEngine engine, byte[] nefFile, byte[] man throw new InvalidOperationException($"Invalid Manifest Hash: {contract.Hash}"); contract.Manifest = manifest_new; } - Check(contract.Nef.Script, contract.Manifest.Abi); + Helper.Check(contract.Nef.Script, contract.Manifest.Abi); contract.UpdateCounter++; // Increase update counter return OnDeploy(engine, contract, data, true); } From df0e755f5fb542ae0fbbcba0394ddf3cb7740dc1 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Mon, 15 Mar 2021 09:40:56 +0800 Subject: [PATCH 58/63] Fix Blockchain initialization (#2397) --- src/neo/Ledger/Blockchain.cs | 13 +++++++++++-- src/neo/NeoSystem.cs | 3 +-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index a1679f70ad..d61cb8b10e 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -27,6 +27,7 @@ public class FillMemoryPool { public IEnumerable Transactions; } public class FillCompleted { } public class Reverify { public IInventory[] Inventories; } public class RelayResult { public IInventory Inventory; public VerifyResult Result; } + internal class Initialize { } private class UnverifiedBlocksList { public LinkedList Blocks = new LinkedList(); public HashSet Nodes = new HashSet(); } private readonly static Script onPersistScript, postPersistScript; @@ -55,8 +56,6 @@ public Blockchain(NeoSystem system) { this.system = system; this.txrouter = Context.ActorOf(TransactionRouter.Props(system)); - if (!NativeContract.Ledger.Initialized(system.StoreView)) - Persist(system.GenesisBlock); } private bool ContainsTransaction(UInt256 hash) @@ -132,6 +131,13 @@ private void OnFillMemoryPool(IEnumerable transactions) Sender.Tell(new FillCompleted()); } + private void OnInitialize() + { + if (!NativeContract.Ledger.Initialized(system.StoreView)) + Persist(system.GenesisBlock); + Sender.Tell(new object()); + } + private void OnInventory(IInventory inventory, bool relay = true) { VerifyResult result = inventory switch @@ -257,6 +263,9 @@ protected override void OnReceive(object message) { switch (message) { + case Initialize: + OnInitialize(); + break; case Import import: OnImport(import.Blocks, import.Verify); break; diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 7ac736d7a0..90946bee45 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -12,7 +12,6 @@ using System.Collections.Immutable; using System.Linq; using System.Threading; -using static Neo.Ledger.Blockchain; namespace Neo { @@ -66,7 +65,7 @@ public NeoSystem(ProtocolSettings settings, string storageEngine = null, string this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); foreach (var plugin in Plugin.Plugins) plugin.OnSystemLoaded(this); - Blockchain.Ask(new FillMemoryPool { Transactions = Enumerable.Empty() }).Wait(); + Blockchain.Ask(new Blockchain.Initialize()).Wait(); } public static Block CreateGenesisBlock(ProtocolSettings settings) => new Block From 3dac7b20e3fb8a31ac45d720436765ec924acd8d Mon Sep 17 00:00:00 2001 From: Qiao Jin <43407364+Qiao-Jin@users.noreply.github.com> Date: Mon, 15 Mar 2021 16:55:43 +0800 Subject: [PATCH 59/63] optimize transaction route (#2398) * optimize transaction route * logic fix * logic fix * restore logic when Blockchain receives transaction --- src/neo/Ledger/Blockchain.cs | 14 +++---------- src/neo/NeoSystem.cs | 9 +++++++++ .../Network/P2P/RemoteNode.ProtocolHandler.cs | 20 ++++++++++++++----- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index d61cb8b10e..8a6c7e0982 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -33,7 +33,6 @@ internal class Initialize { } private readonly static Script onPersistScript, postPersistScript; private const int MaxTxToReverifyPerIdle = 10; private readonly NeoSystem system; - private readonly IActorRef txrouter; private readonly Dictionary block_cache = new Dictionary(); private readonly Dictionary block_cache_unverified = new Dictionary(); private ImmutableHashSet extensibleWitnessWhiteList; @@ -55,13 +54,6 @@ static Blockchain() public Blockchain(NeoSystem system) { this.system = system; - this.txrouter = Context.ActorOf(TransactionRouter.Props(system)); - } - - private bool ContainsTransaction(UInt256 hash) - { - if (system.MemPool.ContainsKey(hash)) return true; - return NativeContract.Ledger.ContainsTransaction(system.StoreView, hash); } private void OnImport(IEnumerable blocks, bool verify) @@ -247,7 +239,7 @@ private VerifyResult OnNewExtensiblePayload(ExtensiblePayload payload) private VerifyResult OnNewTransaction(Transaction transaction) { - if (ContainsTransaction(transaction.Hash)) return VerifyResult.AlreadyExists; + if (system.ContainsTransaction(transaction.Hash)) return VerifyResult.AlreadyExists; return system.MemPool.TryAdd(transaction, system.StoreView); } @@ -300,10 +292,10 @@ protected override void OnReceive(object message) private void OnTransaction(Transaction tx) { - if (ContainsTransaction(tx.Hash)) + if (system.ContainsTransaction(tx.Hash)) SendRelayResult(tx, VerifyResult.AlreadyExists); else - txrouter.Forward(new TransactionRouter.Preverify(tx, true)); + system.TxRouter.Forward(new TransactionRouter.Preverify(tx, true)); } private void Persist(Block block) diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index 90946bee45..fabb540ca1 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -6,6 +6,7 @@ using Neo.Persistence; using Neo.Plugins; using Neo.SmartContract; +using Neo.SmartContract.Native; using Neo.VM; using System; using System.Collections.Generic; @@ -28,6 +29,7 @@ public class NeoSystem : IDisposable public IActorRef Blockchain { get; } public IActorRef LocalNode { get; } public IActorRef TaskManager { get; } + public IActorRef TxRouter; /// /// A readonly view of the store. /// @@ -63,6 +65,7 @@ public NeoSystem(ProtocolSettings settings, string storageEngine = null, string this.Blockchain = ActorSystem.ActorOf(Ledger.Blockchain.Props(this)); this.LocalNode = ActorSystem.ActorOf(Network.P2P.LocalNode.Props(this)); this.TaskManager = ActorSystem.ActorOf(Network.P2P.TaskManager.Props(this)); + this.TxRouter = ActorSystem.ActorOf(TransactionRouter.Props(this)); foreach (var plugin in Plugin.Plugins) plugin.OnSystemLoaded(this); Blockchain.Ask(new Blockchain.Initialize()).Wait(); @@ -166,5 +169,11 @@ public SnapshotCache GetSnapshot() { return new SnapshotCache(store.GetSnapshot()); } + + public bool ContainsTransaction(UInt256 hash) + { + if (MemPool.ContainsKey(hash)) return true; + return NativeContract.Ledger.ContainsTransaction(StoreView, hash); + } } } diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index de08038d16..727cbc0db1 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -1,6 +1,7 @@ using Akka.Actor; using Neo.Cryptography; using Neo.IO.Caching; +using Neo.Ledger; using Neo.Network.P2P.Capabilities; using Neo.Network.P2P.Payloads; using Neo.Persistence; @@ -294,15 +295,24 @@ private void OnHeadersMessageReceived(HeadersPayload payload) private void OnInventoryReceived(IInventory inventory) { + knownHashes.Add(inventory.Hash); pendingKnownHashes.Remove(inventory.Hash); - if (inventory is Block block) + switch (inventory) { - UpdateLastBlockIndex(block.Index); - if (block.Index > NativeContract.Ledger.CurrentIndex(system.StoreView) + InvPayload.MaxHashesCount) return; + case Transaction transaction: + if (!system.ContainsTransaction(transaction.Hash)) + system.TxRouter.Tell(new TransactionRouter.Preverify(transaction, true)); + break; + case Block block: + UpdateLastBlockIndex(block.Index); + if (block.Index > NativeContract.Ledger.CurrentIndex(system.StoreView) + InvPayload.MaxHashesCount) return; + system.Blockchain.Tell(inventory); + break; + default: + system.Blockchain.Tell(inventory); + break; } - knownHashes.Add(inventory.Hash); system.TaskManager.Tell(inventory); - system.Blockchain.Tell(inventory); } private void OnInvMessageReceived(InvPayload payload) From d76343770aa91b59d0e70d372d15d622bdac2e94 Mon Sep 17 00:00:00 2001 From: Shargon Date: Tue, 16 Mar 2021 11:17:18 +0100 Subject: [PATCH 60/63] Remove IsStandardContract (#2400) --- .../ApplicationEngine.Contract.cs | 28 ------------------- .../SmartContract/UT_InteropService.NEO.cs | 23 --------------- 2 files changed, 51 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 0b62df4dcb..79a6aff183 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -1,5 +1,4 @@ using Neo.Cryptography.ECC; -using Neo.Network.P2P.Payloads; using Neo.SmartContract.Manifest; using Neo.SmartContract.Native; using Neo.VM.Types; @@ -12,7 +11,6 @@ partial class ApplicationEngine { public static readonly InteropDescriptor System_Contract_Call = Register("System.Contract.Call", nameof(CallContract), 1 << 15, CallFlags.ReadStates | CallFlags.AllowCall); public static readonly InteropDescriptor System_Contract_CallNative = Register("System.Contract.CallNative", nameof(CallNativeContract), 0, CallFlags.None); - public static readonly InteropDescriptor System_Contract_IsStandard = Register("System.Contract.IsStandard", nameof(IsStandardContract), 1 << 10, CallFlags.ReadStates); public static readonly InteropDescriptor System_Contract_GetCallFlags = Register("System.Contract.GetCallFlags", nameof(GetCallFlags), 1 << 10, CallFlags.None); /// /// Calculate corresponding account scripthash for given public key @@ -52,32 +50,6 @@ protected internal void CallNativeContract(byte version) contract.Invoke(this, version); } - protected internal bool IsStandardContract(UInt160 hash) - { - ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, hash); - - // It's a stored contract - - if (contract != null) return contract.Script.IsStandardContract(); - - // Try to find it in the transaction - - if (ScriptContainer is Transaction tx) - { - foreach (var witness in tx.Witnesses) - { - if (witness.ScriptHash == hash) - { - return witness.VerificationScript.IsStandardContract(); - } - } - } - - // It's not possible to determine if it's standard - - return false; - } - protected internal CallFlags GetCallFlags() { var state = CurrentContext.GetState(); diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 59f7acdf4c..98ade07841 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -104,29 +104,6 @@ public void TestCrypto_CheckMultiSig() Assert.ThrowsException(() => engine.CheckMultisig(pubkeys, signatures)); } - [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.IsStandardContract(new UInt160(hash)).Should().BeFalse(); - - var snapshot = TestBlockchain.GetTestSnapshot(); - var state = TestUtils.GetContract(); - snapshot.AddContract(state.Hash, state); - engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(new byte[] { 0x01 }); - engine.IsStandardContract(state.Hash).Should().BeFalse(); - - state.Nef.Script = Contract.CreateSignatureRedeemScript(ProtocolSettings.Default.StandbyValidators[0]); - engine = ApplicationEngine.Create(TriggerType.Application, null, snapshot); - engine.LoadScript(new byte[] { 0x01 }); - engine.IsStandardContract(state.Hash).Should().BeTrue(); - } - [TestMethod] public void TestContract_Create() { From 39e82ac334f6507bc8342b670a019167d6f36c0d Mon Sep 17 00:00:00 2001 From: Shargon Date: Wed, 17 Mar 2021 06:28:30 +0100 Subject: [PATCH 61/63] UT for SignatureCost (#2389) --- .../neo.UnitTests/SmartContract/UT_Helper.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/neo.UnitTests/SmartContract/UT_Helper.cs b/tests/neo.UnitTests/SmartContract/UT_Helper.cs index 6f292c06e7..03f832efa2 100644 --- a/tests/neo.UnitTests/SmartContract/UT_Helper.cs +++ b/tests/neo.UnitTests/SmartContract/UT_Helper.cs @@ -1,5 +1,10 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography.ECC; +using Neo.Network.P2P.Payloads; using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; using System; namespace Neo.UnitTests.SmartContract @@ -7,6 +12,16 @@ namespace Neo.UnitTests.SmartContract [TestClass] public class UT_Helper { + private KeyPair _key; + + [TestInitialize] + public void Init() + { + var pk = new byte[32]; + new Random().NextBytes(pk); + _key = new KeyPair(pk); + } + [TestMethod] public void TestGetContractHash() { @@ -43,5 +58,46 @@ public void TestIsMultiSigContract() }; Assert.IsFalse(case2.IsMultiSigContract()); } + + [TestMethod] + public void TestSignatureContractCost() + { + var contract = Contract.CreateSignatureContract(_key.PublicKey); + + var tx = TestUtils.CreateRandomHashTransaction(); + tx.Signers[0].Account = contract.ScriptHash; + + using ScriptBuilder invocationScript = new(); + invocationScript.EmitPush(Neo.Wallets.Helper.Sign(tx, _key, ProtocolSettings.Default.Magic)); + tx.Witnesses = new Witness[] { new Witness() { InvocationScript = invocationScript.ToArray(), VerificationScript = contract.Script } }; + + using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, null, null, ProtocolSettings.Default); + engine.LoadScript(contract.Script); + engine.LoadScript(new Script(invocationScript.ToArray(), true), configureState: p => p.CallFlags = CallFlags.None); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + + Assert.AreEqual(Neo.SmartContract.Helper.SignatureContractCost() * PolicyContract.DefaultExecFeeFactor, engine.GasConsumed); + } + + [TestMethod] + public void TestMultiSignatureContractCost() + { + var contract = Contract.CreateMultiSigContract(1, new ECPoint[] { _key.PublicKey }); + + var tx = TestUtils.CreateRandomHashTransaction(); + tx.Signers[0].Account = contract.ScriptHash; + + using ScriptBuilder invocationScript = new(); + invocationScript.EmitPush(Neo.Wallets.Helper.Sign(tx, _key, ProtocolSettings.Default.Magic)); + + using var engine = ApplicationEngine.Create(TriggerType.Verification, tx, null, null, ProtocolSettings.Default); + engine.LoadScript(contract.Script); + engine.LoadScript(new Script(invocationScript.ToArray(), true), configureState: p => p.CallFlags = CallFlags.None); + Assert.AreEqual(VMState.HALT, engine.Execute()); + Assert.IsTrue(engine.ResultStack.Pop().GetBoolean()); + + Assert.AreEqual(Neo.SmartContract.Helper.MultiSignatureContractCost(1, 1) * PolicyContract.DefaultExecFeeFactor, engine.GasConsumed); + } } } From d1f015ccca6019fd9fdb7ff920673ef013a8499d Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 17 Mar 2021 22:47:00 +0800 Subject: [PATCH 62/63] Add XML comments (#2390) --- .editorconfig | 2 + src/neo/BigDecimal.cs | 67 +++-- src/neo/Cryptography/Base58.cs | 28 ++- src/neo/Cryptography/BloomFilter.cs | 32 +++ src/neo/Cryptography/Crypto.cs | 43 +++- src/neo/Cryptography/ECC/ECCurve.cs | 21 +- src/neo/Cryptography/ECC/ECFieldElement.cs | 6 +- src/neo/Cryptography/ECC/ECPoint.cs | 87 +++++-- src/neo/Cryptography/Helper.cs | 81 ++++-- src/neo/Cryptography/MerkleTree.cs | 36 ++- src/neo/Cryptography/Murmur3.cs | 7 + src/neo/Cryptography/RIPEMD160Managed.cs | 47 ++-- src/neo/Helper.cs | 67 ++++- src/neo/IO/Actors/PriorityMessageQueue.cs | 4 +- src/neo/IO/ByteArrayComparer.cs | 4 +- src/neo/IO/ByteArrayEqualityComparer.cs | 2 +- src/neo/IO/Caching/Cache.cs | 4 +- src/neo/IO/Caching/HashSetCache.cs | 4 +- src/neo/IO/Caching/OrderedDictionary.cs | 2 +- src/neo/IO/Caching/ReflectionCache.cs | 2 +- src/neo/IO/Helper.cs | 231 ++++++++++++++--- src/neo/IO/ISerializable.cs | 15 ++ src/neo/IO/Json/JArray.cs | 13 +- src/neo/IO/Json/JBoolean.cs | 16 +- src/neo/IO/Json/JNumber.cs | 23 +- src/neo/IO/Json/JObject.cs | 106 +++++++- src/neo/IO/Json/JString.cs | 18 +- .../Ledger/Blockchain.ApplicationExecuted.cs | 41 ++- src/neo/Ledger/Blockchain.cs | 152 ++++++++--- src/neo/Ledger/HeaderCache.cs | 22 +- src/neo/Ledger/MemoryPool.cs | 66 +++-- src/neo/Ledger/PoolItem.cs | 2 +- .../Ledger/TransactionVerificationContext.cs | 21 +- src/neo/Ledger/VerifyResult.cs | 40 +++ src/neo/LogLevel.cs | 22 ++ src/neo/NeoSystem.cs | 100 +++++++- .../P2P/Capabilities/FullNodeCapability.cs | 10 +- .../P2P/Capabilities/NodeCapability.cs | 39 +-- .../P2P/Capabilities/NodeCapabilityType.cs | 23 +- .../P2P/Capabilities/ServerCapability.cs | 12 +- src/neo/Network/P2P/ChannelsConfig.cs | 13 +- src/neo/Network/P2P/Connection.cs | 43 +++- src/neo/Network/P2P/Helper.cs | 22 +- src/neo/Network/P2P/LocalNode.cs | 78 ++++-- src/neo/Network/P2P/Message.cs | 26 +- src/neo/Network/P2P/MessageCommand.cs | 116 ++++++++- src/neo/Network/P2P/MessageFlags.cs | 10 + src/neo/Network/P2P/Payloads/AddrPayload.cs | 14 ++ src/neo/Network/P2P/Payloads/Block.cs | 47 ++++ .../Network/P2P/Payloads/ExtensiblePayload.cs | 26 ++ .../Network/P2P/Payloads/FilterAddPayload.cs | 7 + .../Network/P2P/Payloads/FilterLoadPayload.cs | 19 ++ .../P2P/Payloads/GetBlockByIndexPayload.cs | 16 ++ .../Network/P2P/Payloads/GetBlocksPayload.cs | 16 ++ src/neo/Network/P2P/Payloads/Header.cs | 37 ++- .../Network/P2P/Payloads/HeadersPayload.cs | 14 ++ .../P2P/Payloads/HighPriorityAttribute.cs | 3 + src/neo/Network/P2P/Payloads/IInventory.cs | 6 + src/neo/Network/P2P/Payloads/IVerifiable.cs | 22 ++ src/neo/Network/P2P/Payloads/InvPayload.cs | 25 ++ src/neo/Network/P2P/Payloads/InventoryType.cs | 14 ++ .../P2P/Payloads/MerkleBlockPayload.cs | 26 +- .../P2P/Payloads/NetworkAddressWithTime.cs | 27 +- .../Network/P2P/Payloads/OracleResponse.cs | 22 +- .../P2P/Payloads/OracleResponseCode.cs | 36 +++ src/neo/Network/P2P/Payloads/PingPayload.cs | 28 ++- src/neo/Network/P2P/Payloads/Signer.cs | 26 +- src/neo/Network/P2P/Payloads/Transaction.cs | 89 ++++++- .../P2P/Payloads/TransactionAttribute.cs | 36 ++- .../P2P/Payloads/TransactionAttributeType.cs | 10 + .../Network/P2P/Payloads/VersionPayload.cs | 37 +++ src/neo/Network/P2P/Payloads/Witness.cs | 31 ++- src/neo/Network/P2P/Payloads/WitnessScope.cs | 19 +- src/neo/Network/P2P/Peer.cs | 106 +++++++- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 12 +- src/neo/Network/P2P/RemoteNode.cs | 123 +++++---- src/neo/Network/P2P/TaskManager.cs | 39 ++- src/neo/Network/UPnP.cs | 133 +++++----- src/neo/Persistence/DataCache.cs | 175 +++++++++---- src/neo/Persistence/IReadOnlyStore.cs | 18 ++ src/neo/Persistence/ISnapshot.cs | 14 ++ src/neo/Persistence/IStore.cs | 21 ++ src/neo/Persistence/MemoryStore.cs | 5 +- src/neo/Persistence/SeekDirection.cs | 10 + src/neo/Persistence/SnapshotCache.cs | 7 + src/neo/Persistence/TrackState.cs | 18 ++ src/neo/Plugins/IApplicationEngineProvider.cs | 13 + src/neo/Plugins/ILogPlugin.cs | 9 + .../Plugins/IMemoryPoolTxObserverPlugin.cs | 15 ++ src/neo/Plugins/IP2PPlugin.cs | 9 + src/neo/Plugins/IPersistencePlugin.cs | 23 ++ src/neo/Plugins/IStorageProvider.cs | 8 + src/neo/Plugins/IWalletProvider.cs | 10 + src/neo/Plugins/MemoryPoolTxRemovalReason.cs | 6 +- src/neo/Plugins/Plugin.cs | 78 +++++- src/neo/ProtocolSettings.cs | 67 +++++ .../ApplicationEngine.Contract.cs | 77 +++++- .../SmartContract/ApplicationEngine.Crypto.cs | 26 ++ .../ApplicationEngine.Iterator.cs | 38 ++- .../ApplicationEngine.OpCodePrices.cs | 3 + .../ApplicationEngine.Runtime.cs | 143 ++++++++++- .../ApplicationEngine.Storage.cs | 99 +++++++- src/neo/SmartContract/ApplicationEngine.cs | 144 ++++++++++- src/neo/SmartContract/BinarySerializer.cs | 65 +++-- src/neo/SmartContract/CallFlags.cs | 32 +++ src/neo/SmartContract/Contract.cs | 78 ++++-- src/neo/SmartContract/ContractParameter.cs | 86 ++++--- .../SmartContract/ContractParameterType.cs | 50 ++++ .../ContractParametersContext.cs | 118 +++++++-- src/neo/SmartContract/ContractState.cs | 37 ++- src/neo/SmartContract/ContractTask.cs | 4 +- .../ContractTaskMethodBuilder.cs | 4 +- src/neo/SmartContract/DeployedContract.cs | 7 + .../SmartContract/ExecutionContextState.cs | 13 +- src/neo/SmartContract/FindOptions.cs | 32 +++ src/neo/SmartContract/Helper.cs | 100 +++++++- src/neo/SmartContract/IInteroperable.cs | 13 + src/neo/SmartContract/InteropDescriptor.cs | 21 ++ .../InteropParameterDescriptor.cs | 28 ++- src/neo/SmartContract/Iterators/IIterator.cs | 12 + src/neo/SmartContract/JsonSerializer.cs | 28 ++- src/neo/SmartContract/KeyBuilder.cs | 40 ++- src/neo/SmartContract/LogEventArgs.cs | 20 ++ src/neo/SmartContract/Manifest/ContractAbi.cs | 25 +- .../Manifest/ContractEventDescriptor.cs | 19 +- .../SmartContract/Manifest/ContractGroup.cs | 27 +- .../Manifest/ContractManifest.cs | 47 ++-- .../Manifest/ContractMethodDescriptor.cs | 25 +- .../Manifest/ContractParameterDefinition.cs | 22 +- .../Manifest/ContractPermission.cs | 38 +-- .../Manifest/ContractPermissionDescriptor.cs | 44 ++++ .../Manifest/WildCardContainer.cs | 39 +-- src/neo/SmartContract/MethodToken.cs | 26 ++ src/neo/SmartContract/Native/AccountState.cs | 6 + .../Native/ContractManagement.cs | 16 +- src/neo/SmartContract/Native/CryptoLib.cs | 23 +- src/neo/SmartContract/Native/FungibleToken.cs | 36 +++ src/neo/SmartContract/Native/GasToken.cs | 3 + .../SmartContract/Native/LedgerContract.cs | 73 ++++++ src/neo/SmartContract/Native/NFTState.cs | 18 ++ src/neo/SmartContract/Native/NameService.cs | 61 ++++- src/neo/SmartContract/Native/NamedCurve.cs | 9 +- .../SmartContract/Native/NativeContract.cs | 119 +++++++-- src/neo/SmartContract/Native/NeoToken.cs | 74 +++++- .../SmartContract/Native/NonfungibleToken.cs | 67 ++++- .../SmartContract/Native/OracleContract.cs | 25 ++ src/neo/SmartContract/Native/OracleRequest.cs | 30 +++ .../SmartContract/Native/PolicyContract.cs | 43 ++++ src/neo/SmartContract/Native/RecordType.cs | 21 ++ src/neo/SmartContract/Native/Role.cs | 14 ++ .../SmartContract/Native/RoleManagement.cs | 14 +- src/neo/SmartContract/Native/StdLib.cs | 35 +++ .../SmartContract/Native/TransactionState.cs | 10 + src/neo/SmartContract/Native/TrimmedBlock.cs | 17 ++ src/neo/SmartContract/NefFile.cs | 54 ++-- src/neo/SmartContract/NotifyEventArgs.cs | 25 ++ src/neo/SmartContract/StorageContext.cs | 10 + src/neo/SmartContract/StorageItem.cs | 47 ++++ src/neo/SmartContract/StorageKey.cs | 18 +- src/neo/SmartContract/TriggerType.cs | 32 ++- src/neo/TimeProvider.cs | 12 +- src/neo/UInt160.cs | 67 ++--- src/neo/UInt256.cs | 67 ++--- src/neo/Utility.cs | 12 + src/neo/VM/Helper.cs | 235 ++++++++++++------ src/neo/Wallets/AssetDescriptor.cs | 26 +- src/neo/Wallets/Helper.cs | 22 ++ src/neo/Wallets/KeyPair.cs | 33 ++- src/neo/Wallets/NEP6/NEP6Account.cs | 2 +- src/neo/Wallets/NEP6/NEP6Contract.cs | 4 +- src/neo/Wallets/NEP6/NEP6Wallet.cs | 78 ++++-- src/neo/Wallets/NEP6/ScryptParameters.cs | 38 ++- src/neo/Wallets/NEP6/WalletLocker.cs | 2 +- src/neo/Wallets/SQLite/UserWallet.cs | 213 ++++++++-------- .../Wallets/SQLite/VerificationContract.cs | 2 +- src/neo/Wallets/SQLite/WalletDataContext.cs | 2 +- src/neo/Wallets/TransferOutput.cs | 18 ++ src/neo/Wallets/Wallet.cs | 202 +++++++++++++-- src/neo/Wallets/WalletAccount.cs | 46 ++++ src/neo/neo.csproj | 1 + .../SmartContract/UT_InteropService.NEO.cs | 6 +- .../SmartContract/UT_InteropService.cs | 8 +- tests/neo.UnitTests/TestUtils.cs | 4 +- .../Wallets/NEP6/UT_NEP6Wallet.cs | 24 +- 184 files changed, 5862 insertions(+), 1190 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5acd074d26..175ec7b14e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -15,3 +15,5 @@ insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 end_of_line = lf + +dotnet_diagnostic.CS1591.severity = silent diff --git a/src/neo/BigDecimal.cs b/src/neo/BigDecimal.cs index 45e2ae770c..45e41ce124 100644 --- a/src/neo/BigDecimal.cs +++ b/src/neo/BigDecimal.cs @@ -3,20 +3,34 @@ namespace Neo { + /// + /// Represents a fixed-point number of arbitrary precision. + /// public struct BigDecimal { private readonly BigInteger value; private readonly byte decimals; + /// + /// The value of the number. + /// public BigInteger Value => value; + + /// + /// The number of decimal places for this number. + /// public byte Decimals => decimals; + + /// + /// The sign of the number. + /// public int Sign => value.Sign; /// - /// Create BigDecimal from BigInteger + /// Initializes a new instance of the struct. /// - /// Value - /// Decimals + /// The value of the number. + /// The number of decimal places for this number. public BigDecimal(BigInteger value, byte decimals) { this.value = value; @@ -24,29 +38,34 @@ public BigDecimal(BigInteger value, byte decimals) } /// - /// Create BigDecimal from decimal + /// Initializes a new instance of the struct with the value of . /// - /// Value + /// The value of the number. public unsafe BigDecimal(decimal value) { - ReadOnlySpan buffer = new ReadOnlySpan(&value, sizeof(decimal)); + ReadOnlySpan buffer = new(&value, sizeof(decimal)); this.decimals = buffer[14]; this.value = new BigInteger(decimal.Multiply((decimal)Math.Pow(10, decimals), value)); } /// - /// Create BigDecimal from decimal + /// Initializes a new instance of the struct with the value of . /// - /// Value - /// Decimals + /// The value of the number. + /// The number of decimal places for this number. public unsafe BigDecimal(decimal value, byte decimals) { - ReadOnlySpan buffer = new ReadOnlySpan(&value, sizeof(decimal)); + ReadOnlySpan buffer = new(&value, sizeof(decimal)); if (buffer[14] > decimals) throw new ArgumentException(null, nameof(value)); this.value = new BigInteger(decimal.Multiply((decimal)Math.Pow(10, decimals), value)); this.decimals = decimals; } + /// + /// Changes the decimals of the . + /// + /// The new decimals field. + /// The that has the new number of decimal places. public BigDecimal ChangeDecimals(byte decimals) { if (this.decimals == decimals) return this; @@ -60,11 +79,18 @@ public BigDecimal ChangeDecimals(byte decimals) BigInteger divisor = BigInteger.Pow(10, this.decimals - decimals); value = BigInteger.DivRem(this.value, divisor, out BigInteger remainder); if (remainder > BigInteger.Zero) - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(decimals)); } return new BigDecimal(value, decimals); } + /// + /// Parses a from the specified . + /// + /// A number represented by a . + /// The number of decimal places for this number. + /// The parsed . + /// is not in the correct format. public static BigDecimal Parse(string s, byte decimals) { if (!TryParse(s, decimals, out BigDecimal result)) @@ -72,6 +98,10 @@ public static BigDecimal Parse(string s, byte decimals) return result; } + /// + /// Gets a representing the number. + /// + /// The representing the number. public override string ToString() { BigInteger divisor = BigInteger.Pow(10, decimals); @@ -80,15 +110,22 @@ public override string ToString() return $"{result}.{remainder.ToString("d" + decimals)}".TrimEnd('0'); } + /// + /// Parses a from the specified . + /// + /// A number represented by a . + /// The number of decimal places for this number. + /// The parsed . + /// if a number is successfully parsed; otherwise, . public static bool TryParse(string s, byte decimals, out BigDecimal result) { int e = 0; int index = s.IndexOfAny(new[] { 'e', 'E' }); if (index >= 0) { - if (!sbyte.TryParse(s.Substring(index + 1), out sbyte e_temp)) + if (!sbyte.TryParse(s[(index + 1)..], out sbyte e_temp)) { - result = default(BigDecimal); + result = default; return false; } e = e_temp; @@ -104,14 +141,14 @@ public static bool TryParse(string s, byte decimals, out BigDecimal result) int ds = e + decimals; if (ds < 0) { - result = default(BigDecimal); + result = default; return false; } if (ds > 0) s += new string('0', ds); if (!BigInteger.TryParse(s, out BigInteger value)) { - result = default(BigDecimal); + result = default; return false; } result = new BigDecimal(value, decimals); diff --git a/src/neo/Cryptography/Base58.cs b/src/neo/Cryptography/Base58.cs index f571f9540d..7e05a7141e 100644 --- a/src/neo/Cryptography/Base58.cs +++ b/src/neo/Cryptography/Base58.cs @@ -6,10 +6,21 @@ namespace Neo.Cryptography { + /// + /// A helper class for base-58 encoder. + /// public static class Base58 { + /// + /// Represents the alphabet of the base-58 encoder. + /// public const string Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + /// + /// Converts the specified , which encodes binary data as base-58 digits, to an equivalent byte array. The encoded contains the checksum of the binary data. + /// + /// The to convert. + /// A byte array that is equivalent to . public static byte[] Base58CheckDecode(this string input) { if (input is null) throw new ArgumentNullException(nameof(input)); @@ -23,6 +34,11 @@ public static byte[] Base58CheckDecode(this string input) return ret; } + /// + /// Converts a byte array to its equivalent representation that is encoded with base-58 digits. The encoded contains the checksum of the binary data. + /// + /// The byte array to convert. + /// The representation, in base-58, of the contents of . public static string Base58CheckEncode(this ReadOnlySpan data) { byte[] checksum = data.Sha256().Sha256(); @@ -34,6 +50,11 @@ public static string Base58CheckEncode(this ReadOnlySpan data) return ret; } + /// + /// Converts the specified , which encodes binary data as base-58 digits, to an equivalent byte array. + /// + /// The to convert. + /// A byte array that is equivalent to . public static byte[] Decode(string input) { // Decode Base58 string to BigInteger @@ -55,10 +76,15 @@ public static byte[] Decode(string input) return Concat(leadingZeros, bytesWithoutLeadingZeros); } + /// + /// Converts a byte array to its equivalent representation that is encoded with base-58 digits. + /// + /// The byte array to convert. + /// The representation, in base-58, of the contents of . public static string Encode(ReadOnlySpan input) { // Decode byte[] to BigInteger - BigInteger value = new BigInteger(input, isUnsigned: true, isBigEndian: true); + BigInteger value = new(input, isUnsigned: true, isBigEndian: true); // Encode BigInteger to Base58 string var sb = new StringBuilder(); diff --git a/src/neo/Cryptography/BloomFilter.cs b/src/neo/Cryptography/BloomFilter.cs index 32cfa50dd4..67af4c2102 100644 --- a/src/neo/Cryptography/BloomFilter.cs +++ b/src/neo/Cryptography/BloomFilter.cs @@ -4,17 +4,36 @@ namespace Neo.Cryptography { + /// + /// Represents a bloom filter. + /// public class BloomFilter { private readonly uint[] seeds; private readonly BitArray bits; + /// + /// The number of hash functions used by the bloom filter. + /// public int K => seeds.Length; + /// + /// The size of the bit array used by the bloom filter. + /// public int M => bits.Length; + /// + /// Used to generate the seeds of the murmur hash functions. + /// public uint Tweak { get; private set; } + /// + /// Initializes a new instance of the class. + /// + /// The size of the bit array used by the bloom filter. + /// The number of hash functions used by the bloom filter. + /// Used to generate the seeds of the murmur hash functions. + /// The initial elements contained in this object. public BloomFilter(int m, int k, uint nTweak, byte[] elements = null) { if (k < 0 || m < 0) throw new ArgumentOutOfRangeException(); @@ -24,12 +43,21 @@ public BloomFilter(int m, int k, uint nTweak, byte[] elements = null) this.Tweak = nTweak; } + /// + /// Adds an element to the . + /// + /// The object to add to the . public void Add(byte[] element) { foreach (uint i in seeds.AsParallel().Select(s => element.Murmur32(s))) bits.Set((int)(i % (uint)bits.Length), true); } + /// + /// Determines whether the contains a specific element. + /// + /// The object to locate in the . + /// if is found in the ; otherwise, . public bool Check(byte[] element) { foreach (uint i in seeds.AsParallel().Select(s => element.Murmur32(s))) @@ -38,6 +66,10 @@ public bool Check(byte[] element) return true; } + /// + /// Gets the bit array in this . + /// + /// The byte array to store the bits. public void GetBits(byte[] newBits) { bits.CopyTo(newBits, 0); diff --git a/src/neo/Cryptography/Crypto.cs b/src/neo/Cryptography/Crypto.cs index ae5adf6aed..aec25ecd09 100644 --- a/src/neo/Cryptography/Crypto.cs +++ b/src/neo/Cryptography/Crypto.cs @@ -3,21 +3,41 @@ namespace Neo.Cryptography { + /// + /// A cryptographic helper class. + /// public static class Crypto { + /// + /// Calculates the 160-bit hash value of the specified message. + /// + /// The message to be hashed. + /// 160-bit hash value. public static byte[] Hash160(ReadOnlySpan message) { return message.Sha256().RIPEMD160(); } + /// + /// Calculates the 256-bit hash value of the specified message. + /// + /// The message to be hashed. + /// 256-bit hash value. public static byte[] Hash256(ReadOnlySpan message) { return message.Sha256().Sha256(); } + /// + /// Signs the specified message using the ECDSA algorithm. + /// + /// The message to be signed. + /// The private key to be used. + /// The public key to be used. + /// The ECDSA signature for the specified message. public static byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey) { - using (var ecdsa = ECDsa.Create(new ECParameters + using var ecdsa = ECDsa.Create(new ECParameters { Curve = ECCurve.NamedCurves.nistP256, D = prikey, @@ -26,12 +46,17 @@ public static byte[] Sign(byte[] message, byte[] prikey, byte[] pubkey) X = pubkey[..32], Y = pubkey[32..] } - })) - { - return ecdsa.SignData(message, HashAlgorithmName.SHA256); - } + }); + return ecdsa.SignData(message, HashAlgorithmName.SHA256); } + /// + /// Verifies that a digital signature is appropriate for the provided key and message. + /// + /// The signed message. + /// The signature to be verified. + /// The public key to be used. + /// if the signature is valid; otherwise, . public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ECC.ECPoint pubkey) { ECCurve curve = @@ -51,6 +76,14 @@ public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan + /// Verifies that a digital signature is appropriate for the provided key and message. + /// + /// The signed message. + /// The signature to be verified. + /// The public key to be used. + /// The curve to be used by the ECDSA algorithm. + /// if the signature is valid; otherwise, . public static bool VerifySignature(ReadOnlySpan message, ReadOnlySpan signature, ReadOnlySpan pubkey, ECC.ECCurve curve) { return VerifySignature(message, signature, ECC.ECPoint.DecodePoint(pubkey, curve)); diff --git a/src/neo/Cryptography/ECC/ECCurve.cs b/src/neo/Cryptography/ECC/ECCurve.cs index 546c794070..ed4f954396 100644 --- a/src/neo/Cryptography/ECC/ECCurve.cs +++ b/src/neo/Cryptography/ECC/ECCurve.cs @@ -3,16 +3,25 @@ namespace Neo.Cryptography.ECC { + /// + /// Represents an elliptic curve. + /// public class ECCurve { internal readonly BigInteger Q; internal readonly ECFieldElement A; internal readonly ECFieldElement B; internal readonly BigInteger N; + /// + /// The point at infinity. + /// public readonly ECPoint Infinity; + /// + /// The generator, or base point, for operations on the curve. + /// public readonly ECPoint G; - public readonly int ExpectedECPointLength; + internal readonly int ExpectedECPointLength; private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G) { @@ -25,7 +34,10 @@ private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G this.G = ECPoint.DecodePoint(G, this); } - public static readonly ECCurve Secp256k1 = new ECCurve + /// + /// Represents a secp256k1 named curve. + /// + public static readonly ECCurve Secp256k1 = new ( BigInteger.Parse("00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", NumberStyles.AllowHexSpecifier), BigInteger.Zero, @@ -34,7 +46,10 @@ private ECCurve(BigInteger Q, BigInteger A, BigInteger B, BigInteger N, byte[] G ("04" + "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798" + "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8").HexToBytes() ); - public static readonly ECCurve Secp256r1 = new ECCurve + /// + /// Represents a secp256r1 named curve. + /// + public static readonly ECCurve Secp256r1 = new ( BigInteger.Parse("00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.AllowHexSpecifier), BigInteger.Parse("00FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", NumberStyles.AllowHexSpecifier), diff --git a/src/neo/Cryptography/ECC/ECFieldElement.cs b/src/neo/Cryptography/ECC/ECFieldElement.cs index edfdc497b0..f0cf95430d 100644 --- a/src/neo/Cryptography/ECC/ECFieldElement.cs +++ b/src/neo/Cryptography/ECC/ECFieldElement.cs @@ -30,7 +30,7 @@ public override bool Equals(object obj) if (obj == this) return true; - if (!(obj is ECFieldElement other)) + if (obj is not ECFieldElement other) return false; return Equals(other); @@ -97,7 +97,7 @@ public ECFieldElement Sqrt() { if (curve.Q.TestBit(1)) { - ECFieldElement z = new ECFieldElement(BigInteger.ModPow(Value, (curve.Q >> 2) + 1, curve.Q), curve); + ECFieldElement z = new(BigInteger.ModPow(Value, (curve.Q >> 2) + 1, curve.Q), curve); return z.Square().Equals(this) ? z : null; } BigInteger qMinusOne = curve.Q - 1; @@ -111,7 +111,7 @@ public ECFieldElement Sqrt() BigInteger U, V; do { - Random rand = new Random(); + Random rand = new(); BigInteger P; do { diff --git a/src/neo/Cryptography/ECC/ECPoint.cs b/src/neo/Cryptography/ECC/ECPoint.cs index 49a3c16074..92346376c4 100644 --- a/src/neo/Cryptography/ECC/ECPoint.cs +++ b/src/neo/Cryptography/ECC/ECPoint.cs @@ -6,12 +6,18 @@ namespace Neo.Cryptography.ECC { + /// + /// Represents a (X,Y) coordinate pair for elliptic curve cryptography (ECC) structures. + /// public class ECPoint : IComparable, IEquatable, ISerializable { internal ECFieldElement X, Y; internal readonly ECCurve Curve; private byte[] _compressedPoint, _uncompressedPoint; + /// + /// Indicates whether it is a point at infinity. + /// public bool IsInfinity { get { return X == null && Y == null; } @@ -19,6 +25,9 @@ public bool IsInfinity public int Size => IsInfinity ? 1 : 33; + /// + /// Initializes a new instance of the class with the secp256r1 curve. + /// public ECPoint() : this(null, null, ECCurve.Secp256r1) { } internal ECPoint(ECFieldElement x, ECFieldElement y, ECCurve curve) @@ -39,6 +48,12 @@ public int CompareTo(ECPoint other) return Y.CompareTo(other.Y); } + /// + /// Decode an object from a sequence of byte. + /// + /// The sequence of byte to be decoded. + /// The object used to construct the . + /// The decoded point. public static ECPoint DecodePoint(ReadOnlySpan encoded, ECCurve curve) { ECPoint p = null; @@ -50,7 +65,7 @@ public static ECPoint DecodePoint(ReadOnlySpan encoded, ECCurve curve) if (encoded.Length != (curve.ExpectedECPointLength + 1)) throw new FormatException("Incorrect length for compressed encoding"); int yTilde = encoded[0] & 1; - BigInteger X1 = new BigInteger(encoded[1..], isUnsigned: true, isBigEndian: true); + BigInteger X1 = new(encoded[1..], isUnsigned: true, isBigEndian: true); p = DecompressPoint(yTilde, X1, curve); p._compressedPoint = encoded.ToArray(); break; @@ -59,8 +74,8 @@ public static ECPoint DecodePoint(ReadOnlySpan encoded, ECCurve curve) { if (encoded.Length != (2 * curve.ExpectedECPointLength + 1)) throw new FormatException("Incorrect length for uncompressed/hybrid encoding"); - BigInteger X1 = new BigInteger(encoded[1..(1 + curve.ExpectedECPointLength)], isUnsigned: true, isBigEndian: true); - BigInteger Y1 = new BigInteger(encoded[(1 + curve.ExpectedECPointLength)..], isUnsigned: true, isBigEndian: true); + BigInteger X1 = new(encoded[1..(1 + curve.ExpectedECPointLength)], isUnsigned: true, isBigEndian: true); + BigInteger Y1 = new(encoded[(1 + curve.ExpectedECPointLength)..], isUnsigned: true, isBigEndian: true); p = new ECPoint(new ECFieldElement(X1, curve), new ECFieldElement(Y1, curve), curve) { _uncompressedPoint = encoded.ToArray() @@ -75,7 +90,7 @@ public static ECPoint DecodePoint(ReadOnlySpan encoded, ECCurve curve) private static ECPoint DecompressPoint(int yTilde, BigInteger X1, ECCurve curve) { - ECFieldElement x = new ECFieldElement(X1, curve); + ECFieldElement x = new(X1, curve); ECFieldElement alpha = x * (x.Square() + curve.A) + curve.B; ECFieldElement beta = alpha.Sqrt(); @@ -105,6 +120,12 @@ void ISerializable.Deserialize(BinaryReader reader) Y = p.Y; } + /// + /// Deserializes an object from a . + /// + /// The for reading data. + /// The object used to construct the . + /// The deserialized point. public static ECPoint DeserializeFrom(BinaryReader reader, ECCurve curve) { Span buffer = stackalloc byte[1 + curve.ExpectedECPointLength * 2]; @@ -134,11 +155,11 @@ public static ECPoint DeserializeFrom(BinaryReader reader, ECCurve curve) } /// - /// Encode ECPoint to byte array - /// Note: The return should't be modified because it could be cached + /// Encodes an object to a byte array. /// - /// Compressed - /// Encoded point + /// Indicates whether to encode it in a compressed format. + /// The encoded point. + /// Note: The return should't be modified because it could be cached. public byte[] EncodePoint(bool commpressed) { if (IsInfinity) return new byte[1]; @@ -166,7 +187,7 @@ public byte[] EncodePoint(bool commpressed) public bool Equals(ECPoint other) { if (ReferenceEquals(this, other)) return true; - if (ReferenceEquals(null, other)) return false; + if (other is null) return false; if (IsInfinity && other.IsInfinity) return true; if (IsInfinity || other.IsInfinity) return false; return X.Equals(other.X) && Y.Equals(other.Y); @@ -177,22 +198,21 @@ public override bool Equals(object obj) return Equals(obj as ECPoint); } - public static ECPoint FromBytes(byte[] pubkey, ECCurve curve) + /// + /// Constructs an object from a byte array. + /// + /// The byte array to be used to construct the object. + /// The object used to construct the . + /// The decoded point. + public static ECPoint FromBytes(byte[] bytes, ECCurve curve) { - switch (pubkey.Length) + return bytes.Length switch { - case 33: - case 65: - return DecodePoint(pubkey, curve); - case 64: - case 72: - return DecodePoint(Concat(new byte[] { 0x04 }, pubkey[^64..]), curve); - case 96: - case 104: - return DecodePoint(Concat(new byte[] { 0x04 }, pubkey[^96..^32]), curve); - default: - throw new FormatException(); - } + 33 or 65 => DecodePoint(bytes, curve), + 64 or 72 => DecodePoint(Concat(new byte[] { 0x04 }, bytes[^64..]), curve), + 96 or 104 => DecodePoint(Concat(new byte[] { 0x04 }, bytes[^96..^32]), curve), + _ => throw new FormatException(), + }; } public override int GetHashCode() @@ -299,6 +319,12 @@ internal static ECPoint Multiply(ECPoint p, BigInteger k) return q; } + /// + /// Parse the object from a . + /// + /// The to be parsed. + /// The object used to construct the . + /// The parsed point. public static ECPoint Parse(string value, ECCurve curve) { return DecodePoint(value.HexToBytes(), curve); @@ -314,6 +340,13 @@ public override string ToString() return EncodePoint(true).ToHexString(); } + /// + /// Try parse the object from a . + /// + /// The to be parsed. + /// The object used to construct the . + /// The parsed point. + /// if was converted successfully; otherwise, . public static bool TryParse(string value, ECCurve curve, out ECPoint point) { try @@ -334,8 +367,8 @@ internal ECPoint Twice() return this; if (this.Y.Value.Sign == 0) return Curve.Infinity; - ECFieldElement TWO = new ECFieldElement(2, Curve); - ECFieldElement THREE = new ECFieldElement(3, Curve); + ECFieldElement TWO = new(2, Curve); + ECFieldElement THREE = new(3, Curve); ECFieldElement gamma = (this.X.Square() * THREE + Curve.A) / (Y * TWO); ECFieldElement x3 = gamma.Square() - this.X * TWO; ECFieldElement y3 = gamma * (this.X - x3) - this.Y; @@ -387,10 +420,10 @@ private static sbyte[] WindowNaf(sbyte width, BigInteger k) if (p == null || n == null) throw new ArgumentNullException(); if (n.Length != 32) - throw new ArgumentException(); + throw new ArgumentException(null, nameof(n)); if (p.IsInfinity) return p; - BigInteger k = new BigInteger(n, isUnsigned: true, isBigEndian: true); + BigInteger k = new(n, isUnsigned: true, isBigEndian: true); if (k.Sign == 0) return p.Curve.Infinity; return Multiply(p, k); diff --git a/src/neo/Cryptography/Helper.cs b/src/neo/Cryptography/Helper.cs index 31ba0cd6ec..deb6063186 100644 --- a/src/neo/Cryptography/Helper.cs +++ b/src/neo/Cryptography/Helper.cs @@ -11,40 +11,74 @@ namespace Neo.Cryptography { + /// + /// A helper class for cryptography + /// public static class Helper { + /// + /// Computes the hash value for the specified byte array using the ripemd160 algorithm. + /// + /// The input to compute the hash code for. + /// The computed hash code. public static byte[] RIPEMD160(this byte[] value) { using var ripemd160 = new RIPEMD160Managed(); return ripemd160.ComputeHash(value); } + /// + /// Computes the hash value for the specified byte array using the ripemd160 algorithm. + /// + /// The input to compute the hash code for. + /// The computed hash code. public static byte[] RIPEMD160(this ReadOnlySpan value) { byte[] source = value.ToArray(); return source.RIPEMD160(); } + /// + /// Computes the hash value for the specified byte array using the murmur algorithm. + /// + /// The input to compute the hash code for. + /// The seed used by the murmur algorithm. + /// The computed hash code. public static uint Murmur32(this byte[] value, uint seed) { - using (Murmur3 murmur = new Murmur3(seed)) - { - return BinaryPrimitives.ReadUInt32LittleEndian(murmur.ComputeHash(value)); - } + using Murmur3 murmur = new(seed); + return BinaryPrimitives.ReadUInt32LittleEndian(murmur.ComputeHash(value)); } + /// + /// Computes the hash value for the specified byte array using the sha256 algorithm. + /// + /// The input to compute the hash code for. + /// The computed hash code. public static byte[] Sha256(this byte[] value) { using var sha256 = SHA256.Create(); return sha256.ComputeHash(value); } + /// + /// Computes the hash value for the specified region of the specified byte array using the sha256 algorithm. + /// + /// The input to compute the hash code for. + /// The offset into the byte array from which to begin using data. + /// The number of bytes in the array to use as data. + /// The computed hash code. public static byte[] Sha256(this byte[] value, int offset, int count) { using var sha256 = SHA256.Create(); return sha256.ComputeHash(value, offset, count); } + /// + /// Computes the hash value for the specified byte array using the sha256 algorithm. + /// + /// The input to compute the hash code for. + /// The computed hash code. public static byte[] Sha256(this ReadOnlySpan value) { byte[] buffer = new byte[32]; @@ -53,6 +87,11 @@ public static byte[] Sha256(this ReadOnlySpan value) return buffer; } + /// + /// Computes the hash value for the specified byte array using the sha256 algorithm. + /// + /// The input to compute the hash code for. + /// The computed hash code. public static byte[] Sha256(this Span value) { return Sha256((ReadOnlySpan)value); @@ -68,28 +107,24 @@ internal static bool Test(this BloomFilter filter, Transaction tx) internal static byte[] ToAesKey(this string password) { - using (SHA256 sha256 = SHA256.Create()) - { - byte[] passwordBytes = Encoding.UTF8.GetBytes(password); - byte[] passwordHash = sha256.ComputeHash(passwordBytes); - byte[] passwordHash2 = sha256.ComputeHash(passwordHash); - Array.Clear(passwordBytes, 0, passwordBytes.Length); - Array.Clear(passwordHash, 0, passwordHash.Length); - return passwordHash2; - } + using SHA256 sha256 = SHA256.Create(); + byte[] passwordBytes = Encoding.UTF8.GetBytes(password); + byte[] passwordHash = sha256.ComputeHash(passwordBytes); + byte[] passwordHash2 = sha256.ComputeHash(passwordHash); + Array.Clear(passwordBytes, 0, passwordBytes.Length); + Array.Clear(passwordHash, 0, passwordHash.Length); + return passwordHash2; } internal static byte[] ToAesKey(this SecureString password) { - using (SHA256 sha256 = SHA256.Create()) - { - byte[] passwordBytes = password.ToArray(); - byte[] passwordHash = sha256.ComputeHash(passwordBytes); - byte[] passwordHash2 = sha256.ComputeHash(passwordHash); - Array.Clear(passwordBytes, 0, passwordBytes.Length); - Array.Clear(passwordHash, 0, passwordHash.Length); - return passwordHash2; - } + using SHA256 sha256 = SHA256.Create(); + byte[] passwordBytes = password.ToArray(); + byte[] passwordHash = sha256.ComputeHash(passwordBytes); + byte[] passwordHash2 = sha256.ComputeHash(passwordHash); + Array.Clear(passwordBytes, 0, passwordBytes.Length); + Array.Clear(passwordHash, 0, passwordHash.Length); + return passwordHash2; } internal static byte[] ToArray(this SecureString s) @@ -98,7 +133,7 @@ internal static byte[] ToArray(this SecureString s) throw new NullReferenceException(); if (s.Length == 0) return Array.Empty(); - List result = new List(); + List result = new(); IntPtr ptr = SecureStringMarshal.SecureStringToGlobalAllocAnsi(s); try { diff --git a/src/neo/Cryptography/MerkleTree.cs b/src/neo/Cryptography/MerkleTree.cs index 4fdb31f6e3..d7d1ff8145 100644 --- a/src/neo/Cryptography/MerkleTree.cs +++ b/src/neo/Cryptography/MerkleTree.cs @@ -7,10 +7,16 @@ namespace Neo.Cryptography { + /// + /// Represents a merkle tree. + /// public class MerkleTree { private readonly MerkleTreeNode root; + /// + /// The depth of the tree. + /// public int Depth { get; } internal MerkleTree(UInt256[] hashes) @@ -32,8 +38,10 @@ private static MerkleTreeNode Build(MerkleTreeNode[] leaves) MerkleTreeNode[] parents = new MerkleTreeNode[(leaves.Length + 1) / 2]; for (int i = 0; i < parents.Length; i++) { - parents[i] = new MerkleTreeNode(); - parents[i].LeftChild = leaves[i * 2]; + parents[i] = new MerkleTreeNode + { + LeftChild = leaves[i * 2] + }; leaves[i * 2].Parent = parents[i]; if (i * 2 + 1 == leaves.Length) { @@ -58,11 +66,16 @@ private static UInt256 Concat(Span buffer, UInt256 hash1, UInt256 hash2) return new UInt256(Crypto.Hash256(buffer)); } + /// + /// Computes the root of the hash tree. + /// + /// The leaves of the hash tree. + /// The root of the hash tree. public static UInt256 ComputeRoot(UInt256[] hashes) { if (hashes.Length == 0) return UInt256.Zero; if (hashes.Length == 1) return hashes[0]; - MerkleTree tree = new MerkleTree(hashes); + MerkleTree tree = new(hashes); return tree.root.Hash; } @@ -80,20 +93,29 @@ private static void DepthFirstSearch(MerkleTreeNode node, IList hashes) } } - // depth-first order + /// + /// Gets all nodes of the hash tree in depth-first order. + /// + /// All nodes of the hash tree. public UInt256[] ToHashArray() { if (root is null) return Array.Empty(); - List hashes = new List(); + List hashes = new(); DepthFirstSearch(root, hashes); return hashes.ToArray(); } + /// + /// Trims the hash tree using the specified bit array. + /// + /// The bit array to be used. public void Trim(BitArray flags) { if (root is null) return; - flags = new BitArray(flags); - flags.Length = 1 << (Depth - 1); + flags = new BitArray(flags) + { + Length = 1 << (Depth - 1) + }; Trim(root, 0, Depth, flags); } diff --git a/src/neo/Cryptography/Murmur3.cs b/src/neo/Cryptography/Murmur3.cs index 671177a63a..08c1e6e6fd 100644 --- a/src/neo/Cryptography/Murmur3.cs +++ b/src/neo/Cryptography/Murmur3.cs @@ -5,6 +5,9 @@ namespace Neo.Cryptography { + /// + /// Computes the murmur hash for the input data. + /// public sealed class Murmur3 : HashAlgorithm { private const uint c1 = 0xcc9e2d51; @@ -20,6 +23,10 @@ public sealed class Murmur3 : HashAlgorithm public override int HashSize => 32; + /// + /// Initializes a new instance of the class with the specified seed. + /// + /// The seed to be used. public Murmur3(uint seed) { this.seed = seed; diff --git a/src/neo/Cryptography/RIPEMD160Managed.cs b/src/neo/Cryptography/RIPEMD160Managed.cs index 89a4de23c8..9c505ce104 100644 --- a/src/neo/Cryptography/RIPEMD160Managed.cs +++ b/src/neo/Cryptography/RIPEMD160Managed.cs @@ -5,20 +5,22 @@ namespace Neo.Cryptography { + /// + /// Computes the ripemd160 hash for the input data. + /// [ComVisible(true)] public class RIPEMD160Managed : HashAlgorithm { - private byte[] _buffer; + private readonly byte[] _buffer; private long _count; // Number of bytes in the hashed message - private uint[] _stateMD160; - private uint[] _blockDWords; + private readonly uint[] _stateMD160; + private readonly uint[] _blockDWords; public override int HashSize => 160; - // - // public constructors - // - + /// + /// Initializes a new instance of the class. + /// public RIPEMD160Managed() { _stateMD160 = new uint[5]; @@ -28,10 +30,6 @@ public RIPEMD160Managed() InitializeState(); } - // - // public methods - // - public override void Initialize() { InitializeState(); @@ -41,22 +39,18 @@ public override void Initialize() Array.Clear(_buffer, 0, _buffer.Length); } - [System.Security.SecuritySafeCritical] // auto-generated + [SecuritySafeCritical] protected override void HashCore(byte[] rgb, int ibStart, int cbSize) { - _HashData(rgb, ibStart, cbSize); + HashData(rgb, ibStart, cbSize); } - [System.Security.SecuritySafeCritical] // auto-generated + [SecuritySafeCritical] protected override byte[] HashFinal() { - return _EndHash(); + return EndHash(); } - // - // private methods - // - private void InitializeState() { _count = 0; @@ -70,8 +64,8 @@ private void InitializeState() _stateMD160[4] = 0xc3d2e1f0; } - [System.Security.SecurityCritical] // auto-generated - private unsafe void _HashData(byte[] partIn, int ibStart, int cbSize) + [SecurityCritical] + private unsafe void HashData(byte[] partIn, int ibStart, int cbSize) { int bufferLen; int partInLen = cbSize; @@ -116,8 +110,8 @@ private unsafe void _HashData(byte[] partIn, int ibStart, int cbSize) } } - [SecurityCritical] // auto-generated - private byte[] _EndHash() + [SecurityCritical] + private byte[] EndHash() { byte[] pad; int padLen; @@ -148,7 +142,7 @@ private byte[] _EndHash() pad[padLen - 8] = (byte)((bitCount >> 0) & 0xff); /* Digest padding */ - _HashData(pad, 0, pad.Length); + HashData(pad, 0, pad.Length); /* Store digest */ DWORDToLittleEndian(hash, _stateMD160, 5); @@ -156,7 +150,7 @@ private byte[] _EndHash() return hash; } - [System.Security.SecurityCritical] // auto-generated + [SecurityCritical] private static unsafe void MDTransform(uint* blockDWords, uint* state, byte* block) { uint aa = state[0]; @@ -999,7 +993,6 @@ As we don't have macros in C# and we don't want to pay the cost of a function ca state[0] = ddd; } - // The five basic functions private static uint F(uint x, uint y, uint z) { return (x ^ y ^ z); @@ -1025,7 +1018,7 @@ private static uint J(uint x, uint y, uint z) return (x ^ (y | ~z)); } - [SecurityCritical] // auto-generated + [SecurityCritical] private unsafe static void DWORDFromLittleEndian(uint* x, int digits, byte* block) { int i; diff --git a/src/neo/Helper.cs b/src/neo/Helper.cs index 0001d75438..83560f5a06 100644 --- a/src/neo/Helper.cs +++ b/src/neo/Helper.cs @@ -11,9 +11,12 @@ namespace Neo { + /// + /// A helper class that provides common functions. + /// public static class Helper { - private static readonly DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private static readonly DateTime unixEpoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int BitLen(int w) @@ -33,6 +36,11 @@ private static int BitLen(int w) : (w < 1 << 29 ? (w < 1 << 28 ? 28 : 29) : (w < 1 << 30 ? 30 : 31))))); } + /// + /// Concatenates the specified byte arrays. + /// + /// The byte arrays to concatenate. + /// The concatenated byte array. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte[] Concat(params byte[][] buffers) { @@ -49,6 +57,12 @@ public static byte[] Concat(params byte[][] buffers) return dst; } + /// + /// Concatenates two byte arrays. + /// + /// The first byte array to concatenate. + /// The second byte array to concatenate. + /// The concatenated byte array. public static byte[] Concat(ReadOnlySpan a, ReadOnlySpan b) { byte[] buffer = new byte[a.Length + b.Length]; @@ -115,6 +129,11 @@ internal static string GetVersion(this Assembly assembly) return (string)attribute.ConstructorArguments[0].Value; } + /// + /// Converts a hex to byte array. + /// + /// The hex to convert. + /// The converted byte array. public static byte[] HexToBytes(this string value) { if (value == null || value.Length == 0) @@ -162,12 +181,17 @@ internal static BigInteger NextBigInteger(this Random rand, int sizeInBits) Span b = stackalloc byte[sizeInBits / 8 + 1]; rand.NextBytes(b); if (sizeInBits % 8 == 0) - b[b.Length - 1] = 0; + b[^1] = 0; else - b[b.Length - 1] &= (byte)((1 << sizeInBits % 8) - 1); + b[^1] &= (byte)((1 << sizeInBits % 8) - 1); return new BigInteger(b); } + /// + /// Finds the sum of the specified integers. + /// + /// The specified integers. + /// The sum of the integers. public static BigInteger Sum(this IEnumerable source) { var sum = BigInteger.Zero; @@ -181,6 +205,11 @@ internal static bool TestBit(this BigInteger i, int index) return (i & (BigInteger.One << index)) > BigInteger.Zero; } + /// + /// Converts a to byte array and eliminates all the leading zeros. + /// + /// The to convert. + /// The converted byte array. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte[] ToByteArrayStandard(this BigInteger i) { @@ -188,35 +217,61 @@ public static byte[] ToByteArrayStandard(this BigInteger i) return i.ToByteArray(); } + /// + /// Converts a byte array to hex . + /// + /// The byte array to convert. + /// The converted hex . public static string ToHexString(this byte[] value) { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); foreach (byte b in value) sb.AppendFormat("{0:x2}", b); return sb.ToString(); } + /// + /// Converts a byte array to hex . + /// + /// The byte array to convert. + /// Indicates whether it should be converted in the reversed byte order. + /// The converted hex . public static string ToHexString(this byte[] value, bool reverse = false) { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); for (int i = 0; i < value.Length; i++) sb.AppendFormat("{0:x2}", value[reverse ? value.Length - i - 1 : i]); return sb.ToString(); } + /// + /// Converts a byte array to hex . + /// + /// The byte array to convert. + /// The converted hex . public static string ToHexString(this ReadOnlySpan value) { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); foreach (byte b in value) sb.AppendFormat("{0:x2}", b); return sb.ToString(); } + /// + /// Converts a to timestamp. + /// + /// The to convert. + /// The converted timestamp. public static uint ToTimestamp(this DateTime time) { return (uint)(time.ToUniversalTime() - unixEpoch).TotalSeconds; } + /// + /// Converts a to timestamp in milliseconds. + /// + /// The to convert. + /// The converted timestamp. public static ulong ToTimestampMS(this DateTime time) { return (ulong)(time.ToUniversalTime() - unixEpoch).TotalMilliseconds; diff --git a/src/neo/IO/Actors/PriorityMessageQueue.cs b/src/neo/IO/Actors/PriorityMessageQueue.cs index b7820b065a..f07d85c8ab 100644 --- a/src/neo/IO/Actors/PriorityMessageQueue.cs +++ b/src/neo/IO/Actors/PriorityMessageQueue.cs @@ -11,8 +11,8 @@ namespace Neo.IO.Actors { internal class PriorityMessageQueue : IMessageQueue, IUnboundedMessageQueueSemantics { - private readonly ConcurrentQueue high = new ConcurrentQueue(); - private readonly ConcurrentQueue low = new ConcurrentQueue(); + private readonly ConcurrentQueue high = new(); + private readonly ConcurrentQueue low = new(); private readonly Func dropper; private readonly Func priority_generator; private int idle = 1; diff --git a/src/neo/IO/ByteArrayComparer.cs b/src/neo/IO/ByteArrayComparer.cs index 8e9b2573c0..8c3f417089 100644 --- a/src/neo/IO/ByteArrayComparer.cs +++ b/src/neo/IO/ByteArrayComparer.cs @@ -6,8 +6,8 @@ namespace Neo.IO { internal class ByteArrayComparer : IComparer { - public static readonly ByteArrayComparer Default = new ByteArrayComparer(1); - public static readonly ByteArrayComparer Reverse = new ByteArrayComparer(-1); + public static readonly ByteArrayComparer Default = new(1); + public static readonly ByteArrayComparer Reverse = new(-1); private readonly int direction; diff --git a/src/neo/IO/ByteArrayEqualityComparer.cs b/src/neo/IO/ByteArrayEqualityComparer.cs index 2a3c64b57a..c6fab99539 100644 --- a/src/neo/IO/ByteArrayEqualityComparer.cs +++ b/src/neo/IO/ByteArrayEqualityComparer.cs @@ -4,7 +4,7 @@ namespace Neo.IO { internal class ByteArrayEqualityComparer : IEqualityComparer { - public static readonly ByteArrayEqualityComparer Default = new ByteArrayEqualityComparer(); + public static readonly ByteArrayEqualityComparer Default = new(); public unsafe bool Equals(byte[] x, byte[] y) { diff --git a/src/neo/IO/Caching/Cache.cs b/src/neo/IO/Caching/Cache.cs index b5095231f3..0af4296f7b 100644 --- a/src/neo/IO/Caching/Cache.cs +++ b/src/neo/IO/Caching/Cache.cs @@ -22,8 +22,8 @@ public CacheItem(TKey key, TValue value) } } - protected readonly ReaderWriterLockSlim RwSyncRootLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); - protected readonly Dictionary InnerDictionary = new Dictionary(); + protected readonly ReaderWriterLockSlim RwSyncRootLock = new(LockRecursionPolicy.SupportsRecursion); + protected readonly Dictionary InnerDictionary = new(); private readonly int max_capacity; public TValue this[TKey key] diff --git a/src/neo/IO/Caching/HashSetCache.cs b/src/neo/IO/Caching/HashSetCache.cs index af34cd76fa..f682b2e5ae 100644 --- a/src/neo/IO/Caching/HashSetCache.cs +++ b/src/neo/IO/Caching/HashSetCache.cs @@ -4,12 +4,12 @@ namespace Neo.IO.Caching { - public class HashSetCache : IReadOnlyCollection where T : IEquatable + class HashSetCache : IReadOnlyCollection where T : IEquatable { /// /// Sets where the Hashes are stored /// - private readonly LinkedList> sets = new LinkedList>(); + private readonly LinkedList> sets = new(); /// /// Maximum capacity of each bucket inside each HashSet of . diff --git a/src/neo/IO/Caching/OrderedDictionary.cs b/src/neo/IO/Caching/OrderedDictionary.cs index 4cbf33392f..3ce4b0ab91 100644 --- a/src/neo/IO/Caching/OrderedDictionary.cs +++ b/src/neo/IO/Caching/OrderedDictionary.cs @@ -21,7 +21,7 @@ protected override TKey GetKeyForItem(TItem item) } } - private readonly InternalCollection collection = new InternalCollection(); + private readonly InternalCollection collection = new(); public int Count => collection.Count; public bool IsReadOnly => false; diff --git a/src/neo/IO/Caching/ReflectionCache.cs b/src/neo/IO/Caching/ReflectionCache.cs index 5709ccd8df..b09ec0fe37 100644 --- a/src/neo/IO/Caching/ReflectionCache.cs +++ b/src/neo/IO/Caching/ReflectionCache.cs @@ -6,7 +6,7 @@ namespace Neo.IO.Caching { internal static class ReflectionCache where T : Enum { - private static readonly Dictionary dictionary = new Dictionary(); + private static readonly Dictionary dictionary = new(); public static int Count => dictionary.Count; diff --git a/src/neo/IO/Helper.cs b/src/neo/IO/Helper.cs index e871bbaedf..5e3f2cb5b1 100644 --- a/src/neo/IO/Helper.cs +++ b/src/neo/IO/Helper.cs @@ -10,61 +10,96 @@ namespace Neo.IO { + /// + /// A helper class for serialization of NEO objects. + /// public static class Helper { + /// + /// Converts a byte array to an object. + /// + /// The type to convert to. + /// The byte array to be converted. + /// The offset into the byte array from which to begin using data. + /// The converted object. public static T AsSerializable(this byte[] value, int start = 0) where T : ISerializable, new() { - using (MemoryStream ms = new MemoryStream(value, start, value.Length - start, false)) - using (BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8)) - { - return reader.ReadSerializable(); - } + using MemoryStream ms = new(value, start, value.Length - start, false); + using BinaryReader reader = new(ms, Utility.StrictUTF8, true); + return reader.ReadSerializable(); } + /// + /// Converts a byte array to an object. + /// + /// The type to convert to. + /// The byte array to be converted. + /// The converted object. public static unsafe T AsSerializable(this ReadOnlySpan value) where T : ISerializable, new() { if (value.IsEmpty) throw new FormatException(); fixed (byte* pointer = value) { - using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, value.Length); - using BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8); + using UnmanagedMemoryStream ms = new(pointer, value.Length); + using BinaryReader reader = new(ms, Utility.StrictUTF8, true); return reader.ReadSerializable(); } } + /// + /// Converts a byte array to an object. + /// + /// The byte array to be converted. + /// The type to convert to. + /// The converted object. public static ISerializable AsSerializable(this byte[] value, Type type) { if (!typeof(ISerializable).GetTypeInfo().IsAssignableFrom(type)) throw new InvalidCastException(); ISerializable serializable = (ISerializable)Activator.CreateInstance(type); - using (MemoryStream ms = new MemoryStream(value, false)) - using (BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8)) - { - serializable.Deserialize(reader); - } + using MemoryStream ms = new(value, false); + using BinaryReader reader = new(ms, Utility.StrictUTF8, true); + serializable.Deserialize(reader); return serializable; } + /// + /// Converts a byte array to an array. + /// + /// The type of the array element. + /// The byte array to be converted. + /// The maximum number of elements contained in the converted array. + /// The converted array. public static T[] AsSerializableArray(this byte[] value, int max = 0x1000000) where T : ISerializable, new() { - using (MemoryStream ms = new MemoryStream(value, false)) - using (BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8)) - { - return reader.ReadSerializableArray(max); - } + using MemoryStream ms = new(value, false); + using BinaryReader reader = new(ms, Utility.StrictUTF8, true); + return reader.ReadSerializableArray(max); } + /// + /// Converts a byte array to an array. + /// + /// The type of the array element. + /// The byte array to be converted. + /// The maximum number of elements contained in the converted array. + /// The converted array. public static unsafe T[] AsSerializableArray(this ReadOnlySpan value, int max = 0x1000000) where T : ISerializable, new() { if (value.IsEmpty) throw new FormatException(); fixed (byte* pointer = value) { - using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, value.Length); - using BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8); + using UnmanagedMemoryStream ms = new(pointer, value.Length); + using BinaryReader reader = new(ms, Utility.StrictUTF8, true); return reader.ReadSerializableArray(max); } } + /// + /// Compresses the specified data using the LZ4 algorithm. + /// + /// The data to be compressed. + /// The compressed data. public static byte[] CompressLz4(this byte[] data) { int maxLength = LZ4Codec.MaximumOutputSize(data.Length); @@ -76,6 +111,12 @@ public static byte[] CompressLz4(this byte[] data) return result; } + /// + /// Decompresses the specified data using the LZ4 algorithm. + /// + /// The compressed data. + /// The maximum data size after decompression. + /// The original data. public static byte[] DecompressLz4(this byte[] data, int maxOutput) { int length = BinaryPrimitives.ReadInt32LittleEndian(data); @@ -86,6 +127,11 @@ public static byte[] DecompressLz4(this byte[] data, int maxOutput) return result; } + /// + /// Fills the buffer with the data in the specified . + /// + /// The to be used. + /// The buffer used to store data. public static void FillBuffer(this BinaryReader reader, Span buffer) { while (!buffer.IsEmpty) @@ -96,6 +142,11 @@ public static void FillBuffer(this BinaryReader reader, Span buffer) } } + /// + /// Gets the size of variable-length of the data. + /// + /// The length of the data. + /// The size of variable-length of the data. public static int GetVarSize(int value) { if (value < 0xFD) @@ -106,6 +157,12 @@ public static int GetVarSize(int value) return sizeof(byte) + sizeof(uint); } + /// + /// Gets the size of the specified array encoded in variable-length encoding. + /// + /// The type of the array element. + /// The specified array. + /// The size of the array. public static int GetVarSize(this IReadOnlyCollection value) { int value_size; @@ -135,12 +192,23 @@ public static int GetVarSize(this IReadOnlyCollection value) return GetVarSize(value.Count) + value_size; } + /// + /// Gets the size of the specified encoded in variable-length encoding. + /// + /// The specified . + /// The size of the . public static int GetVarSize(this string value) { int size = Utility.StrictUTF8.GetByteCount(value); return GetVarSize(size) + size; } + /// + /// Reads a byte array of the specified size from a . + /// + /// The for reading data. + /// The size of the byte array. + /// The byte array read from the . public static byte[] ReadFixedBytes(this BinaryReader reader, int size) { var index = 0; @@ -162,12 +230,25 @@ public static byte[] ReadFixedBytes(this BinaryReader reader, int size) return data; } + /// + /// Reads a of the specified length from a . + /// + /// The for reading data. + /// The length of the . + /// The read from the . public static string ReadFixedString(this BinaryReader reader, int length) { byte[] data = reader.ReadFixedBytes(length); return Utility.StrictUTF8.GetString(data.TakeWhile(p => p != 0).ToArray()); } + /// + /// Reads an array from a . + /// + /// The type of the array element. + /// The for reading data. + /// The maximum number of elements in the array. + /// The array read from the . public static T[] ReadNullableArray(this BinaryReader reader, int max = 0x1000000) where T : class, ISerializable, new() { T[] array = new T[reader.ReadVarInt((ulong)max)]; @@ -176,13 +257,26 @@ public static string ReadFixedString(this BinaryReader reader, int length) return array; } + /// + /// Reads an object from a . + /// + /// The type of the object. + /// The for reading data. + /// The object read from the . public static T ReadSerializable(this BinaryReader reader) where T : ISerializable, new() { - T obj = new T(); + T obj = new(); obj.Deserialize(reader); return obj; } + /// + /// Reads an array from a . + /// + /// The type of the array element. + /// The for reading data. + /// The maximum number of elements in the array. + /// The array read from the . public static T[] ReadSerializableArray(this BinaryReader reader, int max = 0x1000000) where T : ISerializable, new() { T[] array = new T[reader.ReadVarInt((ulong)max)]; @@ -194,11 +288,23 @@ public static string ReadFixedString(this BinaryReader reader, int length) return array; } + /// + /// Reads a byte array from a . + /// + /// The for reading data. + /// The maximum size of the byte array. + /// The byte array read from the . public static byte[] ReadVarBytes(this BinaryReader reader, int max = 0x1000000) { return reader.ReadFixedBytes((int)reader.ReadVarInt((ulong)max)); } + /// + /// Reads an integer from a . + /// + /// The for reading data. + /// The maximum value of the integer. + /// The integer read from the . public static ulong ReadVarInt(this BinaryReader reader, ulong max = ulong.MaxValue) { byte fb = reader.ReadByte(); @@ -215,38 +321,62 @@ public static ulong ReadVarInt(this BinaryReader reader, ulong max = ulong.MaxVa return value; } + /// + /// Reads a from a . + /// + /// The for reading data. + /// The maximum size of the . + /// The read from the . public static string ReadVarString(this BinaryReader reader, int max = 0x1000000) { return Utility.StrictUTF8.GetString(reader.ReadVarBytes(max)); } + /// + /// Converts an object to a byte array. + /// + /// The object to be converted. + /// The converted byte array. public static byte[] ToArray(this ISerializable value) { - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms, Utility.StrictUTF8)) - { - value.Serialize(writer); - writer.Flush(); - return ms.ToArray(); - } + using MemoryStream ms = new(); + using BinaryWriter writer = new(ms, Utility.StrictUTF8, true); + value.Serialize(writer); + writer.Flush(); + return ms.ToArray(); } + /// + /// Converts an array to a byte array. + /// + /// The type of the array element. + /// The array to be converted. + /// The converted byte array. public static byte[] ToByteArray(this IReadOnlyCollection value) where T : ISerializable { - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms, Utility.StrictUTF8)) - { - writer.Write(value); - writer.Flush(); - return ms.ToArray(); - } + using MemoryStream ms = new(); + using BinaryWriter writer = new(ms, Utility.StrictUTF8, true); + writer.Write(value); + writer.Flush(); + return ms.ToArray(); } + /// + /// Writes an object into a . + /// + /// The for writing data. + /// The object to be written. public static void Write(this BinaryWriter writer, ISerializable value) { value.Serialize(writer); } + /// + /// Writes an array into a . + /// + /// The type of the array element. + /// The for writing data. + /// The array to be written. public static void Write(this BinaryWriter writer, IReadOnlyCollection value) where T : ISerializable { writer.WriteVarInt(value.Count); @@ -256,20 +386,32 @@ public static void Write(this BinaryWriter writer, IReadOnlyCollection val } } + /// + /// Writes a into a . + /// + /// The for writing data. + /// The to be written. + /// The fixed size of the . public static void WriteFixedString(this BinaryWriter writer, string value, int length) { if (value == null) throw new ArgumentNullException(nameof(value)); if (value.Length > length) - throw new ArgumentException(); + throw new ArgumentException(null, nameof(value)); byte[] bytes = Utility.StrictUTF8.GetBytes(value); if (bytes.Length > length) - throw new ArgumentException(); + throw new ArgumentException(null, nameof(value)); writer.Write(bytes); if (bytes.Length < length) writer.Write(stackalloc byte[length - bytes.Length]); } + /// + /// Writes an array into a . + /// + /// The type of the array element. + /// The for writing data. + /// The array to be written. public static void WriteNullableArray(this BinaryWriter writer, T[] value) where T : class, ISerializable { writer.WriteVarInt(value.Length); @@ -282,16 +424,26 @@ public static void WriteNullableArray(this BinaryWriter writer, T[] value) wh } } + /// + /// Writes a byte array into a . + /// + /// The for writing data. + /// The byte array to be written. public static void WriteVarBytes(this BinaryWriter writer, ReadOnlySpan value) { writer.WriteVarInt(value.Length); writer.Write(value); } + /// + /// Writes an integer into a . + /// + /// The for writing data. + /// The integer to be written. public static void WriteVarInt(this BinaryWriter writer, long value) { if (value < 0) - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(value)); if (value < 0xFD) { writer.Write((byte)value); @@ -313,6 +465,11 @@ public static void WriteVarInt(this BinaryWriter writer, long value) } } + /// + /// Writes a into a . + /// + /// The for writing data. + /// The to be written. public static void WriteVarString(this BinaryWriter writer, string value) { writer.WriteVarBytes(Utility.StrictUTF8.GetBytes(value)); diff --git a/src/neo/IO/ISerializable.cs b/src/neo/IO/ISerializable.cs index d03540deb2..6167608c07 100644 --- a/src/neo/IO/ISerializable.cs +++ b/src/neo/IO/ISerializable.cs @@ -2,11 +2,26 @@ namespace Neo.IO { + /// + /// Represents NEO objects that can be serialized. + /// public interface ISerializable { + /// + /// The size of the object in bytes after serialization. + /// int Size { get; } + /// + /// Serializes the object using the specified . + /// + /// The for writing data. void Serialize(BinaryWriter writer); + + /// + /// Deserializes the object using the specified . + /// + /// The for reading data. void Deserialize(BinaryReader reader); } } diff --git a/src/neo/IO/Json/JArray.cs b/src/neo/IO/Json/JArray.cs index 4da0659f68..c7359afbc1 100644 --- a/src/neo/IO/Json/JArray.cs +++ b/src/neo/IO/Json/JArray.cs @@ -5,14 +5,25 @@ namespace Neo.IO.Json { + /// + /// Represents a JSON array. + /// public class JArray : JObject, IList { - private readonly List items = new List(); + private readonly List items = new(); + /// + /// Initializes a new instance of the class. + /// + /// The initial items in the array. public JArray(params JObject[] items) : this((IEnumerable)items) { } + /// + /// Initializes a new instance of the class. + /// + /// The initial items in the array. public JArray(IEnumerable items) { this.items.AddRange(items); diff --git a/src/neo/IO/Json/JBoolean.cs b/src/neo/IO/Json/JBoolean.cs index 71e4c5dc3f..29be911c2f 100644 --- a/src/neo/IO/Json/JBoolean.cs +++ b/src/neo/IO/Json/JBoolean.cs @@ -2,10 +2,20 @@ namespace Neo.IO.Json { + /// + /// Represents a JSON boolean value. + /// public class JBoolean : JObject { - public bool Value { get; private set; } + /// + /// Gets the value of the JSON object. + /// + public bool Value { get; } + /// + /// Initializes a new instance of the class with the specified value. + /// + /// The value of the JSON object. public JBoolean(bool value = false) { this.Value = value; @@ -16,6 +26,10 @@ public override bool AsBoolean() return Value; } + /// + /// Converts the current JSON object to a floating point number. + /// + /// The number 1 if value is ; otherwise, 0. public override double AsNumber() { return Value ? 1 : 0; diff --git a/src/neo/IO/Json/JNumber.cs b/src/neo/IO/Json/JNumber.cs index 356b024522..00d07c1715 100644 --- a/src/neo/IO/Json/JNumber.cs +++ b/src/neo/IO/Json/JNumber.cs @@ -4,19 +4,40 @@ namespace Neo.IO.Json { + /// + /// Represents a JSON number. + /// public class JNumber : JObject { + /// + /// Represents the largest safe integer in JSON. + /// public static readonly long MAX_SAFE_INTEGER = (long)Math.Pow(2, 53) - 1; + + /// + /// Represents the smallest safe integer in JSON. + /// public static readonly long MIN_SAFE_INTEGER = -MAX_SAFE_INTEGER; - public double Value { get; private set; } + /// + /// Gets the value of the JSON object. + /// + public double Value { get; } + /// + /// Initializes a new instance of the class with the specified value. + /// + /// The value of the JSON object. public JNumber(double value = 0) { if (!double.IsFinite(value)) throw new FormatException(); this.Value = value; } + /// + /// Converts the current JSON object to a boolean value. + /// + /// if value is not zero; otherwise, . public override bool AsBoolean() { return Value != 0; diff --git a/src/neo/IO/Json/JObject.cs b/src/neo/IO/Json/JObject.cs index 8aae4bf76f..9f5534753e 100644 --- a/src/neo/IO/Json/JObject.cs +++ b/src/neo/IO/Json/JObject.cs @@ -6,11 +6,26 @@ namespace Neo.IO.Json { + /// + /// Represents a JSON object. + /// public class JObject { + /// + /// Represents a object. + /// public static readonly JObject Null = null; + + /// + /// Gets or sets the properties of the JSON object. + /// public IDictionary Properties { get; } = new OrderedDictionary(); + /// + /// Gets or sets the properties of the JSON object. + /// + /// The name of the property to get or set. + /// The property with the specified name. public JObject this[string name] { get @@ -25,30 +40,64 @@ public JObject this[string name] } } + /// + /// Converts the current JSON object to a boolean value. + /// + /// The converted value. public virtual bool AsBoolean() { return true; } + /// + /// Converts the current JSON object to a floating point number. + /// + /// The converted value. public virtual double AsNumber() { return double.NaN; } + /// + /// Converts the current JSON object to a . + /// + /// The converted value. public virtual string AsString() { return ToString(); } + /// + /// Determines whether the JSON object contains a property with the specified name. + /// + /// The property name to locate in the JSON object. + /// if the JSON object contains a property with the name; otherwise, . public bool ContainsProperty(string key) { return Properties.ContainsKey(key); } + /// + /// Converts the current JSON object to a object. + /// + /// The converted value. + /// The JSON object is not a . public virtual JArray GetArray() => throw new InvalidCastException(); + /// + /// Converts the current JSON object to a boolean value. + /// + /// The converted value. + /// The JSON object is not a . public virtual bool GetBoolean() => throw new InvalidCastException(); + /// + /// Converts the current JSON object to a 32-bit signed integer. + /// + /// The converted value. + /// The JSON object is not a . + /// The JSON object cannot be converted to an integer. + /// The JSON object cannot be converted to a 32-bit signed integer. public int GetInt32() { double d = GetNumber(); @@ -56,13 +105,29 @@ public int GetInt32() return checked((int)d); } + /// + /// Converts the current JSON object to a floating point number. + /// + /// The converted value. + /// The JSON object is not a . public virtual double GetNumber() => throw new InvalidCastException(); + /// + /// Converts the current JSON object to a . + /// + /// The converted value. + /// The JSON object is not a . public virtual string GetString() => throw new InvalidCastException(); + /// + /// Parses a JSON object from a byte array. + /// + /// The byte array that contains the JSON object. + /// The maximum nesting depth when parsing the JSON object. + /// The parsed JSON object. public static JObject Parse(ReadOnlySpan value, int max_nest = 100) { - Utf8JsonReader reader = new Utf8JsonReader(value, new JsonReaderOptions + Utf8JsonReader reader = new(value, new JsonReaderOptions { AllowTrailingCommas = false, CommentHandling = JsonCommentHandling.Skip, @@ -80,6 +145,12 @@ public static JObject Parse(ReadOnlySpan value, int max_nest = 100) } } + /// + /// Parses a JSON object from a . + /// + /// The that contains the JSON object. + /// The maximum nesting depth when parsing the JSON object. + /// The parsed JSON object. public static JObject Parse(string value, int max_nest = 100) { return Parse(Utility.StrictUTF8.GetBytes(value), max_nest); @@ -103,7 +174,7 @@ private static JObject Read(ref Utf8JsonReader reader, bool skipReading = false) private static JArray ReadArray(ref Utf8JsonReader reader) { - JArray array = new JArray(); + JArray array = new(); while (reader.Read()) { switch (reader.TokenType) @@ -120,7 +191,7 @@ private static JArray ReadArray(ref Utf8JsonReader reader) private static JObject ReadObject(ref Utf8JsonReader reader) { - JObject obj = new JObject(); + JObject obj = new(); while (reader.Read()) { switch (reader.TokenType) @@ -152,10 +223,15 @@ private static string ReadString(ref Utf8JsonReader reader) } } + /// + /// Encode the current JSON object into a byte array. + /// + /// Indicates whether indentation is required. + /// The encoded JSON object. public byte[] ToByteArray(bool indented) { - using MemoryStream ms = new MemoryStream(); - using Utf8JsonWriter writer = new Utf8JsonWriter(ms, new JsonWriterOptions + using MemoryStream ms = new(); + using Utf8JsonWriter writer = new(ms, new JsonWriterOptions { Indented = indented, SkipValidation = true @@ -165,16 +241,32 @@ public byte[] ToByteArray(bool indented) return ms.ToArray(); } + /// + /// Encode the current JSON object into a . + /// + /// The encoded JSON object. public override string ToString() { return ToString(false); } + /// + /// Encode the current JSON object into a . + /// + /// Indicates whether indentation is required. + /// The encoded JSON object. public string ToString(bool indented) { return Utility.StrictUTF8.GetString(ToByteArray(indented)); } + /// + /// Converts the current JSON object to an . + /// + /// The type of the . + /// If the current JSON object cannot be converted to type , then the default value is returned. + /// Indicates whether case should be ignored during conversion. + /// The converted value. public virtual T TryGetEnum(T defaultValue = default, bool ignoreCase = false) where T : Enum { return defaultValue; @@ -219,6 +311,10 @@ public static implicit operator JObject(string value) return (JString)value; } + /// + /// Creates a copy of the current JSON object. + /// + /// A copy of the current JSON object. public virtual JObject Clone() { var cloned = new JObject(); diff --git a/src/neo/IO/Json/JString.cs b/src/neo/IO/Json/JString.cs index c057c0cb34..834f32de24 100644 --- a/src/neo/IO/Json/JString.cs +++ b/src/neo/IO/Json/JString.cs @@ -4,15 +4,29 @@ namespace Neo.IO.Json { + /// + /// Represents a JSON string. + /// public class JString : JObject { - public string Value { get; private set; } + /// + /// Gets the value of the JSON object. + /// + public string Value { get; } + /// + /// Initializes a new instance of the class with the specified value. + /// + /// The value of the JSON object. public JString(string value) { - this.Value = value ?? throw new ArgumentNullException(); + this.Value = value ?? throw new ArgumentNullException(nameof(value)); } + /// + /// Converts the current JSON object to a boolean value. + /// + /// if value is not empty; otherwise, . public override bool AsBoolean() { return !string.IsNullOrEmpty(Value); diff --git a/src/neo/Ledger/Blockchain.ApplicationExecuted.cs b/src/neo/Ledger/Blockchain.ApplicationExecuted.cs index 698fffeadd..3a5f0d5aa0 100644 --- a/src/neo/Ledger/Blockchain.ApplicationExecuted.cs +++ b/src/neo/Ledger/Blockchain.ApplicationExecuted.cs @@ -11,13 +11,40 @@ partial class Blockchain { partial class ApplicationExecuted { - public Transaction Transaction; - public TriggerType Trigger { get; internal set; } - public VMState VMState { get; internal set; } - public Exception Exception { get; internal set; } - public long GasConsumed { get; internal set; } - public StackItem[] Stack { get; internal set; } - public NotifyEventArgs[] Notifications { get; internal set; } + /// + /// The transaction that contains the executed script. This field could be if the contract is invoked by system. + /// + public Transaction Transaction { get; } + + /// + /// The trigger of the execution. + /// + public TriggerType Trigger { get; } + + /// + /// The state of the virtual machine after the contract is executed. + /// + public VMState VMState { get; } + + /// + /// The exception that caused the execution to terminate abnormally. This field could be if the execution ends normally. + /// + public Exception Exception { get; } + + /// + /// GAS spent to execute. + /// + public long GasConsumed { get; } + + /// + /// Items on the stack of the virtual machine after execution. + /// + public StackItem[] Stack { get; } + + /// + /// The notifications sent during the execution. + /// + public NotifyEventArgs[] Notifications { get; } internal ApplicationExecuted(ApplicationEngine engine) { diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 8a6c7e0982..e135194687 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -17,40 +17,118 @@ namespace Neo.Ledger { + /// + /// Actor used to verify and relay . + /// public sealed partial class Blockchain : UntypedActor { + /// + /// Sent by the when a smart contract is executed. + /// public partial class ApplicationExecuted { } - public class PersistCompleted { public Block Block; } - public class Import { public IEnumerable Blocks; public bool Verify = true; } + + /// + /// Sent by the when a is persisted. + /// + public class PersistCompleted + { + /// + /// The that is persisted. + /// + public Block Block { get; init; } + } + + /// + /// Sent to the when importing blocks. + /// + public class Import + { + /// + /// The blocks to be imported. + /// + public IEnumerable Blocks { get; init; } + + /// + /// Indicates whether the blocks need to be verified when importing. + /// + public bool Verify { get; init; } = true; + } + + /// + /// Sent by the when the import is complete. + /// public class ImportCompleted { } - public class FillMemoryPool { public IEnumerable Transactions; } + + /// + /// Sent to the when the consensus is filling the memory pool. + /// + public class FillMemoryPool + { + /// + /// The transactions to be sent. + /// + public IEnumerable Transactions { get; init; } + } + + /// + /// Sent by the when the memory pool is filled. + /// public class FillCompleted { } - public class Reverify { public IInventory[] Inventories; } - public class RelayResult { public IInventory Inventory; public VerifyResult Result; } + + /// + /// Sent to the when inventories need to be re-verified. + /// + public class Reverify + { + /// + /// The inventories to be re-verified. + /// + public IReadOnlyList Inventories { get; init; } + } + + /// + /// Sent by the when an is relayed. + /// + public class RelayResult + { + /// + /// The that is relayed. + /// + public IInventory Inventory { get; init; } + /// + /// The result. + /// + public VerifyResult Result { get; init; } + } + internal class Initialize { } - private class UnverifiedBlocksList { public LinkedList Blocks = new LinkedList(); public HashSet Nodes = new HashSet(); } + private class UnverifiedBlocksList { public LinkedList Blocks = new(); public HashSet Nodes = new(); } private readonly static Script onPersistScript, postPersistScript; private const int MaxTxToReverifyPerIdle = 10; private readonly NeoSystem system; - private readonly Dictionary block_cache = new Dictionary(); - private readonly Dictionary block_cache_unverified = new Dictionary(); + private readonly Dictionary block_cache = new(); + private readonly Dictionary block_cache_unverified = new(); private ImmutableHashSet extensibleWitnessWhiteList; static Blockchain() { - using (ScriptBuilder sb = new ScriptBuilder()) + using (ScriptBuilder sb = new()) { sb.EmitSysCall(ApplicationEngine.System_Contract_NativeOnPersist); onPersistScript = sb.ToArray(); } - using (ScriptBuilder sb = new ScriptBuilder()) + using (ScriptBuilder sb = new()) { sb.EmitSysCall(ApplicationEngine.System_Contract_NativePostPersist); postPersistScript = sb.ToArray(); } } + /// + /// Initializes a new instance of the class. + /// + /// The object that contains the . public Blockchain(NeoSystem system) { this.system = system; @@ -172,7 +250,7 @@ private VerifyResult OnNewBlock(Block block) if (block.Index == currentHeight + 1) { Block block_persist = block; - List blocksToPersistList = new List(); + List blocksToPersistList = new(); while (true) { blocksToPersistList.Add(block_persist); @@ -302,12 +380,12 @@ private void Persist(Block block) { using (SnapshotCache snapshot = system.GetSnapshot()) { - List all_application_executed = new List(); + List all_application_executed = new(); using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.OnPersist, null, snapshot, block, system.Settings, 0)) { engine.LoadScript(onPersistScript); if (engine.Execute() != VMState.HALT) throw new InvalidOperationException(); - ApplicationExecuted application_executed = new ApplicationExecuted(engine); + ApplicationExecuted application_executed = new(engine); Context.System.EventStream.Publish(application_executed); all_application_executed.Add(application_executed); } @@ -315,27 +393,25 @@ private void Persist(Block block) // Warning: Do not write into variable snapshot directly. Write into variable clonedSnapshot and commit instead. foreach (Transaction tx in block.Transactions) { - using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, clonedSnapshot, block, system.Settings, tx.SystemFee)) + using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.Application, tx, clonedSnapshot, block, system.Settings, tx.SystemFee); + engine.LoadScript(tx.Script); + if (engine.Execute() == VMState.HALT) { - engine.LoadScript(tx.Script); - if (engine.Execute() == VMState.HALT) - { - clonedSnapshot.Commit(); - } - else - { - clonedSnapshot = snapshot.CreateSnapshot(); - } - ApplicationExecuted application_executed = new ApplicationExecuted(engine); - Context.System.EventStream.Publish(application_executed); - all_application_executed.Add(application_executed); + clonedSnapshot.Commit(); } + else + { + clonedSnapshot = snapshot.CreateSnapshot(); + } + ApplicationExecuted application_executed = new(engine); + Context.System.EventStream.Publish(application_executed); + all_application_executed.Add(application_executed); } using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.PostPersist, null, snapshot, block, system.Settings, 0)) { engine.LoadScript(postPersistScript); if (engine.Execute() != VMState.HALT) throw new InvalidOperationException(); - ApplicationExecuted application_executed = new ApplicationExecuted(engine); + ApplicationExecuted application_executed = new(engine); Context.System.EventStream.Publish(application_executed); all_application_executed.Add(application_executed); } @@ -370,6 +446,11 @@ private void Persist(Block block) Debug.Assert(header.Index == block.Index); } + /// + /// Gets a object used for creating the actor. + /// + /// The object that contains the . + /// The object used for creating the actor. public static Props Props(NeoSystem system) { return Akka.Actor.Props.Create(() => new Blockchain(system)).WithMailbox("blockchain-mailbox"); @@ -377,7 +458,7 @@ public static Props Props(NeoSystem system) private void SendRelayResult(IInventory inventory, VerifyResult result) { - RelayResult rr = new RelayResult + RelayResult rr = new() { Inventory = inventory, Result = result @@ -412,23 +493,18 @@ private static ImmutableHashSet UpdateExtensibleWitnessWhiteList(Protoc internal class BlockchainMailbox : PriorityMailbox { - public BlockchainMailbox(Akka.Actor.Settings settings, Config config) + public BlockchainMailbox(Settings settings, Config config) : base(settings, config) { } internal protected override bool IsHighPriority(object message) { - switch (message) + return message switch { - case Header[] _: - case Block _: - case ExtensiblePayload _: - case Terminated _: - return true; - default: - return false; - } + Header[] or Block or ExtensiblePayload or Terminated => true, + _ => false, + }; } } } diff --git a/src/neo/Ledger/HeaderCache.cs b/src/neo/Ledger/HeaderCache.cs index 3c65c408d9..6ac75a6440 100644 --- a/src/neo/Ledger/HeaderCache.cs +++ b/src/neo/Ledger/HeaderCache.cs @@ -7,11 +7,19 @@ namespace Neo.Ledger { + /// + /// Used to cache the headers of the blocks that have not been received. + /// public sealed class HeaderCache : IDisposable, IEnumerable
{ - private readonly IndexedQueue
headers = new IndexedQueue
(); - private readonly ReaderWriterLockSlim readerWriterLock = new ReaderWriterLockSlim(); + private readonly IndexedQueue
headers = new(); + private readonly ReaderWriterLockSlim readerWriterLock = new(); + /// + /// Gets the at the specified index in the cache. + /// + /// The zero-based index of the to get. + /// The at the specified index in the cache. public Header this[uint index] { get @@ -33,9 +41,19 @@ public Header this[uint index] } } + /// + /// Gets the number of elements in the cache. + /// public int Count => headers.Count; + + /// + /// Indicates whether the cache is full. + /// public bool Full => headers.Count >= 10000; + /// + /// Gets the last in the cache. Or if the cache is empty. + /// public Header Last { get diff --git a/src/neo/Ledger/MemoryPool.cs b/src/neo/Ledger/MemoryPool.cs index 11f54173c0..36f8f4a7fe 100644 --- a/src/neo/Ledger/MemoryPool.cs +++ b/src/neo/Ledger/MemoryPool.cs @@ -12,6 +12,9 @@ namespace Neo.Ledger { + /// + /// Used to cache verified transactions before being written into the block. + /// public class MemoryPool : IReadOnlyCollection { // Allow a reverified transaction to be rebroadcasted if it has been this many block times since last broadcast. @@ -33,16 +36,16 @@ public class MemoryPool : IReadOnlyCollection /// performed by the blockchain actor do not need to acquire the read lock; they only need the write /// lock for write operations. ///
- private readonly ReaderWriterLockSlim _txRwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + private readonly ReaderWriterLockSlim _txRwLock = new(LockRecursionPolicy.SupportsRecursion); /// /// Store all verified unsorted transactions currently in the pool. /// - private readonly Dictionary _unsortedTransactions = new Dictionary(); + private readonly Dictionary _unsortedTransactions = new(); /// /// Stores the verified sorted transactins currently in the pool. /// - private readonly SortedSet _sortedTransactions = new SortedSet(); + private readonly SortedSet _sortedTransactions = new(); /// /// Store the unverified transactions currently in the pool. @@ -51,8 +54,8 @@ public class MemoryPool : IReadOnlyCollection /// The top ones that could make it into the next block get verified and moved into the verified data structures /// (_unsortedTransactions, and _sortedTransactions) after each block. /// - private readonly Dictionary _unverifiedTransactions = new Dictionary(); - private readonly SortedSet _unverifiedSortedTransactions = new SortedSet(); + private readonly Dictionary _unverifiedTransactions = new(); + private readonly SortedSet _unverifiedSortedTransactions = new(); // Internal methods to aid in unit testing internal int SortedTxCount => _sortedTransactions.Count; @@ -66,7 +69,7 @@ public class MemoryPool : IReadOnlyCollection /// /// Store all verified unsorted transactions' senders' fee currently in the memory pool. /// - private TransactionVerificationContext VerificationContext = new TransactionVerificationContext(); + private TransactionVerificationContext VerificationContext = new(); /// /// Total count of transactions in the pool. @@ -92,8 +95,15 @@ public int Count /// public int VerifiedCount => _unsortedTransactions.Count; // read of 32 bit type is atomic (no lock) + /// + /// Total count of unverified transactions in the pool. + /// public int UnVerifiedCount => _unverifiedTransactions.Count; + /// + /// Initializes a new instance of the class. + /// + /// The object that contains the . public MemoryPool(NeoSystem system) { _system = system; @@ -104,11 +114,13 @@ public MemoryPool(NeoSystem system) /// /// Determine whether the pool is holding this transaction and has at some point verified it. - /// Note: The pool may not have verified it since the last block was persisted. To get only the - /// transactions that have been verified during this block use GetVerifiedTransactions() /// - /// the transaction hash - /// true if the MemoryPool contain the transaction + /// The transaction hash. + /// if the contains the transaction; otherwise, . + /// + /// Note: The pool may not have verified it since the last block was persisted. To get only the + /// transactions that have been verified during this block use . + /// public bool ContainsKey(UInt256 hash) { _txRwLock.EnterReadLock(); @@ -122,6 +134,12 @@ public bool ContainsKey(UInt256 hash) } } + /// + /// Gets the associated with the specified hash. + /// + /// The hash of the to get. + /// When this method returns, contains the associated with the specified hash, if the hash is found; otherwise, . + /// if the contains a with the specified hash; otherwise, . public bool TryGetValue(UInt256 hash, out Transaction tx) { _txRwLock.EnterReadLock(); @@ -157,6 +175,10 @@ public IEnumerator GetEnumerator() IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + /// + /// Gets the verified transactions in the . + /// + /// The verified transactions. public IEnumerable GetVerifiedTransactions() { _txRwLock.EnterReadLock(); @@ -170,6 +192,11 @@ public IEnumerable GetVerifiedTransactions() } } + /// + /// Gets both the verified and the unverified transactions in the . + /// + /// The verified transactions. + /// The unverified transactions. public void GetVerifiedAndUnverifiedTransactions(out IEnumerable verifiedTransactions, out IEnumerable unverifiedTransactions) { @@ -185,6 +212,10 @@ public void GetVerifiedAndUnverifiedTransactions(out IEnumerable ve } } + /// + /// Gets the sorted verified transactions in the . + /// + /// The sorted verified transactions. public IEnumerable GetSortedVerifiedTransactions() { _txRwLock.EnterReadLock(); @@ -240,15 +271,6 @@ internal bool CanTransactionFitInPool(Transaction tx) return GetLowestFeeTransaction(out _, out _).CompareTo(tx) <= 0; } - /// - /// Adds an already verified transaction to the memory pool. - /// - /// Note: This must only be called from a single thread (the Blockchain actor). To add a transaction to the pool - /// tell the Blockchain actor about the transaction. - /// - /// - /// - /// internal VerifyResult TryAdd(Transaction tx, DataCache snapshot) { var poolItem = new PoolItem(tx); @@ -287,7 +309,7 @@ internal VerifyResult TryAdd(Transaction tx, DataCache snapshot) private List RemoveOverCapacity() { - List removedTransactions = new List(); + List removedTransactions = new(); do { PoolItem minItem = GetLowestFeeTransaction(out var unsortedPool, out var sortedPool); @@ -387,8 +409,8 @@ private int ReverifyTransactions(SortedSet verifiedSortedTxPool, SortedSet unverifiedSortedTxPool, int count, double millisecondsTimeout, DataCache snapshot) { DateTime reverifyCutOffTimeStamp = TimeProvider.Current.UtcNow.AddMilliseconds(millisecondsTimeout); - List reverifiedItems = new List(count); - List invalidItems = new List(); + List reverifiedItems = new(count); + List invalidItems = new(); _txRwLock.EnterWriteLock(); try diff --git a/src/neo/Ledger/PoolItem.cs b/src/neo/Ledger/PoolItem.cs index e7f5fa999f..317388b5c7 100644 --- a/src/neo/Ledger/PoolItem.cs +++ b/src/neo/Ledger/PoolItem.cs @@ -6,7 +6,7 @@ namespace Neo.Ledger /// /// Represents an item in the Memory Pool. /// - // Note: PoolItem objects don't consider transaction priority (low or high) in their compare CompareTo method. + /// Note: PoolItem objects don't consider transaction priority (low or high) in their compare CompareTo method. /// This is because items of differing priority are never added to the same sorted set in MemoryPool. /// internal class PoolItem : IComparable diff --git a/src/neo/Ledger/TransactionVerificationContext.cs b/src/neo/Ledger/TransactionVerificationContext.cs index cfa9dc15cd..7e6b654bbe 100644 --- a/src/neo/Ledger/TransactionVerificationContext.cs +++ b/src/neo/Ledger/TransactionVerificationContext.cs @@ -6,18 +6,25 @@ namespace Neo.Ledger { + /// + /// The context used to verify the transaction. + /// public class TransactionVerificationContext { /// /// Store all verified unsorted transactions' senders' fee currently in the memory pool. /// - private readonly Dictionary senderFee = new Dictionary(); + private readonly Dictionary senderFee = new(); /// /// Store oracle responses /// - private readonly Dictionary oracleResponses = new Dictionary(); + private readonly Dictionary oracleResponses = new(); + /// + /// Adds a verified to the context. + /// + /// The verified . public void AddTransaction(Transaction tx) { var oracle = tx.GetAttribute(); @@ -29,6 +36,12 @@ public void AddTransaction(Transaction tx) senderFee.Add(tx.Sender, tx.SystemFee + tx.NetworkFee); } + /// + /// Determine whether the specified conflicts with other transactions. + /// + /// The specified . + /// The snapshot used to verify the . + /// if the passes the check; otherwise, . public bool CheckTransaction(Transaction tx, DataCache snapshot) { BigInteger balance = NativeContract.GAS.BalanceOf(snapshot, tx.Sender); @@ -44,6 +57,10 @@ public bool CheckTransaction(Transaction tx, DataCache snapshot) return true; } + /// + /// Removes a from the context. + /// + /// The to be removed. public void RemoveTransaction(Transaction tx) { if ((senderFee[tx.Sender] -= tx.SystemFee + tx.NetworkFee) == 0) senderFee.Remove(tx.Sender); diff --git a/src/neo/Ledger/VerifyResult.cs b/src/neo/Ledger/VerifyResult.cs index ab5c1f673e..383aca36e4 100644 --- a/src/neo/Ledger/VerifyResult.cs +++ b/src/neo/Ledger/VerifyResult.cs @@ -1,15 +1,55 @@ +using Neo.Network.P2P.Payloads; + namespace Neo.Ledger { + /// + /// Represents a verifying result of . + /// public enum VerifyResult : byte { + /// + /// Indicates that the verification was successful. + /// Succeed, + + /// + /// Indicates that an with the same hash already exists. + /// AlreadyExists, + + /// + /// Indicates that the is full and the transaction cannot be verified. + /// OutOfMemory, + + /// + /// Indicates that the previous block of the current block has not been received, so the block cannot be verified. + /// UnableToVerify, + + /// + /// Indicates that the is invalid. + /// Invalid, + + /// + /// Indicates that the has expired. + /// Expired, + + /// + /// Indicates that the failed to verify due to insufficient fees. + /// InsufficientFunds, + + /// + /// Indicates that the failed to verify because it didn't comply with the policy. + /// PolicyFail, + + /// + /// Indicates that the failed to verify due to other reasons. + /// Unknown } } diff --git a/src/neo/LogLevel.cs b/src/neo/LogLevel.cs index 0c1c84334d..79951c8988 100644 --- a/src/neo/LogLevel.cs +++ b/src/neo/LogLevel.cs @@ -2,12 +2,34 @@ namespace Neo { + /// + /// Represents the level of logs. + /// public enum LogLevel : byte { + /// + /// The debug log level. + /// Debug = DebugLevel, + + /// + /// The information log level. + /// Info = InfoLevel, + + /// + /// The warning log level. + /// Warning = WarningLevel, + + /// + /// The error log level. + /// Error = ErrorLevel, + + /// + /// The fatal log level. + /// Fatal = Error + 1 } } diff --git a/src/neo/NeoSystem.cs b/src/neo/NeoSystem.cs index fabb540ca1..0d530b9711 100644 --- a/src/neo/NeoSystem.cs +++ b/src/neo/NeoSystem.cs @@ -16,20 +16,55 @@ namespace Neo { + /// + /// Represents the basic unit that contains all the components required for running of a NEO node. + /// public class NeoSystem : IDisposable { + /// + /// Triggered when a service is added to the . + /// public event EventHandler ServiceAdded; + + /// + /// The protocol settings of the . + /// public ProtocolSettings Settings { get; } + + /// + /// The used to create actors for the . + /// public ActorSystem ActorSystem { get; } = ActorSystem.Create(nameof(NeoSystem), $"akka {{ log-dead-letters = off , loglevel = warning, loggers = [ \"{typeof(Utility.Logger).AssemblyQualifiedName}\" ] }}" + $"blockchain-mailbox {{ mailbox-type: \"{typeof(BlockchainMailbox).AssemblyQualifiedName}\" }}" + $"task-manager-mailbox {{ mailbox-type: \"{typeof(TaskManagerMailbox).AssemblyQualifiedName}\" }}" + $"remote-node-mailbox {{ mailbox-type: \"{typeof(RemoteNodeMailbox).AssemblyQualifiedName}\" }}"); + + /// + /// The genesis block of the NEO blockchain. + /// public Block GenesisBlock { get; } + + /// + /// The actor of the . + /// public IActorRef Blockchain { get; } + + /// + /// The actor of the . + /// public IActorRef LocalNode { get; } + + /// + /// The actor of the . + /// public IActorRef TaskManager { get; } + + /// + /// The transaction router actor of the . + /// public IActorRef TxRouter; + /// /// A readonly view of the store. /// @@ -37,9 +72,18 @@ public class NeoSystem : IDisposable /// It doesn't need to be disposed because the inside it is null. /// public DataCache StoreView => new SnapshotCache(store); + + /// + /// The memory pool of the . + /// public MemoryPool MemPool { get; } - public HeaderCache HeaderCache { get; } = new HeaderCache(); - internal RelayCache RelayCache { get; } = new RelayCache(100); + + /// + /// The header cache of the . + /// + public HeaderCache HeaderCache { get; } = new(); + + internal RelayCache RelayCache { get; } = new(100); private ImmutableList services = ImmutableList.Empty; private readonly string storage_engine; @@ -55,6 +99,12 @@ static NeoSystem() Plugin.LoadPlugins(); } + /// + /// Initializes a new instance of the class. + /// + /// The protocol settings of the . + /// The storage engine used to create the objects. If this parameter is , a default in-memory storage engine will be used. + /// The path of the storage. If is the default in-memory storage engine, this parameter is ignored. public NeoSystem(ProtocolSettings settings, string storageEngine = null, string storagePath = null) { this.Settings = settings; @@ -71,7 +121,12 @@ public NeoSystem(ProtocolSettings settings, string storageEngine = null, string Blockchain.Ask(new Blockchain.Initialize()).Wait(); } - public static Block CreateGenesisBlock(ProtocolSettings settings) => new Block + /// + /// Creates the genesis block for the NEO blockchain. + /// + /// The of the NEO system. + /// The genesis block. + public static Block CreateGenesisBlock(ProtocolSettings settings) => new() { Header = new Header { @@ -107,12 +162,22 @@ public void Dispose() store.Dispose(); } + /// + /// Adds a service to the . + /// + /// The service object to be added. public void AddService(object service) { ImmutableInterlocked.Update(ref services, p => p.Add(service)); ServiceAdded?.Invoke(this, service); } + /// + /// Gets a specified type of service object from the . + /// + /// The type of the service object. + /// An action used to filter the service objects. This parameter can be . + /// The service object found. public T GetService(Func filter = null) { IEnumerable result = services.OfType(); @@ -122,6 +187,10 @@ public T GetService(Func filter = null) return result.FirstOrDefault(filter); } + /// + /// Blocks the current thread until the specified actor has stopped. + /// + /// The actor to wait. public void EnsureStoped(IActorRef actor) { using Inbox inbox = Inbox.Create(ActorSystem); @@ -130,6 +199,11 @@ public void EnsureStoped(IActorRef actor) inbox.Receive(TimeSpan.FromMinutes(5)); } + /// + /// Loads an at the specified path. + /// + /// The path of the storage. + /// The loaded . public IStore LoadStore(string path) { return string.IsNullOrEmpty(storage_engine) || storage_engine == nameof(MemoryStore) @@ -137,6 +211,10 @@ public IStore LoadStore(string path) : Plugin.Storages[storage_engine].GetStore(path); } + /// + /// Resumes the startup process of . + /// + /// if the startup process is resumed; otherwise, . public bool ResumeNodeStartup() { if (Interlocked.Decrement(ref suspend) != 0) @@ -149,6 +227,10 @@ public bool ResumeNodeStartup() return true; } + /// + /// Starts the with the specified configuration. + /// + /// The configuration used to start the . public void StartNode(ChannelsConfig config) { start_message = config; @@ -160,16 +242,28 @@ public void StartNode(ChannelsConfig config) } } + /// + /// Suspends the startup process of . + /// public void SuspendNodeStartup() { Interlocked.Increment(ref suspend); } + /// + /// Gets a snapshot of the blockchain storage. + /// + /// public SnapshotCache GetSnapshot() { return new SnapshotCache(store.GetSnapshot()); } + /// + /// Determines whether the specified transaction exists in the memory pool or storage. + /// + /// The hash of the transaction + /// if the transaction exists; otherwise, . public bool ContainsTransaction(UInt256 hash) { if (MemPool.ContainsKey(hash)) return true; diff --git a/src/neo/Network/P2P/Capabilities/FullNodeCapability.cs b/src/neo/Network/P2P/Capabilities/FullNodeCapability.cs index ccaabbdb5a..c1fd2e6ce1 100644 --- a/src/neo/Network/P2P/Capabilities/FullNodeCapability.cs +++ b/src/neo/Network/P2P/Capabilities/FullNodeCapability.cs @@ -2,8 +2,14 @@ namespace Neo.Network.P2P.Capabilities { + /// + /// Indicates that a node has complete block data. + /// public class FullNodeCapability : NodeCapability { + /// + /// Indicates the current block height of the node. + /// public uint StartHeight; public override int Size => @@ -11,9 +17,9 @@ public class FullNodeCapability : NodeCapability sizeof(uint); // Start Height /// - /// Constructor + /// Initializes a new instance of the class. /// - /// Start Height + /// The current block height of the node. public FullNodeCapability(uint startHeight = 0) : base(NodeCapabilityType.FullNode) { StartHeight = startHeight; diff --git a/src/neo/Network/P2P/Capabilities/NodeCapability.cs b/src/neo/Network/P2P/Capabilities/NodeCapability.cs index 034afb6be8..7a6f458c2b 100644 --- a/src/neo/Network/P2P/Capabilities/NodeCapability.cs +++ b/src/neo/Network/P2P/Capabilities/NodeCapability.cs @@ -4,19 +4,22 @@ namespace Neo.Network.P2P.Capabilities { + /// + /// Represents the capabilities of a NEO node. + /// public abstract class NodeCapability : ISerializable { /// - /// Type + /// Indicates the type of the . /// public readonly NodeCapabilityType Type; public virtual int Size => sizeof(NodeCapabilityType); // Type /// - /// Constructor + /// Initializes a new instance of the class. /// - /// Type + /// The type of the . protected NodeCapability(NodeCapabilityType type) { this.Type = type; @@ -32,26 +35,28 @@ void ISerializable.Deserialize(BinaryReader reader) DeserializeWithoutType(reader); } + /// + /// Deserializes an object from a . + /// + /// The for reading data. + /// The deserialized . public static NodeCapability DeserializeFrom(BinaryReader reader) { - NodeCapability capability; NodeCapabilityType type = (NodeCapabilityType)reader.ReadByte(); - switch (type) + NodeCapability capability = type switch { - case NodeCapabilityType.TcpServer: - case NodeCapabilityType.WsServer: - capability = new ServerCapability(type); - break; - case NodeCapabilityType.FullNode: - capability = new FullNodeCapability(); - break; - default: - throw new FormatException(); - } + NodeCapabilityType.TcpServer or NodeCapabilityType.WsServer => new ServerCapability(type), + NodeCapabilityType.FullNode => new FullNodeCapability(), + _ => throw new FormatException(), + }; capability.DeserializeWithoutType(reader); return capability; } + /// + /// Deserializes the object from a . + /// + /// The for reading data. protected abstract void DeserializeWithoutType(BinaryReader reader); void ISerializable.Serialize(BinaryWriter writer) @@ -60,6 +65,10 @@ void ISerializable.Serialize(BinaryWriter writer) SerializeWithoutType(writer); } + /// + /// Serializes the object to a . + /// + /// The for writing data. protected abstract void SerializeWithoutType(BinaryWriter writer); } } diff --git a/src/neo/Network/P2P/Capabilities/NodeCapabilityType.cs b/src/neo/Network/P2P/Capabilities/NodeCapabilityType.cs index 5ea0594d7b..a2769dd760 100644 --- a/src/neo/Network/P2P/Capabilities/NodeCapabilityType.cs +++ b/src/neo/Network/P2P/Capabilities/NodeCapabilityType.cs @@ -1,12 +1,31 @@ namespace Neo.Network.P2P.Capabilities { + /// + /// Represents the type of . + /// public enum NodeCapabilityType : byte { - //Servers + #region Servers + + /// + /// Indicates that the node is listening on a Tcp port. + /// TcpServer = 0x01, + + /// + /// Indicates that the node is listening on a WebSocket port. + /// WsServer = 0x02, - //Others + #endregion + + #region Others + + /// + /// Indicates that the node has complete block data. + /// FullNode = 0x10 + + #endregion } } diff --git a/src/neo/Network/P2P/Capabilities/ServerCapability.cs b/src/neo/Network/P2P/Capabilities/ServerCapability.cs index 0ce31e130e..e03338b321 100644 --- a/src/neo/Network/P2P/Capabilities/ServerCapability.cs +++ b/src/neo/Network/P2P/Capabilities/ServerCapability.cs @@ -3,8 +3,14 @@ namespace Neo.Network.P2P.Capabilities { + /// + /// Indicates that the node is a server. + /// public class ServerCapability : NodeCapability { + /// + /// Indicates the port that the node is listening on. + /// public ushort Port; public override int Size => @@ -12,10 +18,10 @@ public class ServerCapability : NodeCapability sizeof(ushort); // Port /// - /// Constructor + /// Initializes a new instance of the class. /// - /// Channel - /// Port + /// The type of the . It must be or + /// The port that the node is listening on. public ServerCapability(NodeCapabilityType type, ushort port = 0) : base(type) { if (type != NodeCapabilityType.TcpServer && type != NodeCapabilityType.WsServer) diff --git a/src/neo/Network/P2P/ChannelsConfig.cs b/src/neo/Network/P2P/ChannelsConfig.cs index 2882876bd2..88d01a7419 100644 --- a/src/neo/Network/P2P/ChannelsConfig.cs +++ b/src/neo/Network/P2P/ChannelsConfig.cs @@ -2,30 +2,33 @@ namespace Neo.Network.P2P { + /// + /// Represents the settings to start . + /// public class ChannelsConfig { /// - /// Tcp configuration + /// Tcp configuration. /// public IPEndPoint Tcp { get; set; } /// - /// Web socket configuration + /// Web socket configuration. /// public IPEndPoint WebSocket { get; set; } /// - /// Minimum desired connections + /// Minimum desired connections. /// public int MinDesiredConnections { get; set; } = Peer.DefaultMinDesiredConnections; /// - /// Max allowed connections + /// Max allowed connections. /// public int MaxConnections { get; set; } = Peer.DefaultMaxConnections; /// - /// Max allowed connections per address + /// Max allowed connections per address. /// public int MaxConnectionsPerAddress { get; set; } = 3; } diff --git a/src/neo/Network/P2P/Connection.cs b/src/neo/Network/P2P/Connection.cs index b12b2723a5..5bca0b5aa4 100644 --- a/src/neo/Network/P2P/Connection.cs +++ b/src/neo/Network/P2P/Connection.cs @@ -7,27 +7,45 @@ namespace Neo.Network.P2P { + /// + /// Represents a connection of the P2P network. + /// public abstract class Connection : UntypedActor { internal class Close { public bool Abort; } - internal class Ack : Tcp.Event { public static Ack Instance = new Ack(); } + internal class Ack : Tcp.Event { public static Ack Instance = new(); } /// - /// connection initial timeout (in seconds) before any package has been accepted + /// connection initial timeout (in seconds) before any package has been accepted. /// private const int connectionTimeoutLimitStart = 10; + /// - /// connection timeout (in seconds) after every `OnReceived(ByteString data)` event + /// connection timeout (in seconds) after every `OnReceived(ByteString data)` event. /// private const int connectionTimeoutLimit = 60; + /// + /// The address of the remote node. + /// public IPEndPoint Remote { get; } + + /// + /// The address of the local node. + /// public IPEndPoint Local { get; } private ICancelable timer; private readonly IActorRef tcp; private readonly WebSocket ws; private bool disconnected = false; + + /// + /// Initializes a new instance of the class. + /// + /// The underlying connection object. + /// The address of the remote node. + /// The address of the local node. protected Connection(object connection, IPEndPoint remote, IPEndPoint local) { this.Remote = remote; @@ -65,12 +83,16 @@ private void WsReceive() failure: ex => new Tcp.ErrorClosed(ex.Message)); } + /// + /// Disconnect from the remote node. + /// + /// Indicates whether the TCP ABORT command should be sent. public void Disconnect(bool abort = false) { disconnected = true; if (tcp != null) { - tcp.Tell(abort ? (Tcp.CloseCommand)Tcp.Abort.Instance : Tcp.Close.Instance); + tcp.Tell(abort ? Tcp.Abort.Instance : Tcp.Close.Instance); } else { @@ -79,10 +101,17 @@ public void Disconnect(bool abort = false) Context.Stop(Self); } + /// + /// Called when a TCP ACK message is received. + /// protected virtual void OnAck() { } + /// + /// Called when data is received. + /// + /// The received data. protected abstract void OnData(ByteString data); protected override void OnReceive(object message) @@ -127,6 +156,10 @@ protected override void PostStop() base.PostStop(); } + /// + /// Sends data to the remote node. + /// + /// protected void SendData(ByteString data) { if (tcp != null) @@ -135,7 +168,7 @@ protected void SendData(ByteString data) } else { - ArraySegment segment = new ArraySegment(data.ToArray()); + ArraySegment segment = new(data.ToArray()); ws.SendAsync(segment, WebSocketMessageType.Binary, true, CancellationToken.None).PipeTo(Self, success: () => Ack.Instance, failure: ex => new Tcp.ErrorClosed(ex.Message)); diff --git a/src/neo/Network/P2P/Helper.cs b/src/neo/Network/P2P/Helper.cs index 98f9c7518d..a788ee7ed3 100644 --- a/src/neo/Network/P2P/Helper.cs +++ b/src/neo/Network/P2P/Helper.cs @@ -5,21 +5,35 @@ namespace Neo.Network.P2P { + /// + /// A helper class for . + /// public static class Helper { + /// + /// Calculates the hash of a . + /// + /// The object to hash. + /// The hash of the object. public static UInt256 CalculateHash(this IVerifiable verifiable) { - using MemoryStream ms = new MemoryStream(); - using BinaryWriter writer = new BinaryWriter(ms); + using MemoryStream ms = new(); + using BinaryWriter writer = new(ms); verifiable.SerializeUnsigned(writer); writer.Flush(); return new UInt256(ms.ToArray().Sha256()); } + /// + /// Gets the data of a object to be hashed. + /// + /// The object to hash. + /// The magic number of the network. + /// The data to hash. public static byte[] GetSignData(this IVerifiable verifiable, uint magic) { - using MemoryStream ms = new MemoryStream(); - using BinaryWriter writer = new BinaryWriter(ms); + using MemoryStream ms = new(); + using BinaryWriter writer = new(ms); writer.Write(magic); writer.Write(verifiable.Hash); writer.Flush(); diff --git a/src/neo/Network/P2P/LocalNode.cs b/src/neo/Network/P2P/LocalNode.cs index 88232c34a0..459fb18465 100644 --- a/src/neo/Network/P2P/LocalNode.cs +++ b/src/neo/Network/P2P/LocalNode.cs @@ -12,31 +12,68 @@ namespace Neo.Network.P2P { + /// + /// Actor used to manage the connections of the local node. + /// public class LocalNode : Peer { + /// + /// Sent to to relay an . + /// public class RelayDirectly { public IInventory Inventory; } + + /// + /// Sent to to send an . + /// public class SendDirectly { public IInventory Inventory; } + + /// + /// Sent to to request for an instance of . + /// public class GetInstance { } + /// + /// Indicates the protocol version of the local node. + /// public const uint ProtocolVersion = 0; + private const int MaxCountFromSeedList = 5; private readonly IPEndPoint[] SeedList; private readonly NeoSystem system; - internal readonly ConcurrentDictionary RemoteNodes = new ConcurrentDictionary(); + internal readonly ConcurrentDictionary RemoteNodes = new(); + /// + /// Indicates the number of connected nodes. + /// public int ConnectedCount => RemoteNodes.Count; + + /// + /// Indicates the number of unconnected nodes. When the number of connections is not enough, it will automatically connect to these nodes. + /// public int UnconnectedCount => UnconnectedPeers.Count; + + /// + /// The random number used to identify the local node. + /// public static readonly uint Nonce; + + /// + /// The identifier of the client software of the local node. + /// public static string UserAgent { get; set; } static LocalNode() { - Random rand = new Random(); + Random rand = new(); Nonce = (uint)rand.Next(); UserAgent = $"/{Assembly.GetExecutingAssembly().GetName().Name}:{Assembly.GetExecutingAssembly().GetVersion()}/"; } + /// + /// Initializes a new instance of the class. + /// + /// The object that contains the . public LocalNode(NeoSystem system) { this.system = system; @@ -53,7 +90,7 @@ public LocalNode(NeoSystem system) /// /// Packs a MessageCommand to a full Message with an optional ISerializable payload. - /// Forwards it to . + /// Forwards it to . /// /// The message command to be packed. /// Optional payload to be Serialized along the message. @@ -63,7 +100,7 @@ private void BroadcastMessage(MessageCommand command, ISerializable payload = nu } /// - /// Broadcast a message to all connected nodes, namely . + /// Broadcast a message to all connected nodes. /// /// The message to be broadcasted. private void BroadcastMessage(Message message) => SendToRemoteNodes(message); @@ -112,11 +149,12 @@ internal static IPEndPoint GetIpEndPoint(string hostAndPort) } /// - /// Check the new connection
- /// If it is equal to the Nonce of local or any remote node, it'll return false, else we'll return true and update the Listener address of the connected remote node. + /// Checks the new connection. + /// If it is equal to the nonce of local or any remote node, it'll return false, else we'll return true and update the Listener address of the connected remote node. ///
- /// Remote node actor - /// Remote node + /// Remote node actor. + /// Remote node object. + /// if the new connection is allowed; otherwise, . public bool AllowNewConnection(IActorRef actor, RemoteNode node) { if (node.Version.Magic != system.Settings.Magic) return false; @@ -133,26 +171,33 @@ public bool AllowNewConnection(IActorRef actor, RemoteNode node) return true; } + /// + /// Gets the connected remote nodes. + /// + /// public IEnumerable GetRemoteNodes() { return RemoteNodes.Values; } + /// + /// Gets the unconnected nodes. + /// + /// public IEnumerable GetUnconnectedPeers() { return UnconnectedPeers; } /// - /// Override of abstract class that is triggered when is empty. - /// Performs a BroadcastMessage with the command `MessageCommand.GetAddr`, which, eventually, tells all known connections. - /// If there are no connected peers it will try with the default, respecting MaxCountFromSeedList limit. + /// Performs a broadcast with the command , which, eventually, tells all known connections. + /// If there are no connected peers it will try with the default, respecting limit. /// - /// The count of peers required + /// Number of peers that are being requested. protected override void NeedMorePeers(int count) { count = Math.Max(count, MaxCountFromSeedList); - if (ConnectedPeers.Count > 0) + if (!ConnectedPeers.IsEmpty) { BroadcastMessage(MessageCommand.GetAddr); } @@ -161,7 +206,7 @@ protected override void NeedMorePeers(int count) // Will call AddPeers with default SeedList set cached on . // It will try to add those, sequentially, to the list of currently unconnected ones. - Random rand = new Random(); + Random rand = new(); AddPeers(SeedList.Where(u => u != null).OrderBy(p => rand.Next()).Take(count)); } } @@ -210,6 +255,11 @@ protected override void OnTcpConnected(IActorRef connection) connection.Tell(new RemoteNode.StartProtocol()); } + /// + /// Gets a object used for creating the actor. + /// + /// The object that contains the . + /// The object used for creating the actor. public static Props Props(NeoSystem system) { return Akka.Actor.Props.Create(() => new LocalNode(system)); diff --git a/src/neo/Network/P2P/Message.cs b/src/neo/Network/P2P/Message.cs index 15a7e0783a..7cd7515b62 100644 --- a/src/neo/Network/P2P/Message.cs +++ b/src/neo/Network/P2P/Message.cs @@ -8,27 +8,47 @@ namespace Neo.Network.P2P { + /// + /// Represents a message on the NEO network. + /// public class Message : ISerializable { + /// + /// Indicates the maximum size of . + /// public const int PayloadMaxSize = 0x02000000; + private const int CompressionMinSize = 128; private const int CompressionThreshold = 64; /// - /// Flags that represents whether a message is compressed. - /// 0 for None, 1 for Compressed. + /// The flags of the message. /// public MessageFlags Flags; + + /// + /// The command of the message. + /// public MessageCommand Command; + + /// + /// The payload of the message. + /// public ISerializable Payload; private byte[] _payload_compressed; public int Size => sizeof(MessageFlags) + sizeof(MessageCommand) + _payload_compressed.GetVarSize(); + /// + /// Creates a new instance of the class. + /// + /// The command of the message. + /// The payload of the message. For the messages that don't require a payload, it should be . + /// public static Message Create(MessageCommand command, ISerializable payload = null) { - Message message = new Message + Message message = new() { Flags = MessageFlags.None, Command = command, diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index c66c15799a..0b0474f5de 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -1,58 +1,164 @@ +using Neo.Cryptography; using Neo.IO.Caching; using Neo.Network.P2P.Payloads; namespace Neo.Network.P2P { + /// + /// Represents the command of a message. + /// public enum MessageCommand : byte { - //handshaking + #region handshaking + + /// + /// Sent when a connection is established. + /// [ReflectionCache(typeof(VersionPayload))] Version = 0x00, + + /// + /// Sent to respond to messages. + /// Verack = 0x01, - //connectivity + #endregion + + #region connectivity + + /// + /// Sent to request for remote nodes. + /// GetAddr = 0x10, + + /// + /// Sent to respond to messages. + /// [ReflectionCache(typeof(AddrPayload))] Addr = 0x11, + + /// + /// Sent to detect whether the connection has been disconnected. + /// [ReflectionCache(typeof(PingPayload))] Ping = 0x18, + + /// + /// Sent to respond to messages. + /// [ReflectionCache(typeof(PingPayload))] Pong = 0x19, - //synchronization + #endregion + + #region synchronization + + /// + /// Sent to request for headers. + /// [ReflectionCache(typeof(GetBlockByIndexPayload))] GetHeaders = 0x20, + + /// + /// Sent to respond to messages. + /// [ReflectionCache(typeof(HeadersPayload))] Headers = 0x21, + + /// + /// Sent to request for blocks. + /// [ReflectionCache(typeof(GetBlocksPayload))] GetBlocks = 0x24, + + /// + /// Sent to request for memory pool. + /// Mempool = 0x25, + + /// + /// Sent to relay inventories. + /// [ReflectionCache(typeof(InvPayload))] Inv = 0x27, + + /// + /// Sent to request for inventories. + /// [ReflectionCache(typeof(InvPayload))] GetData = 0x28, + + /// + /// Sent to request for blocks. + /// [ReflectionCache(typeof(GetBlockByIndexPayload))] GetBlockByIndex = 0x29, + + /// + /// Sent to respond to messages when the inventories are not found. + /// [ReflectionCache(typeof(InvPayload))] NotFound = 0x2a, + + /// + /// Sent to send a transaction. + /// [ReflectionCache(typeof(Transaction))] Transaction = 0x2b, + + /// + /// Sent to send a block. + /// [ReflectionCache(typeof(Block))] Block = 0x2c, + + /// + /// Sent to send an . + /// [ReflectionCache(typeof(ExtensiblePayload))] Extensible = 0x2e, + + /// + /// Sent to reject an inventory. + /// Reject = 0x2f, - //SPV protocol + #endregion + + #region SPV protocol + + /// + /// Sent to load the . + /// [ReflectionCache(typeof(FilterLoadPayload))] FilterLoad = 0x30, + + /// + /// Sent to update the items for the . + /// [ReflectionCache(typeof(FilterAddPayload))] FilterAdd = 0x31, + + /// + /// Sent to clear the . + /// FilterClear = 0x32, + + /// + /// Sent to send a filtered block. + /// [ReflectionCache(typeof(MerkleBlockPayload))] MerkleBlock = 0x38, - //others + #endregion + + #region others + + /// + /// Sent to send an alert. + /// Alert = 0x40, + + #endregion } } diff --git a/src/neo/Network/P2P/MessageFlags.cs b/src/neo/Network/P2P/MessageFlags.cs index 4e8a34c1a7..37b56a83f9 100644 --- a/src/neo/Network/P2P/MessageFlags.cs +++ b/src/neo/Network/P2P/MessageFlags.cs @@ -2,10 +2,20 @@ namespace Neo.Network.P2P { + /// + /// Represents the flags of a message. + /// [Flags] public enum MessageFlags : byte { + /// + /// No flag is set for the message. + /// None = 0, + + /// + /// Indicates that the message is compressed. + /// Compressed = 1 << 0 } } diff --git a/src/neo/Network/P2P/Payloads/AddrPayload.cs b/src/neo/Network/P2P/Payloads/AddrPayload.cs index 0a8f09c71e..6b1458f35b 100644 --- a/src/neo/Network/P2P/Payloads/AddrPayload.cs +++ b/src/neo/Network/P2P/Payloads/AddrPayload.cs @@ -4,14 +4,28 @@ namespace Neo.Network.P2P.Payloads { + /// + /// This message is sent to respond to messages. + /// public class AddrPayload : ISerializable { + /// + /// Indicates the maximum number of nodes sent each time. + /// public const int MaxCountToSend = 200; + /// + /// The list of nodes. + /// public NetworkAddressWithTime[] AddressList; public int Size => AddressList.GetVarSize(); + /// + /// Creates a new instance of the class. + /// + /// The list of nodes. + /// The created payload. public static AddrPayload Create(params NetworkAddressWithTime[] addresses) { return new AddrPayload diff --git a/src/neo/Network/P2P/Payloads/Block.cs b/src/neo/Network/P2P/Payloads/Block.cs index 83827ac5ed..2670a90615 100644 --- a/src/neo/Network/P2P/Payloads/Block.cs +++ b/src/neo/Network/P2P/Payloads/Block.cs @@ -9,19 +9,61 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents a block. + /// public sealed class Block : IEquatable, IInventory { + /// + /// The header of the block. + /// public Header Header; + + /// + /// The transaction list of the block. + /// public Transaction[] Transactions; public UInt256 Hash => Header.Hash; + + /// + /// The version of the block. + /// public uint Version => Header.Version; + + /// + /// The hash of the previous block. + /// public UInt256 PrevHash => Header.PrevHash; + + /// + /// The merkle root of the transactions. + /// public UInt256 MerkleRoot => Header.MerkleRoot; + + /// + /// The timestamp of the block. + /// public ulong Timestamp => Header.Timestamp; + + /// + /// The index of the block. + /// public uint Index => Header.Index; + + /// + /// The primary index of the consensus node that generated this block. + /// public byte PrimaryIndex => Header.PrimaryIndex; + + /// + /// The multi-signature address of the consensus nodes that generates the next block. + /// public UInt160 NextConsensus => Header.NextConsensus; + + /// + /// The witness of the block. + /// public Witness Witness => Header.Witness; InventoryType IInventory.InventoryType => InventoryType.Block; @@ -67,6 +109,11 @@ public void Serialize(BinaryWriter writer) void IVerifiable.SerializeUnsigned(BinaryWriter writer) => ((IVerifiable)Header).SerializeUnsigned(writer); + /// + /// Converts the block to a JSON object. + /// + /// The used during the conversion. + /// The block represented by a JSON object. public JObject ToJson(ProtocolSettings settings) { JObject json = Header.ToJson(settings); diff --git a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs index ca86c4b0f5..36155cdc8b 100644 --- a/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs +++ b/src/neo/Network/P2P/Payloads/ExtensiblePayload.cs @@ -8,13 +8,39 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents an extensible message that can be relayed. + /// public class ExtensiblePayload : IInventory { + /// + /// The category of the extension. + /// public string Category; + + /// + /// Indicates that the payload is only valid when the block height is greater than or equal to this value. + /// public uint ValidBlockStart; + + /// + /// Indicates that the payload is only valid when the block height is less than this value. + /// public uint ValidBlockEnd; + + /// + /// The sender of the payload. + /// public UInt160 Sender; + + /// + /// The data of the payload. + /// public byte[] Data; + + /// + /// The witness of the payload. It must match the . + /// public Witness Witness; private UInt256 _hash = null; diff --git a/src/neo/Network/P2P/Payloads/FilterAddPayload.cs b/src/neo/Network/P2P/Payloads/FilterAddPayload.cs index 3a7a4a9e6f..73bf36bfb0 100644 --- a/src/neo/Network/P2P/Payloads/FilterAddPayload.cs +++ b/src/neo/Network/P2P/Payloads/FilterAddPayload.cs @@ -1,10 +1,17 @@ +using Neo.Cryptography; using Neo.IO; using System.IO; namespace Neo.Network.P2P.Payloads { + /// + /// This message is sent to update the items for the . + /// public class FilterAddPayload : ISerializable { + /// + /// The items to be added. + /// public byte[] Data; public int Size => Data.GetVarSize(); diff --git a/src/neo/Network/P2P/Payloads/FilterLoadPayload.cs b/src/neo/Network/P2P/Payloads/FilterLoadPayload.cs index dfba728fc0..88995e3721 100644 --- a/src/neo/Network/P2P/Payloads/FilterLoadPayload.cs +++ b/src/neo/Network/P2P/Payloads/FilterLoadPayload.cs @@ -5,14 +5,33 @@ namespace Neo.Network.P2P.Payloads { + /// + /// This message is sent to load the . + /// public class FilterLoadPayload : ISerializable { + /// + /// The data of the . + /// public byte[] Filter; + + /// + /// The number of hash functions used by the . + /// public byte K; + + /// + /// Used to generate the seeds of the murmur hash functions. + /// public uint Tweak; public int Size => Filter.GetVarSize() + sizeof(byte) + sizeof(uint); + /// + /// Creates a new instance of the class. + /// + /// The fields in the filter will be copied to the payload. + /// The created payload. public static FilterLoadPayload Create(BloomFilter filter) { byte[] buffer = new byte[filter.M / 8]; diff --git a/src/neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs b/src/neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs index c07a8a8c0a..9ef1482756 100644 --- a/src/neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs +++ b/src/neo/Network/P2P/Payloads/GetBlockByIndexPayload.cs @@ -4,13 +4,29 @@ namespace Neo.Network.P2P.Payloads { + /// + /// This message is sent to request for blocks by index. + /// public class GetBlockByIndexPayload : ISerializable { + /// + /// The starting index of the blocks to request. + /// public uint IndexStart; + + /// + /// The number of blocks to request. + /// public short Count; public int Size => sizeof(uint) + sizeof(short); + /// + /// Creates a new instance of the class. + /// + /// The starting index of the blocks to request. + /// The number of blocks to request. Set this parameter to -1 to request as many blocks as possible. + /// The created payload. public static GetBlockByIndexPayload Create(uint index_start, short count = -1) { return new GetBlockByIndexPayload diff --git a/src/neo/Network/P2P/Payloads/GetBlocksPayload.cs b/src/neo/Network/P2P/Payloads/GetBlocksPayload.cs index eccd66ed19..fa446c2d00 100644 --- a/src/neo/Network/P2P/Payloads/GetBlocksPayload.cs +++ b/src/neo/Network/P2P/Payloads/GetBlocksPayload.cs @@ -4,13 +4,29 @@ namespace Neo.Network.P2P.Payloads { + /// + /// This message is sent to request for blocks by hash. + /// public class GetBlocksPayload : ISerializable { + /// + /// The starting hash of the blocks to request. + /// public UInt256 HashStart; + + /// + /// The number of blocks to request. + /// public short Count; public int Size => sizeof(short) + HashStart.Size; + /// + /// Creates a new instance of the class. + /// + /// The starting hash of the blocks to request. + /// The number of blocks to request. Set this parameter to -1 to request as many blocks as possible. + /// The created payload. public static GetBlocksPayload Create(UInt256 hash_start, short count = -1) { return new GetBlocksPayload diff --git a/src/neo/Network/P2P/Payloads/Header.cs b/src/neo/Network/P2P/Payloads/Header.cs index 5b4c7671aa..227587362e 100644 --- a/src/neo/Network/P2P/Payloads/Header.cs +++ b/src/neo/Network/P2P/Payloads/Header.cs @@ -10,6 +10,9 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents the header of a block. + /// public sealed class Header : IEquatable
, IVerifiable { private uint version; @@ -19,44 +22,69 @@ public sealed class Header : IEquatable
, IVerifiable private uint index; private byte primaryIndex; private UInt160 nextConsensus; + + /// + /// The witness of the block. + /// public Witness Witness; + /// + /// The version of the block. + /// public uint Version { get => version; set { version = value; _hash = null; } } + /// + /// The hash of the previous block. + /// public UInt256 PrevHash { get => prevHash; set { prevHash = value; _hash = null; } } + /// + /// The merkle root of the transactions. + /// public UInt256 MerkleRoot { get => merkleRoot; set { merkleRoot = value; _hash = null; } } + /// + /// The timestamp of the block. + /// public ulong Timestamp { get => timestamp; set { timestamp = value; _hash = null; } } + /// + /// The index of the block. + /// public uint Index { get => index; set { index = value; _hash = null; } } + /// + /// The primary index of the consensus node that generated this block. + /// public byte PrimaryIndex { get => primaryIndex; set { primaryIndex = value; _hash = null; } } + /// + /// The multi-signature address of the consensus nodes that generates the next block. + /// public UInt160 NextConsensus { get => nextConsensus; @@ -94,7 +122,7 @@ Witness[] IVerifiable.Witnesses } set { - if (value.Length != 1) throw new ArgumentException(); + if (value.Length != 1) throw new ArgumentException(null, nameof(value)); Witness = value[0]; } } @@ -162,9 +190,14 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.Write(nextConsensus); } + /// + /// Converts the header to a JSON object. + /// + /// The used during the conversion. + /// The header represented by a JSON object. public JObject ToJson(ProtocolSettings settings) { - JObject json = new JObject(); + JObject json = new(); json["hash"] = Hash.ToString(); json["size"] = Size; json["version"] = version; diff --git a/src/neo/Network/P2P/Payloads/HeadersPayload.cs b/src/neo/Network/P2P/Payloads/HeadersPayload.cs index 9251a7f5a1..52858a3a54 100644 --- a/src/neo/Network/P2P/Payloads/HeadersPayload.cs +++ b/src/neo/Network/P2P/Payloads/HeadersPayload.cs @@ -4,14 +4,28 @@ namespace Neo.Network.P2P.Payloads { + /// + /// This message is sent to respond to messages. + /// public class HeadersPayload : ISerializable { + /// + /// Indicates the maximum number of headers sent each time. + /// public const int MaxHeadersCount = 2000; + /// + /// The list of headers. + /// public Header[] Headers; public int Size => Headers.GetVarSize(); + /// + /// Creates a new instance of the class. + /// + /// The list of headers. + /// The created payload. public static HeadersPayload Create(params Header[] headers) { return new HeadersPayload diff --git a/src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs b/src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs index d3077d3e88..8cc87e04ae 100644 --- a/src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs +++ b/src/neo/Network/P2P/Payloads/HighPriorityAttribute.cs @@ -5,6 +5,9 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Indicates that the transaction is of high priority. + /// public class HighPriorityAttribute : TransactionAttribute { public override bool AllowMultiple => false; diff --git a/src/neo/Network/P2P/Payloads/IInventory.cs b/src/neo/Network/P2P/Payloads/IInventory.cs index d4846e8d76..5165e28a36 100644 --- a/src/neo/Network/P2P/Payloads/IInventory.cs +++ b/src/neo/Network/P2P/Payloads/IInventory.cs @@ -1,7 +1,13 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents a message that can be relayed on the NEO network. + /// public interface IInventory : IVerifiable { + /// + /// The type of the inventory. + /// InventoryType InventoryType { get; } } } diff --git a/src/neo/Network/P2P/Payloads/IVerifiable.cs b/src/neo/Network/P2P/Payloads/IVerifiable.cs index c9d0a9acb5..d4d31d69d4 100644 --- a/src/neo/Network/P2P/Payloads/IVerifiable.cs +++ b/src/neo/Network/P2P/Payloads/IVerifiable.cs @@ -4,16 +4,38 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents an object that can be verified in the NEO network. + /// public interface IVerifiable : ISerializable { + /// + /// The hash of the object. + /// UInt256 Hash => this.CalculateHash(); + /// + /// The witnesses of the object. + /// Witness[] Witnesses { get; set; } + /// + /// Deserializes the part of the object other than . + /// + /// The for reading data. void DeserializeUnsigned(BinaryReader reader); + /// + /// Gets the script hashes that should be verified for this object. + /// + /// The snapshot to be used. + /// The script hashes that should be verified. UInt160[] GetScriptHashesForVerifying(DataCache snapshot); + /// + /// Serializes the part of the object other than . + /// + /// The for writing data. void SerializeUnsigned(BinaryWriter writer); } } diff --git a/src/neo/Network/P2P/Payloads/InvPayload.cs b/src/neo/Network/P2P/Payloads/InvPayload.cs index b54a834ed5..f8fbc3fbf9 100644 --- a/src/neo/Network/P2P/Payloads/InvPayload.cs +++ b/src/neo/Network/P2P/Payloads/InvPayload.cs @@ -5,15 +5,34 @@ namespace Neo.Network.P2P.Payloads { + /// + /// This message is sent to relay inventories. + /// public class InvPayload : ISerializable { + /// + /// Indicates the maximum number of inventories sent each time. + /// public const int MaxHashesCount = 500; + /// + /// The type of the inventories. + /// public InventoryType Type; + + /// + /// The hashes of the inventories. + /// public UInt256[] Hashes; public int Size => sizeof(InventoryType) + Hashes.GetVarSize(); + /// + /// Creates a new instance of the class. + /// + /// The type of the inventories. + /// The hashes of the inventories. + /// The created payload. public static InvPayload Create(InventoryType type, params UInt256[] hashes) { return new InvPayload @@ -23,6 +42,12 @@ public static InvPayload Create(InventoryType type, params UInt256[] hashes) }; } + /// + /// Creates a group of the instance. + /// + /// The type of the inventories. + /// The hashes of the inventories. + /// The created payloads. public static IEnumerable CreateGroup(InventoryType type, UInt256[] hashes) { for (int i = 0; i < hashes.Length; i += MaxHashesCount) diff --git a/src/neo/Network/P2P/Payloads/InventoryType.cs b/src/neo/Network/P2P/Payloads/InventoryType.cs index 3e6bfa423f..27eca53900 100644 --- a/src/neo/Network/P2P/Payloads/InventoryType.cs +++ b/src/neo/Network/P2P/Payloads/InventoryType.cs @@ -1,9 +1,23 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents the type of an inventory. + /// public enum InventoryType : byte { + /// + /// Indicates that the inventory is a . + /// TX = MessageCommand.Transaction, + + /// + /// Indicates that the inventory is a . + /// Block = MessageCommand.Block, + + /// + /// Indicates that the inventory is an . + /// Extensible = MessageCommand.Extensible } } diff --git a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs index 84a3162ff2..f906e69039 100644 --- a/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs +++ b/src/neo/Network/P2P/Payloads/MerkleBlockPayload.cs @@ -7,18 +7,42 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents a block that is filtered by a . + /// public class MerkleBlockPayload : ISerializable { + /// + /// The header of the block. + /// public Header Header; + + /// + /// The number of the transactions in the block. + /// public int TxCount; + + /// + /// The nodes of the transactions hash tree. + /// public UInt256[] Hashes; + + /// + /// The data in the that filtered the block. + /// public byte[] Flags; public int Size => Header.Size + sizeof(int) + Hashes.GetVarSize() + Flags.GetVarSize(); + /// + /// Creates a new instance of the class. + /// + /// The original block. + /// The data in the that filtered the block. + /// The created payload. public static MerkleBlockPayload Create(Block block, BitArray flags) { - MerkleTree tree = new MerkleTree(block.Transactions.Select(p => p.Hash).ToArray()); + MerkleTree tree = new(block.Transactions.Select(p => p.Hash).ToArray()); tree.Trim(flags); byte[] buffer = new byte[(flags.Length + 7) / 8]; flags.CopyTo(buffer, 0); diff --git a/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs b/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs index a65e28c846..6ac32c1dc8 100644 --- a/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs +++ b/src/neo/Network/P2P/Payloads/NetworkAddressWithTime.cs @@ -7,15 +7,40 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Sent with an to respond to messages. + /// public class NetworkAddressWithTime : ISerializable { + /// + /// The time when connected to the node. + /// public uint Timestamp; + + /// + /// The address of the node. + /// public IPAddress Address; + + /// + /// The capabilities of the node. + /// public NodeCapability[] Capabilities; - public IPEndPoint EndPoint => new IPEndPoint(Address, Capabilities.Where(p => p.Type == NodeCapabilityType.TcpServer).Select(p => (ServerCapability)p).FirstOrDefault()?.Port ?? 0); + /// + /// The of the Tcp server. + /// + public IPEndPoint EndPoint => new(Address, Capabilities.Where(p => p.Type == NodeCapabilityType.TcpServer).Select(p => (ServerCapability)p).FirstOrDefault()?.Port ?? 0); + public int Size => sizeof(uint) + 16 + Capabilities.GetVarSize(); + /// + /// Creates a new instance of the class. + /// + /// The address of the node. + /// The time when connected to the node. + /// The capabilities of the node. + /// The created payload. public static NetworkAddressWithTime Create(IPAddress address, uint timestamp, params NodeCapability[] capabilities) { return new NetworkAddressWithTime diff --git a/src/neo/Network/P2P/Payloads/OracleResponse.cs b/src/neo/Network/P2P/Payloads/OracleResponse.cs index 81bf449292..c27e98fec2 100644 --- a/src/neo/Network/P2P/Payloads/OracleResponse.cs +++ b/src/neo/Network/P2P/Payloads/OracleResponse.cs @@ -10,14 +10,34 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Indicates that the transaction is an oracle response. + /// public class OracleResponse : TransactionAttribute { + /// + /// Indicates the maximum size of the field. + /// public const int MaxResultSize = ushort.MaxValue; + /// + /// Represents the fixed value of the field of the oracle responding transaction. + /// public static readonly byte[] FixedScript; + /// + /// The ID of the oracle request. + /// public ulong Id; + + /// + /// The response code for the oracle request. + /// public OracleResponseCode Code; + + /// + /// The result for the oracle request. + /// public byte[] Result; public override TransactionAttributeType Type => TransactionAttributeType.OracleResponse; @@ -30,7 +50,7 @@ public class OracleResponse : TransactionAttribute static OracleResponse() { - using ScriptBuilder sb = new ScriptBuilder(); + using ScriptBuilder sb = new(); sb.EmitDynamicCall(NativeContract.Oracle.Hash, "finish"); FixedScript = sb.ToArray(); } diff --git a/src/neo/Network/P2P/Payloads/OracleResponseCode.cs b/src/neo/Network/P2P/Payloads/OracleResponseCode.cs index e3ad54577a..6806c5697b 100644 --- a/src/neo/Network/P2P/Payloads/OracleResponseCode.cs +++ b/src/neo/Network/P2P/Payloads/OracleResponseCode.cs @@ -1,17 +1,53 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents the response code for the oracle request. + /// public enum OracleResponseCode : byte { + /// + /// Indicates that the request has been successfully completed. + /// Success = 0x00, + /// + /// Indicates that the protocol of the request is not supported. + /// ProtocolNotSupported = 0x10, + + /// + /// Indicates that the oracle nodes cannot reach a consensus on the result of the request. + /// ConsensusUnreachable = 0x12, + + /// + /// Indicates that the requested Uri does not exist. + /// NotFound = 0x14, + + /// + /// Indicates that the request was not completed within the specified time. + /// Timeout = 0x16, + + /// + /// Indicates that there is no permission to request the resource. + /// Forbidden = 0x18, + + /// + /// Indicates that the data for the response is too large. + /// ResponseTooLarge = 0x1a, + + /// + /// Indicates that the request failed due to insufficient balance. + /// InsufficientFunds = 0x1c, + /// + /// Indicates that the request failed due to other errors. + /// Error = 0xff } } diff --git a/src/neo/Network/P2P/Payloads/PingPayload.cs b/src/neo/Network/P2P/Payloads/PingPayload.cs index d5005c4e90..e2cd10b29f 100644 --- a/src/neo/Network/P2P/Payloads/PingPayload.cs +++ b/src/neo/Network/P2P/Payloads/PingPayload.cs @@ -4,10 +4,24 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Sent to detect whether the connection has been disconnected. + /// public class PingPayload : ISerializable { + /// + /// The latest block index. + /// public uint LastBlockIndex; + + /// + /// The timestamp when the message was sent. + /// public uint Timestamp; + + /// + /// A random number. This number must be the same in and messages. + /// public uint Nonce; public int Size => @@ -15,13 +29,23 @@ public class PingPayload : ISerializable sizeof(uint) + //Timestamp sizeof(uint); //Nonce - + /// + /// Creates a new instance of the class. + /// + /// The latest block index. + /// The created payload. public static PingPayload Create(uint height) { - Random rand = new Random(); + Random rand = new(); return Create(height, (uint)rand.Next()); } + /// + /// Creates a new instance of the class. + /// + /// The latest block index. + /// The random number. + /// The created payload. public static PingPayload Create(uint height, uint nonce) { return new PingPayload diff --git a/src/neo/Network/P2P/Payloads/Signer.cs b/src/neo/Network/P2P/Payloads/Signer.cs index 4be59a349f..0f3def8712 100644 --- a/src/neo/Network/P2P/Payloads/Signer.cs +++ b/src/neo/Network/P2P/Payloads/Signer.cs @@ -7,14 +7,32 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents a signer of a . + /// public class Signer : ISerializable { // This limits maximum number of AllowedContracts or AllowedGroups here private const int MaxSubitems = 16; + /// + /// The account of the signer. + /// public UInt160 Account; + + /// + /// The scopes of the witness. + /// public WitnessScope Scopes; + + /// + /// The contracts that allowed by the witness. Only available when the flag is set. + /// public UInt160[] AllowedContracts; + + /// + /// The groups that allowed by the witness. Only available when the flag is set. + /// public ECPoint[] AllowedGroups; public int Size => @@ -33,10 +51,10 @@ public void Deserialize(BinaryReader reader) throw new FormatException(); AllowedContracts = Scopes.HasFlag(WitnessScope.CustomContracts) ? reader.ReadSerializableArray(MaxSubitems) - : new UInt160[0]; + : Array.Empty(); AllowedGroups = Scopes.HasFlag(WitnessScope.CustomGroups) ? reader.ReadSerializableArray(MaxSubitems) - : new ECPoint[0]; + : Array.Empty(); } public void Serialize(BinaryWriter writer) @@ -49,6 +67,10 @@ public void Serialize(BinaryWriter writer) writer.Write(AllowedGroups); } + /// + /// Converts the signer to a JSON object. + /// + /// The signer represented by a JSON object. public JObject ToJson() { var json = new JObject(); diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index b114c15f73..7608ae6d1e 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -17,12 +17,23 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents a transaction. + /// public class Transaction : IEquatable, IInventory, IInteroperable { + /// + /// The maximum size of a transaction. + /// public const int MaxTransactionSize = 102400; + + /// + /// The maximum increment of the field. + /// public const uint MaxValidUntilBlockIncrement = 5760; // 24 hour + /// - /// Maximum number of attributes that can be contained within a transaction + /// The maximum number of attributes that can be contained within a transaction. /// public const int MaxTransactionAttributes = 16; @@ -36,6 +47,9 @@ public class Transaction : IEquatable, IInventory, IInteroperable private byte[] script; private Witness[] witnesses; + /// + /// The size of a transaction header. + /// public const int HeaderSize = sizeof(byte) + //Version sizeof(uint) + //Nonce @@ -44,6 +58,9 @@ public class Transaction : IEquatable, IInventory, IInteroperable sizeof(uint); //ValidUntilBlock private Dictionary _attributesCache; + /// + /// The attributes of the transaction. + /// public TransactionAttribute[] Attributes { get => attributes; @@ -51,8 +68,7 @@ public TransactionAttribute[] Attributes } /// - /// The NetworkFee for the transaction divided by its Size. - /// Note that this property must be used with care. Getting the value of this property multiple times will return the same result. The value of this property can only be obtained after the transaction has been completely built (no longer modified). + /// The for the transaction divided by its . /// public long FeePerByte => NetworkFee / Size; @@ -72,20 +88,26 @@ public UInt256 Hash InventoryType IInventory.InventoryType => InventoryType.TX; /// - /// Distributed to consensus nodes. + /// The network fee of the transaction. /// - public long NetworkFee + public long NetworkFee //Distributed to consensus nodes. { get => netfee; set { netfee = value; _hash = null; } } + /// + /// The nonce of the transaction. + /// public uint Nonce { get => nonce; set { nonce = value; _hash = null; } } + /// + /// The script of the transaction. + /// public byte[] Script { get => script; @@ -93,11 +115,14 @@ public byte[] Script } /// - /// The first signer is the sender of the transaction, regardless of its WitnessScope. - /// The sender will pay the fees of the transaction. + /// The sender is the first signer of the transaction, regardless of its . /// + /// Note: The sender will pay the fees of the transaction. public UInt160 Sender => _signers[0].Account; + /// + /// The signers of the transaction. + /// public Signer[] Signers { get => _signers; @@ -122,20 +147,26 @@ public int Size } /// - /// Fee to be burned. + /// The system fee of the transaction. /// - public long SystemFee + public long SystemFee //Fee to be burned. { get => sysfee; set { sysfee = value; _hash = null; } } + /// + /// Indicates that the transaction is only valid before this block height. + /// public uint ValidUntilBlock { get => validUntilBlock; set { validUntilBlock = value; _hash = null; } } + /// + /// The version of the transaction. + /// public byte Version { get => version; @@ -163,7 +194,7 @@ void ISerializable.Deserialize(BinaryReader reader) private static IEnumerable DeserializeAttributes(BinaryReader reader, int maxCount) { int count = (int)reader.ReadVarInt((ulong)maxCount); - HashSet hashset = new HashSet(); + HashSet hashset = new(); while (count-- > 0) { TransactionAttribute attribute = TransactionAttribute.DeserializeFrom(reader); @@ -177,7 +208,7 @@ private static IEnumerable DeserializeSigners(BinaryReader reader, int m { int count = (int)reader.ReadVarInt((ulong)maxCount); if (count == 0) throw new FormatException(); - HashSet hashset = new HashSet(); + HashSet hashset = new(); for (int i = 0; i < count; i++) { Signer signer = reader.ReadSerializable(); @@ -220,11 +251,21 @@ void IInteroperable.FromStackItem(StackItem stackItem) throw new NotSupportedException(); } + /// + /// Gets the attribute of the specified type. + /// + /// The type of the attribute. + /// The first attribute of this type. Or if there is no attribute of this type. public T GetAttribute() where T : TransactionAttribute { return GetAttributes().FirstOrDefault(); } + /// + /// Gets all attributes of the specified type. + /// + /// The type of the attributes. + /// All the attributes of this type. public IEnumerable GetAttributes() where T : TransactionAttribute { _attributesCache ??= attributes.GroupBy(p => p.GetType()).ToDictionary(p => p.Key, p => p.ToArray()); @@ -261,9 +302,14 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer) writer.WriteVarBytes(Script); } + /// + /// Converts the transaction to a JSON object. + /// + /// The used during the conversion. + /// The transaction represented by a JSON object. public JObject ToJson(ProtocolSettings settings) { - JObject json = new JObject(); + JObject json = new(); json["hash"] = Hash.ToString(); json["size"] = Size; json["version"] = Version; @@ -279,6 +325,13 @@ public JObject ToJson(ProtocolSettings settings) return json; } + /// + /// Verifies the transaction. + /// + /// The used to verify the transaction. + /// The snapshot used to verify the transaction. + /// The used to verify the transaction. + /// The result of the verification. public VerifyResult Verify(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) { VerifyResult result = VerifyStateIndependent(settings); @@ -286,6 +339,13 @@ public VerifyResult Verify(ProtocolSettings settings, DataCache snapshot, Transa return VerifyStateDependent(settings, snapshot, context); } + /// + /// Verifies the state-dependent part of the transaction. + /// + /// The used to verify the transaction. + /// The snapshot used to verify the transaction. + /// The used to verify the transaction. + /// The result of the verification. public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, DataCache snapshot, TransactionVerificationContext context) { uint height = NativeContract.Ledger.CurrentIndex(snapshot); @@ -321,6 +381,11 @@ public virtual VerifyResult VerifyStateDependent(ProtocolSettings settings, Data return VerifyResult.Succeed; } + /// + /// Verifies the state-independent part of the transaction. + /// + /// The used to verify the transaction. + /// The result of the verification. public virtual VerifyResult VerifyStateIndependent(ProtocolSettings settings) { if (Size > MaxTransactionSize) return VerifyResult.Invalid; diff --git a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs index acd3238119..cfb3a921d5 100644 --- a/src/neo/Network/P2P/Payloads/TransactionAttribute.cs +++ b/src/neo/Network/P2P/Payloads/TransactionAttribute.cs @@ -7,10 +7,21 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents an attribute of a transaction. + /// public abstract class TransactionAttribute : ISerializable { + /// + /// The type of the attribute. + /// public abstract TransactionAttributeType Type { get; } + + /// + /// Indicates whether multiple instances of this attribute are allowed. + /// public abstract bool AllowMultiple { get; } + public virtual int Size => sizeof(TransactionAttributeType); public void Deserialize(BinaryReader reader) @@ -20,17 +31,30 @@ public void Deserialize(BinaryReader reader) DeserializeWithoutType(reader); } + /// + /// Deserializes an object from a . + /// + /// The for reading data. + /// The deserialized attribute. public static TransactionAttribute DeserializeFrom(BinaryReader reader) { TransactionAttributeType type = (TransactionAttributeType)reader.ReadByte(); - if (!(ReflectionCache.CreateInstance(type) is TransactionAttribute attribute)) + if (ReflectionCache.CreateInstance(type) is not TransactionAttribute attribute) throw new FormatException(); attribute.DeserializeWithoutType(reader); return attribute; } + /// + /// Deserializes the object from a . + /// + /// The for reading data. protected abstract void DeserializeWithoutType(BinaryReader reader); + /// + /// Converts the attribute to a JSON object. + /// + /// The attribute represented by a JSON object. public virtual JObject ToJson() { return new JObject @@ -45,8 +69,18 @@ public void Serialize(BinaryWriter writer) SerializeWithoutType(writer); } + /// + /// Serializes the object to a . + /// + /// The for writing data. protected abstract void SerializeWithoutType(BinaryWriter writer); + /// + /// Verifies the attribute with the transaction. + /// + /// The snapshot used to verify the attribute. + /// The that contains the attribute. + /// if the verification passes; otherwise, . public virtual bool Verify(DataCache snapshot, Transaction tx) => true; } } diff --git a/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs b/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs index 26679ca92b..f7b787b7ff 100644 --- a/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs +++ b/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs @@ -2,10 +2,20 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents the type of a . + /// public enum TransactionAttributeType : byte { + /// + /// Indicates that the transaction is of high priority. + /// [ReflectionCache(typeof(HighPriorityAttribute))] HighPriority = 0x01, + + /// + /// Indicates that the transaction is an oracle response. + /// [ReflectionCache(typeof(OracleResponse))] OracleResponse = 0x11 } diff --git a/src/neo/Network/P2P/Payloads/VersionPayload.cs b/src/neo/Network/P2P/Payloads/VersionPayload.cs index abb170baf2..c8ca128da5 100644 --- a/src/neo/Network/P2P/Payloads/VersionPayload.cs +++ b/src/neo/Network/P2P/Payloads/VersionPayload.cs @@ -6,15 +6,44 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Sent when a connection is established. + /// public class VersionPayload : ISerializable { + /// + /// Indicates the maximum number of capabilities contained in a . + /// public const int MaxCapabilities = 32; + /// + /// The magic number of the network. + /// public uint Magic; + + /// + /// The protocol version of the node. + /// public uint Version; + + /// + /// The time when connected to the node. + /// public uint Timestamp; + + /// + /// A random number used to identify the node. + /// public uint Nonce; + + /// + /// A used to identify the client software of the node. + /// public string UserAgent; + + /// + /// The capabilities of the node. + /// public NodeCapability[] Capabilities; public int Size => @@ -25,6 +54,14 @@ public class VersionPayload : ISerializable UserAgent.GetVarSize() + // UserAgent Capabilities.GetVarSize(); // Capabilities + /// + /// Creates a new instance of the class. + /// + /// The magic number of the network. + /// The random number used to identify the node. + /// The used to identify the client software of the node. + /// The capabilities of the node. + /// public static VersionPayload Create(uint magic, uint nonce, string userAgent, params NodeCapability[] capabilities) { return new VersionPayload diff --git a/src/neo/Network/P2P/Payloads/Witness.cs b/src/neo/Network/P2P/Payloads/Witness.cs index d6cfcf7470..f5dc6f3163 100644 --- a/src/neo/Network/P2P/Payloads/Witness.cs +++ b/src/neo/Network/P2P/Payloads/Witness.cs @@ -6,24 +6,33 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents a witness of an object. + /// public class Witness : ISerializable { - /// - /// This is designed to allow a MultiSig 21/11 (committee) - /// Invocation = 11 * (64 + 2) = 726 - /// + // This is designed to allow a MultiSig 21/11 (committee) + // Invocation = 11 * (64 + 2) = 726 private const int MaxInvocationScript = 1024; - /// - /// Verification = m + (PUSH_PubKey * 21) + length + null + syscall = 1 + ((2 + 33) * 21) + 2 + 1 + 5 = 744 - /// + // Verification = m + (PUSH_PubKey * 21) + length + null + syscall = 1 + ((2 + 33) * 21) + 2 + 1 + 5 = 744 private const int MaxVerificationScript = 1024; + /// + /// The invocation script of the witness. Used to pass arguments for . + /// public byte[] InvocationScript; + + /// + /// The verification script of the witness. It can be empty if the contract is deployed. + /// public byte[] VerificationScript; private UInt160 _scriptHash; - public virtual UInt160 ScriptHash + /// + /// The hash of the . + /// + public UInt160 ScriptHash { get { @@ -49,9 +58,13 @@ void ISerializable.Serialize(BinaryWriter writer) writer.WriteVarBytes(VerificationScript); } + /// + /// Converts the witness to a JSON object. + /// + /// The witness represented by a JSON object. public JObject ToJson() { - JObject json = new JObject(); + JObject json = new(); json["invocation"] = Convert.ToBase64String(InvocationScript); json["verification"] = Convert.ToBase64String(VerificationScript); return json; diff --git a/src/neo/Network/P2P/Payloads/WitnessScope.cs b/src/neo/Network/P2P/Payloads/WitnessScope.cs index 7ef499ad41..394014a8e4 100644 --- a/src/neo/Network/P2P/Payloads/WitnessScope.cs +++ b/src/neo/Network/P2P/Payloads/WitnessScope.cs @@ -2,35 +2,38 @@ namespace Neo.Network.P2P.Payloads { + /// + /// Represents the scope of a . + /// [Flags] public enum WitnessScope : byte { /// - /// No contract was witnessed. Only sign the transaction. + /// Indicates that no contract was witnessed. Only sign the transaction. /// None = 0, /// - /// CalledByEntry means that this condition must hold: EntryScriptHash == CallingScriptHash - /// No params is needed, as the witness/permission/signature given on first invocation will automatically expire if entering deeper internal invokes - /// This can be default safe choice for native NEO/GAS (previously used on Neo 2 as "attach" mode) + /// Indicates that the calling contract must be the entry contract. + /// The witness/permission/signature given on first invocation will automatically expire if entering deeper internal invokes. + /// This can be the default safe choice for native NEO/GAS (previously used on Neo 2 as "attach" mode). /// CalledByEntry = 0x01, /// - /// Custom hash for contract-specific + /// Custom hash for contract-specific. /// CustomContracts = 0x10, /// - /// Custom pubkey for group members + /// Custom pubkey for group members. /// CustomGroups = 0x20, /// - /// Global allows this witness in all contexts (default Neo2 behavior) - /// This cannot be combined with other flags + /// This allows the witness in all contexts (default Neo2 behavior). /// + /// Note: It cannot be combined with other flags. Global = 0x80 } } diff --git a/src/neo/Network/P2P/Peer.cs b/src/neo/Network/P2P/Peer.cs index 106f530b71..a7b491892a 100644 --- a/src/neo/Network/P2P/Peer.cs +++ b/src/neo/Network/P2P/Peer.cs @@ -18,46 +18,114 @@ namespace Neo.Network.P2P { + /// + /// Actor used to manage the connections of the local node. + /// public abstract class Peer : UntypedActor { - public class Peers { public IEnumerable EndPoints; } - public class Connect { public IPEndPoint EndPoint; public bool IsTrusted = false; } + /// + /// Sent to to add more unconnected peers. + /// + public class Peers + { + /// + /// The unconnected peers to be added. + /// + public IEnumerable EndPoints { get; init; } + } + + /// + /// Sent to to connect to a remote node. + /// + public class Connect + { + /// + /// The address of the remote node. + /// + public IPEndPoint EndPoint { get; init; } + + /// + /// Indicates whether the remote node is trusted. A trusted node will always be connected. + /// + public bool IsTrusted { get; init; } + } + private class Timer { } private class WsConnected { public WebSocket Socket; public IPEndPoint Remote; public IPEndPoint Local; } + /// + /// The default minimum number of desired connections. + /// public const int DefaultMinDesiredConnections = 10; + + /// + /// The default maximum number of desired connections. + /// public const int DefaultMaxConnections = DefaultMinDesiredConnections * 4; private static readonly IActorRef tcp_manager = Context.System.Tcp(); private IActorRef tcp_listener; private IWebHost ws_host; private ICancelable timer; - protected ActorSelection Connections => Context.ActorSelection("connection_*"); - private static readonly HashSet localAddresses = new HashSet(); - private readonly Dictionary ConnectedAddresses = new Dictionary(); + private static readonly HashSet localAddresses = new(); + private readonly Dictionary ConnectedAddresses = new(); + /// /// A dictionary that stores the connected nodes. /// - protected readonly ConcurrentDictionary ConnectedPeers = new ConcurrentDictionary(); + protected readonly ConcurrentDictionary ConnectedPeers = new(); + /// - /// An ImmutableHashSet that stores the Peers received: 1) from other nodes or 2) from default file. + /// A set that stores the peers received from other nodes. /// If the number of desired connections is not enough, first try to connect with the peers from this set. /// protected ImmutableHashSet UnconnectedPeers = ImmutableHashSet.Empty; + /// - /// When a TCP connection request is sent to a peer, the peer will be added to the ImmutableHashSet. + /// When a TCP connection request is sent to a peer, the peer will be added to the set. /// If a Tcp.Connected or a Tcp.CommandFailed (with TCP.Command of type Tcp.Connect) is received, the related peer will be removed. /// protected ImmutableHashSet ConnectingPeers = ImmutableHashSet.Empty; - protected HashSet TrustedIpAddresses { get; } = new HashSet(); + /// + /// A hash set to store the trusted nodes. A trusted node will always be connected. + /// + protected HashSet TrustedIpAddresses { get; } = new(); + + /// + /// The port listened by the local Tcp server. + /// public int ListenerTcpPort { get; private set; } + + /// + /// The port listened by the local WebSocket server. + /// public int ListenerWsPort { get; private set; } + + /// + /// Indicates the maximum number of connections with the same address. + /// public int MaxConnectionsPerAddress { get; private set; } = 3; + + /// + /// Indicates the minimum number of desired connections. + /// public int MinDesiredConnections { get; private set; } = DefaultMinDesiredConnections; + + /// + /// Indicates the maximum number of connections. + /// public int MaxConnections { get; private set; } = DefaultMaxConnections; + + /// + /// Indicates the maximum number of unconnected peers stored in . + /// protected int UnconnectedMax { get; } = 1000; + + /// + /// Indicates the maximum number of pending connections. + /// protected virtual int ConnectingMax { get @@ -89,6 +157,11 @@ protected void AddPeers(IEnumerable peers) } } + /// + /// Tries to connect the a remote peer. + /// + /// The address of the remote peer. + /// Indicates whether the remote node is trusted. A trusted node will always be connected. protected void ConnectToPeer(IPEndPoint endPoint, bool isTrusted = false) { endPoint = endPoint.Unmap(); @@ -116,7 +189,7 @@ private static bool IsIntranetAddress(IPAddress address) } /// - /// Abstract method for asking for more peers. Currently triggered when UnconnectedPeers is empty. + /// Called for asking for more peers. /// /// Number of peers that are being requested. protected abstract void NeedMorePeers(int count); @@ -230,6 +303,10 @@ private void OnTcpConnected(IPEndPoint remote, IPEndPoint local) } } + /// + /// Called when a Tcp connection is established. + /// + /// The connection actor. protected virtual void OnTcpConnected(IActorRef connection) { } @@ -271,7 +348,7 @@ private void OnTimer() if (UnconnectedPeers.Count == 0) NeedMorePeers(MinDesiredConnections - ConnectedPeers.Count); - Random rand = new Random(); + Random rand = new(); IPEndPoint[] endpoints = UnconnectedPeers.OrderBy(u => rand.Next()).Take(MinDesiredConnections - ConnectedPeers.Count).ToArray(); ImmutableInterlocked.Update(ref UnconnectedPeers, p => p.Except(endpoints)); foreach (IPEndPoint endpoint in endpoints) @@ -314,6 +391,13 @@ private async Task ProcessWebSocketAsync(HttpContext context) }); } + /// + /// Gets a object used for creating the protocol actor. + /// + /// The underlying connection object. + /// The address of the remote node. + /// The address of the local node. + /// The object used for creating the protocol actor. protected abstract Props ProtocolProps(object connection, IPEndPoint remote, IPEndPoint local); } } diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 727cbc0db1..ed85d130a2 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -27,7 +27,7 @@ protected override UInt256 GetKeyForItem((UInt256, DateTime) item) } } - private readonly PendingKnownHashesCollection pendingKnownHashes = new PendingKnownHashesCollection(); + private readonly PendingKnownHashesCollection pendingKnownHashes = new(); private readonly HashSetCache knownHashes; private readonly HashSetCache sentHashes; private bool verack = false; @@ -151,7 +151,7 @@ private void OnFilterLoadMessageReceived(FilterLoadPayload payload) /// private void OnGetAddrMessageReceived() { - Random rand = new Random(); + Random rand = new(); IEnumerable peers = localNode.RemoteNodes.Values .Where(p => p.ListenerTcpPort > 0) .GroupBy(p => p.Remote.Address, (k, g) => g.First()) @@ -177,7 +177,7 @@ private void OnGetBlocksMessageReceived(GetBlocksPayload payload) TrimmedBlock state = NativeContract.Ledger.GetTrimmedBlock(snapshot, hash); if (state == null) return; uint currentHeight = NativeContract.Ledger.CurrentIndex(snapshot); - List hashes = new List(); + List hashes = new(); for (uint i = 1; i <= count; i++) { uint index = state.Index + i; @@ -206,7 +206,7 @@ private void OnGetBlockByIndexMessageReceived(GetBlockByIndexPayload payload) } else { - BitArray flags = new BitArray(block.Transactions.Select(p => bloom_filter.Test(p)).ToArray()); + BitArray flags = new(block.Transactions.Select(p => bloom_filter.Test(p)).ToArray()); EnqueueMessage(Message.Create(MessageCommand.MerkleBlock, MerkleBlockPayload.Create(block, flags))); } } @@ -241,7 +241,7 @@ private void OnGetDataMessageReceived(InvPayload payload) } else { - BitArray flags = new BitArray(block.Transactions.Select(p => bloom_filter.Test(p)).ToArray()); + BitArray flags = new(block.Transactions.Select(p => bloom_filter.Test(p)).ToArray()); EnqueueMessage(Message.Create(MessageCommand.MerkleBlock, MerkleBlockPayload.Create(block, flags))); } } @@ -274,7 +274,7 @@ private void OnGetHeadersMessageReceived(GetBlockByIndexPayload payload) { DataCache snapshot = system.StoreView; if (payload.IndexStart > NativeContract.Ledger.CurrentIndex(snapshot)) return; - List
headers = new List
(); + List
headers = new(); uint count = payload.Count == -1 ? HeadersPayload.MaxHeadersCount : (uint)payload.Count; for (uint i = 0; i < count; i++) { diff --git a/src/neo/Network/P2P/RemoteNode.cs b/src/neo/Network/P2P/RemoteNode.cs index d05af34818..04f9a25ef6 100644 --- a/src/neo/Network/P2P/RemoteNode.cs +++ b/src/neo/Network/P2P/RemoteNode.cs @@ -16,6 +16,9 @@ namespace Neo.Network.P2P { + /// + /// Represents a connection of the NEO network. + /// public partial class RemoteNode : Connection { internal class StartProtocol { } @@ -23,20 +26,47 @@ internal class Relay { public IInventory Inventory; } private readonly NeoSystem system; private readonly LocalNode localNode; - private readonly Queue message_queue_high = new Queue(); - private readonly Queue message_queue_low = new Queue(); + private readonly Queue message_queue_high = new(); + private readonly Queue message_queue_low = new(); private DateTime lastSent = TimeProvider.Current.UtcNow; private readonly bool[] sentCommands = new bool[1 << (sizeof(MessageCommand) * 8)]; private ByteString msg_buffer = ByteString.Empty; private bool ack = true; + private uint lastHeightSent = 0; - public IPEndPoint Listener => new IPEndPoint(Remote.Address, ListenerTcpPort); + /// + /// The address of the remote Tcp server. + /// + public IPEndPoint Listener => new(Remote.Address, ListenerTcpPort); + + /// + /// The port listened by the remote Tcp server. If the remote node is not a server, this field is 0. + /// public int ListenerTcpPort { get; private set; } = 0; + + /// + /// The sent by the remote node. + /// public VersionPayload Version { get; private set; } + + /// + /// The index of the last block sent by the remote node. + /// public uint LastBlockIndex { get; private set; } = 0; - public uint LastHeightSent { get; private set; } = 0; + + /// + /// Indicates whether the remote node is a full node. + /// public bool IsFullNode { get; private set; } = false; + /// + /// Initializes a new instance of the class. + /// + /// The object that contains the . + /// The that manages the . + /// The underlying connection object. + /// The address of the remote node. + /// The address of the local node. public RemoteNode(NeoSystem system, LocalNode localNode, object connection, IPEndPoint remote, IPEndPoint local) : base(connection, remote, local) { @@ -76,35 +106,16 @@ private void EnqueueMessage(MessageCommand command, ISerializable payload = null /// The message to be added. private void EnqueueMessage(Message message) { - bool is_single = false; - switch (message.Command) + bool is_single = message.Command switch { - case MessageCommand.Addr: - case MessageCommand.GetAddr: - case MessageCommand.GetBlocks: - case MessageCommand.GetHeaders: - case MessageCommand.Mempool: - case MessageCommand.Ping: - case MessageCommand.Pong: - is_single = true; - break; - } - Queue message_queue; - switch (message.Command) + MessageCommand.Addr or MessageCommand.GetAddr or MessageCommand.GetBlocks or MessageCommand.GetHeaders or MessageCommand.Mempool or MessageCommand.Ping or MessageCommand.Pong => true, + _ => false, + }; + Queue message_queue = message.Command switch { - case MessageCommand.Alert: - case MessageCommand.Extensible: - case MessageCommand.FilterAdd: - case MessageCommand.FilterClear: - case MessageCommand.FilterLoad: - case MessageCommand.GetAddr: - case MessageCommand.Mempool: - message_queue = message_queue_high; - break; - default: - message_queue = message_queue_low; - break; - } + MessageCommand.Alert or MessageCommand.Extensible or MessageCommand.FilterAdd or MessageCommand.FilterClear or MessageCommand.FilterLoad or MessageCommand.GetAddr or MessageCommand.Mempool => message_queue_high, + _ => message_queue_low, + }; if (!is_single || message_queue.All(p => p.Command != message.Command)) { message_queue.Enqueue(message); @@ -138,8 +149,8 @@ protected override void OnReceive(object message) case Message msg: if (msg.Payload is PingPayload payload) { - if (payload.LastBlockIndex > LastHeightSent) - LastHeightSent = payload.LastBlockIndex; + if (payload.LastBlockIndex > lastHeightSent) + lastHeightSent = payload.LastBlockIndex; else if (msg.Command == MessageCommand.Ping) break; } @@ -227,44 +238,26 @@ public RemoteNodeMailbox(Settings settings, Config config) : base(settings, conf internal protected override bool IsHighPriority(object message) { - switch (message) + return message switch { - case Message msg: - switch (msg.Command) - { - case MessageCommand.Extensible: - case MessageCommand.FilterAdd: - case MessageCommand.FilterClear: - case MessageCommand.FilterLoad: - case MessageCommand.Verack: - case MessageCommand.Version: - case MessageCommand.Alert: - return true; - default: - return false; - } - case Tcp.ConnectionClosed _: - case Connection.Close _: - case Connection.Ack _: - return true; - default: - return false; - } + Message msg => msg.Command switch + { + MessageCommand.Extensible or MessageCommand.FilterAdd or MessageCommand.FilterClear or MessageCommand.FilterLoad or MessageCommand.Verack or MessageCommand.Version or MessageCommand.Alert => true, + _ => false, + }, + Tcp.ConnectionClosed _ or Connection.Close _ or Connection.Ack _ => true, + _ => false, + }; } internal protected override bool ShallDrop(object message, IEnumerable queue) { - if (!(message is Message msg)) return false; - switch (msg.Command) + if (message is not Message msg) return false; + return msg.Command switch { - case MessageCommand.GetAddr: - case MessageCommand.GetBlocks: - case MessageCommand.GetHeaders: - case MessageCommand.Mempool: - return queue.OfType().Any(p => p.Command == msg.Command); - default: - return false; - } + MessageCommand.GetAddr or MessageCommand.GetBlocks or MessageCommand.GetHeaders or MessageCommand.Mempool => queue.OfType().Any(p => p.Command == msg.Command), + _ => false, + }; } } } diff --git a/src/neo/Network/P2P/TaskManager.cs b/src/neo/Network/P2P/TaskManager.cs index 60aed1772d..01efadc650 100644 --- a/src/neo/Network/P2P/TaskManager.cs +++ b/src/neo/Network/P2P/TaskManager.cs @@ -15,12 +15,26 @@ namespace Neo.Network.P2P { + /// + /// Actor used to manage the tasks of inventories. + /// public class TaskManager : UntypedActor { internal class Register { public VersionPayload Version; } internal class Update { public uint LastBlockIndex; } internal class NewTasks { public InvPayload Payload; } - public class RestartTasks { public InvPayload Payload; } + + /// + /// Sent to to restart tasks for inventories. + /// + public class RestartTasks + { + /// + /// The inventories that need to restart. + /// + public InvPayload Payload { get; init; } + } + private class Timer { } private static readonly TimeSpan TimerInterval = TimeSpan.FromSeconds(30); @@ -34,13 +48,17 @@ private class Timer { } /// A set of known hashes, of inventories or payloads, already received. /// private readonly HashSetCache knownHashes; - private readonly Dictionary globalInvTasks = new Dictionary(); - private readonly Dictionary globalIndexTasks = new Dictionary(); - private readonly Dictionary sessions = new Dictionary(); + private readonly Dictionary globalInvTasks = new(); + private readonly Dictionary globalIndexTasks = new(); + private readonly Dictionary sessions = new(); private readonly ICancelable timer = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimerInterval, TimerInterval, Context.Self, new Timer(), ActorRefs.NoSender); private bool HasHeaderTask => globalInvTasks.ContainsKey(HeaderTaskHash); + /// + /// Initializes a new instance of the class. + /// + /// The object that contains the . public TaskManager(NeoSystem system) { this.system = system; @@ -80,7 +98,7 @@ private void OnNewTasks(InvPayload payload) return; } - HashSet hashes = new HashSet(payload.Hashes); + HashSet hashes = new(payload.Hashes); // Remove all previously processed knownHashes from the list that is being requested hashes.Remove(knownHashes); // Add to AvailableTasks the ones, of type InventoryType.Block, that are global (already under process by other sessions) @@ -159,7 +177,7 @@ protected override void OnReceive(object message) private void OnRegister(VersionPayload version) { Context.Watch(Sender); - TaskSession session = new TaskSession(version); + TaskSession session = new(version); sessions.Add(Sender, session); RequestTasks(Sender, session); } @@ -304,6 +322,11 @@ protected override void PostStop() base.PostStop(); } + /// + /// Gets a object used for creating the actor. + /// + /// The object that contains the . + /// The object used for creating the actor. public static Props Props(NeoSystem system) { return Akka.Actor.Props.Create(() => new TaskManager(system)).WithMailbox("task-manager-mailbox"); @@ -321,7 +344,7 @@ private void RequestTasks(IActorRef remoteNode, TaskSession session) session.AvailableTasks.Remove(knownHashes); // Search any similar hash that is on Singleton's knowledge, which means, on the way or already processed session.AvailableTasks.RemoveWhere(p => NativeContract.Ledger.ContainsBlock(snapshot, p)); - HashSet hashes = new HashSet(session.AvailableTasks); + HashSet hashes = new(session.AvailableTasks); if (hashes.Count > 0) { foreach (UInt256 hash in hashes.ToArray()) @@ -397,7 +420,7 @@ internal protected override bool IsHighPriority(object message) internal protected override bool ShallDrop(object message, IEnumerable queue) { - if (!(message is TaskManager.NewTasks tasks)) return false; + if (message is not TaskManager.NewTasks tasks) return false; // Remove duplicate tasks if (queue.OfType().Any(x => x.Payload.Type == tasks.Payload.Type && x.Payload.Hashes.SequenceEqual(tasks.Payload.Hashes))) return true; return false; diff --git a/src/neo/Network/UPnP.cs b/src/neo/Network/UPnP.cs index 1a5eec4a29..afa6b4590b 100644 --- a/src/neo/Network/UPnP.cs +++ b/src/neo/Network/UPnP.cs @@ -8,81 +8,89 @@ namespace Neo.Network { - public class UPnP + /// + /// Provides methods for interacting with UPnP devices. + /// + public static class UPnP { private static string _serviceUrl; + /// + /// Gets or sets the timeout for discovering the UPnP device. + /// public static TimeSpan TimeOut { get; set; } = TimeSpan.FromSeconds(3); + /// + /// Sends an Udp broadcast message to discover the UPnP device. + /// + /// if the UPnP device is successfully discovered; otherwise, . public static bool Discover() { - using (Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) + using Socket s = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + s.ReceiveTimeout = (int)TimeOut.TotalMilliseconds; + s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); + string req = "M-SEARCH * HTTP/1.1\r\n" + + "HOST: 239.255.255.250:1900\r\n" + + "ST:upnp:rootdevice\r\n" + + "MAN:\"ssdp:discover\"\r\n" + + "MX:3\r\n\r\n"; + byte[] data = Encoding.ASCII.GetBytes(req); + IPEndPoint ipe = new(IPAddress.Broadcast, 1900); + + DateTime start = DateTime.Now; + + try { - s.ReceiveTimeout = (int)TimeOut.TotalMilliseconds; - s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); - string req = "M-SEARCH * HTTP/1.1\r\n" + - "HOST: 239.255.255.250:1900\r\n" + - "ST:upnp:rootdevice\r\n" + - "MAN:\"ssdp:discover\"\r\n" + - "MX:3\r\n\r\n"; - byte[] data = Encoding.ASCII.GetBytes(req); - IPEndPoint ipe = new IPEndPoint(IPAddress.Broadcast, 1900); + s.SendTo(data, ipe); + s.SendTo(data, ipe); + s.SendTo(data, ipe); + } + catch + { + return false; + } - DateTime start = DateTime.Now; + Span buffer = stackalloc byte[0x1000]; + do + { + int length; try { - s.SendTo(data, ipe); - s.SendTo(data, ipe); - s.SendTo(data, ipe); - } - catch - { - return false; - } - - Span buffer = stackalloc byte[0x1000]; + length = s.Receive(buffer); - do - { - int length; - try + string resp = Encoding.ASCII.GetString(buffer[..length]).ToLowerInvariant(); + if (resp.Contains("upnp:rootdevice")) { - length = s.Receive(buffer); - - string resp = Encoding.ASCII.GetString(buffer[..length]).ToLowerInvariant(); - if (resp.Contains("upnp:rootdevice")) + resp = resp[(resp.IndexOf("location:") + 9)..]; + resp = resp.Substring(0, resp.IndexOf("\r")).Trim(); + if (!string.IsNullOrEmpty(_serviceUrl = GetServiceUrl(resp))) { - resp = resp.Substring(resp.IndexOf("location:") + 9); - resp = resp.Substring(0, resp.IndexOf("\r")).Trim(); - if (!string.IsNullOrEmpty(_serviceUrl = GetServiceUrl(resp))) - { - return true; - } + return true; } } - catch - { - continue; - } } - while (DateTime.Now - start < TimeOut); - - return false; + catch + { + continue; + } } + while (DateTime.Now - start < TimeOut); + + return false; } private static string GetServiceUrl(string resp) { try { - XmlDocument desc = new XmlDocument() { XmlResolver = null }; + XmlDocument desc = new() { XmlResolver = null }; HttpWebRequest request = WebRequest.CreateHttp(resp); using (WebResponse response = request.GetResponse()) { desc.Load(response.GetResponseStream()); } - XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable); + XmlNamespaceManager nsMgr = new(desc.NameTable); nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr); if (!typen.Value.Contains("InternetGatewayDevice")) @@ -103,6 +111,12 @@ private static string CombineUrls(string resp, string p) return resp.Substring(0, n) + p; } + /// + /// Attempt to create a port forwarding. + /// + /// The port to forward. + /// The of the port. + /// The description of the forward. public static void ForwardPort(int port, ProtocolType protocol, string description) { if (string.IsNullOrEmpty(_serviceUrl)) @@ -114,6 +128,11 @@ public static void ForwardPort(int port, ProtocolType protocol, string descripti "0", "AddPortMapping"); } + /// + /// Attempt to delete a port forwarding. + /// + /// The port to forward. + /// The of the port. public static void DeleteForwardingRule(int port, ProtocolType protocol) { if (string.IsNullOrEmpty(_serviceUrl)) @@ -127,13 +146,17 @@ public static void DeleteForwardingRule(int port, ProtocolType protocol) "", "DeletePortMapping"); } + /// + /// Attempt to get the external IP address of the local host. + /// + /// The external IP address of the local host. public static IPAddress GetExternalIP() { if (string.IsNullOrEmpty(_serviceUrl)) throw new Exception("No UPnP service available or Discover() has not been called"); XmlDocument xdoc = SOAPRequest(_serviceUrl, "" + "", "GetExternalIPAddress"); - XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable); + XmlNamespaceManager nsMgr = new(xdoc.NameTable); nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value; return IPAddress.Parse(IP); @@ -152,17 +175,13 @@ private static XmlDocument SOAPRequest(string url, string soap, string function) byte[] b = Encoding.UTF8.GetBytes(req); r.Headers["SOAPACTION"] = "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\""; r.ContentType = "text/xml; charset=\"utf-8\""; - using (Stream reqs = r.GetRequestStream()) - { - reqs.Write(b, 0, b.Length); - XmlDocument resp = new XmlDocument() { XmlResolver = null }; - WebResponse wres = r.GetResponse(); - using (Stream ress = wres.GetResponseStream()) - { - resp.Load(ress); - return resp; - } - } + using Stream reqs = r.GetRequestStream(); + reqs.Write(b, 0, b.Length); + XmlDocument resp = new() { XmlResolver = null }; + WebResponse wres = r.GetResponse(); + using Stream ress = wres.GetResponseStream(); + resp.Load(ress); + return resp; } } } diff --git a/src/neo/Persistence/DataCache.cs b/src/neo/Persistence/DataCache.cs index a5b96fc5fe..fbd81e3c39 100644 --- a/src/neo/Persistence/DataCache.cs +++ b/src/neo/Persistence/DataCache.cs @@ -6,18 +6,41 @@ namespace Neo.Persistence { + /// + /// Represents a cache for the underlying storage of the NEO blockchain. + /// public abstract class DataCache { + /// + /// Represents an entry in the cache. + /// public class Trackable { + /// + /// The key of the entry. + /// public StorageKey Key; + + /// + /// The data of the entry. + /// public StorageItem Item; + + /// + /// The state of the entry. + /// public TrackState State; } - private readonly Dictionary dictionary = new Dictionary(); - private readonly HashSet changeSet = new HashSet(); + private readonly Dictionary dictionary = new(); + private readonly HashSet changeSet = new(); + /// + /// Reads a specified entry from the cache. If the entry is not in the cache, it will be automatically loaded from the underlying storage. + /// + /// The key of the entry. + /// The cached data. + /// If the entry doesn't exist. public StorageItem this[StorageKey key] { get @@ -45,15 +68,12 @@ public StorageItem this[StorageKey key] } /// - /// Try to Add a specific key, with associated value, to the current cached dictionary. - /// It will not read from internal state. - /// However, if previously cached into Dictionary, request may fail. + /// Adds a new entry to the cache. /// - /// Key to be possible added. - /// Key will not be added if value exists cached and the modification was not a Deleted one. - /// - /// Corresponding value to be added, in the case of sucess. - /// If cached on dictionary, with any state rather than `Deleted`, an Exception will be raised. + /// The key of the entry. + /// The data of the entry. + /// The entry has already been cached. + /// Note: This method does not read the internal storage to check whether the record already exists. public void Add(StorageKey key, StorageItem value) { lock (dictionary) @@ -70,14 +90,19 @@ public void Add(StorageKey key, StorageItem value) } } + /// + /// Adds a new entry to the underlying storage. + /// + /// The key of the entry. + /// The data of the entry. protected abstract void AddInternal(StorageKey key, StorageItem value); /// - /// Update internals with all changes cached on Dictionary which are not None. + /// Commits all changes in the cache to the underlying storage. /// public virtual void Commit() { - LinkedList deletedItem = new LinkedList(); + LinkedList deletedItem = new(); foreach (Trackable trackable in GetChangeSet()) switch (trackable.State) { @@ -101,15 +126,19 @@ public virtual void Commit() changeSet.Clear(); } + /// + /// Creates a snapshot, which uses this instance as the underlying storage. + /// + /// The snapshot of this instance. public DataCache CreateSnapshot() { return new ClonedCache(this); } /// - /// Delete key from cached Dictionary or search in Internal. + /// Deletes an entry from the cache. /// - /// Key to be deleted. + /// The key of the entry. public void Delete(StorageKey key) { lock (dictionary) @@ -142,13 +171,17 @@ public void Delete(StorageKey key) } } + /// + /// Deletes an entry from the underlying storage. + /// + /// The key of the entry. protected abstract void DeleteInternal(StorageKey key); /// - /// Find the entries that start with the `key_prefix` + /// Finds the entries starting with the specified prefix. /// - /// Must maintain the deserialized format of StorageKey - /// Entries found with the desired prefix + /// The prefix of the key. + /// The entries found with the desired prefix. public IEnumerable<(StorageKey Key, StorageItem Value)> Find(byte[] key_prefix = null) { foreach (var (key, value) in Seek(key_prefix, SeekDirection.Forward)) @@ -159,12 +192,12 @@ public void Delete(StorageKey key) } /// - /// Find the entries that between [start, end) + /// Finds the entries that between [start, end). /// - /// Start key (inclusive) - /// End key (exclusive) + /// The start key (inclusive). + /// The end key (exclusive). /// The search direction. - /// Entries found with the desired range + /// The entries found with the desired range. public IEnumerable<(StorageKey Key, StorageItem Value)> FindRange(byte[] start, byte[] end, SeekDirection direction = SeekDirection.Forward) { ByteArrayComparer comparer = direction == SeekDirection.Forward @@ -177,6 +210,10 @@ public void Delete(StorageKey key) yield break; } + /// + /// Gets the change set in the cache. + /// + /// The change set. public IEnumerable GetChangeSet() { lock (dictionary) @@ -186,6 +223,11 @@ public IEnumerable GetChangeSet() } } + /// + /// Determines whether the cache contains the specified entry. + /// + /// The key of the entry. + /// if the cache contains an entry with the specified key; otherwise, . public bool Contains(StorageKey key) { lock (dictionary) @@ -199,18 +241,26 @@ public bool Contains(StorageKey key) } } + /// + /// Determines whether the underlying storage contains the specified entry. + /// + /// The key of the entry. + /// if the underlying storage contains an entry with the specified key; otherwise, . protected abstract bool ContainsInternal(StorageKey key); + /// + /// Reads a specified entry from the underlying storage. + /// + /// The key of the entry. + /// The data of the entry. Or if the entry doesn't exist. protected abstract StorageItem GetInternal(StorageKey key); /// - /// Try to Get a specific key from current cached dictionary. - /// Otherwise, tries to get from internal data with TryGetInternal. + /// Reads a specified entry from the cache, and mark it as . If the entry is not in the cache, it will be automatically loaded from the underlying storage. /// - /// Key to be searched. - /// Function that may replace current object stored. - /// If object already exists the factory passed as parameter will not be used. - /// + /// The key of the entry. + /// A delegate used to create the entry if it doesn't exist. If the entry already exists, the factory will not be used. + /// The cached data. Or if it doesn't exist and the is not provided. public StorageItem GetAndChange(StorageKey key, Func factory = null) { lock (dictionary) @@ -253,6 +303,12 @@ public StorageItem GetAndChange(StorageKey key, Func factory = null } } + /// + /// Reads a specified entry from the cache. If the entry is not in the cache, it will be automatically loaded from the underlying storage. If the entry doesn't exist, the factory will be used to create a new one. + /// + /// The key of the entry. + /// A delegate used to create the entry if it doesn't exist. If the entry already exists, the factory will not be used. + /// The cached data. public StorageItem GetOrAdd(StorageKey key, Func factory) { lock (dictionary) @@ -289,10 +345,10 @@ public StorageItem GetOrAdd(StorageKey key, Func factory) } /// - /// Seek to the entry with specific key + /// Seeks to the entry with the specified key. /// - /// The key to be sought - /// The direction of seek + /// The key to be sought. + /// The direction of seek. /// An enumerator containing all the entries after seeking. public IEnumerable<(StorageKey Key, StorageItem Value)> Seek(byte[] keyOrPrefix = null, SeekDirection direction = SeekDirection.Forward) { @@ -321,34 +377,43 @@ public StorageItem GetOrAdd(StorageKey key, Func factory) p.Key, p.Value )); - using (var e1 = cached.GetEnumerator()) - using (var e2 = uncached.GetEnumerator()) + using var e1 = cached.GetEnumerator(); + using var e2 = uncached.GetEnumerator(); + (byte[] KeyBytes, StorageKey Key, StorageItem Item) i1, i2; + bool c1 = e1.MoveNext(); + bool c2 = e2.MoveNext(); + i1 = c1 ? e1.Current : default; + i2 = c2 ? e2.Current : default; + while (c1 || c2) { - (byte[] KeyBytes, StorageKey Key, StorageItem Item) i1, i2; - bool c1 = e1.MoveNext(); - bool c2 = e2.MoveNext(); - i1 = c1 ? e1.Current : default; - i2 = c2 ? e2.Current : default; - while (c1 || c2) + if (!c2 || (c1 && comparer.Compare(i1.KeyBytes, i2.KeyBytes) < 0)) { - if (!c2 || (c1 && comparer.Compare(i1.KeyBytes, i2.KeyBytes) < 0)) - { - yield return (i1.Key, i1.Item); - c1 = e1.MoveNext(); - i1 = c1 ? e1.Current : default; - } - else - { - yield return (i2.Key, i2.Item); - c2 = e2.MoveNext(); - i2 = c2 ? e2.Current : default; - } + yield return (i1.Key, i1.Item); + c1 = e1.MoveNext(); + i1 = c1 ? e1.Current : default; + } + else + { + yield return (i2.Key, i2.Item); + c2 = e2.MoveNext(); + i2 = c2 ? e2.Current : default; } } } + /// + /// Seeks to the entry with the specified key in the underlying storage. + /// + /// The key to be sought. + /// The direction of seek. + /// An enumerator containing all the entries after seeking. protected abstract IEnumerable<(StorageKey Key, StorageItem Value)> SeekInternal(byte[] keyOrPrefix, SeekDirection direction); + /// + /// Reads a specified entry from the cache. If the entry is not in the cache, it will be automatically loaded from the underlying storage. + /// + /// The key of the entry. + /// The cached data. Or if it is neither in the cache nor in the underlying storage. public StorageItem TryGet(StorageKey key) { lock (dictionary) @@ -370,8 +435,18 @@ public StorageItem TryGet(StorageKey key) } } + /// + /// Reads a specified entry from the underlying storage. + /// + /// The key of the entry. + /// The data of the entry. Or if it doesn't exist. protected abstract StorageItem TryGetInternal(StorageKey key); + /// + /// Updates an entry in the underlying storage. + /// + /// The key of the entry. + /// The data of the entry. protected abstract void UpdateInternal(StorageKey key, StorageItem value); } } diff --git a/src/neo/Persistence/IReadOnlyStore.cs b/src/neo/Persistence/IReadOnlyStore.cs index 7200691084..d04c74e4cc 100644 --- a/src/neo/Persistence/IReadOnlyStore.cs +++ b/src/neo/Persistence/IReadOnlyStore.cs @@ -7,8 +7,26 @@ namespace Neo.Persistence /// public interface IReadOnlyStore { + /// + /// Seeks to the entry with the specified key. + /// + /// The key to be sought. + /// The direction of seek. + /// An enumerator containing all the entries after seeking. IEnumerable<(byte[] Key, byte[] Value)> Seek(byte[] key, SeekDirection direction); + + /// + /// Reads a specified entry from the database. + /// + /// The key of the entry. + /// The data of the entry. Or if it doesn't exist. byte[] TryGet(byte[] key); + + /// + /// Determines whether the database contains the specified entry. + /// + /// The key of the entry. + /// if the database contains an entry with the specified key; otherwise, . bool Contains(byte[] key); } } diff --git a/src/neo/Persistence/ISnapshot.cs b/src/neo/Persistence/ISnapshot.cs index 12bb0780af..ff2afb158c 100644 --- a/src/neo/Persistence/ISnapshot.cs +++ b/src/neo/Persistence/ISnapshot.cs @@ -7,8 +7,22 @@ namespace Neo.Persistence /// public interface ISnapshot : IDisposable, IReadOnlyStore { + /// + /// Commits all changes in the snapshot to the database. + /// void Commit(); + + /// + /// Deletes an entry from the snapshot. + /// + /// The key of the entry. void Delete(byte[] key); + + /// + /// Puts an entry to the snapshot. + /// + /// The key of the entry. + /// The data of the entry. void Put(byte[] key, byte[] value); } } diff --git a/src/neo/Persistence/IStore.cs b/src/neo/Persistence/IStore.cs index dbf2087760..1a9e1bb271 100644 --- a/src/neo/Persistence/IStore.cs +++ b/src/neo/Persistence/IStore.cs @@ -7,9 +7,30 @@ namespace Neo.Persistence /// public interface IStore : IDisposable, IReadOnlyStore { + /// + /// Deletes an entry from the database. + /// + /// The key of the entry. void Delete(byte[] key); + + /// + /// Creates a snapshot of the database. + /// + /// A snapshot of the database. ISnapshot GetSnapshot(); + + /// + /// Puts an entry to the database. + /// + /// The key of the entry. + /// The data of the entry. void Put(byte[] key, byte[] value); + + /// + /// Puts an entry to the database synchronously. + /// + /// The key of the entry. + /// The data of the entry. void PutSync(byte[] key, byte[] value) => Put(key, value); } } diff --git a/src/neo/Persistence/MemoryStore.cs b/src/neo/Persistence/MemoryStore.cs index 1cf51dde51..3023a8398d 100644 --- a/src/neo/Persistence/MemoryStore.cs +++ b/src/neo/Persistence/MemoryStore.cs @@ -5,9 +5,12 @@ namespace Neo.Persistence { + /// + /// An in-memory implementation that uses ConcurrentDictionary as the underlying storage. + /// public class MemoryStore : IStore { - private readonly ConcurrentDictionary innerData = new ConcurrentDictionary(ByteArrayEqualityComparer.Default); + private readonly ConcurrentDictionary innerData = new(ByteArrayEqualityComparer.Default); public void Delete(byte[] key) { diff --git a/src/neo/Persistence/SeekDirection.cs b/src/neo/Persistence/SeekDirection.cs index ca48561ab3..2868f5a132 100644 --- a/src/neo/Persistence/SeekDirection.cs +++ b/src/neo/Persistence/SeekDirection.cs @@ -1,8 +1,18 @@ namespace Neo.Persistence { + /// + /// Represents the direction when searching from the database. + /// public enum SeekDirection : sbyte { + /// + /// Indicates that the search should be performed in ascending order. + /// Forward = 1, + + /// + /// Indicates that the search should be performed in descending order. + /// Backward = -1 } } diff --git a/src/neo/Persistence/SnapshotCache.cs b/src/neo/Persistence/SnapshotCache.cs index a6e6bf9452..5d61207952 100644 --- a/src/neo/Persistence/SnapshotCache.cs +++ b/src/neo/Persistence/SnapshotCache.cs @@ -6,11 +6,18 @@ namespace Neo.Persistence { + /// + /// Represents a cache for the snapshot or database of the NEO blockchain. + /// public class SnapshotCache : DataCache, IDisposable { private readonly IReadOnlyStore store; private readonly ISnapshot snapshot; + /// + /// Initializes a new instance of the class. + /// + /// An to create a readonly cache; or an to create a snapshot cache. public SnapshotCache(IReadOnlyStore store) { this.store = store; diff --git a/src/neo/Persistence/TrackState.cs b/src/neo/Persistence/TrackState.cs index 30bc210539..22e7643c7f 100644 --- a/src/neo/Persistence/TrackState.cs +++ b/src/neo/Persistence/TrackState.cs @@ -1,10 +1,28 @@ namespace Neo.Persistence { + /// + /// Represents the state of a cached entry. + /// public enum TrackState : byte { + /// + /// Indicates that the entry has been loaded from the underlying storage, but has not been modified. + /// None, + + /// + /// Indicates that this is a newly added record. + /// Added, + + /// + /// Indicates that the entry has been loaded from the underlying storage, and has been modified. + /// Changed, + + /// + /// Indicates that the entry should be deleted from the underlying storage when committing. + /// Deleted } } diff --git a/src/neo/Plugins/IApplicationEngineProvider.cs b/src/neo/Plugins/IApplicationEngineProvider.cs index c217cf4926..ae8f9b2a19 100644 --- a/src/neo/Plugins/IApplicationEngineProvider.cs +++ b/src/neo/Plugins/IApplicationEngineProvider.cs @@ -4,8 +4,21 @@ namespace Neo.Plugins { + /// + /// A provider for creating instances. + /// public interface IApplicationEngineProvider { + /// + /// Creates a new instance of the class or its subclass. This method will be called by . + /// + /// The trigger of the execution. + /// The container of the script. + /// The snapshot used by the engine during execution. + /// The block being persisted. It should be if the is . + /// The used by the engine. + /// The maximum gas used in this execution. The execution will fail when the gas is exhausted. + /// The engine instance created. ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas); } } diff --git a/src/neo/Plugins/ILogPlugin.cs b/src/neo/Plugins/ILogPlugin.cs index 0934cc414a..98da0ca7ae 100644 --- a/src/neo/Plugins/ILogPlugin.cs +++ b/src/neo/Plugins/ILogPlugin.cs @@ -1,7 +1,16 @@ namespace Neo.Plugins { + /// + /// A plug-in interface for logs. + /// public interface ILogPlugin { + /// + /// Writes a new log to the plugin. + /// + /// The source of the log. Used to identify the producer of the log. + /// The level of the log. + /// The message of the log. void Log(string source, LogLevel level, object message); } } diff --git a/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs b/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs index 54b7c2d50d..531eca9dda 100644 --- a/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs +++ b/src/neo/Plugins/IMemoryPoolTxObserverPlugin.cs @@ -3,9 +3,24 @@ namespace Neo.Plugins { + /// + /// An interface that allows plugins to observe changes in the memory pool. + /// public interface IMemoryPoolTxObserverPlugin { + /// + /// Called when a transaction is added to the memory pool. + /// + /// The object that contains the memory pool. + /// The transaction added. void TransactionAdded(NeoSystem system, Transaction tx); + + /// + /// Called when transactions are removed from the memory pool. + /// + /// The object that contains the memory pool. + /// The reason the transactions were removed. + /// The removed transactions. void TransactionsRemoved(NeoSystem system, MemoryPoolTxRemovalReason reason, IEnumerable transactions); } } diff --git a/src/neo/Plugins/IP2PPlugin.cs b/src/neo/Plugins/IP2PPlugin.cs index 7d8d7db8cd..4378b8a4d5 100644 --- a/src/neo/Plugins/IP2PPlugin.cs +++ b/src/neo/Plugins/IP2PPlugin.cs @@ -2,8 +2,17 @@ namespace Neo.Plugins { + /// + /// An interface that allows plugins to observe the messages on the network. + /// public interface IP2PPlugin { + /// + /// Called when a message is received from a remote node. + /// + /// The object that contains the local node. + /// The received message. + /// if the should be dropped; otherwise, . bool OnP2PMessage(NeoSystem system, Message message) => true; } } diff --git a/src/neo/Plugins/IPersistencePlugin.cs b/src/neo/Plugins/IPersistencePlugin.cs index 17b5f1b319..0b3efac727 100644 --- a/src/neo/Plugins/IPersistencePlugin.cs +++ b/src/neo/Plugins/IPersistencePlugin.cs @@ -6,10 +6,33 @@ namespace Neo.Plugins { + /// + /// An interface that allows plugins to observe the persisted blocks. + /// public interface IPersistencePlugin { + /// + /// Called when a block is being persisted. + /// + /// The object that contains the blockchain. + /// The block being persisted. + /// The snapshot used for persistence. + /// The execution result of the contracts in the block. void OnPersist(NeoSystem system, Block block, DataCache snapshot, IReadOnlyList applicationExecutedList) { } + + /// + /// Called when a block has been persisted. + /// + /// The object that contains the blockchain. + /// The block being persisted. + /// The snapshot used for persistence. void OnCommit(NeoSystem system, Block block, DataCache snapshot) { } + + /// + /// Indicates whether to allow exceptions to be thrown from the plugin when committing. + /// + /// The exception to be thrown. + /// if the exception should be thrown; otherwise, . bool ShouldThrowExceptionFromCommit(Exception ex) => false; } } diff --git a/src/neo/Plugins/IStorageProvider.cs b/src/neo/Plugins/IStorageProvider.cs index b8b7197b6c..d5f1fa0fed 100644 --- a/src/neo/Plugins/IStorageProvider.cs +++ b/src/neo/Plugins/IStorageProvider.cs @@ -2,8 +2,16 @@ namespace Neo.Plugins { + /// + /// A provider used to create instances. + /// public interface IStorageProvider { + /// + /// Creates a new instance of the interface. + /// + /// The path of the database. + /// The created instance. IStore GetStore(string path); } } diff --git a/src/neo/Plugins/IWalletProvider.cs b/src/neo/Plugins/IWalletProvider.cs index 0fd1e86e8a..e9a29b671d 100644 --- a/src/neo/Plugins/IWalletProvider.cs +++ b/src/neo/Plugins/IWalletProvider.cs @@ -3,10 +3,20 @@ namespace Neo.Plugins { + /// + /// A provider for obtaining wallet instance. + /// public interface IWalletProvider { + /// + /// Triggered when a wallet is opened or closed. + /// event EventHandler WalletChanged; + /// + /// Get the currently opened instance. + /// + /// The opened wallet. Or if no wallet is opened. Wallet GetWallet(); } } diff --git a/src/neo/Plugins/MemoryPoolTxRemovalReason.cs b/src/neo/Plugins/MemoryPoolTxRemovalReason.cs index ddc7e10981..8e9b05f170 100644 --- a/src/neo/Plugins/MemoryPoolTxRemovalReason.cs +++ b/src/neo/Plugins/MemoryPoolTxRemovalReason.cs @@ -1,11 +1,15 @@ namespace Neo.Plugins { + /// + /// The reason a transaction was removed. + /// public enum MemoryPoolTxRemovalReason : byte { /// - /// The transaction was ejected since it was the lowest priority transaction and the MemoryPool capacity was exceeded. + /// The transaction was ejected since it was the lowest priority transaction and the memory pool capacity was exceeded. /// CapacityExceeded, + /// /// The transaction was ejected due to failing re-validation after a block was persisted. /// diff --git a/src/neo/Plugins/Plugin.cs b/src/neo/Plugins/Plugin.cs index 1f8e4475b3..ca01e396ac 100644 --- a/src/neo/Plugins/Plugin.cs +++ b/src/neo/Plugins/Plugin.cs @@ -9,22 +9,52 @@ namespace Neo.Plugins { + /// + /// Represents the base class of all plugins. Any plugin should inherit this class. The plugins are automatically loaded when the process starts. + /// public abstract class Plugin : IDisposable { - public static readonly List Plugins = new List(); - internal static readonly List Loggers = new List(); - internal static readonly Dictionary Storages = new Dictionary(); - internal static readonly List PersistencePlugins = new List(); - internal static readonly List P2PPlugins = new List(); - internal static readonly List TxObserverPlugins = new List(); - + /// + /// A list of all loaded plugins. + /// + public static readonly List Plugins = new(); + + internal static readonly List Loggers = new(); + internal static readonly Dictionary Storages = new(); + internal static readonly List PersistencePlugins = new(); + internal static readonly List P2PPlugins = new(); + internal static readonly List TxObserverPlugins = new(); + + /// + /// The directory containing the plugin dll files. Files can be contained in any subdirectory. + /// public static readonly string PluginsDirectory = Combine(GetDirectoryName(Assembly.GetEntryAssembly().Location), "Plugins"); + private static readonly FileSystemWatcher configWatcher; + /// + /// Indicates the location of the plugin configuration file. + /// public virtual string ConfigFile => Combine(PluginsDirectory, GetType().Assembly.GetName().Name, "config.json"); + + /// + /// Indicates the name of the plugin. + /// public virtual string Name => GetType().Name; + + /// + /// Indicates the description of the plugin. + /// public virtual string Description => ""; + + /// + /// Indicates the location of the plugin dll file. + /// public virtual string Path => Combine(PluginsDirectory, GetType().Assembly.ManifestModule.ScopeName); + + /// + /// Indicates the version of the plugin. + /// public virtual Version Version => GetType().Assembly.GetName().Version; static Plugin() @@ -43,6 +73,9 @@ static Plugin() } } + /// + /// Initializes a new instance of the class. + /// protected Plugin() { Plugins.Add(this); @@ -57,6 +90,9 @@ protected Plugin() Configure(); } + /// + /// Called when the plugin is loaded and need to load the configure file, or the configuration file has been modified and needs to be reconfigured. + /// protected virtual void Configure() { } @@ -89,7 +125,7 @@ private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEven if (args.Name.Contains(".resources")) return null; - AssemblyName an = new AssemblyName(args.Name); + AssemblyName an = new(args.Name); Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.FullName == args.Name); if (assembly is null) @@ -118,6 +154,10 @@ public virtual void Dispose() { } + /// + /// Loads the configuration file from the path of . + /// + /// The content of the configuration file read. protected IConfigurationSection GetConfiguration() { return new ConfigurationBuilder().AddJsonFile(ConfigFile, optional: true).Build().GetSection("PluginConfiguration"); @@ -145,7 +185,7 @@ private static void LoadPlugin(Assembly assembly) internal static void LoadPlugins() { if (!Directory.Exists(PluginsDirectory)) return; - List assemblies = new List(); + List assemblies = new(); foreach (string filename in Directory.EnumerateFiles(PluginsDirectory, "*.dll", SearchOption.TopDirectoryOnly)) { try @@ -160,20 +200,40 @@ internal static void LoadPlugins() } } + /// + /// Write a log for the plugin. + /// + /// The message of the log. + /// The level of the log. protected void Log(object message, LogLevel level = LogLevel.Info) { Utility.Log($"{nameof(Plugin)}:{Name}", level, message); } + /// + /// Called when a message to the plugins is received. The messnage is sent by calling . + /// + /// The received message. + /// if the has been handled; otherwise, . + /// If a message has been handled by a plugin, the other plugins won't receive it anymore. protected virtual bool OnMessage(object message) { return false; } + /// + /// Called when a is loaded. + /// + /// The loaded . internal protected virtual void OnSystemLoaded(NeoSystem system) { } + /// + /// Sends a message to all plugins. It can be handled by . + /// + /// The message to send. + /// if the is handled by a plugin; otherwise, . public static bool SendMessage(object message) { foreach (Plugin plugin in Plugins) diff --git a/src/neo/ProtocolSettings.cs b/src/neo/ProtocolSettings.cs index bba0eb4fd6..767170a42d 100644 --- a/src/neo/ProtocolSettings.cs +++ b/src/neo/ProtocolSettings.cs @@ -7,24 +7,80 @@ namespace Neo { + /// + /// Represents the protocol settings of the NEO system. + /// public record ProtocolSettings { + /// + /// The magic number of the NEO network. + /// public uint Magic { get; init; } + + /// + /// The address version of the NEO system. + /// public byte AddressVersion { get; init; } + + /// + /// The public keys of the standby committee members. + /// public IReadOnlyList StandbyCommittee { get; init; } + + /// + /// The number of members of the committee in NEO system. + /// public int CommitteeMembersCount => StandbyCommittee.Count; + + /// + /// The number of the validators in NEO system. + /// public int ValidatorsCount { get; init; } + + /// + /// The default seed nodes list. + /// public string[] SeedList { get; init; } + + /// + /// Indicates the time in milliseconds between two blocks. + /// public uint MillisecondsPerBlock { get; init; } + + /// + /// Indicates the time between two blocks. + /// public TimeSpan TimePerBlock => TimeSpan.FromMilliseconds(MillisecondsPerBlock); + + /// + /// Indicates the maximum number of transactions that can be contained in a block. + /// public uint MaxTransactionsPerBlock { get; init; } + + /// + /// Indicates the maximum number of transactions that can be contained in the memory pool. + /// public int MemoryPoolMaxTransactions { get; init; } + + /// + /// Indicates the maximum number of blocks that can be traced in the smart contract. + /// public uint MaxTraceableBlocks { get; init; } + + /// + /// Contains the update history of all native contracts. + /// public IReadOnlyDictionary NativeUpdateHistory { get; init; } private IReadOnlyList _standbyValidators; + /// + /// The public keys of the standby validators. + /// public IReadOnlyList StandbyValidators => _standbyValidators ??= StandbyCommittee.Take(ValidatorsCount).ToArray(); + /// + /// The default protocol settings for NEO MainNet. + /// public static ProtocolSettings Default { get; } = new ProtocolSettings { Magic = 0x4F454Eu, @@ -83,6 +139,12 @@ public record ProtocolSettings } }; + /// + /// Loads the at the specified path. + /// + /// The path of the settings file. + /// Indicates whether the file is optional. + /// The loaded . public static ProtocolSettings Load(string path, bool optional = true) { IConfigurationRoot config = new ConfigurationBuilder().AddJsonFile(path, optional).Build(); @@ -90,6 +152,11 @@ public static ProtocolSettings Load(string path, bool optional = true) return Load(section); } + /// + /// Loads the with the specified . + /// + /// The to be loaded. + /// The loaded . public static ProtocolSettings Load(IConfigurationSection section) { return new ProtocolSettings diff --git a/src/neo/SmartContract/ApplicationEngine.Contract.cs b/src/neo/SmartContract/ApplicationEngine.Contract.cs index 79a6aff183..4a9fdc0788 100644 --- a/src/neo/SmartContract/ApplicationEngine.Contract.cs +++ b/src/neo/SmartContract/ApplicationEngine.Contract.cs @@ -9,18 +9,56 @@ namespace Neo.SmartContract { partial class ApplicationEngine { + /// + /// The of System.Contract.Call. + /// Use it to call another contract dynamically. + /// public static readonly InteropDescriptor System_Contract_Call = Register("System.Contract.Call", nameof(CallContract), 1 << 15, CallFlags.ReadStates | CallFlags.AllowCall); + + /// + /// The of System.Contract.CallNative. + /// + /// Note: It is for internal use only. Do not use it directly in smart contracts. public static readonly InteropDescriptor System_Contract_CallNative = Register("System.Contract.CallNative", nameof(CallNativeContract), 0, CallFlags.None); + + /// + /// The of System.Contract.GetCallFlags. + /// Gets the of the current context. + /// public static readonly InteropDescriptor System_Contract_GetCallFlags = Register("System.Contract.GetCallFlags", nameof(GetCallFlags), 1 << 10, CallFlags.None); + /// - /// Calculate corresponding account scripthash for given public key - /// Warning: check first that input public key is valid, before creating the script. + /// The of System.Contract.CreateStandardAccount. + /// Calculates corresponding account scripthash for the given public key. /// public static readonly InteropDescriptor System_Contract_CreateStandardAccount = Register("System.Contract.CreateStandardAccount", nameof(CreateStandardAccount), 1 << 8, CallFlags.None); + + /// + /// The of System.Contract.CreateMultisigAccount. + /// Calculates corresponding multisig account scripthash for the given public keys. + /// public static readonly InteropDescriptor System_Contract_CreateMultisigAccount = Register("System.Contract.CreateMultisigAccount", nameof(CreateMultisigAccount), 1 << 8, CallFlags.None); + + /// + /// The of System.Contract.NativeOnPersist. + /// + /// Note: It is for internal use only. Do not use it directly in smart contracts. public static readonly InteropDescriptor System_Contract_NativeOnPersist = Register("System.Contract.NativeOnPersist", nameof(NativeOnPersist), 0, CallFlags.States); + + /// + /// The of System.Contract.NativePostPersist. + /// + /// Note: It is for internal use only. Do not use it directly in smart contracts. public static readonly InteropDescriptor System_Contract_NativePostPersist = Register("System.Contract.NativePostPersist", nameof(NativePostPersist), 0, CallFlags.States); + /// + /// The implementation of System.Contract.Call. + /// Use it to call another contract dynamically. + /// + /// The hash of the contract to be called. + /// The method of the contract to be called. + /// The to be used to call the contract. + /// The arguments to be used. protected internal void CallContract(UInt160 contractHash, string method, CallFlags callFlags, Array args) { if (method.StartsWith('_')) throw new ArgumentException($"Invalid Method Name: {method}"); @@ -37,6 +75,11 @@ protected internal void CallContract(UInt160 contractHash, string method, CallFl CallContractInternal(contract, md, callFlags, hasReturnValue, args); } + /// + /// The implementation of System.Contract.CallNative. + /// Calls to a native contract. + /// + /// The version of the native contract to be called. protected internal void CallNativeContract(byte version) { NativeContract contract = NativeContract.GetContract(CurrentScriptHash); @@ -50,22 +93,44 @@ protected internal void CallNativeContract(byte version) contract.Invoke(this, version); } + /// + /// The implementation of System.Contract.GetCallFlags. + /// Gets the of the current context. + /// + /// The of the current context. protected internal CallFlags GetCallFlags() { var state = CurrentContext.GetState(); return state.CallFlags; } - protected internal UInt160 CreateStandardAccount(ECPoint pubKey) + /// + /// The implementation of System.Contract.CreateStandardAccount. + /// Calculates corresponding account scripthash for the given public key. + /// + /// The public key of the account. + /// The hash of the account. + internal protected static UInt160 CreateStandardAccount(ECPoint pubKey) { return Contract.CreateSignatureRedeemScript(pubKey).ToScriptHash(); } - protected internal UInt160 CreateMultisigAccount(int m, ECPoint[] pubKeys) + /// + /// The implementation of System.Contract.CreateMultisigAccount. + /// Calculates corresponding multisig account scripthash for the given public keys. + /// + /// The minimum number of correct signatures that need to be provided in order for the verification to pass. + /// The public keys of the account. + /// The hash of the account. + internal protected static UInt160 CreateMultisigAccount(int m, ECPoint[] pubKeys) { return Contract.CreateMultiSigRedeemScript(m, pubKeys).ToScriptHash(); } + /// + /// The implementation of System.Contract.NativeOnPersist. + /// Calls to the of all native contracts. + /// protected internal async void NativeOnPersist() { try @@ -86,6 +151,10 @@ protected internal async void NativeOnPersist() } } + /// + /// The implementation of System.Contract.NativePostPersist. + /// Calls to the of all native contracts. + /// protected internal async void NativePostPersist() { try diff --git a/src/neo/SmartContract/ApplicationEngine.Crypto.cs b/src/neo/SmartContract/ApplicationEngine.Crypto.cs index 5fdc592be2..688f985d60 100644 --- a/src/neo/SmartContract/ApplicationEngine.Crypto.cs +++ b/src/neo/SmartContract/ApplicationEngine.Crypto.cs @@ -7,11 +7,30 @@ namespace Neo.SmartContract { partial class ApplicationEngine { + /// + /// The price of Neo.Crypto.CheckSig. + /// public const long CheckSigPrice = 1 << 15; + /// + /// The of Neo.Crypto.CheckSig. + /// Checks the signature for the current script container. + /// public static readonly InteropDescriptor Neo_Crypto_CheckSig = Register("Neo.Crypto.CheckSig", nameof(CheckSig), CheckSigPrice, CallFlags.None); + + /// + /// The of Neo.Crypto.CheckMultisig. + /// Checks the signatures for the current script container. + /// public static readonly InteropDescriptor Neo_Crypto_CheckMultisig = Register("Neo.Crypto.CheckMultisig", nameof(CheckMultisig), 0, CallFlags.None); + /// + /// The implementation of Neo.Crypto.CheckSig. + /// Checks the signature for the current script container. + /// + /// The public key of the account. + /// The signature of the current script container. + /// if the signature is valid; otherwise, . protected internal bool CheckSig(byte[] pubkey, byte[] signature) { try @@ -24,6 +43,13 @@ protected internal bool CheckSig(byte[] pubkey, byte[] signature) } } + /// + /// The implementation of Neo.Crypto.CheckMultisig. + /// Checks the signatures for the current script container. + /// + /// The public keys of the account. + /// The signatures of the current script container. + /// if the signatures are valid; otherwise, . protected internal bool CheckMultisig(byte[][] pubkeys, byte[][] signatures) { byte[] message = ScriptContainer.GetSignData(ProtocolSettings.Magic); diff --git a/src/neo/SmartContract/ApplicationEngine.Iterator.cs b/src/neo/SmartContract/ApplicationEngine.Iterator.cs index f8f6be2af7..60efbde6e2 100644 --- a/src/neo/SmartContract/ApplicationEngine.Iterator.cs +++ b/src/neo/SmartContract/ApplicationEngine.Iterator.cs @@ -7,10 +7,30 @@ namespace Neo.SmartContract { partial class ApplicationEngine { + /// + /// The of System.Iterator.Create. + /// Creates an with the specified . + /// public static readonly InteropDescriptor System_Iterator_Create = Register("System.Iterator.Create", nameof(CreateIterator), 1 << 4, CallFlags.None); + + /// + /// The of System.Iterator.Next. + /// Advances the iterator to the next element of the collection. + /// public static readonly InteropDescriptor System_Iterator_Next = Register("System.Iterator.Next", nameof(IteratorNext), 1 << 15, CallFlags.None); + + /// + /// The of System.Iterator.Value. + /// Gets the element in the collection at the current position of the iterator. + /// public static readonly InteropDescriptor System_Iterator_Value = Register("System.Iterator.Value", nameof(IteratorValue), 1 << 4, CallFlags.None); + /// + /// The implementation of System.Iterator.Create. + /// Creates an with the specified . + /// + /// The wrapped by the iterator. + /// The created iterator. protected internal IIterator CreateIterator(StackItem item) { return item switch @@ -19,16 +39,28 @@ protected internal IIterator CreateIterator(StackItem item) Map map => new MapWrapper(map, ReferenceCounter), VM.Types.Buffer buffer => new ByteArrayWrapper(buffer), PrimitiveType primitive => new ByteArrayWrapper(primitive), - _ => throw new ArgumentException() + _ => throw new ArgumentException(null, nameof(item)) }; } - protected internal bool IteratorNext(IIterator iterator) + /// + /// The implementation of System.Iterator.Next. + /// Advances the iterator to the next element of the collection. + /// + /// The iterator to be advanced. + /// if the iterator was successfully advanced to the next element; if the iterator has passed the end of the collection. + internal protected static bool IteratorNext(IIterator iterator) { return iterator.Next(); } - protected internal StackItem IteratorValue(IIterator iterator) + /// + /// The implementation of System.Iterator.Value. + /// Gets the element in the collection at the current position of the iterator. + /// + /// The iterator to be used. + /// The element in the collection at the current position of the iterator. + internal protected static StackItem IteratorValue(IIterator iterator) { return iterator.Value(); } diff --git a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs index 1d1c1c07c0..f86efd2534 100644 --- a/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs +++ b/src/neo/SmartContract/ApplicationEngine.OpCodePrices.cs @@ -5,6 +5,9 @@ namespace Neo.SmartContract { partial class ApplicationEngine { + /// + /// The prices of all the opcodes. + /// public static readonly IReadOnlyDictionary OpCodePrices = new Dictionary { [OpCode.PUSHINT8] = 1 << 0, diff --git a/src/neo/SmartContract/ApplicationEngine.Runtime.cs b/src/neo/SmartContract/ApplicationEngine.Runtime.cs index b8e3ac0faa..cfb980c4ed 100644 --- a/src/neo/SmartContract/ApplicationEngine.Runtime.cs +++ b/src/neo/SmartContract/ApplicationEngine.Runtime.cs @@ -13,50 +13,147 @@ namespace Neo.SmartContract { partial class ApplicationEngine { + /// + /// The maximum length of event name. + /// public const int MaxEventName = 32; + + /// + /// The maximum size of notification objects. + /// public const int MaxNotificationSize = 1024; + /// + /// The of System.Runtime.Platform. + /// Gets the name of the current platform. + /// public static readonly InteropDescriptor System_Runtime_Platform = Register("System.Runtime.Platform", nameof(GetPlatform), 1 << 3, CallFlags.None); + + /// + /// The of System.Runtime.GetTrigger. + /// Gets the trigger of the execution. + /// public static readonly InteropDescriptor System_Runtime_GetTrigger = Register("System.Runtime.GetTrigger", nameof(Trigger), 1 << 3, CallFlags.None); + + /// + /// The of System.Runtime.GetTime. + /// Gets the timestamp of the current block. + /// public static readonly InteropDescriptor System_Runtime_GetTime = Register("System.Runtime.GetTime", nameof(GetTime), 1 << 3, CallFlags.None); + + /// + /// The of System.Runtime.GetScriptContainer. + /// Gets the current script container. + /// public static readonly InteropDescriptor System_Runtime_GetScriptContainer = Register("System.Runtime.GetScriptContainer", nameof(GetScriptContainer), 1 << 3, CallFlags.None); + + /// + /// The of System.Runtime.GetExecutingScriptHash. + /// Gets the script hash of the current context. + /// public static readonly InteropDescriptor System_Runtime_GetExecutingScriptHash = Register("System.Runtime.GetExecutingScriptHash", nameof(CurrentScriptHash), 1 << 4, CallFlags.None); + + /// + /// The of System.Runtime.GetCallingScriptHash. + /// Gets the script hash of the calling contract. + /// public static readonly InteropDescriptor System_Runtime_GetCallingScriptHash = Register("System.Runtime.GetCallingScriptHash", nameof(CallingScriptHash), 1 << 4, CallFlags.None); + + /// + /// The of System.Runtime.GetEntryScriptHash. + /// Gets the script hash of the entry context. + /// public static readonly InteropDescriptor System_Runtime_GetEntryScriptHash = Register("System.Runtime.GetEntryScriptHash", nameof(EntryScriptHash), 1 << 4, CallFlags.None); + + /// + /// The of System.Runtime.CheckWitness. + /// Determines whether the specified account has witnessed the current transaction. + /// public static readonly InteropDescriptor System_Runtime_CheckWitness = Register("System.Runtime.CheckWitness", nameof(CheckWitness), 1 << 10, CallFlags.None); + + /// + /// The of System.Runtime.GetInvocationCounter. + /// Gets the number of times the current contract has been called during the execution. + /// public static readonly InteropDescriptor System_Runtime_GetInvocationCounter = Register("System.Runtime.GetInvocationCounter", nameof(GetInvocationCounter), 1 << 4, CallFlags.None); + + /// + /// The of System.Runtime.Log. + /// Writes a log. + /// public static readonly InteropDescriptor System_Runtime_Log = Register("System.Runtime.Log", nameof(RuntimeLog), 1 << 15, CallFlags.AllowNotify); + + /// + /// The of System.Runtime.Notify. + /// Sends a notification. + /// public static readonly InteropDescriptor System_Runtime_Notify = Register("System.Runtime.Notify", nameof(RuntimeNotify), 1 << 15, CallFlags.AllowNotify); + + /// + /// The of System.Runtime.GetNotifications. + /// Gets the notifications sent by the specified contract during the execution. + /// public static readonly InteropDescriptor System_Runtime_GetNotifications = Register("System.Runtime.GetNotifications", nameof(GetNotifications), 1 << 8, CallFlags.None); + + /// + /// The of System.Runtime.GasLeft. + /// Gets the remaining GAS that can be spent in order to complete the execution. + /// public static readonly InteropDescriptor System_Runtime_GasLeft = Register("System.Runtime.GasLeft", nameof(GasLeft), 1 << 4, CallFlags.None); - protected internal string GetPlatform() + /// + /// The implementation of System.Runtime.Platform. + /// Gets the name of the current platform. + /// + /// It always returns "NEO". + internal protected static string GetPlatform() { return "NEO"; } + /// + /// The implementation of System.Runtime.GetTime. + /// Gets the timestamp of the current block. + /// + /// The timestamp of the current block. protected internal ulong GetTime() { return PersistingBlock.Timestamp; } + /// + /// The implementation of System.Runtime.GetScriptContainer. + /// Gets the current script container. + /// + /// The current script container. protected internal StackItem GetScriptContainer() { if (ScriptContainer is not IInteroperable interop) throw new InvalidOperationException(); return interop.ToStackItem(ReferenceCounter); } + /// + /// The implementation of System.Runtime.CheckWitness. + /// Determines whether the specified account has witnessed the current transaction. + /// + /// The hash or public key of the account. + /// if the account has witnessed the current transaction; otherwise, . protected internal bool CheckWitness(byte[] hashOrPubkey) { UInt160 hash = hashOrPubkey.Length switch { 20 => new UInt160(hashOrPubkey), 33 => Contract.CreateSignatureRedeemScript(ECPoint.DecodePoint(hashOrPubkey, ECCurve.Secp256r1)).ToScriptHash(), - _ => throw new ArgumentException() + _ => throw new ArgumentException(null, nameof(hashOrPubkey)) }; return CheckWitnessInternal(hash); } + /// + /// Determines whether the specified account has witnessed the current transaction. + /// + /// The hash of the account. + /// if the account has witnessed the current transaction; otherwise, . protected internal bool CheckWitnessInternal(UInt160 hash) { if (hash.Equals(CallingScriptHash)) return true; @@ -111,6 +208,11 @@ protected internal bool CheckWitnessInternal(UInt160 hash) return hashes_for_verifying.Contains(hash); } + /// + /// The implementation of System.Runtime.GetInvocationCounter. + /// Gets the number of times the current contract has been called during the execution. + /// + /// The number of times the current contract has been called during the execution. protected internal int GetInvocationCounter() { if (!invocationCounter.TryGetValue(CurrentScriptHash, out var counter)) @@ -120,32 +222,53 @@ protected internal int GetInvocationCounter() return counter; } + /// + /// The implementation of System.Runtime.Log. + /// Writes a log. + /// + /// The message of the log. protected internal void RuntimeLog(byte[] state) { - if (state.Length > MaxNotificationSize) throw new ArgumentException(); + if (state.Length > MaxNotificationSize) throw new ArgumentException(null, nameof(state)); string message = Utility.StrictUTF8.GetString(state); Log?.Invoke(this, new LogEventArgs(ScriptContainer, CurrentScriptHash, message)); } + /// + /// The implementation of System.Runtime.Notify. + /// Sends a notification. + /// + /// The name of the event. + /// The arguments of the event. protected internal void RuntimeNotify(byte[] eventName, Array state) { - if (eventName.Length > MaxEventName) throw new ArgumentException(); - using (MemoryStream ms = new MemoryStream(MaxNotificationSize)) - using (BinaryWriter writer = new BinaryWriter(ms)) - { - BinarySerializer.Serialize(writer, state, MaxNotificationSize); - } + if (eventName.Length > MaxEventName) throw new ArgumentException(null, nameof(eventName)); + using MemoryStream ms = new(MaxNotificationSize); + using BinaryWriter writer = new(ms, Utility.StrictUTF8, true); + BinarySerializer.Serialize(writer, state, MaxNotificationSize); SendNotification(CurrentScriptHash, Utility.StrictUTF8.GetString(eventName), state); } + /// + /// Sends a notification for the specified contract. + /// + /// The hash of the specified contract. + /// The name of the event. + /// The arguments of the event. protected internal void SendNotification(UInt160 hash, string eventName, Array state) { - NotifyEventArgs notification = new NotifyEventArgs(ScriptContainer, hash, eventName, (Array)state.DeepCopy()); + NotifyEventArgs notification = new(ScriptContainer, hash, eventName, (Array)state.DeepCopy()); Notify?.Invoke(this, notification); notifications ??= new List(); notifications.Add(notification); } + /// + /// The implementation of System.Runtime.GetNotifications. + /// Gets the notifications sent by the specified contract during the execution. + /// + /// The hash of the specified contract. It can be set to to get all notifications. + /// The notifications sent during the execution. protected internal NotifyEventArgs[] GetNotifications(UInt160 hash) { IEnumerable notifications = Notifications; diff --git a/src/neo/SmartContract/ApplicationEngine.Storage.cs b/src/neo/SmartContract/ApplicationEngine.Storage.cs index f92cb57ad7..0b58859163 100644 --- a/src/neo/SmartContract/ApplicationEngine.Storage.cs +++ b/src/neo/SmartContract/ApplicationEngine.Storage.cs @@ -7,17 +7,63 @@ namespace Neo.SmartContract { partial class ApplicationEngine { + /// + /// The maximum size of storage keys. + /// public const int MaxStorageKeySize = 64; + + /// + /// The maximum size of storage values. + /// public const int MaxStorageValueSize = ushort.MaxValue; + /// + /// The of System.Storage.GetContext. + /// Gets the storage context for the current contract. + /// public static readonly InteropDescriptor System_Storage_GetContext = Register("System.Storage.GetContext", nameof(GetStorageContext), 1 << 4, CallFlags.ReadStates); + + /// + /// The of System.Storage.GetReadOnlyContext. + /// Gets the readonly storage context for the current contract. + /// public static readonly InteropDescriptor System_Storage_GetReadOnlyContext = Register("System.Storage.GetReadOnlyContext", nameof(GetReadOnlyContext), 1 << 4, CallFlags.ReadStates); + + /// + /// The of System.Storage.AsReadOnly. + /// Converts the specified storage context to a new readonly storage context. + /// public static readonly InteropDescriptor System_Storage_AsReadOnly = Register("System.Storage.AsReadOnly", nameof(AsReadOnly), 1 << 4, CallFlags.ReadStates); + + /// + /// The of System.Storage.Get. + /// Gets the entry with the specified key from the storage. + /// public static readonly InteropDescriptor System_Storage_Get = Register("System.Storage.Get", nameof(Get), 1 << 15, CallFlags.ReadStates); + + /// + /// The of System.Storage.Find. + /// Finds the entries from the storage. + /// public static readonly InteropDescriptor System_Storage_Find = Register("System.Storage.Find", nameof(Find), 1 << 15, CallFlags.ReadStates); + + /// + /// The of System.Storage.Put. + /// Puts a new entry into the storage. + /// public static readonly InteropDescriptor System_Storage_Put = Register("System.Storage.Put", nameof(Put), 1 << 15, CallFlags.WriteStates); + + /// + /// The of System.Storage.Delete. + /// Deletes an entry from the storage. + /// public static readonly InteropDescriptor System_Storage_Delete = Register("System.Storage.Delete", nameof(Delete), 1 << 15, CallFlags.WriteStates); + /// + /// The implementation of System.Storage.GetContext. + /// Gets the storage context for the current contract. + /// + /// The storage context for the current contract. protected internal StorageContext GetStorageContext() { ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, CurrentScriptHash); @@ -28,6 +74,11 @@ protected internal StorageContext GetStorageContext() }; } + /// + /// The implementation of System.Storage.GetReadOnlyContext. + /// Gets the readonly storage context for the current contract. + /// + /// The storage context for the current contract. protected internal StorageContext GetReadOnlyContext() { ContractState contract = NativeContract.ContractManagement.GetContract(Snapshot, CurrentScriptHash); @@ -38,7 +89,13 @@ protected internal StorageContext GetReadOnlyContext() }; } - protected internal StorageContext AsReadOnly(StorageContext context) + /// + /// The implementation of System.Storage.AsReadOnly. + /// Converts the specified storage context to a new readonly storage context. + /// + /// The storage context to convert. + /// The readonly storage context. + internal protected static StorageContext AsReadOnly(StorageContext context) { if (!context.IsReadOnly) context = new StorageContext @@ -49,6 +106,13 @@ protected internal StorageContext AsReadOnly(StorageContext context) return context; } + /// + /// The implementation of System.Storage.Get. + /// Gets the entry with the specified key from the storage. + /// + /// The context of the storage. + /// The key of the entry. + /// The value of the entry. Or if the entry doesn't exist. protected internal byte[] Get(StorageContext context, byte[] key) { return Snapshot.TryGet(new StorageKey @@ -58,29 +122,44 @@ protected internal byte[] Get(StorageContext context, byte[] key) })?.Value; } + /// + /// The implementation of System.Storage.Find. + /// Finds the entries from the storage. + /// + /// The context of the storage. + /// The prefix of keys to find. + /// The options of the search. + /// An iterator for the results. protected internal IIterator Find(StorageContext context, byte[] prefix, FindOptions options) { if ((options & ~FindOptions.All) != 0) throw new ArgumentOutOfRangeException(nameof(options)); if (options.HasFlag(FindOptions.KeysOnly) && (options.HasFlag(FindOptions.ValuesOnly) || options.HasFlag(FindOptions.DeserializeValues) || options.HasFlag(FindOptions.PickField0) || options.HasFlag(FindOptions.PickField1))) - throw new ArgumentException(); + throw new ArgumentException(null, nameof(options)); if (options.HasFlag(FindOptions.ValuesOnly) && (options.HasFlag(FindOptions.KeysOnly) || options.HasFlag(FindOptions.RemovePrefix))) - throw new ArgumentException(); + throw new ArgumentException(null, nameof(options)); if (options.HasFlag(FindOptions.PickField0) && options.HasFlag(FindOptions.PickField1)) - throw new ArgumentException(); + throw new ArgumentException(null, nameof(options)); if ((options.HasFlag(FindOptions.PickField0) || options.HasFlag(FindOptions.PickField1)) && !options.HasFlag(FindOptions.DeserializeValues)) - throw new ArgumentException(); + throw new ArgumentException(null, nameof(options)); byte[] prefix_key = StorageKey.CreateSearchPrefix(context.Id, prefix); return new StorageIterator(Snapshot.Find(prefix_key).GetEnumerator(), options, ReferenceCounter); } + /// + /// The implementation of System.Storage.Put. + /// Puts a new entry into the storage. + /// + /// The context of the storage. + /// The key of the entry. + /// The value of the entry. protected internal void Put(StorageContext context, byte[] key, byte[] value) { if (key.Length > MaxStorageKeySize || value.Length > MaxStorageValueSize || context.IsReadOnly) throw new ArgumentException(); int newDataSize; - StorageKey skey = new StorageKey + StorageKey skey = new() { Id = context.Id, Key = key @@ -107,9 +186,15 @@ protected internal void Put(StorageContext context, byte[] key, byte[] value) item.Value = value; } + /// + /// The implementation of System.Storage.Delete. + /// Deletes an entry from the storage. + /// + /// The context of the storage. + /// The key of the entry. protected internal void Delete(StorageContext context, byte[] key) { - if (context.IsReadOnly) throw new ArgumentException(); + if (context.IsReadOnly) throw new ArgumentException(null, nameof(context)); Snapshot.Delete(new StorageKey { Id = context.Id, diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 2ab06af570..87ce6688e7 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -18,14 +18,24 @@ namespace Neo.SmartContract { + /// + /// A virtual machine used to execute smart contracts in the NEO system. + /// public partial class ApplicationEngine : ExecutionEngine { /// - /// This constant can be used for testing scripts. + /// The maximum cost that can be spent when a contract is executed in test mode. /// public const long TestModeGas = 20_00000000; + /// + /// Triggered when a contract calls System.Runtime.Notify. + /// public static event EventHandler Notify; + + /// + /// Triggered when a contract calls System.Runtime.Log. + /// public static event EventHandler Log; private static IApplicationEngineProvider applicationEngineProvider; @@ -33,26 +43,87 @@ public partial class ApplicationEngine : ExecutionEngine private readonly long gas_amount; private List notifications; private List disposables; - private readonly Dictionary invocationCounter = new Dictionary(); - private readonly Dictionary contractTasks = new Dictionary(); + private readonly Dictionary invocationCounter = new(); + private readonly Dictionary contractTasks = new(); private readonly uint exec_fee_factor; internal readonly uint StoragePrice; + /// + /// Gets the descriptors of all interoperable services available in NEO. + /// public static IReadOnlyDictionary Services => services; + private List Disposables => disposables ??= new List(); + + /// + /// The trigger of the execution. + /// public TriggerType Trigger { get; } + + /// + /// The container that containing the executed script. This field could be if the contract is invoked by system. + /// public IVerifiable ScriptContainer { get; } + + /// + /// The snapshot used to read or write data. + /// public DataCache Snapshot { get; } + + /// + /// The block being persisted. This field could be if the is . + /// public Block PersistingBlock { get; } + + /// + /// The used by the engine. + /// public ProtocolSettings ProtocolSettings { get; } + + /// + /// GAS spent to execute. + /// public long GasConsumed { get; private set; } = 0; + + /// + /// The remaining GAS that can be spent in order to complete the execution. + /// public long GasLeft => gas_amount - GasConsumed; + + /// + /// The exception that caused the execution to terminate abnormally. This field could be if no exception is thrown. + /// public Exception FaultException { get; private set; } + + /// + /// The script hash of the current context. This field could be if no context is loaded to the engine. + /// public UInt160 CurrentScriptHash => CurrentContext?.GetScriptHash(); + + /// + /// The script hash of the calling contract. This field could be if the current context is the entry context. + /// public UInt160 CallingScriptHash => CurrentContext?.GetState().CallingScriptHash; + + /// + /// The script hash of the entry context. This field could be if no context is loaded to the engine. + /// public UInt160 EntryScriptHash => EntryContext?.GetScriptHash(); + + /// + /// The notifications sent during the execution. + /// public IReadOnlyList Notifications => notifications ?? (IReadOnlyList)Array.Empty(); + /// + /// Initializes a new instance of the class. + /// + /// The trigger of the execution. + /// The container of the script. + /// The snapshot used by the engine during execution. + /// The block being persisted. It should be if the is . + /// The used by the engine. + /// The maximum gas used in this execution. The execution will fail when the gas is exhausted. protected ApplicationEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas) { this.Trigger = trigger; @@ -65,6 +136,10 @@ protected ApplicationEngine(TriggerType trigger, IVerifiable container, DataCach this.StoragePrice = snapshot is null || persistingBlock?.Index == 0 ? PolicyContract.DefaultStoragePrice : NativeContract.Policy.GetStoragePrice(Snapshot); } + /// + /// Adds GAS to and checks if it has exceeded the maximum limit. + /// + /// The amount of GAS to be added. protected internal void AddGas(long gas) { GasConsumed = checked(GasConsumed + gas); @@ -135,7 +210,7 @@ internal ContractTask CallFromNativeContract(UInt160 callingScriptHash, UInt160 ExecutionContext context_new = CallContractInternal(hash, method, CallFlags.All, false, args); ExecutionContextState state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; - ContractTask task = new ContractTask(); + ContractTask task = new(); contractTasks.Add(context_new, task.GetAwaiter()); return task; } @@ -145,7 +220,7 @@ internal ContractTask CallFromNativeContract(UInt160 callingScriptHash, UI ExecutionContext context_new = CallContractInternal(hash, method, CallFlags.All, true, args); ExecutionContextState state = context_new.GetState(); state.CallingScriptHash = callingScriptHash; - ContractTask task = new ContractTask(); + ContractTask task = new(); contractTasks.Add(context_new, task.GetAwaiter()); return task; } @@ -159,6 +234,16 @@ protected override void ContextUnloaded(ExecutionContext context) awaiter.SetResult(this); } + /// + /// Use the loaded to create a new instance of the class. If no is loaded, the constructor of will be called. + /// + /// The trigger of the execution. + /// The container of the script. + /// The snapshot used by the engine during execution. + /// The block being persisted. It should be if the is . + /// The used by the engine. + /// The maximum gas used in this execution. The execution will fail when the gas is exhausted. + /// The engine instance created. public static ApplicationEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, ProtocolSettings settings = null, long gas = TestModeGas) { return applicationEngineProvider?.Create(trigger, container, snapshot, persistingBlock, settings, gas) @@ -176,6 +261,13 @@ protected override void LoadContext(ExecutionContext context) base.LoadContext(context); } + /// + /// Loads a deployed contract to the invocation stack. If the _initialize method is found on the contract, loads it as well. + /// + /// The contract to be loaded. + /// The method of the contract to be called. + /// The used to call the method. + /// The loaded context. public ExecutionContext LoadContract(ContractState contract, ContractMethodDescriptor method, CallFlags callFlags) { ExecutionContext context = LoadScript(contract.Script, @@ -198,6 +290,14 @@ public ExecutionContext LoadContract(ContractState contract, ContractMethodDescr return context; } + /// + /// Loads a script to the invocation stack. + /// + /// The script to be loaded. + /// The number of return values of the script. + /// The initial position of the instruction pointer. + /// The action used to configure the state of the loaded context. + /// The loaded context. public ExecutionContext LoadScript(Script script, int rvcount = -1, int initialPosition = 0, Action configureState = null) { // Create and configure context @@ -223,6 +323,11 @@ protected override ExecutionContext LoadToken(ushort tokenId) return CallContractInternal(token.Hash, token.Method, token.CallFlags, token.HasReturnValue, args); } + /// + /// Converts an to a that used in the virtual machine. + /// + /// The to convert. + /// The converted . protected internal StackItem Convert(object value) { if (value is IDisposable disposable) Disposables.Add(disposable); @@ -252,6 +357,12 @@ protected internal StackItem Convert(object value) }; } + /// + /// Converts a to an that to be used as an argument of an interoperable service or native contract. + /// + /// The to convert. + /// The descriptor of the parameter. + /// The converted . protected internal object Convert(StackItem item, InteropParameterDescriptor descriptor) { if (descriptor.IsArray) @@ -295,6 +406,10 @@ public override void Dispose() base.Dispose(); } + /// + /// Determines whether the of the current context meets the specified requirements. + /// + /// The requirements to check. protected void ValidateCallFlags(CallFlags requiredCallFlags) { ExecutionContextState state = CurrentContext.GetState(); @@ -307,6 +422,10 @@ protected override void OnSysCall(uint method) OnSysCall(services[method]); } + /// + /// Invokes the specified interoperable service. + /// + /// The descriptor of the interoperable service. protected virtual void OnSysCall(InteropDescriptor descriptor) { ValidateCallFlags(descriptor.RequiredCallFlags); @@ -353,8 +472,8 @@ private static Block CreateDummyBlock(DataCache snapshot, ProtocolSettings setti private static InteropDescriptor Register(string name, string handler, long fixedPrice, CallFlags requiredCallFlags) { - MethodInfo method = typeof(ApplicationEngine).GetMethod(handler, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - ?? typeof(ApplicationEngine).GetProperty(handler, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetMethod; + MethodInfo method = typeof(ApplicationEngine).GetMethod(handler, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static) + ?? typeof(ApplicationEngine).GetProperty(handler, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static).GetMethod; InteropDescriptor descriptor = new() { Name = name, @@ -372,6 +491,17 @@ internal static void ResetApplicationEngineProvider() Exchange(ref applicationEngineProvider, null); } + /// + /// Creates a new instance of the class, and use it to run the specified script. + /// + /// The script to be executed. + /// The snapshot used by the engine during execution. + /// The container of the script. + /// The block being persisted. + /// The used by the engine. + /// The initial position of the instruction pointer. + /// The maximum gas used in this execution. The execution will fail when the gas is exhausted. + /// The engine instance created. public static ApplicationEngine Run(byte[] script, DataCache snapshot, IVerifiable container = null, Block persistingBlock = null, ProtocolSettings settings = null, int offset = 0, long gas = TestModeGas) { persistingBlock ??= CreateDummyBlock(snapshot, settings ?? ProtocolSettings.Default); diff --git a/src/neo/SmartContract/BinarySerializer.cs b/src/neo/SmartContract/BinarySerializer.cs index 4b41984b6b..1ba2602ec2 100644 --- a/src/neo/SmartContract/BinarySerializer.cs +++ b/src/neo/SmartContract/BinarySerializer.cs @@ -12,6 +12,9 @@ namespace Neo.SmartContract { + /// + /// A binary serializer for . + /// public static class BinarySerializer { private class ContainerPlaceholder : StackItem @@ -32,27 +35,49 @@ public ContainerPlaceholder(StackItemType type, int count) public override bool GetBoolean() => throw new NotSupportedException(); } + /// + /// Deserializes a from byte array. + /// + /// The byte array to parse. + /// The maximum length of arrays during the deserialization. + /// The used by the . + /// The deserialized . public static StackItem Deserialize(byte[] data, uint maxArraySize, ReferenceCounter referenceCounter = null) { - using MemoryStream ms = new MemoryStream(data, false); - using BinaryReader reader = new BinaryReader(ms); + using MemoryStream ms = new(data, false); + using BinaryReader reader = new(ms, Utility.StrictUTF8, true); return Deserialize(reader, maxArraySize, (uint)data.Length, referenceCounter); } + /// + /// Deserializes a from byte array. + /// + /// The byte array to parse. + /// The maximum length of arrays during the deserialization. + /// The used by the . + /// The deserialized . public static unsafe StackItem Deserialize(ReadOnlySpan data, uint maxArraySize, ReferenceCounter referenceCounter = null) { if (data.IsEmpty) throw new FormatException(); fixed (byte* pointer = data) { - using UnmanagedMemoryStream ms = new UnmanagedMemoryStream(pointer, data.Length); - using BinaryReader reader = new BinaryReader(ms); + using UnmanagedMemoryStream ms = new(pointer, data.Length); + using BinaryReader reader = new(ms, Utility.StrictUTF8, true); return Deserialize(reader, maxArraySize, (uint)data.Length, referenceCounter); } } + /// + /// Deserializes a from . + /// + /// The for reading data. + /// The maximum length of arrays during the deserialization. + /// The maximum size of during the deserialization. + /// The used by the . + /// The deserialized . public static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uint maxItemSize, ReferenceCounter referenceCounter) { - Stack deserialized = new Stack(); + Stack deserialized = new(); int undeserialized = 1; while (undeserialized-- > 0) { @@ -72,7 +97,7 @@ public static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uint deserialized.Push(reader.ReadVarBytes((int)maxItemSize)); break; case StackItemType.Buffer: - Buffer buffer = new Buffer((int)reader.ReadVarInt(maxItemSize)); + Buffer buffer = new((int)reader.ReadVarInt(maxItemSize)); reader.FillBuffer(buffer.InnerBuffer); deserialized.Push(buffer); break; @@ -95,7 +120,7 @@ public static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uint throw new FormatException(); } } - Stack stack_temp = new Stack(); + Stack stack_temp = new(); while (deserialized.Count > 0) { StackItem item = deserialized.Pop(); @@ -104,19 +129,19 @@ public static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uint switch (placeholder.Type) { case StackItemType.Array: - Array array = new Array(referenceCounter); + Array array = new(referenceCounter); for (int i = 0; i < placeholder.ElementCount; i++) array.Add(stack_temp.Pop()); item = array; break; case StackItemType.Struct: - Struct @struct = new Struct(referenceCounter); + Struct @struct = new(referenceCounter); for (int i = 0; i < placeholder.ElementCount; i++) @struct.Add(stack_temp.Pop()); item = @struct; break; case StackItemType.Map: - Map map = new Map(referenceCounter); + Map map = new(referenceCounter); for (int i = 0; i < placeholder.ElementCount; i++) { StackItem key = stack_temp.Pop(); @@ -132,19 +157,31 @@ public static StackItem Deserialize(BinaryReader reader, uint maxArraySize, uint return stack_temp.Peek(); } + /// + /// Serializes a to byte array. + /// + /// The to be serialized. + /// The maximum size of the result. + /// The serialized byte array. public static byte[] Serialize(StackItem item, uint maxSize) { - using MemoryStream ms = new MemoryStream(); - using BinaryWriter writer = new BinaryWriter(ms); + using MemoryStream ms = new(); + using BinaryWriter writer = new(ms, Utility.StrictUTF8, true); Serialize(writer, item, maxSize); writer.Flush(); return ms.ToArray(); } + /// + /// Serializes a into . + /// + /// The for writing data. + /// The to be serialized. + /// The maximum size of the result. public static void Serialize(BinaryWriter writer, StackItem item, uint maxSize) { - HashSet serialized = new HashSet(ReferenceEqualityComparer.Instance); - Stack unserialized = new Stack(); + HashSet serialized = new(ReferenceEqualityComparer.Instance); + Stack unserialized = new(); unserialized.Push(item); while (unserialized.Count > 0) { diff --git a/src/neo/SmartContract/CallFlags.cs b/src/neo/SmartContract/CallFlags.cs index b56b70e585..530f5ea512 100644 --- a/src/neo/SmartContract/CallFlags.cs +++ b/src/neo/SmartContract/CallFlags.cs @@ -2,18 +2,50 @@ namespace Neo.SmartContract { + /// + /// Represents the operations allowed when a contract is called. + /// [Flags] public enum CallFlags : byte { + /// + /// No flag is set. + /// None = 0, + /// + /// Indicates that the called contract is allowed to read states. + /// ReadStates = 0b00000001, + + /// + /// Indicates that the called contract is allowed to write states. + /// WriteStates = 0b00000010, + + /// + /// Indicates that the called contract is allowed to call another contract. + /// AllowCall = 0b00000100, + + /// + /// Indicates that the called contract is allowed to send notifications. + /// AllowNotify = 0b00001000, + /// + /// Indicates that the called contract is allowed to read or write states. + /// States = ReadStates | WriteStates, + + /// + /// Indicates that the called contract is allowed to read states or call another contract. + /// ReadOnly = ReadStates | AllowCall, + + /// + /// All flags are set. + /// All = States | AllowCall | AllowNotify } } diff --git a/src/neo/SmartContract/Contract.cs b/src/neo/SmartContract/Contract.cs index a730ad4d86..005b8a0da7 100644 --- a/src/neo/SmartContract/Contract.cs +++ b/src/neo/SmartContract/Contract.cs @@ -6,12 +6,25 @@ namespace Neo.SmartContract { + /// + /// Represents a contract that can be invoked. + /// public class Contract { + /// + /// The script of the contract. + /// public byte[] Script; + + /// + /// The parameters of the contract. + /// public ContractParameterType[] ParameterList; private UInt160 _scriptHash; + /// + /// The hash of the contract. + /// public virtual UInt160 ScriptHash { get @@ -24,6 +37,12 @@ public virtual UInt160 ScriptHash } } + /// + /// Creates a new instance of the class. + /// + /// The parameters of the contract. + /// The script of the contract. + /// The created contract. public static Contract Create(ContractParameterType[] parameterList, byte[] redeemScript) { return new Contract @@ -34,9 +53,11 @@ public static Contract Create(ContractParameterType[] parameterList, byte[] rede } /// - /// Construct special Contract with empty Script, will get the Script with scriptHash from blockchain when doing the Verify - /// verification = snapshot.Contracts.TryGet(hashes[i])?.Script; + /// Constructs a special contract with empty script, will get the script with scriptHash from blockchain when doing the verification. /// + /// The hash of the contract. + /// The parameters of the contract. + /// The created contract. public static Contract Create(UInt160 scriptHash, params ContractParameterType[] parameterList) { return new Contract @@ -47,6 +68,12 @@ public static Contract Create(UInt160 scriptHash, params ContractParameterType[] }; } + /// + /// Creates a multi-sig contract. + /// + /// The minimum number of correct signatures that need to be provided in order for the verification to pass. + /// The public keys of the contract. + /// The created contract. public static Contract CreateMultiSigContract(int m, IReadOnlyCollection publicKeys) { return new Contract @@ -56,23 +83,32 @@ public static Contract CreateMultiSigContract(int m, IReadOnlyCollection + /// Creates the script of multi-sig contract. + /// + /// The minimum number of correct signatures that need to be provided in order for the verification to pass. + /// The public keys of the contract. + /// The created script. public static byte[] CreateMultiSigRedeemScript(int m, IReadOnlyCollection publicKeys) { if (!(1 <= m && m <= publicKeys.Count && publicKeys.Count <= 1024)) throw new ArgumentException(); - using (ScriptBuilder sb = new ScriptBuilder()) + using ScriptBuilder sb = new(); + sb.EmitPush(m); + foreach (ECPoint publicKey in publicKeys.OrderBy(p => p)) { - sb.EmitPush(m); - foreach (ECPoint publicKey in publicKeys.OrderBy(p => p)) - { - sb.EmitPush(publicKey.EncodePoint(true)); - } - sb.EmitPush(publicKeys.Count); - sb.EmitSysCall(ApplicationEngine.Neo_Crypto_CheckMultisig); - return sb.ToArray(); + sb.EmitPush(publicKey.EncodePoint(true)); } + sb.EmitPush(publicKeys.Count); + sb.EmitSysCall(ApplicationEngine.Neo_Crypto_CheckMultisig); + return sb.ToArray(); } + /// + /// Creates a signature contract. + /// + /// The public key of the contract. + /// The created contract. public static Contract CreateSignatureContract(ECPoint publicKey) { return new Contract @@ -82,16 +118,24 @@ public static Contract CreateSignatureContract(ECPoint publicKey) }; } + /// + /// Creates the script of signature contract. + /// + /// The public key of the contract. + /// The created script. public static byte[] CreateSignatureRedeemScript(ECPoint publicKey) { - using (ScriptBuilder sb = new ScriptBuilder()) - { - sb.EmitPush(publicKey.EncodePoint(true)); - sb.EmitSysCall(ApplicationEngine.Neo_Crypto_CheckSig); - return sb.ToArray(); - } + using ScriptBuilder sb = new(); + sb.EmitPush(publicKey.EncodePoint(true)); + sb.EmitSysCall(ApplicationEngine.Neo_Crypto_CheckSig); + return sb.ToArray(); } + /// + /// Gets the BFT address for the specified public keys. + /// + /// The public keys to be used. + /// The BFT address. public static UInt160 GetBFTAddress(IReadOnlyCollection pubkeys) { return CreateMultiSigRedeemScript(pubkeys.Count - (pubkeys.Count - 1) / 3, pubkeys).ToScriptHash(); diff --git a/src/neo/SmartContract/ContractParameter.cs b/src/neo/SmartContract/ContractParameter.cs index 1bae945d05..9b20476324 100644 --- a/src/neo/SmartContract/ContractParameter.cs +++ b/src/neo/SmartContract/ContractParameter.cs @@ -8,13 +8,30 @@ namespace Neo.SmartContract { + /// + /// Represents a parameter of a contract method. + /// public class ContractParameter { + /// + /// The type of the parameter. + /// public ContractParameterType Type; + + /// + /// The value of the parameter. + /// public object Value; + /// + /// Initializes a new instance of the class. + /// public ContractParameter() { } + /// + /// Initializes a new instance of the class with the specified type. + /// + /// The type of the parameter. public ContractParameter(ContractParameterType type) { this.Type = type; @@ -31,53 +48,42 @@ public ContractParameter(ContractParameterType type) ContractParameterType.String => "", ContractParameterType.Array => new List(), ContractParameterType.Map => new List>(), - _ => throw new ArgumentException(), + _ => throw new ArgumentException(null, nameof(type)), }; } + /// + /// Converts the parameter from a JSON object. + /// + /// The parameter represented by a JSON object. + /// The converted parameter. public static ContractParameter FromJson(JObject json) { - ContractParameter parameter = new ContractParameter + ContractParameter parameter = new() { Type = Enum.Parse(json["type"].GetString()) }; if (json["value"] != null) - switch (parameter.Type) + parameter.Value = parameter.Type switch { - case ContractParameterType.Signature: - case ContractParameterType.ByteArray: - parameter.Value = Convert.FromBase64String(json["value"].AsString()); - break; - case ContractParameterType.Boolean: - parameter.Value = json["value"].AsBoolean(); - break; - case ContractParameterType.Integer: - parameter.Value = BigInteger.Parse(json["value"].AsString()); - break; - case ContractParameterType.Hash160: - parameter.Value = UInt160.Parse(json["value"].AsString()); - break; - case ContractParameterType.Hash256: - parameter.Value = UInt256.Parse(json["value"].AsString()); - break; - case ContractParameterType.PublicKey: - parameter.Value = ECPoint.Parse(json["value"].AsString(), ECCurve.Secp256r1); - break; - case ContractParameterType.String: - parameter.Value = json["value"].AsString(); - break; - case ContractParameterType.Array: - parameter.Value = ((JArray)json["value"]).Select(p => FromJson(p)).ToList(); - break; - case ContractParameterType.Map: - parameter.Value = ((JArray)json["value"]).Select(p => new KeyValuePair(FromJson(p["key"]), FromJson(p["value"]))).ToList(); - break; - default: - throw new ArgumentException(); - } + ContractParameterType.Signature or ContractParameterType.ByteArray => Convert.FromBase64String(json["value"].AsString()), + ContractParameterType.Boolean => json["value"].AsBoolean(), + ContractParameterType.Integer => BigInteger.Parse(json["value"].AsString()), + ContractParameterType.Hash160 => UInt160.Parse(json["value"].AsString()), + ContractParameterType.Hash256 => UInt256.Parse(json["value"].AsString()), + ContractParameterType.PublicKey => ECPoint.Parse(json["value"].AsString(), ECCurve.Secp256r1), + ContractParameterType.String => json["value"].AsString(), + ContractParameterType.Array => ((JArray)json["value"]).Select(p => FromJson(p)).ToList(), + ContractParameterType.Map => ((JArray)json["value"]).Select(p => new KeyValuePair(FromJson(p["key"]), FromJson(p["value"]))).ToList(), + _ => throw new ArgumentException(null, nameof(json)), + }; return parameter; } + /// + /// Sets the value of the parameter. + /// + /// The form of the value. public void SetValue(string text) { switch (Type) @@ -113,6 +119,10 @@ public void SetValue(string text) } } + /// + /// Converts the parameter to a JSON object. + /// + /// The parameter represented by a JSON object. public JObject ToJson() { return ToJson(this, null); @@ -120,7 +130,7 @@ public JObject ToJson() private static JObject ToJson(ContractParameter parameter, HashSet context) { - JObject json = new JObject(); + JObject json = new(); json["type"] = parameter.Type; if (parameter.Value != null) switch (parameter.Type) @@ -155,7 +165,7 @@ private static JObject ToJson(ContractParameter parameter, HashSet>)parameter.Value).Select(p => { - JObject item = new JObject(); + JObject item = new(); item["key"] = ToJson(p.Key, context); item["value"] = ToJson(p.Value, context); return item; @@ -187,7 +197,7 @@ private static string ToString(ContractParameter parameter, HashSet + /// Represents the type of . + /// public enum ContractParameterType : byte { + /// + /// Indicates that the parameter can be of any type. + /// Any = 0x00, + /// + /// Indicates that the parameter is of Boolean type. + /// Boolean = 0x10, + + /// + /// Indicates that the parameter is an integer. + /// Integer = 0x11, + + /// + /// Indicates that the parameter is a byte array. + /// ByteArray = 0x12, + + /// + /// Indicates that the parameter is a string. + /// String = 0x13, + + /// + /// Indicates that the parameter is a 160-bit hash. + /// Hash160 = 0x14, + + /// + /// Indicates that the parameter is a 256-bit hash. + /// Hash256 = 0x15, + + /// + /// Indicates that the parameter is a public key. + /// PublicKey = 0x16, + + /// + /// Indicates that the parameter is a signature. + /// Signature = 0x17, + /// + /// Indicates that the parameter is an array. + /// Array = 0x20, + + /// + /// Indicates that the parameter is a map. + /// Map = 0x22, + /// + /// Indicates that the parameter is an interoperable interface. + /// InteropInterface = 0x30, + /// + /// It can be only used as the return type of a method, meaning that the method has no return value. + /// Void = 0xff } } diff --git a/src/neo/SmartContract/ContractParametersContext.cs b/src/neo/SmartContract/ContractParametersContext.cs index ee32c8f5d2..da47e16406 100644 --- a/src/neo/SmartContract/ContractParametersContext.cs +++ b/src/neo/SmartContract/ContractParametersContext.cs @@ -11,6 +11,9 @@ namespace Neo.SmartContract { + /// + /// The context used to add witnesses for . + /// public class ContractParametersContext { private class ContextItem @@ -49,10 +52,21 @@ public JObject ToJson() } } + /// + /// The to add witnesses. + /// public readonly IVerifiable Verifiable; + + /// + /// The snapshot used to read data. + /// public readonly DataCache Snapshot; + private readonly Dictionary ContextItems; + /// + /// Determines whether all witnesses are ready to be added. + /// public bool Completed { get @@ -63,17 +77,17 @@ public bool Completed } } - /// - /// Cache for public ScriptHashes field - /// private UInt160[] _ScriptHashes = null; - /// - /// ScriptHashes are the verifiable ScriptHashes from Verifiable element - /// Equivalent to: Verifiable.GetScriptHashesForVerifying(Blockchain.Singleton.GetSnapshot()) + /// Gets the script hashes to be verified for the . /// public IReadOnlyList ScriptHashes => _ScriptHashes ??= Verifiable.GetScriptHashesForVerifying(Snapshot); + /// + /// Initializes a new instance of the class. + /// + /// The snapshot used to read data. + /// The to add witnesses. public ContractParametersContext(DataCache snapshot, IVerifiable verifiable) { this.Verifiable = verifiable; @@ -81,6 +95,13 @@ public ContractParametersContext(DataCache snapshot, IVerifiable verifiable) this.ContextItems = new Dictionary(); } + /// + /// Adds a parameter to the specified witness script. + /// + /// The contract contains the script. + /// The index of the parameter. + /// The value of the parameter. + /// if the parameter is added successfully; otherwise, . public bool Add(Contract contract, int index, object parameter) { ContextItem item = CreateItem(contract); @@ -89,6 +110,12 @@ public bool Add(Contract contract, int index, object parameter) return true; } + /// + /// Adds parameters to the specified witness script. + /// + /// The contract contains the script. + /// The values of the parameters. + /// if the parameters are added successfully; otherwise, . public bool Add(Contract contract, params object[] parameters) { ContextItem item = CreateItem(contract); @@ -100,6 +127,13 @@ public bool Add(Contract contract, params object[] parameters) return true; } + /// + /// Adds a signature to the specified witness script. + /// + /// The contract contains the script. + /// The public key for the signature. + /// The signature. + /// if the signature is added successfully; otherwise, . public bool AddSignature(Contract contract, ECPoint pubkey, byte[] signature) { if (contract.Script.IsMultiSigContract(out _, out ECPoint[] points)) @@ -164,18 +198,24 @@ private ContextItem CreateItem(Contract contract) return item; } + /// + /// Converts the context from a JSON object. + /// + /// The context represented by a JSON object. + /// The snapshot used to read data. + /// The converted context. public static ContractParametersContext FromJson(JObject json, DataCache snapshot) { var type = typeof(ContractParametersContext).GetTypeInfo().Assembly.GetType(json["type"].AsString()); if (!typeof(IVerifiable).IsAssignableFrom(type)) throw new FormatException(); var verifiable = (IVerifiable)Activator.CreateInstance(type); - using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(json["data"].AsString()), false)) - using (BinaryReader reader = new BinaryReader(ms, Utility.StrictUTF8)) + using (MemoryStream ms = new(Convert.FromBase64String(json["data"].AsString()), false)) + using (BinaryReader reader = new(ms, Utility.StrictUTF8)) { verifiable.DeserializeUnsigned(reader); } - ContractParametersContext context = new ContractParametersContext(snapshot, verifiable); + ContractParametersContext context = new(snapshot, verifiable); foreach (var property in json["items"].Properties) { context.ContextItems.Add(UInt160.Parse(property.Key), new ContextItem(property.Value)); @@ -183,11 +223,22 @@ public static ContractParametersContext FromJson(JObject json, DataCache snapsho return context; } + /// + /// Gets the parameter with the specified index from the witness script. + /// + /// The hash of the witness script. + /// The specified index. + /// The parameter with the specified index. public ContractParameter GetParameter(UInt160 scriptHash, int index) { return GetParameters(scriptHash)?[index]; } + /// + /// Gets the parameters from the witness script. + /// + /// The hash of the witness script. + /// The parameters from the witness script. public IReadOnlyList GetParameters(UInt160 scriptHash) { if (!ContextItems.TryGetValue(scriptHash, out ContextItem item)) @@ -195,6 +246,11 @@ public IReadOnlyList GetParameters(UInt160 scriptHash) return item.Parameters; } + /// + /// Gets the signatures from the witness script. + /// + /// The hash of the witness script. + /// The signatures from the witness script. public IReadOnlyDictionary GetSignatures(UInt160 scriptHash) { if (!ContextItems.TryGetValue(scriptHash, out ContextItem item)) @@ -202,6 +258,11 @@ public IReadOnlyDictionary GetSignatures(UInt160 scriptHash) return item.Signatures; } + /// + /// Gets the witness script with the specified hash. + /// + /// The hash of the witness script. + /// The witness script. public byte[] GetScript(UInt160 scriptHash) { if (!ContextItems.TryGetValue(scriptHash, out ContextItem item)) @@ -209,6 +270,11 @@ public byte[] GetScript(UInt160 scriptHash) return item.Script; } + /// + /// Gets the witnesses of the . + /// + /// The witnesses of the . + /// The witnesses are not ready to be added. public Witness[] GetWitnesses() { if (!Completed) throw new InvalidOperationException(); @@ -216,33 +282,41 @@ public Witness[] GetWitnesses() for (int i = 0; i < ScriptHashes.Count; i++) { ContextItem item = ContextItems[ScriptHashes[i]]; - using (ScriptBuilder sb = new ScriptBuilder()) + using ScriptBuilder sb = new(); + for (int j = item.Parameters.Length - 1; j >= 0; j--) { - for (int j = item.Parameters.Length - 1; j >= 0; j--) - { - sb.EmitPush(item.Parameters[j]); - } - witnesses[i] = new Witness - { - InvocationScript = sb.ToArray(), - VerificationScript = item.Script ?? Array.Empty() - }; + sb.EmitPush(item.Parameters[j]); } + witnesses[i] = new Witness + { + InvocationScript = sb.ToArray(), + VerificationScript = item.Script ?? Array.Empty() + }; } return witnesses; } + /// + /// Parses the context from a JSON . + /// + /// The JSON . + /// The snapshot used to read data. + /// The parsed context. public static ContractParametersContext Parse(string value, DataCache snapshot) { return FromJson(JObject.Parse(value), snapshot); } + /// + /// Converts the context to a JSON object. + /// + /// The context represented by a JSON object. public JObject ToJson() { - JObject json = new JObject(); + JObject json = new(); json["type"] = Verifiable.GetType().FullName; - using (MemoryStream ms = new MemoryStream()) - using (BinaryWriter writer = new BinaryWriter(ms, Utility.StrictUTF8)) + using (MemoryStream ms = new()) + using (BinaryWriter writer = new(ms, Utility.StrictUTF8)) { Verifiable.SerializeUnsigned(writer); writer.Flush(); diff --git a/src/neo/SmartContract/ContractState.cs b/src/neo/SmartContract/ContractState.cs index f9b7fed5d8..cc3007b717 100644 --- a/src/neo/SmartContract/ContractState.cs +++ b/src/neo/SmartContract/ContractState.cs @@ -8,14 +8,39 @@ namespace Neo.SmartContract { + /// + /// Represents a deployed contract. + /// public class ContractState : IInteroperable { + /// + /// The id of the contract. + /// public int Id; + + /// + /// Indicates the number of times the contract has been updated. + /// public ushort UpdateCounter; + + /// + /// The hash of the contract. + /// public UInt160 Hash; + + /// + /// The nef of the contract. + /// public NefFile Nef; + + /// + /// The manifest of the contract. + /// public ContractManifest Manifest; + /// + /// The script of the contract. + /// public byte[] Script => Nef.Script; void IInteroperable.FromStackItem(StackItem stackItem) @@ -29,16 +54,20 @@ void IInteroperable.FromStackItem(StackItem stackItem) } /// - /// Return true if is allowed + /// Determines whether the current contract has the permission to call the specified contract. /// - /// The contract that we are calling - /// The method that we are calling - /// Return true or false + /// The contract to be called. + /// The method to be called. + /// if the contract allows to be called; otherwise, . public bool CanCall(ContractState targetContract, string targetMethod) { return Manifest.Permissions.Any(u => u.IsAllowed(targetContract, targetMethod)); } + /// + /// Converts the contract to a JSON object. + /// + /// The contract represented by a JSON object. public JObject ToJson() { return new JObject diff --git a/src/neo/SmartContract/ContractTask.cs b/src/neo/SmartContract/ContractTask.cs index c93c0cc7bd..db654ff058 100644 --- a/src/neo/SmartContract/ContractTask.cs +++ b/src/neo/SmartContract/ContractTask.cs @@ -20,7 +20,7 @@ public ContractTask() awaiter = CreateAwaiter(); } - protected virtual ContractTaskAwaiter CreateAwaiter() => new ContractTaskAwaiter(); + protected virtual ContractTaskAwaiter CreateAwaiter() => new(); public virtual ContractTaskAwaiter GetAwaiter() => awaiter; @@ -30,7 +30,7 @@ public ContractTask() [AsyncMethodBuilder(typeof(ContractTaskMethodBuilder<>))] class ContractTask : ContractTask { - protected override ContractTaskAwaiter CreateAwaiter() => new ContractTaskAwaiter(); + protected override ContractTaskAwaiter CreateAwaiter() => new(); public override ContractTaskAwaiter GetAwaiter() => (ContractTaskAwaiter)base.GetAwaiter(); diff --git a/src/neo/SmartContract/ContractTaskMethodBuilder.cs b/src/neo/SmartContract/ContractTaskMethodBuilder.cs index b5f8128fac..97342d948e 100644 --- a/src/neo/SmartContract/ContractTaskMethodBuilder.cs +++ b/src/neo/SmartContract/ContractTaskMethodBuilder.cs @@ -9,7 +9,7 @@ sealed class ContractTaskMethodBuilder public ContractTask Task => task ??= new ContractTask(); - public static ContractTaskMethodBuilder Create() => new ContractTaskMethodBuilder(); + public static ContractTaskMethodBuilder Create() => new(); public void SetException(Exception exception) { @@ -51,7 +51,7 @@ sealed class ContractTaskMethodBuilder public ContractTask Task => task ??= new ContractTask(); - public static ContractTaskMethodBuilder Create() => new ContractTaskMethodBuilder(); + public static ContractTaskMethodBuilder Create() => new(); public void SetException(Exception exception) { diff --git a/src/neo/SmartContract/DeployedContract.cs b/src/neo/SmartContract/DeployedContract.cs index 4a5e3ba2a5..bab8f3b105 100644 --- a/src/neo/SmartContract/DeployedContract.cs +++ b/src/neo/SmartContract/DeployedContract.cs @@ -4,10 +4,17 @@ namespace Neo.SmartContract { + /// + /// Represents a deployed contract that can be invoked. + /// public class DeployedContract : Contract { public override UInt160 ScriptHash { get; } + /// + /// Initializes a new instance of the class with the specified . + /// + /// The corresponding to the contract. public DeployedContract(ContractState contract) { if (contract is null) throw new ArgumentNullException(nameof(contract)); diff --git a/src/neo/SmartContract/ExecutionContextState.cs b/src/neo/SmartContract/ExecutionContextState.cs index bbbdefbae9..a2a0f7613a 100644 --- a/src/neo/SmartContract/ExecutionContextState.cs +++ b/src/neo/SmartContract/ExecutionContextState.cs @@ -1,24 +1,29 @@ +using Neo.VM; + namespace Neo.SmartContract { + /// + /// Represents the custom state in . + /// public class ExecutionContextState { /// - /// Script hash + /// The script hash of the current context. /// public UInt160 ScriptHash { get; set; } /// - /// Calling script hash + /// The script hash of the calling contract. /// public UInt160 CallingScriptHash { get; set; } /// - /// The ContractState of the current context. + /// The of the current context. /// public ContractState Contract { get; set; } /// - /// Execution context rights + /// The of the current context. /// public CallFlags CallFlags { get; set; } = CallFlags.All; } diff --git a/src/neo/SmartContract/FindOptions.cs b/src/neo/SmartContract/FindOptions.cs index 9b4c20c2ba..a295b1319a 100644 --- a/src/neo/SmartContract/FindOptions.cs +++ b/src/neo/SmartContract/FindOptions.cs @@ -2,18 +2,50 @@ namespace Neo.SmartContract { + /// + /// Specify the options to be used during the search. + /// [Flags] public enum FindOptions : byte { + /// + /// No option is set. The results will be an iterator of (key, value). + /// None = 0, + /// + /// Indicates that only keys need to be returned. The results will be an iterator of keys. + /// KeysOnly = 1 << 0, + + /// + /// Indicates that the prefix byte of keys should be removed before return. + /// RemovePrefix = 1 << 1, + + /// + /// Indicates that only values need to be returned. The results will be an iterator of values. + /// ValuesOnly = 1 << 2, + + /// + /// Indicates that values should be deserialized before return. + /// DeserializeValues = 1 << 3, + + /// + /// Indicates that only the field 0 of the deserialized values need to be returned. This flag must be set together with . + /// PickField0 = 1 << 4, + + /// + /// Indicates that only the field 1 of the deserialized values need to be returned. This flag must be set together with . + /// PickField1 = 1 << 5, + /// + /// This value is only for internal use, and shouldn't be used in smart contracts. + /// All = KeysOnly | RemovePrefix | ValuesOnly | DeserializeValues | PickField0 | PickField1 } } diff --git a/src/neo/SmartContract/Helper.cs b/src/neo/SmartContract/Helper.cs index bbcd88fa19..da8cde9120 100644 --- a/src/neo/SmartContract/Helper.cs +++ b/src/neo/SmartContract/Helper.cs @@ -13,32 +13,59 @@ namespace Neo.SmartContract { + /// + /// A helper class related to smart contract. + /// public static class Helper { + /// + /// The maximum GAS that can be consumed when is called. + /// public const long MaxVerificationGas = 0_50000000; + /// + /// Calculates the verification fee for a signature address. + /// + /// The calculated cost. public static long SignatureContractCost() => ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * 2 + ApplicationEngine.OpCodePrices[OpCode.SYSCALL] + ApplicationEngine.CheckSigPrice; + /// + /// Calculates the verification fee for a multi-signature address. + /// + /// The minimum number of correct signatures that need to be provided in order for the verification to pass. + /// The number of public keys in the account. + /// The calculated cost. public static long MultiSignatureContractCost(int m, int n) { long fee = ApplicationEngine.OpCodePrices[OpCode.PUSHDATA1] * (m + n); - using (ScriptBuilder sb = new ScriptBuilder()) + using (ScriptBuilder sb = new()) fee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(m).ToArray()[0]]; - using (ScriptBuilder sb = new ScriptBuilder()) + using (ScriptBuilder sb = new()) fee += ApplicationEngine.OpCodePrices[(OpCode)sb.EmitPush(n).ToArray()[0]]; fee += ApplicationEngine.OpCodePrices[OpCode.SYSCALL]; fee += ApplicationEngine.CheckSigPrice * n; return fee; } + /// + /// Check the correctness of the script and ABI. + /// + /// The script of the contract. + /// The ABI of the contract. public static void Check(byte[] script, ContractAbi abi) { Check(new Script(script, true), abi); } + /// + /// Check the correctness of the script and ABI. + /// + /// The script of the contract. + /// The ABI of the contract. + /// Note: The passed to this method should be constructed with strict mode. public static void Check(this Script script, ContractAbi abi) { foreach (ContractMethodDescriptor method in abi.Methods) @@ -47,6 +74,13 @@ public static void Check(this Script script, ContractAbi abi) _ = abi.Events.ToDictionary(p => p.Name); // Check the uniqueness of the event names. } + /// + /// Computes the hash of a deployed contract. + /// + /// The sender of the transaction that deployed the contract. + /// The checksum of the nef file of the contract. + /// The name of the contract. + /// The hash of the contract. public static UInt160 GetContractHash(UInt160 sender, uint nefCheckSum, string name) { using var sb = new ScriptBuilder(); @@ -58,24 +92,48 @@ public static UInt160 GetContractHash(UInt160 sender, uint nefCheckSum, string n return sb.ToArray().ToScriptHash(); } + /// + /// Gets the script hash of the specified . + /// + /// The specified . + /// The script hash of the context. public static UInt160 GetScriptHash(this ExecutionContext context) { return context.GetState().ScriptHash; } + /// + /// Determines whether the specified contract is a multi-signature contract. + /// + /// The script of the contract. + /// if the contract is a multi-signature contract; otherwise, . public static bool IsMultiSigContract(this byte[] script) { return IsMultiSigContract(script, out _, out _, null); } + /// + /// Determines whether the specified contract is a multi-signature contract. + /// + /// The script of the contract. + /// The minimum number of correct signatures that need to be provided in order for the verification to pass. + /// The number of public keys in the account. + /// if the contract is a multi-signature contract; otherwise, . public static bool IsMultiSigContract(this byte[] script, out int m, out int n) { return IsMultiSigContract(script, out m, out n, null); } + /// + /// Determines whether the specified contract is a multi-signature contract. + /// + /// The script of the contract. + /// The minimum number of correct signatures that need to be provided in order for the verification to pass. + /// The public keys in the account. + /// if the contract is a multi-signature contract; otherwise, . public static bool IsMultiSigContract(this byte[] script, out int m, out ECPoint[] points) { - List list = new List(); + List list = new(); if (IsMultiSigContract(script, out m, out _, list)) { points = list.ToArray(); @@ -144,6 +202,11 @@ private static bool IsMultiSigContract(byte[] script, out int m, out int n, List return true; } + /// + /// Determines whether the specified contract is a signature contract. + /// + /// The script of the contract. + /// if the contract is a signature contract; otherwise, . public static bool IsSignatureContract(this byte[] script) { if (script.Length != 40) return false; @@ -155,28 +218,57 @@ public static bool IsSignatureContract(this byte[] script) return true; } + /// + /// Determines whether the specified contract is a standard contract. A standard contract is either a signature contract or a multi-signature contract. + /// + /// The script of the contract. + /// if the contract is a standard contract; otherwise, . public static bool IsStandardContract(this byte[] script) { return script.IsSignatureContract() || script.IsMultiSigContract(); } + /// + /// Convert the to an . + /// + /// The type of the . + /// The to convert. + /// The converted . public static T ToInteroperable(this StackItem item) where T : IInteroperable, new() { - T t = new T(); + T t = new(); t.FromStackItem(item); return t; } + /// + /// Computes the hash of the specified script. + /// + /// The specified script. + /// The hash of the script. public static UInt160 ToScriptHash(this byte[] script) { return new UInt160(Crypto.Hash160(script)); } + /// + /// Computes the hash of the specified script. + /// + /// The specified script. + /// The hash of the script. public static UInt160 ToScriptHash(this ReadOnlySpan script) { return new UInt160(Crypto.Hash160(script)); } + /// + /// Verifies the witnesses of the specified . + /// + /// The to be verified. + /// The to be used for the verification. + /// The snapshot used to read data. + /// The maximum GAS that can be used. + /// if the is verified as valid; otherwise, . public static bool VerifyWitnesses(this IVerifiable verifiable, ProtocolSettings settings, DataCache snapshot, long gas) { if (gas < 0) return false; diff --git a/src/neo/SmartContract/IInteroperable.cs b/src/neo/SmartContract/IInteroperable.cs index ec53ef48c0..be495c354f 100644 --- a/src/neo/SmartContract/IInteroperable.cs +++ b/src/neo/SmartContract/IInteroperable.cs @@ -3,9 +3,22 @@ namespace Neo.SmartContract { + /// + /// Represents the object that can be converted to and from . + /// public interface IInteroperable { + /// + /// Convert a to the current object. + /// + /// The to convert. void FromStackItem(StackItem stackItem); + + /// + /// Convert the current object to a . + /// + /// The used by the . + /// The converted . StackItem ToStackItem(ReferenceCounter referenceCounter); } } diff --git a/src/neo/SmartContract/InteropDescriptor.cs b/src/neo/SmartContract/InteropDescriptor.cs index d8b2798d1c..e5f1f8ada2 100644 --- a/src/neo/SmartContract/InteropDescriptor.cs +++ b/src/neo/SmartContract/InteropDescriptor.cs @@ -7,11 +7,20 @@ namespace Neo.SmartContract { + /// + /// Represents a descriptor of an interoperable service. + /// public record InteropDescriptor { + /// + /// The name of the interoperable service. + /// public string Name { get; init; } private uint _hash; + /// + /// The hash of the interoperable service. + /// public uint Hash { get @@ -22,13 +31,25 @@ public uint Hash } } + /// + /// The used to handle the interoperable service. + /// public MethodInfo Handler { get; init; } private IReadOnlyList _parameters; + /// + /// The parameters of the interoperable service. + /// public IReadOnlyList Parameters => _parameters ??= Handler.GetParameters().Select(p => new InteropParameterDescriptor(p)).ToList().AsReadOnly(); + /// + /// The fixed price for calling the interoperable service. It can be 0 if the interoperable service has a variable price. + /// public long FixedPrice { get; init; } + /// + /// The required for the interoperable service. + /// public CallFlags RequiredCallFlags { get; init; } public static implicit operator uint(InteropDescriptor descriptor) diff --git a/src/neo/SmartContract/InteropParameterDescriptor.cs b/src/neo/SmartContract/InteropParameterDescriptor.cs index 6fce4848ae..93f949a29f 100644 --- a/src/neo/SmartContract/InteropParameterDescriptor.cs +++ b/src/neo/SmartContract/InteropParameterDescriptor.cs @@ -7,16 +7,42 @@ namespace Neo.SmartContract { + /// + /// Represents a descriptor of an interoperable service parameter. + /// public class InteropParameterDescriptor { + /// + /// The name of the parameter. + /// public string Name { get; } + + /// + /// The type of the parameter. + /// public Type Type { get; } + + /// + /// The converter to convert the parameter from to . + /// public Func Converter { get; } + + /// + /// Indicates whether the parameter is an enumeration. + /// public bool IsEnum => Type.IsEnum; + + /// + /// Indicates whether the parameter is an array. + /// public bool IsArray => Type.IsArray && Type.GetElementType() != typeof(byte); + + /// + /// Indicates whether the parameter is an . + /// public bool IsInterface { get; } - private static readonly Dictionary> converters = new Dictionary> + private static readonly Dictionary> converters = new() { [typeof(StackItem)] = p => p, [typeof(VM.Types.Pointer)] = p => p, diff --git a/src/neo/SmartContract/Iterators/IIterator.cs b/src/neo/SmartContract/Iterators/IIterator.cs index d131b75977..4e59fadf7b 100644 --- a/src/neo/SmartContract/Iterators/IIterator.cs +++ b/src/neo/SmartContract/Iterators/IIterator.cs @@ -3,9 +3,21 @@ namespace Neo.SmartContract.Iterators { + /// + /// Represents iterators in smart contract. + /// public interface IIterator : IDisposable { + /// + /// Advances the iterator to the next element of the collection. + /// + /// if the iterator was successfully advanced to the next element; if the iterator has passed the end of the collection. bool Next(); + + /// + /// Gets the element in the collection at the current position of the iterator. + /// + /// The element in the collection at the current position of the iterator. StackItem Value(); } } diff --git a/src/neo/SmartContract/JsonSerializer.cs b/src/neo/SmartContract/JsonSerializer.cs index ef772183c9..1629ae9b7f 100644 --- a/src/neo/SmartContract/JsonSerializer.cs +++ b/src/neo/SmartContract/JsonSerializer.cs @@ -13,13 +13,16 @@ namespace Neo.SmartContract { + /// + /// A JSON serializer for . + /// public static class JsonSerializer { /// - /// Convert stack item in json + /// Serializes a to a . /// - /// Item - /// Json + /// The to serialize. + /// The serialized object. public static JObject Serialize(StackItem item) { switch (item) @@ -68,15 +71,21 @@ public static JObject Serialize(StackItem item) } } + /// + /// Serializes a to JSON. + /// + /// The to convert. + /// The maximum size of the JSON output. + /// A byte array containing the JSON output. public static byte[] SerializeToByteArray(StackItem item, uint maxSize) { - using MemoryStream ms = new MemoryStream(); - using Utf8JsonWriter writer = new Utf8JsonWriter(ms, new JsonWriterOptions + using MemoryStream ms = new(); + using Utf8JsonWriter writer = new(ms, new JsonWriterOptions { Indented = false, SkipValidation = false }); - Stack stack = new Stack(); + Stack stack = new(); stack.Push(item); while (stack.Count > 0) { @@ -136,10 +145,11 @@ public static byte[] SerializeToByteArray(StackItem item, uint maxSize) } /// - /// Convert json object to stack item + /// Deserializes a from . /// - /// Json - /// Return stack item + /// The to deserialize. + /// The used by the . + /// The deserialized . public static StackItem Deserialize(JObject json, ReferenceCounter referenceCounter = null) { switch (json) diff --git a/src/neo/SmartContract/KeyBuilder.cs b/src/neo/SmartContract/KeyBuilder.cs index 80bea0e783..099de8bd52 100644 --- a/src/neo/SmartContract/KeyBuilder.cs +++ b/src/neo/SmartContract/KeyBuilder.cs @@ -4,26 +4,44 @@ namespace Neo.SmartContract { + /// + /// Used to build storage keys for native contracts. + /// public class KeyBuilder { private readonly int id; - private readonly MemoryStream stream = new MemoryStream(); + private readonly MemoryStream stream = new(); + /// + /// Initializes a new instance of the class. + /// + /// The id of the contract. + /// The prefix of the key. public KeyBuilder(int id, byte prefix) { this.id = id; this.stream.WriteByte(prefix); } + /// + /// Adds part of the key to the builder. + /// + /// Part of the key. + /// A reference to this instance after the add operation has completed. public KeyBuilder Add(ReadOnlySpan key) { stream.Write(key); return this; } + /// + /// Adds part of the key to the builder. + /// + /// Part of the key. + /// A reference to this instance after the add operation has completed. public KeyBuilder Add(ISerializable key) { - using (BinaryWriter writer = new BinaryWriter(stream, Utility.StrictUTF8, true)) + using (BinaryWriter writer = new(stream, Utility.StrictUTF8, true)) { key.Serialize(writer); writer.Flush(); @@ -31,19 +49,35 @@ public KeyBuilder Add(ISerializable key) return this; } + /// + /// Adds part of the key to the builder. + /// + /// The type of the parameter. + /// Part of the key. + /// A reference to this instance after the add operation has completed. unsafe public KeyBuilder Add(T key) where T : unmanaged { return Add(new ReadOnlySpan(&key, sizeof(T))); } + /// + /// Adds part of the key to the builder with big-endian. + /// + /// The type of the parameter. + /// Part of the key. + /// A reference to this instance after the add operation has completed. unsafe public KeyBuilder AddBigEndian(T key) where T : unmanaged { - ReadOnlySpan buffer = new ReadOnlySpan(&key, sizeof(T)); + ReadOnlySpan buffer = new(&key, sizeof(T)); for (int i = buffer.Length - 1; i >= 0; i--) stream.WriteByte(buffer[i]); return this; } + /// + /// Gets the storage key generated by the builder. + /// + /// The storage key. public byte[] ToArray() { using (stream) diff --git a/src/neo/SmartContract/LogEventArgs.cs b/src/neo/SmartContract/LogEventArgs.cs index 8c007fe101..7173ef6226 100644 --- a/src/neo/SmartContract/LogEventArgs.cs +++ b/src/neo/SmartContract/LogEventArgs.cs @@ -3,12 +3,32 @@ namespace Neo.SmartContract { + /// + /// The of . + /// public class LogEventArgs : EventArgs { + /// + /// The container that containing the executed script. + /// public IVerifiable ScriptContainer { get; } + + /// + /// The script hash of the contract that sends the log. + /// public UInt160 ScriptHash { get; } + + /// + /// The message of the log. + /// public string Message { get; } + /// + /// Initializes a new instance of the class. + /// + /// The container that containing the executed script. + /// The script hash of the contract that sends the log. + /// The message of the log. public LogEventArgs(IVerifiable container, UInt160 script_hash, string message) { this.ScriptContainer = container; diff --git a/src/neo/SmartContract/Manifest/ContractAbi.cs b/src/neo/SmartContract/Manifest/ContractAbi.cs index db4fdfe344..0213bf0b7c 100644 --- a/src/neo/SmartContract/Manifest/ContractAbi.cs +++ b/src/neo/SmartContract/Manifest/ContractAbi.cs @@ -9,19 +9,20 @@ namespace Neo.SmartContract.Manifest { /// - /// NeoContract ABI + /// Represents the ABI of a smart contract. /// + /// For more details, see NEP-14. public class ContractAbi : IInteroperable { private IReadOnlyDictionary<(string, int), ContractMethodDescriptor> methodDictionary; /// - /// Methods is an array of Method objects which describe the details of each method in the contract. + /// Gets the methods in the ABI. /// public ContractMethodDescriptor[] Methods { get; set; } /// - /// Events is an array of Event objects which describe the details of each event in the contract. + /// Gets the events in the ABI. /// public ContractEventDescriptor[] Events { get; set; } @@ -42,13 +43,13 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) } /// - /// Parse ContractAbi from json + /// Converts the ABI from a JSON object. /// - /// Json - /// Return ContractAbi + /// The ABI represented by a JSON object. + /// The converted ABI. public static ContractAbi FromJson(JObject json) { - ContractAbi abi = new ContractAbi + ContractAbi abi = new() { Methods = ((JArray)json["methods"]).Select(u => ContractMethodDescriptor.FromJson(u)).ToArray(), Events = ((JArray)json["events"]).Select(u => ContractEventDescriptor.FromJson(u)).ToArray() @@ -57,6 +58,12 @@ public static ContractAbi FromJson(JObject json) return abi; } + /// + /// Gets the method with the specified name. + /// + /// The name of the method. + /// The number of parameters of the method. It can be set to -1 to search for the method with the specified name and any number of parameters. + /// The method that matches the specified name and number of parameters. If is set to -1, the first method with the specified name will be returned. public ContractMethodDescriptor GetMethod(string name, int pcount) { if (pcount < -1 || pcount > ushort.MaxValue) throw new ArgumentOutOfRangeException(nameof(pcount)); @@ -72,6 +79,10 @@ public ContractMethodDescriptor GetMethod(string name, int pcount) } } + /// + /// Converts the ABI to a JSON object. + /// + /// The ABI represented by a JSON object. public JObject ToJson() { var json = new JObject(); diff --git a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs index 69570011d0..18b1415ab9 100644 --- a/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractEventDescriptor.cs @@ -7,15 +7,18 @@ namespace Neo.SmartContract.Manifest { + /// + /// Represents an event in a smart contract ABI. + /// public class ContractEventDescriptor : IInteroperable { /// - /// Name is the name of the method, which can be any valid identifier. + /// The name of the event or method. /// public string Name { get; set; } /// - /// Parameters is an array of Parameter objects which describe the details of each parameter in the method. + /// The parameters of the event or method. /// public ContractParameterDefinition[] Parameters { get; set; } @@ -36,13 +39,13 @@ public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) } /// - /// Parse ContractEventDescriptor from json + /// Converts the event from a JSON object. /// - /// Json - /// Return ContractEventDescriptor + /// The event represented by a JSON object. + /// The converted event. public static ContractEventDescriptor FromJson(JObject json) { - ContractEventDescriptor descriptor = new ContractEventDescriptor + ContractEventDescriptor descriptor = new() { Name = json["name"].GetString(), Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), @@ -52,6 +55,10 @@ public static ContractEventDescriptor FromJson(JObject json) return descriptor; } + /// + /// Converts the event to a JSON object. + /// + /// The event represented by a JSON object. public virtual JObject ToJson() { var json = new JObject(); diff --git a/src/neo/SmartContract/Manifest/ContractGroup.cs b/src/neo/SmartContract/Manifest/ContractGroup.cs index a756e56f35..3b3fd66bdd 100644 --- a/src/neo/SmartContract/Manifest/ContractGroup.cs +++ b/src/neo/SmartContract/Manifest/ContractGroup.cs @@ -9,18 +9,19 @@ namespace Neo.SmartContract.Manifest { /// - /// A group represents a set of mutually trusted contracts. A contract will trust and allow any contract in the same group to invoke it, and the user interface will not give any warnings. + /// Represents a set of mutually trusted contracts. + /// A contract will trust and allow any contract in the same group to invoke it, and the user interface will not give any warnings. /// A group is identified by a public key and must be accompanied by a signature for the contract hash to prove that the contract is indeed included in the group. /// public class ContractGroup : IInteroperable { /// - /// Pubkey represents the public key of the group. + /// The public key of the group. /// public ECPoint PubKey { get; set; } /// - /// Signature is the signature of the contract hash. + /// The signature of the contract hash which can be verified by . /// public byte[] Signature { get; set; } @@ -37,13 +38,13 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) } /// - /// Parse ContractManifestGroup from json + /// Converts the group from a JSON object. /// - /// Json - /// Return ContractManifestGroup + /// The group represented by a JSON object. + /// The converted group. public static ContractGroup FromJson(JObject json) { - ContractGroup group = new ContractGroup + ContractGroup group = new() { PubKey = ECPoint.Parse(json["pubkey"].GetString(), ECCurve.Secp256r1), Signature = Convert.FromBase64String(json["signature"].GetString()), @@ -53,16 +54,20 @@ public static ContractGroup FromJson(JObject json) } /// - /// Return true if the signature is valid + /// Determines whether the signature in the group is valid. /// - /// Contract Hash - /// Return true or false + /// The hash of the contract. + /// if the signature is valid; otherwise, . public bool IsValid(UInt160 hash) { return Crypto.VerifySignature(hash.ToArray(), Signature, PubKey); } - public virtual JObject ToJson() + /// + /// Converts the group to a JSON object. + /// + /// The group represented by a JSON object. + public JObject ToJson() { var json = new JObject(); json["pubkey"] = PubKey.ToString(); diff --git a/src/neo/SmartContract/Manifest/ContractManifest.cs b/src/neo/SmartContract/Manifest/ContractManifest.cs index 7c95c278e5..cda156362b 100644 --- a/src/neo/SmartContract/Manifest/ContractManifest.cs +++ b/src/neo/SmartContract/Manifest/ContractManifest.cs @@ -9,49 +9,51 @@ namespace Neo.SmartContract.Manifest { /// + /// Represents the manifest of a smart contract. /// When a smart contract is deployed, it must explicitly declare the features and permissions it will use. /// When it is running, it will be limited by its declared list of features and permissions, and cannot make any behavior beyond the scope of the list. /// + /// For more details, see NEP-15. public class ContractManifest : IInteroperable { /// - /// Max length for a valid Contract Manifest + /// The maximum length of a manifest. /// public const int MaxLength = ushort.MaxValue; /// - /// Contract name + /// The name of the contract. /// public string Name { get; set; } /// - /// A group represents a set of mutually trusted contracts. A contract will trust and allow any contract in the same group to invoke it, and the user interface will not give any warnings. + /// The groups of the contract. /// public ContractGroup[] Groups { get; set; } /// - /// NEP10 - SupportedStandards + /// Indicates which standards the contract supports. It can be a list of NEPs. /// public string[] SupportedStandards { get; set; } /// - /// For technical details of ABI, please refer to NEP-3: NeoContract ABI. (https://github.com/neo-project/proposals/blob/master/nep-3.mediawiki) + /// The ABI of the contract. /// public ContractAbi Abi { get; set; } /// - /// The permissions field is an array containing a set of Permission objects. It describes which contracts may be invoked and which methods are called. + /// The permissions of the contract. /// public ContractPermission[] Permissions { get; set; } /// - /// The trusts field is an array containing a set of contract hashes or group public keys. It can also be assigned with a wildcard *. If it is a wildcard *, then it means that it trusts any contract. + /// The trusted contracts and groups of the contract. /// If a contract is trusted, the user interface will not give any warnings when called by the contract. /// public WildcardContainer Trusts { get; set; } /// - /// Custom user data + /// Custom user data. /// public JObject Extra { get; set; } @@ -87,13 +89,13 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) } /// - /// Parse ContractManifest from json + /// Converts the manifest from a JSON object. /// - /// Json - /// Return ContractManifest + /// The manifest represented by a JSON object. + /// The converted manifest. public static ContractManifest FromJson(JObject json) { - ContractManifest manifest = new ContractManifest + ContractManifest manifest = new() { Name = json["name"].GetString(), Groups = ((JArray)json["groups"]).Select(u => ContractGroup.FromJson(u)).ToArray(), @@ -115,21 +117,27 @@ public static ContractManifest FromJson(JObject json) } /// - /// Parse ContractManifest from json + /// Parse the manifest from a byte array containing JSON data. /// - /// Json - /// Return ContractManifest + /// The byte array containing JSON data. + /// The parsed manifest. public static ContractManifest Parse(ReadOnlySpan json) { if (json.Length > MaxLength) throw new ArgumentException(null, nameof(json)); return FromJson(JObject.Parse(json)); } + /// + /// Parse the manifest from a JSON . + /// + /// The JSON . + /// The parsed manifest. public static ContractManifest Parse(string json) => Parse(Utility.StrictUTF8.GetBytes(json)); - /// + /// Converts the manifest to a JSON object. /// + /// The manifest represented by a JSON object. public JObject ToJson() { return new JObject @@ -145,9 +153,10 @@ public JObject ToJson() } /// - /// Return true if is valid + /// Determines whether the manifest is valid. /// - /// Return true or false + /// The hash of the contract. + /// if the manifest is valid; otherwise, . public bool IsValid(UInt160 hash) { return Groups.All(u => u.IsValid(hash)); diff --git a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs index 94029e5a08..b2188750b7 100644 --- a/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractMethodDescriptor.cs @@ -6,19 +6,24 @@ namespace Neo.SmartContract.Manifest { + /// + /// Represents a method in a smart contract ABI. + /// public class ContractMethodDescriptor : ContractEventDescriptor { /// - /// Returntype indicates the return type of the method. It can be one of the following values: - /// Any, Signature, Boolean, Integer, Hash160, Hash256, ByteArray, PublicKey, String, Array, Map, InteropInterface, Void. + /// Indicates the return type of the method. It can be any value of . /// public ContractParameterType ReturnType { get; set; } + /// + /// The position of the method in the contract script. + /// public int Offset { get; set; } /// - /// Determine if it's safe to call this method - /// If a method is marked as safe, the user interface will not give any warnings when it is called by any other contract. + /// Indicates whether the method is a safe method. + /// If a method is marked as safe, the user interface will not give any warnings when it is called by other contracts. /// public bool Safe { get; set; } @@ -41,13 +46,13 @@ public override StackItem ToStackItem(ReferenceCounter referenceCounter) } /// - /// Parse ContractMethodDescription from json + /// Converts the method from a JSON object. /// - /// Json - /// Return ContractMethodDescription + /// The method represented by a JSON object. + /// The converted method. public new static ContractMethodDescriptor FromJson(JObject json) { - ContractMethodDescriptor descriptor = new ContractMethodDescriptor + ContractMethodDescriptor descriptor = new() { Name = json["name"].GetString(), Parameters = ((JArray)json["parameters"]).Select(u => ContractParameterDefinition.FromJson(u)).ToArray(), @@ -62,6 +67,10 @@ public override StackItem ToStackItem(ReferenceCounter referenceCounter) return descriptor; } + /// + /// Converts the method to a JSON object. + /// + /// The method represented by a JSON object. public override JObject ToJson() { var json = base.ToJson(); diff --git a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs index e18a04b199..0393066517 100644 --- a/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs +++ b/src/neo/SmartContract/Manifest/ContractParameterDefinition.cs @@ -5,16 +5,18 @@ namespace Neo.SmartContract.Manifest { + /// + /// Represents a parameter of an event or method in ABI. + /// public class ContractParameterDefinition : IInteroperable { /// - /// Name is the name of the parameter, which can be any valid identifier. + /// The name of the parameter. /// public string Name { get; set; } /// - /// Type indicates the type of the parameter. It can be one of the following values: - /// Any, Signature, Boolean, Integer, Hash160, Hash256, ByteArray, PublicKey, String, Array, Map, InteropInterface. + /// The type of the parameter. It can be any value of except . /// public ContractParameterType Type { get; set; } @@ -31,13 +33,13 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) } /// - /// Parse ContractParameterDefinition from json + /// Converts the parameter from a JSON object. /// - /// Json - /// Return ContractParameterDefinition + /// The parameter represented by a JSON object. + /// The converted parameter. public static ContractParameterDefinition FromJson(JObject json) { - ContractParameterDefinition parameter = new ContractParameterDefinition + ContractParameterDefinition parameter = new() { Name = json["name"].GetString(), Type = Enum.Parse(json["type"].GetString()) @@ -49,7 +51,11 @@ public static ContractParameterDefinition FromJson(JObject json) return parameter; } - public virtual JObject ToJson() + /// + /// Converts the parameter to a JSON object. + /// + /// The parameter represented by a JSON object. + public JObject ToJson() { var json = new JObject(); json["name"] = Name; diff --git a/src/neo/SmartContract/Manifest/ContractPermission.cs b/src/neo/SmartContract/Manifest/ContractPermission.cs index 2fbeeabdae..de4e52e4df 100644 --- a/src/neo/SmartContract/Manifest/ContractPermission.cs +++ b/src/neo/SmartContract/Manifest/ContractPermission.cs @@ -9,23 +9,28 @@ namespace Neo.SmartContract.Manifest { /// - /// The permissions field is an array containing a set of Permission objects. It describes which contracts may be invoked and which methods are called. + /// Represents a permission of a contract. It describes which contracts may be invoked and which methods are called. + /// If a contract invokes a contract or method that is not declared in the manifest at runtime, the invocation will fail. /// public class ContractPermission : IInteroperable { /// - /// The contract field indicates the contract to be invoked. It can be a hash of a contract, a public key of a group, or a wildcard *. - /// If it specifies a hash of a contract, then the contract will be invoked; If it specifies a public key of a group, then any contract in this group will be invoked; If it specifies a wildcard*, then any contract will be invoked. + /// Indicates which contract to be invoked. + /// It can be a hash of a contract, a public key of a group, or a wildcard *. + /// If it specifies a hash of a contract, then the contract will be invoked; If it specifies a public key of a group, then any contract in this group may be invoked; If it specifies a wildcard *, then any contract may be invoked. /// public ContractPermissionDescriptor Contract { get; set; } /// - /// The methods field is an array containing a set of methods to be called. It can also be assigned with a wildcard *. If it is a wildcard *, then it means that any method can be called. - /// If a contract invokes a contract or method that is not declared in the manifest at runtime, the invocation will fail. + /// Indicates which methods to be called. + /// It can also be assigned with a wildcard *. If it is a wildcard *, then it means that any method can be called. /// public WildcardContainer Methods { get; set; } - public static readonly ContractPermission DefaultPermission = new ContractPermission + /// + /// A default permission that both and fields are set to wildcard *. + /// + public static readonly ContractPermission DefaultPermission = new() { Contract = ContractPermissionDescriptor.CreateWildcard(), Methods = WildcardContainer.CreateWildcard() @@ -57,13 +62,13 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) } /// - /// Parse ContractPermission from json + /// Converts the permission from a JSON object. /// - /// Json - /// Return ContractPermission + /// The permission represented by a JSON object. + /// The converted permission. public static ContractPermission FromJson(JObject json) { - ContractPermission permission = new ContractPermission + ContractPermission permission = new() { Contract = ContractPermissionDescriptor.FromJson(json["contract"]), Methods = WildcardContainer.FromJson(json["methods"], u => u.GetString()), @@ -74,9 +79,10 @@ public static ContractPermission FromJson(JObject json) return permission; } - /// + /// Converts the permission to a JSON object. /// + /// The permission represented by a JSON object. public JObject ToJson() { var json = new JObject(); @@ -86,11 +92,11 @@ public JObject ToJson() } /// - /// Return true if is allowed + /// Determines whether the method of the specified contract can be called by this contract. /// - /// The contract that we are calling - /// The method that we are calling - /// Return true or false + /// The contract being called. + /// The method of the specified contract. + /// if the contract allows to be called; otherwise, . public bool IsAllowed(ContractState targetContract, string targetMethod) { if (Contract.IsHash) diff --git a/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs b/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs index 83e0ddbeb0..6bd840f2c0 100644 --- a/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs +++ b/src/neo/SmartContract/Manifest/ContractPermissionDescriptor.cs @@ -5,13 +5,34 @@ namespace Neo.SmartContract.Manifest { + /// + /// Indicates which contracts are authorized to be called. + /// public class ContractPermissionDescriptor : IEquatable { + /// + /// The hash of the contract. It can't be set with . + /// public UInt160 Hash { get; } + + /// + /// The group of the contracts. It can't be set with . + /// public ECPoint Group { get; } + /// + /// Indicates whether is set. + /// public bool IsHash => Hash != null; + + /// + /// Indicates whether is set. + /// public bool IsGroup => Group != null; + + /// + /// Indicates whether it is a wildcard. + /// public bool IsWildcard => Hash is null && Group is null; private ContractPermissionDescriptor(UInt160 hash, ECPoint group) @@ -35,16 +56,30 @@ internal ContractPermissionDescriptor(ReadOnlySpan span) } } + /// + /// Creates a new instance of the class with the specified contract hash. + /// + /// The contract to be called. + /// The created permission descriptor. public static ContractPermissionDescriptor Create(UInt160 hash) { return new ContractPermissionDescriptor(hash, null); } + /// + /// Creates a new instance of the class with the specified group. + /// + /// The group of the contracts to be called. + /// The created permission descriptor. public static ContractPermissionDescriptor Create(ECPoint group) { return new ContractPermissionDescriptor(null, group); } + /// + /// Creates a new instance of the class with wildcard. + /// + /// The created permission descriptor. public static ContractPermissionDescriptor CreateWildcard() { return new ContractPermissionDescriptor(null, null); @@ -70,6 +105,11 @@ public override int GetHashCode() return HashCode.Combine(Hash, Group); } + /// + /// Converts the permission descriptor from a JSON object. + /// + /// The permission descriptor represented by a JSON object. + /// The converted permission descriptor. public static ContractPermissionDescriptor FromJson(JObject json) { string str = json.GetString(); @@ -82,6 +122,10 @@ public static ContractPermissionDescriptor FromJson(JObject json) throw new FormatException(); } + /// + /// Converts the permission descriptor to a JSON object. + /// + /// The permission descriptor represented by a JSON object. public JObject ToJson() { if (IsHash) return Hash.ToString(); diff --git a/src/neo/SmartContract/Manifest/WildCardContainer.cs b/src/neo/SmartContract/Manifest/WildCardContainer.cs index d85f23fbd8..96ed3f839d 100644 --- a/src/neo/SmartContract/Manifest/WildCardContainer.cs +++ b/src/neo/SmartContract/Manifest/WildCardContainer.cs @@ -6,44 +6,47 @@ namespace Neo.SmartContract.Manifest { + /// + /// A list that supports wildcard. + /// + /// The type of the elements. public class WildcardContainer : IReadOnlyList { private readonly T[] _data; public T this[int index] => _data[index]; - /// - /// Number of items - /// public int Count => _data?.Length ?? 0; /// - /// Is wildcard? + /// Indicates whether the list is a wildcard. /// public bool IsWildcard => _data is null; - /// - /// Constructor - /// - /// Data private WildcardContainer(T[] data) { _data = data; } /// - /// Create a new WildCardContainer + /// Creates a new instance of the class with the initial elements. /// - /// Data - /// WildCardContainer - public static WildcardContainer Create(params T[] data) => new WildcardContainer(data); + /// The initial elements. + /// The created list. + public static WildcardContainer Create(params T[] data) => new(data); /// - /// Create a wildcard + /// Creates a new instance of the class with wildcard. /// - /// WildCardContainer - public static WildcardContainer CreateWildcard() => new WildcardContainer(null); + /// The created list. + public static WildcardContainer CreateWildcard() => new(null); + /// + /// Converts the list from a JSON object. + /// + /// The list represented by a JSON object. + /// A converter for elements. + /// The converted list. public static WildcardContainer FromJson(JObject json, Func elementSelector) { switch (json) @@ -60,13 +63,17 @@ public static WildcardContainer FromJson(JObject json, Func eleme public IEnumerator GetEnumerator() { - if (_data == null) return ((IReadOnlyList)new T[0]).GetEnumerator(); + if (_data == null) return ((IReadOnlyList)Array.Empty()).GetEnumerator(); return ((IReadOnlyList)_data).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + /// + /// Converts the list to a JSON object. + /// + /// The list represented by a JSON object. public JObject ToJson() { if (IsWildcard) return "*"; diff --git a/src/neo/SmartContract/MethodToken.cs b/src/neo/SmartContract/MethodToken.cs index 42ba7d9c44..90955755e2 100644 --- a/src/neo/SmartContract/MethodToken.cs +++ b/src/neo/SmartContract/MethodToken.cs @@ -5,12 +5,34 @@ namespace Neo.SmartContract { + /// + /// Represents the methods that a contract will call statically. + /// public class MethodToken : ISerializable { + /// + /// The hash of the contract to be called. + /// public UInt160 Hash; + + /// + /// The name of the method to be called. + /// public string Method; + + /// + /// The number of parameters of the method to be called. + /// public ushort ParametersCount; + + /// + /// Indicates whether the method to be called has a return value. + /// public bool HasReturnValue; + + /// + /// The to be used to call the contract. + /// public CallFlags CallFlags; public int Size => @@ -40,6 +62,10 @@ void ISerializable.Serialize(BinaryWriter writer) writer.Write((byte)CallFlags); } + /// + /// Converts the token to a JSON object. + /// + /// The token represented by a JSON object. public JObject ToJson() { return new JObject diff --git a/src/neo/SmartContract/Native/AccountState.cs b/src/neo/SmartContract/Native/AccountState.cs index 491866a799..ca6108d968 100644 --- a/src/neo/SmartContract/Native/AccountState.cs +++ b/src/neo/SmartContract/Native/AccountState.cs @@ -4,8 +4,14 @@ namespace Neo.SmartContract.Native { + /// + /// The base class of account state for all native tokens. + /// public class AccountState : IInteroperable { + /// + /// The balance of the account. + /// public BigInteger Balance; public virtual void FromStackItem(StackItem stackItem) diff --git a/src/neo/SmartContract/Native/ContractManagement.cs b/src/neo/SmartContract/Native/ContractManagement.cs index 817c8c4961..135caa5b55 100644 --- a/src/neo/SmartContract/Native/ContractManagement.cs +++ b/src/neo/SmartContract/Native/ContractManagement.cs @@ -12,6 +12,9 @@ namespace Neo.SmartContract.Native { + /// + /// A native contract used to manage all deployed smart contracts. + /// public sealed class ContractManagement : NativeContract { private const byte Prefix_MinimumDeploymentFee = 20; @@ -118,12 +121,23 @@ private void SetMinimumDeploymentFee(ApplicationEngine engine, BigInteger value) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_MinimumDeploymentFee)).Set(value); } + /// + /// Gets the deployed contract with the specified hash. + /// + /// The snapshot used to read data. + /// The hash of the deployed contract. + /// The deployed contract. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public ContractState GetContract(DataCache snapshot, UInt160 hash) { return snapshot.TryGet(CreateStorageKey(Prefix_Contract).Add(hash))?.GetInteroperable(); } + /// + /// Gets all deployed contracts. + /// + /// The snapshot used to read data. + /// The deployed contracts. public IEnumerable ListContracts(DataCache snapshot) { byte[] listContractsPrefix = CreateStorageKey(Prefix_Contract).ToArray(); @@ -158,7 +172,7 @@ private async ContractTask Deploy(ApplicationEngine engine, byte[ StorageKey key = CreateStorageKey(Prefix_Contract).Add(hash); if (engine.Snapshot.Contains(key)) throw new InvalidOperationException($"Contract Already Exists: {hash}"); - ContractState contract = new ContractState + ContractState contract = new() { Id = GetNextAvailableId(engine.Snapshot), UpdateCounter = 0, diff --git a/src/neo/SmartContract/Native/CryptoLib.cs b/src/neo/SmartContract/Native/CryptoLib.cs index 0d16095c0b..2511927a3e 100644 --- a/src/neo/SmartContract/Native/CryptoLib.cs +++ b/src/neo/SmartContract/Native/CryptoLib.cs @@ -5,9 +5,12 @@ namespace Neo.SmartContract.Native { + /// + /// A native contract library that provides cryptographic algorithms. + /// public sealed class CryptoLib : NativeContract { - private static readonly Dictionary curves = new Dictionary + private static readonly Dictionary curves = new() { [NamedCurve.secp256k1] = ECCurve.Secp256k1, [NamedCurve.secp256r1] = ECCurve.Secp256r1 @@ -15,18 +18,36 @@ public sealed class CryptoLib : NativeContract internal CryptoLib() { } + /// + /// Computes the hash value for the specified byte array using the ripemd160 algorithm. + /// + /// The input to compute the hash code for. + /// The computed hash code. [ContractMethod(CpuFee = 1 << 15, Name = "ripemd160")] public static byte[] RIPEMD160(byte[] data) { return data.RIPEMD160(); } + /// + /// Computes the hash value for the specified byte array using the sha256 algorithm. + /// + /// The input to compute the hash code for. + /// The computed hash code. [ContractMethod(CpuFee = 1 << 15)] public static byte[] Sha256(byte[] data) { return data.Sha256(); } + /// + /// Verifies that a digital signature is appropriate for the provided key and message using the ECDSA algorithm. + /// + /// The signed message. + /// The public key to be used. + /// The signature to be verified. + /// The curve to be used by the ECDSA algorithm. + /// if the signature is valid; otherwise, . [ContractMethod(CpuFee = 1 << 15)] public static bool VerifyWithECDsa(byte[] message, byte[] pubkey, byte[] signature, NamedCurve curve) { diff --git a/src/neo/SmartContract/Native/FungibleToken.cs b/src/neo/SmartContract/Native/FungibleToken.cs index 1308bd76e4..cd90b86ba7 100644 --- a/src/neo/SmartContract/Native/FungibleToken.cs +++ b/src/neo/SmartContract/Native/FungibleToken.cs @@ -9,18 +9,43 @@ namespace Neo.SmartContract.Native { + /// + /// The base class of all native tokens that are compatible with NEP-17. + /// + /// The type of account state. public abstract class FungibleToken : NativeContract where TState : AccountState, new() { + /// + /// The symbol of the token. + /// [ContractMethod] public abstract string Symbol { get; } + + /// + /// The number of decimal places of the token. + /// [ContractMethod] public abstract byte Decimals { get; } + + /// + /// The factor used when calculating the displayed value of the token value. + /// public BigInteger Factor { get; } + /// + /// The prefix for storing total supply. + /// protected const byte Prefix_TotalSupply = 11; + + /// + /// The prefix for storing account states. + /// protected const byte Prefix_Account = 20; + /// + /// Initializes a new instance of the class. + /// protected FungibleToken() { this.Factor = BigInteger.Pow(10, Decimals); @@ -87,6 +112,11 @@ internal async ContractTask Burn(ApplicationEngine engine, UInt160 account, BigI await PostTransfer(engine, account, null, amount, StackItem.Null, false); } + /// + /// Gets the total supply of the token. + /// + /// The snapshot used to read data. + /// The total supply of the token. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public virtual BigInteger TotalSupply(DataCache snapshot) { @@ -95,6 +125,12 @@ public virtual BigInteger TotalSupply(DataCache snapshot) return storage; } + /// + /// Gets the balance of the specified account. + /// + /// The snapshot used to read data. + /// The owner of the account. + /// The balance of the account. Or 0 if the account doesn't exist. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public virtual BigInteger BalanceOf(DataCache snapshot, UInt160 account) { diff --git a/src/neo/SmartContract/Native/GasToken.cs b/src/neo/SmartContract/Native/GasToken.cs index 228b79c8fd..12e308a942 100644 --- a/src/neo/SmartContract/Native/GasToken.cs +++ b/src/neo/SmartContract/Native/GasToken.cs @@ -3,6 +3,9 @@ namespace Neo.SmartContract.Native { + /// + /// Represents the GAS token in the NEO system. + /// public sealed class GasToken : FungibleToken { public override string Symbol => "GAS"; diff --git a/src/neo/SmartContract/Native/LedgerContract.cs b/src/neo/SmartContract/Native/LedgerContract.cs index 6488f3e91b..3d087e0b93 100644 --- a/src/neo/SmartContract/Native/LedgerContract.cs +++ b/src/neo/SmartContract/Native/LedgerContract.cs @@ -9,6 +9,9 @@ namespace Neo.SmartContract.Native { + /// + /// A native contract for storing all blocks and transactions. + /// public sealed class LedgerContract : NativeContract { private const byte Prefix_BlockHash = 9; @@ -55,6 +58,12 @@ private bool IsTraceableBlock(DataCache snapshot, uint index, uint maxTraceableB return index + maxTraceableBlocks > currentIndex; } + /// + /// Gets the hash of the specified block. + /// + /// The snapshot used to read data. + /// The index of the block. + /// The hash of the block. public UInt256 GetBlockHash(DataCache snapshot, uint index) { StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_BlockHash).AddBigEndian(index)); @@ -62,28 +71,56 @@ public UInt256 GetBlockHash(DataCache snapshot, uint index) return new UInt256(item.Value); } + /// + /// Gets the hash of the current block. + /// + /// The snapshot used to read data. + /// The hash of the current block. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public UInt256 CurrentHash(DataCache snapshot) { return snapshot[CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable().Hash; } + /// + /// Gets the index of the current block. + /// + /// The snapshot used to read data. + /// The index of the current block. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public uint CurrentIndex(DataCache snapshot) { return snapshot[CreateStorageKey(Prefix_CurrentBlock)].GetInteroperable().Index; } + /// + /// Determine whether the specified block is contained in the blockchain. + /// + /// The snapshot used to read data. + /// The hash of the block. + /// if the blockchain contains the block; otherwise, . public bool ContainsBlock(DataCache snapshot, UInt256 hash) { return snapshot.Contains(CreateStorageKey(Prefix_Block).Add(hash)); } + /// + /// Determine whether the specified transaction is contained in the blockchain. + /// + /// The snapshot used to read data. + /// The hash of the transaction. + /// if the blockchain contains the transaction; otherwise, . public bool ContainsTransaction(DataCache snapshot, UInt256 hash) { return snapshot.Contains(CreateStorageKey(Prefix_Transaction).Add(hash)); } + /// + /// Gets a with the specified hash. + /// + /// The snapshot used to read data. + /// The hash of the block. + /// The trimmed block. public TrimmedBlock GetTrimmedBlock(DataCache snapshot, UInt256 hash) { StorageItem item = snapshot.TryGet(CreateStorageKey(Prefix_Block).Add(hash)); @@ -107,6 +144,12 @@ private TrimmedBlock GetBlock(ApplicationEngine engine, byte[] indexOrHash) return block; } + /// + /// Gets a block with the specified hash. + /// + /// The snapshot used to read data. + /// The hash of the block. + /// The block with the specified hash. public Block GetBlock(DataCache snapshot, UInt256 hash) { TrimmedBlock state = GetTrimmedBlock(snapshot, hash); @@ -118,6 +161,12 @@ public Block GetBlock(DataCache snapshot, UInt256 hash) }; } + /// + /// Gets a block with the specified index. + /// + /// The snapshot used to read data. + /// The index of the block. + /// The block with the specified index. public Block GetBlock(DataCache snapshot, uint index) { UInt256 hash = GetBlockHash(snapshot, index); @@ -125,11 +174,23 @@ public Block GetBlock(DataCache snapshot, uint index) return GetBlock(snapshot, hash); } + /// + /// Gets a block header with the specified hash. + /// + /// The snapshot used to read data. + /// The hash of the block. + /// The block header with the specified hash. public Header GetHeader(DataCache snapshot, UInt256 hash) { return GetTrimmedBlock(snapshot, hash)?.Header; } + /// + /// Gets a block header with the specified index. + /// + /// The snapshot used to read data. + /// The index of the block. + /// The block header with the specified index. public Header GetHeader(DataCache snapshot, uint index) { UInt256 hash = GetBlockHash(snapshot, index); @@ -137,11 +198,23 @@ public Header GetHeader(DataCache snapshot, uint index) return GetHeader(snapshot, hash); } + /// + /// Gets a with the specified hash. + /// + /// The snapshot used to read data. + /// The hash of the transaction. + /// The with the specified hash. public TransactionState GetTransactionState(DataCache snapshot, UInt256 hash) { return snapshot.TryGet(CreateStorageKey(Prefix_Transaction).Add(hash))?.GetInteroperable(); } + /// + /// Gets a transaction with the specified hash. + /// + /// The snapshot used to read data. + /// The hash of the transaction. + /// The transaction with the specified hash. public Transaction GetTransaction(DataCache snapshot, UInt256 hash) { return GetTransactionState(snapshot, hash)?.Transaction; diff --git a/src/neo/SmartContract/Native/NFTState.cs b/src/neo/SmartContract/Native/NFTState.cs index e9f464ea95..59496e5792 100644 --- a/src/neo/SmartContract/Native/NFTState.cs +++ b/src/neo/SmartContract/Native/NFTState.cs @@ -5,13 +5,31 @@ namespace Neo.SmartContract.Native { + /// + /// The base class of the token states for . + /// public abstract class NFTState : IInteroperable { + /// + /// The owner of the token. + /// public UInt160 Owner; + + /// + /// The name of the token. + /// public string Name; + /// + /// The id of the token. + /// public abstract byte[] Id { get; } + /// + /// Converts the token to a . + /// + /// The used by the . + /// The converted map. public virtual Map ToMap(ReferenceCounter referenceCounter) { return new Map(referenceCounter) { ["name"] = Name }; diff --git a/src/neo/SmartContract/Native/NameService.cs b/src/neo/SmartContract/Native/NameService.cs index 799125d7a7..d6ebea8ff9 100644 --- a/src/neo/SmartContract/Native/NameService.cs +++ b/src/neo/SmartContract/Native/NameService.cs @@ -15,6 +15,9 @@ namespace Neo.SmartContract.Native { + /// + /// A native name service for NEO system. + /// public sealed class NameService : NonfungibleToken { public override string Symbol => "NNS"; @@ -25,10 +28,10 @@ public sealed class NameService : NonfungibleToken private const byte Prefix_Record = 12; private const uint OneYear = 365 * 24 * 3600; - private static readonly Regex rootRegex = new Regex("^[a-z][a-z0-9]{0,15}$", RegexOptions.Singleline); - private static readonly Regex nameRegex = new Regex("^(?=.{3,255}$)([a-z0-9]{1,62}\\.)+[a-z][a-z0-9]{0,15}$", RegexOptions.Singleline); - private static readonly Regex ipv4Regex = new Regex("^(?=\\d+\\.\\d+\\.\\d+\\.\\d+$)(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\\.?){4}$", RegexOptions.Singleline); - private static readonly Regex ipv6Regex = new Regex("(?:^)(([0-9a-f]{1,4}:){7,7}[0-9a-f]{1,4}|([0-9a-f]{1,4}:){1,7}:|([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}|([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}|([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}|([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:((:[0-9a-f]{1,4}){1,6})|:((:[0-9a-f]{1,4}){1,7}|:))(?=$)", RegexOptions.Singleline); + private static readonly Regex rootRegex = new("^[a-z][a-z0-9]{0,15}$", RegexOptions.Singleline); + private static readonly Regex nameRegex = new("^(?=.{3,255}$)([a-z0-9]{1,62}\\.)+[a-z][a-z0-9]{0,15}$", RegexOptions.Singleline); + private static readonly Regex ipv4Regex = new("^(?=\\d+\\.\\d+\\.\\d+\\.\\d+$)(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\\.?){4}$", RegexOptions.Singleline); + private static readonly Regex ipv6Regex = new("(?:^)(([0-9a-f]{1,4}:){7,7}[0-9a-f]{1,4}|([0-9a-f]{1,4}:){1,7}:|([0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2}|([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3}|([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4}|([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:((:[0-9a-f]{1,4}){1,6})|:((:[0-9a-f]{1,4}){1,7}|:))(?=$)", RegexOptions.Singleline); internal NameService() { @@ -76,6 +79,11 @@ private void AddRoot(ApplicationEngine engine, string root) roots.Insert(~index, root); } + /// + /// Gets all the root names in the system. + /// + /// The snapshot used to read data. + /// All the root names in the system. public IEnumerable GetRoots(DataCache snapshot) { return snapshot[CreateStorageKey(Prefix_Roots)].GetInteroperable(); @@ -89,12 +97,25 @@ private void SetPrice(ApplicationEngine engine, long price) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_DomainPrice)).Set(price); } + /// + /// Gets the price for registering a name. + /// + /// The snapshot used to read data. + /// The price for registering a name. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public long GetPrice(DataCache snapshot) { return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_DomainPrice)]; } + /// + /// Determine whether the specified name is available to be registered. + /// + /// The snapshot used to read data. + /// The name to check. + /// if the name is available; otherwise, . + /// The format of is incorrect. + /// The root name doesn't exist. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public bool IsAvailable(DataCache snapshot, string name) { @@ -120,7 +141,7 @@ private async ContractTask Register(ApplicationEngine engine, string name, StringList roots = engine.Snapshot[CreateStorageKey(Prefix_Roots)].GetInteroperable(); if (roots.BinarySearch(names[1]) < 0) throw new InvalidOperationException(); engine.AddGas(GetPrice(engine.Snapshot)); - NameState state = new NameState + NameState state = new() { Owner = owner, Name = name, @@ -198,6 +219,13 @@ private void SetRecord(ApplicationEngine engine, string name, RecordType type, s item.Value = Utility.StrictUTF8.GetBytes(data); } + /// + /// Gets a record for the specified name. + /// + /// The snapshot used to read data. + /// The name for the record. + /// The type of the record. + /// The record without resolved. Or if no record is found. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public string GetRecord(DataCache snapshot, string name, RecordType type) { @@ -209,6 +237,12 @@ public string GetRecord(DataCache snapshot, string name, RecordType type) return Utility.StrictUTF8.GetString(item.Value); } + /// + /// Gets all the records for the specified name. + /// + /// The snapshot used to read data. + /// The name for the record. + /// All the records for the name. public IEnumerable<(RecordType Type, string Data)> GetRecords(DataCache snapshot, string name) { if (!nameRegex.IsMatch(name)) throw new ArgumentException(null, nameof(name)); @@ -229,6 +263,13 @@ private void DeleteRecord(ApplicationEngine engine, string name, RecordType type engine.Snapshot.Delete(CreateStorageKey(Prefix_Record).Add(hash_domain).Add(GetKey(Utility.StrictUTF8.GetBytes(name))).Add(type)); } + /// + /// Gets and resolves a record for the specified name. + /// + /// The snapshot used to read data. + /// The name for the record. + /// The type of the record. + /// The resolved record. [ContractMethod(CpuFee = 1 << 17, RequiredCallFlags = CallFlags.ReadStates)] public string Resolve(DataCache snapshot, string name, RecordType type) { @@ -244,9 +285,19 @@ private string Resolve(DataCache snapshot, string name, RecordType type, int red return Resolve(snapshot, data, type, redirect - 1); } + /// + /// The token state of . + /// public class NameState : NFTState { + /// + /// Indicates when the name expires. + /// public uint Expiration; + + /// + /// The administrator of the name. + /// public UInt160 Admin; public override byte[] Id => Utility.StrictUTF8.GetBytes(Name); diff --git a/src/neo/SmartContract/Native/NamedCurve.cs b/src/neo/SmartContract/Native/NamedCurve.cs index f7b149856e..ca5955681b 100644 --- a/src/neo/SmartContract/Native/NamedCurve.cs +++ b/src/neo/SmartContract/Native/NamedCurve.cs @@ -1,14 +1,21 @@ namespace Neo.SmartContract.Native { /// - /// RFC 4492 + /// Represents the named curve used in ECDSA. /// /// /// https://tools.ietf.org/html/rfc4492#section-5.1.1 /// public enum NamedCurve : byte { + /// + /// The secp256k1 curve. + /// secp256k1 = 22, + + /// + /// The secp256r1 curve, which known as prime256v1 or nistP-256. + /// secp256r1 = 23 } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 2004b76845..cf6f6484de 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -8,36 +8,106 @@ namespace Neo.SmartContract.Native { + /// + /// The base class of all native contracts. + /// public abstract class NativeContract { - private static readonly List contractsList = new List(); - private static readonly Dictionary contractsDictionary = new Dictionary(); - private readonly Dictionary methods = new Dictionary(); + private static readonly List contractsList = new(); + private static readonly Dictionary contractsDictionary = new(); + private readonly Dictionary methods = new(); private static int id_counter = 0; #region Named Native Contracts - public static ContractManagement ContractManagement { get; } = new ContractManagement(); - public static StdLib StdLib { get; } = new StdLib(); - public static CryptoLib CryptoLib { get; } = new CryptoLib(); - public static LedgerContract Ledger { get; } = new LedgerContract(); - public static NeoToken NEO { get; } = new NeoToken(); - public static GasToken GAS { get; } = new GasToken(); - public static PolicyContract Policy { get; } = new PolicyContract(); - public static RoleManagement RoleManagement { get; } = new RoleManagement(); - public static OracleContract Oracle { get; } = new OracleContract(); - public static NameService NameService { get; } = new NameService(); + + /// + /// Gets the instance of the class. + /// + public static ContractManagement ContractManagement { get; } = new(); + + /// + /// Gets the instance of the class. + /// + public static StdLib StdLib { get; } = new(); + + /// + /// Gets the instance of the class. + /// + public static CryptoLib CryptoLib { get; } = new(); + + /// + /// Gets the instance of the class. + /// + public static LedgerContract Ledger { get; } = new(); + + /// + /// Gets the instance of the class. + /// + public static NeoToken NEO { get; } = new(); + + /// + /// Gets the instance of the class. + /// + public static GasToken GAS { get; } = new(); + + /// + /// Gets the instance of the class. + /// + public static PolicyContract Policy { get; } = new(); + + /// + /// Gets the instance of the class. + /// + public static RoleManagement RoleManagement { get; } = new(); + + /// + /// Gets the instance of the class. + /// + public static OracleContract Oracle { get; } = new(); + + /// + /// Gets the instance of the class. + /// + public static NameService NameService { get; } = new(); + #endregion + /// + /// Gets all native contracts. + /// public static IReadOnlyCollection Contracts { get; } = contractsList; + + /// + /// The name of the native contract. + /// public string Name => GetType().Name; + + /// + /// The nef of the native contract. + /// public NefFile Nef { get; } + + /// + /// The hash of the native contract. + /// public UInt160 Hash { get; } + + /// + /// The id of the native contract. + /// public int Id { get; } = --id_counter; + + /// + /// The manifest of the native contract. + /// public ContractManifest Manifest { get; } + /// + /// Initializes a new instance of the class. + /// protected NativeContract() { - List descriptors = new List(); + List descriptors = new(); foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) { ContractMethodAttribute attribute = member.GetCustomAttribute(); @@ -46,7 +116,7 @@ protected NativeContract() } descriptors = descriptors.OrderBy(p => p.Name).ThenBy(p => p.Parameters.Length).ToList(); byte[] script; - using (ScriptBuilder sb = new ScriptBuilder()) + using (ScriptBuilder sb = new()) { foreach (ContractMethodMetadata method in descriptors) { @@ -84,7 +154,12 @@ protected NativeContract() contractsDictionary.Add(Hash, this); } - protected bool CheckCommittee(ApplicationEngine engine) + /// + /// Checks whether the committee has witnessed the current transaction. + /// + /// The that is executing the contract. + /// if the committee has witnessed the current transaction; otherwise, . + protected static bool CheckCommittee(ApplicationEngine engine) { UInt160 committeeMultiSigAddr = NEO.GetCommitteeAddress(engine.Snapshot); return engine.CheckWitnessInternal(committeeMultiSigAddr); @@ -95,6 +170,11 @@ private protected KeyBuilder CreateStorageKey(byte prefix) return new KeyBuilder(Id, prefix); } + /// + /// Gets the native contract with the specified hash. + /// + /// The hash of the native contract. + /// The native contract with the specified hash. public static NativeContract GetContract(UInt160 hash) { contractsDictionary.TryGetValue(hash, out var contract); @@ -113,7 +193,7 @@ internal async void Invoke(ApplicationEngine engine, byte version) if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}."); engine.AddGas(method.CpuFee * Policy.GetExecFeeFactor(engine.Snapshot) + method.StorageFee * Policy.GetStoragePrice(engine.Snapshot)); - List parameters = new List(); + List parameters = new(); if (method.NeedApplicationEngine) parameters.Add(engine); if (method.NeedSnapshot) parameters.Add(engine.Snapshot); for (int i = 0; i < method.Parameters.Length; i++) @@ -135,6 +215,11 @@ internal async void Invoke(ApplicationEngine engine, byte version) } } + /// + /// Determine whether the specified contract is a native contract. + /// + /// The hash of the contract. + /// if the contract is native; otherwise, . public static bool IsNative(UInt160 hash) { return contractsDictionary.ContainsKey(hash); diff --git a/src/neo/SmartContract/Native/NeoToken.cs b/src/neo/SmartContract/Native/NeoToken.cs index 197c58ea1e..32c10a18ac 100644 --- a/src/neo/SmartContract/Native/NeoToken.cs +++ b/src/neo/SmartContract/Native/NeoToken.cs @@ -13,12 +13,22 @@ namespace Neo.SmartContract.Native { + /// + /// Represents the NEO token in the NEO system. + /// public sealed class NeoToken : FungibleToken { public override string Symbol => "NEO"; public override byte Decimals => 0; + + /// + /// Indicates the total amount of NEO. + /// public BigInteger TotalAmount { get; } + /// + /// Indicates the effective voting turnout in NEO. The voted candidates will only be effective when the voting turnout exceeds this value. + /// public const decimal EffectiveVoterTurnout = 0.2M; private const byte Prefix_VotersCount = 1; @@ -113,13 +123,19 @@ private void CheckCandidate(DataCache snapshot, ECPoint pubkey, CandidateState c } } + /// + /// Determine whether the votes should be recounted at the specified height. + /// + /// The height to be checked. + /// The number of committee members in the system. + /// if the votes should be recounted; otherwise, . public static bool ShouldRefreshCommittee(uint height, int committeeMembersCount) => height % committeeMembersCount == 0; internal override ContractTask Initialize(ApplicationEngine engine) { var cachedCommittee = new CachedCommittee(engine.ProtocolSettings.StandbyCommittee.Select(p => (p, BigInteger.Zero))); engine.Snapshot.Add(CreateStorageKey(Prefix_Committee), new StorageItem(cachedCommittee)); - engine.Snapshot.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(new byte[0])); + engine.Snapshot.Add(CreateStorageKey(Prefix_VotersCount), new StorageItem(System.Array.Empty())); engine.Snapshot.Add(CreateStorageKey(Prefix_GasPerBlock).AddBigEndian(0u), new StorageItem(5 * GAS.Factor)); engine.Snapshot.Add(CreateStorageKey(Prefix_RegisterPrice), new StorageItem(1000 * GAS.Factor)); return Mint(engine, Contract.GetBFTAddress(engine.ProtocolSettings.StandbyValidators), TotalAmount, false); @@ -185,6 +201,11 @@ private void SetGasPerBlock(ApplicationEngine engine, BigInteger gasPerBlock) entry.Set(gasPerBlock); } + /// + /// Gets the amount of GAS generated in each block. + /// + /// The snapshot used to read data. + /// The amount of GAS generated. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public BigInteger GetGasPerBlock(DataCache snapshot) { @@ -200,6 +221,11 @@ private void SetRegisterPrice(ApplicationEngine engine, long registerPrice) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_RegisterPrice)).Set(registerPrice); } + /// + /// Gets the fees to be paid to register as a candidate. + /// + /// The snapshot used to read data. + /// The amount of the fees. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public long GetRegisterPrice(DataCache snapshot) { @@ -214,6 +240,13 @@ public long GetRegisterPrice(DataCache snapshot) .Select(u => (BinaryPrimitives.ReadUInt32BigEndian(u.Key.Key.AsSpan(^sizeof(uint))), (BigInteger)u.Value)); } + /// + /// Get the amount of unclaimed GAS in the specified account. + /// + /// The snapshot used to read data. + /// The account to check. + /// The block index used when calculating GAS. + /// The amount of unclaimed GAS. [ContractMethod(CpuFee = 1 << 17, RequiredCallFlags = CallFlags.ReadStates)] public BigInteger UnclaimedGas(DataCache snapshot, UInt160 account, uint end) { @@ -288,6 +321,11 @@ private async ContractTask Vote(ApplicationEngine engine, UInt160 account, return true; } + /// + /// Gets all registered candidates. + /// + /// The snapshot used to read data. + /// All the registered candidates. [ContractMethod(CpuFee = 1 << 22, RequiredCallFlags = CallFlags.ReadStates)] public (ECPoint PublicKey, BigInteger Votes)[] GetCandidates(DataCache snapshot) { @@ -299,12 +337,22 @@ private async ContractTask Vote(ApplicationEngine engine, UInt160 account, )).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes)).ToArray(); } + /// + /// Gets all the members of the committee. + /// + /// The snapshot used to read data. + /// The public keys of the members. [ContractMethod(CpuFee = 1 << 16, RequiredCallFlags = CallFlags.ReadStates)] public ECPoint[] GetCommittee(DataCache snapshot) { return GetCommitteeFromCache(snapshot).Select(p => p.PublicKey).OrderBy(p => p).ToArray(); } + /// + /// Gets the address of the committee. + /// + /// The snapshot used to read data. + /// The address of the committee. public UInt160 GetCommitteeAddress(DataCache snapshot) { ECPoint[] committees = GetCommittee(snapshot); @@ -316,6 +364,12 @@ private CachedCommittee GetCommitteeFromCache(DataCache snapshot) return snapshot[CreateStorageKey(Prefix_Committee)].GetInteroperable(); } + /// + /// Computes the validators of the next block. + /// + /// The snapshot used to read data. + /// The used during computing. + /// The public keys of the validators. public ECPoint[] ComputeNextBlockValidators(DataCache snapshot, ProtocolSettings settings) { return ComputeCommitteeMembers(snapshot, settings).Select(p => p.PublicKey).Take(settings.ValidatorsCount).OrderBy(p => p).ToArray(); @@ -337,6 +391,12 @@ private ECPoint[] GetNextBlockValidators(ApplicationEngine engine) return GetNextBlockValidators(engine.Snapshot, engine.ProtocolSettings.ValidatorsCount); } + /// + /// Gets the validators of the next block. + /// + /// The snapshot used to read data. + /// The number of validators in the system. + /// The public keys of the validators. public ECPoint[] GetNextBlockValidators(DataCache snapshot, int validatorsCount) { return GetCommitteeFromCache(snapshot) @@ -346,9 +406,19 @@ public ECPoint[] GetNextBlockValidators(DataCache snapshot, int validatorsCount) .ToArray(); } + /// + /// Represents the account state of . + /// public class NeoAccountState : AccountState { + /// + /// The height of the block where the balance changed last time. + /// public uint BalanceHeight; + + /// + /// The voting target of the account. This field can be . + /// public ECPoint VoteTo; public override void FromStackItem(StackItem stackItem) @@ -386,7 +456,7 @@ public StackItem ToStackItem(ReferenceCounter referenceCounter) } } - public class CachedCommittee : List<(ECPoint PublicKey, BigInteger Votes)>, IInteroperable + internal class CachedCommittee : List<(ECPoint PublicKey, BigInteger Votes)>, IInteroperable { public CachedCommittee() { diff --git a/src/neo/SmartContract/Native/NonfungibleToken.cs b/src/neo/SmartContract/Native/NonfungibleToken.cs index b6d7d2b55c..7974aa5440 100644 --- a/src/neo/SmartContract/Native/NonfungibleToken.cs +++ b/src/neo/SmartContract/Native/NonfungibleToken.cs @@ -1,5 +1,3 @@ -#pragma warning disable IDE0051 - using Neo.IO; using Neo.Persistence; using Neo.SmartContract.Iterators; @@ -14,18 +12,36 @@ namespace Neo.SmartContract.Native { + /// + /// The base class of all native tokens that are compatible with NEP-11. + /// + /// The type of the token state. public abstract class NonfungibleToken : NativeContract where TokenState : NFTState, new() { + /// + /// The symbol of the token. + /// [ContractMethod] public abstract string Symbol { get; } + + /// + /// The number of decimal places of the token. It always return 0 in native NFT. + /// [ContractMethod] public byte Decimals => 0; private const byte Prefix_TotalSupply = 11; private const byte Prefix_Account = 7; + + /// + /// The prefix for storing token states. + /// protected const byte Prefix_Token = 5; + /// + /// Initializes a new instance of the class. + /// protected NonfungibleToken() { var events = new List(Manifest.Abi.Events) @@ -62,6 +78,11 @@ protected NonfungibleToken() Manifest.Abi.Events = events.ToArray(); } + /// + /// Gets the storage key of the specified token id. + /// + /// The id of the token. + /// The storage key. protected virtual byte[] GetKey(byte[] tokenId) => tokenId; internal override ContractTask Initialize(ApplicationEngine engine) @@ -98,24 +119,47 @@ private protected ContractTask Burn(ApplicationEngine engine, StorageKey key) return PostTransfer(engine, token.Owner, null, token.Id); } + /// + /// Gets the total supply of the token. + /// + /// The snapshot used to read data. + /// The total supply of the token. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public BigInteger TotalSupply(DataCache snapshot) { return snapshot[CreateStorageKey(Prefix_TotalSupply)]; } + /// + /// Gets the owner of the token. + /// + /// The snapshot used to read data. + /// The id of the token. + /// The owner of the token. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public UInt160 OwnerOf(DataCache snapshot, byte[] tokenId) { return snapshot[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().Owner; } + /// + /// Gets the properties of the token. + /// + /// The that is executing the contract. + /// The id of the token. + /// [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public Map Properties(ApplicationEngine engine, byte[] tokenId) { return engine.Snapshot[CreateStorageKey(Prefix_Token).Add(GetKey(tokenId))].GetInteroperable().ToMap(engine.ReferenceCounter); } + /// + /// Gets the balance of the specified account. + /// + /// The snapshot used to read data. + /// The owner of the account. + /// The balance of the account. Or 0 if the account doesn't exist. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public BigInteger BalanceOf(DataCache snapshot, UInt160 owner) { @@ -123,6 +167,11 @@ public BigInteger BalanceOf(DataCache snapshot, UInt160 owner) return snapshot.TryGet(CreateStorageKey(Prefix_Account).Add(owner))?.GetInteroperable().Balance ?? BigInteger.Zero; } + /// + /// Get all the tokens that have been issued in the contract. + /// + /// The snapshot used to read data. + /// All the tokens that have been issued. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] protected IIterator Tokens(DataCache snapshot) { @@ -130,6 +179,12 @@ protected IIterator Tokens(DataCache snapshot) return new StorageIterator(results, FindOptions.ValuesOnly | FindOptions.DeserializeValues | FindOptions.PickField1, null); } + /// + /// Gets all the tokens owned by the specified account. + /// + /// The snapshot used to read data. + /// The owner of the account. + /// All the tokens owned by the specified account. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] protected IIterator TokensOf(DataCache snapshot, UInt160 owner) { @@ -165,6 +220,12 @@ private protected async ContractTask Transfer(ApplicationEngine engine, UI return true; } + /// + /// Called when a token is transferred. + /// + /// The that is executing the contract. + /// The account where the token is transferred from. + /// The token that is transferred. protected virtual void OnTransferred(ApplicationEngine engine, UInt160 from, TokenState token) { } @@ -180,7 +241,7 @@ private async ContractTask PostTransfer(ApplicationEngine engine, UInt160 from, class NFTAccountState : AccountState { - public readonly List Tokens = new List(); + public readonly List Tokens = new(); public void Add(byte[] tokenId) { diff --git a/src/neo/SmartContract/Native/OracleContract.cs b/src/neo/SmartContract/Native/OracleContract.cs index e63a406b99..512b38ec73 100644 --- a/src/neo/SmartContract/Native/OracleContract.cs +++ b/src/neo/SmartContract/Native/OracleContract.cs @@ -15,6 +15,9 @@ namespace Neo.SmartContract.Native { + /// + /// The native Oracle service for NEO system. + /// public sealed class OracleContract : NativeContract { private const int MaxUrlLength = 256; @@ -89,6 +92,11 @@ private void SetPrice(ApplicationEngine engine, long price) engine.Snapshot.GetAndChange(CreateStorageKey(Prefix_Price)).Set(price); } + /// + /// Gets the price for an Oracle request. + /// + /// The snapshot used to read data. + /// The price for an Oracle request. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public long GetPrice(DataCache snapshot) { @@ -117,16 +125,33 @@ private UInt256 GetOriginalTxid(ApplicationEngine engine) return request.OriginalTxid; } + /// + /// Gets a pending request with the specified id. + /// + /// The snapshot used to read data. + /// The id of the request. + /// The pending request. Or if no request with the specified id is found. public OracleRequest GetRequest(DataCache snapshot, ulong id) { return snapshot.TryGet(CreateStorageKey(Prefix_Request).AddBigEndian(id))?.GetInteroperable(); } + /// + /// Gets all the pending requests. + /// + /// The snapshot used to read data. + /// All the pending requests. public IEnumerable<(ulong, OracleRequest)> GetRequests(DataCache snapshot) { return snapshot.Find(CreateStorageKey(Prefix_Request).ToArray()).Select(p => (BinaryPrimitives.ReadUInt64BigEndian(p.Key.Key.AsSpan(1)), p.Value.GetInteroperable())); } + /// + /// Gets the requests with the specified url. + /// + /// The snapshot used to read data. + /// The url of the requests. + /// All the requests with the specified url. public IEnumerable<(ulong, OracleRequest)> GetRequestsByUrl(DataCache snapshot, string url) { IdList list = snapshot.TryGet(CreateStorageKey(Prefix_IdList).Add(GetUrlHash(url)))?.GetInteroperable(); diff --git a/src/neo/SmartContract/Native/OracleRequest.cs b/src/neo/SmartContract/Native/OracleRequest.cs index 8066116d99..52e3eba429 100644 --- a/src/neo/SmartContract/Native/OracleRequest.cs +++ b/src/neo/SmartContract/Native/OracleRequest.cs @@ -5,14 +5,44 @@ namespace Neo.SmartContract.Native { + /// + /// Represents an Oracle request in smart contracts. + /// public class OracleRequest : IInteroperable { + /// + /// The original transaction that sent the related request. + /// public UInt256 OriginalTxid; + + /// + /// The maximum amount of GAS that can be used when executing response callback. + /// public long GasForResponse; + + /// + /// The url of the request. + /// public string Url; + + /// + /// The filter for the response. + /// public string Filter; + + /// + /// The hash of the callback contract. + /// public UInt160 CallbackContract; + + /// + /// The name of the callback method. + /// public string CallbackMethod; + + /// + /// The user-defined object that will be passed to the callback. + /// public byte[] UserData; public void FromStackItem(StackItem stackItem) diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 2de9e54591..b7f11beb29 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -6,12 +6,34 @@ namespace Neo.SmartContract.Native { + /// + /// A native contract that manages the system policies. + /// public sealed class PolicyContract : NativeContract { + /// + /// The default execution fee factor. + /// public const uint DefaultExecFeeFactor = 30; + + /// + /// The default storage price. + /// public const uint DefaultStoragePrice = 100000; + + /// + /// The default network fee per byte of transactions. + /// public const uint DefaultFeePerByte = 1000; + + /// + /// The maximum execution fee factor that the committee can set. + /// public const uint MaxExecFeeFactor = 1000; + + /// + /// The maximum storage price that the committee can set. + /// public const uint MaxStoragePrice = 10000000; private const byte Prefix_BlockedAccount = 15; @@ -31,24 +53,45 @@ internal override ContractTask Initialize(ApplicationEngine engine) return ContractTask.CompletedTask; } + /// + /// Gets the network fee per transaction byte. + /// + /// The snapshot used to read data. + /// The network fee per transaction byte. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public long GetFeePerByte(DataCache snapshot) { return (long)(BigInteger)snapshot[CreateStorageKey(Prefix_FeePerByte)]; } + /// + /// Gets the execution fee factor. This is a multiplier that can be adjusted by the committee to adjust the system fees for transactions. + /// + /// The snapshot used to read data. + /// The execution fee factor. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public uint GetExecFeeFactor(DataCache snapshot) { return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_ExecFeeFactor)]; } + /// + /// Gets the storage price. + /// + /// The snapshot used to read data. + /// The storage price. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public uint GetStoragePrice(DataCache snapshot) { return (uint)(BigInteger)snapshot[CreateStorageKey(Prefix_StoragePrice)]; } + /// + /// Determines whether the specified account is blocked. + /// + /// The snapshot used to read data. + /// The account to be checked. + /// if the account is blocked; otherwise, . [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public bool IsBlocked(DataCache snapshot, UInt160 account) { diff --git a/src/neo/SmartContract/Native/RecordType.cs b/src/neo/SmartContract/Native/RecordType.cs index 9ecf067c61..086345c2b9 100644 --- a/src/neo/SmartContract/Native/RecordType.cs +++ b/src/neo/SmartContract/Native/RecordType.cs @@ -1,15 +1,36 @@ namespace Neo.SmartContract.Native { + /// + /// Represents the type of a name record. + /// public enum RecordType : byte { #region [RFC 1035](https://tools.ietf.org/html/rfc1035) + + /// + /// Address record. + /// A = 1, + + /// + /// Canonical name record. + /// CNAME = 5, + + /// + /// Text record. + /// TXT = 16, + #endregion #region [RFC 3596](https://tools.ietf.org/html/rfc3596) + + /// + /// IPv6 address record. + /// AAAA = 28, + #endregion } } diff --git a/src/neo/SmartContract/Native/Role.cs b/src/neo/SmartContract/Native/Role.cs index 59a927e8a0..432cb9ae17 100644 --- a/src/neo/SmartContract/Native/Role.cs +++ b/src/neo/SmartContract/Native/Role.cs @@ -1,9 +1,23 @@ namespace Neo.SmartContract.Native { + /// + /// Represents the roles in the NEO system. + /// public enum Role : byte { + /// + /// The validators of state. Used to generate and sign the state root. + /// StateValidator = 4, + + /// + /// The nodes used to process Oracle requests. + /// Oracle = 8, + + /// + /// NeoFS Alphabet nodes. + /// NeoFSAlphabetNode = 16 } } diff --git a/src/neo/SmartContract/Native/RoleManagement.cs b/src/neo/SmartContract/Native/RoleManagement.cs index 0ca1bbef44..4a1b3bd300 100644 --- a/src/neo/SmartContract/Native/RoleManagement.cs +++ b/src/neo/SmartContract/Native/RoleManagement.cs @@ -10,12 +10,22 @@ namespace Neo.SmartContract.Native { + /// + /// A native contract for managing roles in NEO system. + /// public sealed class RoleManagement : NativeContract { internal RoleManagement() { } + /// + /// Gets the list of nodes for the specified role. + /// + /// The snapshot used to read data. + /// The type of the role. + /// The index of the block to be queried. + /// The public keys of the nodes. [ContractMethod(CpuFee = 1 << 15, RequiredCallFlags = CallFlags.ReadStates)] public ECPoint[] GetDesignatedByRole(DataCache snapshot, Role role, uint index) { @@ -34,7 +44,7 @@ public ECPoint[] GetDesignatedByRole(DataCache snapshot, Role role, uint index) private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] nodes) { if (nodes.Length == 0 || nodes.Length > 32) - throw new ArgumentException(); + throw new ArgumentException(null, nameof(nodes)); if (!Enum.IsDefined(typeof(Role), role)) throw new ArgumentOutOfRangeException(nameof(role)); if (!CheckCommittee(engine)) @@ -45,7 +55,7 @@ private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] node var key = CreateStorageKey((byte)role).AddBigEndian(index); if (engine.Snapshot.Contains(key)) throw new InvalidOperationException(); - NodeList list = new NodeList(); + NodeList list = new(); list.AddRange(nodes); list.Sort(); engine.Snapshot.Add(key, new StorageItem(list)); diff --git a/src/neo/SmartContract/Native/StdLib.cs b/src/neo/SmartContract/Native/StdLib.cs index 96d270c6a2..c3b6dbaeb7 100644 --- a/src/neo/SmartContract/Native/StdLib.cs +++ b/src/neo/SmartContract/Native/StdLib.cs @@ -9,6 +9,9 @@ namespace Neo.SmartContract.Native { + /// + /// A native contract library that provides useful functions. + /// public sealed class StdLib : NativeContract { internal StdLib() { } @@ -37,6 +40,12 @@ private static StackItem JsonDeserialize(ApplicationEngine engine, byte[] json) return JsonSerializer.Deserialize(JObject.Parse(json, 10), engine.ReferenceCounter); } + /// + /// Converts an integer to a . + /// + /// The integer to convert. + /// The base of the integer. Only support 10 and 16. + /// The converted . [ContractMethod(CpuFee = 1 << 12)] public static string Itoa(BigInteger value, int @base) { @@ -48,6 +57,12 @@ public static string Itoa(BigInteger value, int @base) }; } + /// + /// Converts a to an integer. + /// + /// The to convert. + /// The base of the integer. Only support 10 and 16. + /// The converted integer. [ContractMethod(CpuFee = 1 << 12)] public static BigInteger Atoi(string value, int @base) { @@ -59,24 +74,44 @@ public static BigInteger Atoi(string value, int @base) }; } + /// + /// Encodes a byte array into a base64 . + /// + /// The byte array to be encoded. + /// The encoded . [ContractMethod(CpuFee = 1 << 12)] public static string Base64Encode(byte[] data) { return Convert.ToBase64String(data); } + /// + /// Decodes a byte array from a base64 . + /// + /// The base64 . + /// The decoded byte array. [ContractMethod(CpuFee = 1 << 12)] public static byte[] Base64Decode(string s) { return Convert.FromBase64String(s); } + /// + /// Encodes a byte array into a base58 . + /// + /// The byte array to be encoded. + /// The encoded . [ContractMethod(CpuFee = 1 << 12)] public static string Base58Encode(byte[] data) { return Base58.Encode(data); } + /// + /// Decodes a byte array from a base58 . + /// + /// The base58 . + /// The decoded byte array. [ContractMethod(CpuFee = 1 << 12)] public static byte[] Base58Decode(string s) { diff --git a/src/neo/SmartContract/Native/TransactionState.cs b/src/neo/SmartContract/Native/TransactionState.cs index 671146a6de..6353a57fc7 100644 --- a/src/neo/SmartContract/Native/TransactionState.cs +++ b/src/neo/SmartContract/Native/TransactionState.cs @@ -5,9 +5,19 @@ namespace Neo.SmartContract.Native { + /// + /// Represents a transaction that has been included in a block. + /// public class TransactionState : IInteroperable { + /// + /// The block containing this transaction. + /// public uint BlockIndex; + + /// + /// The transaction. + /// public Transaction Transaction; private StackItem _rawTransaction; diff --git a/src/neo/SmartContract/Native/TrimmedBlock.cs b/src/neo/SmartContract/Native/TrimmedBlock.cs index d0d5de8aec..88774a84a0 100644 --- a/src/neo/SmartContract/Native/TrimmedBlock.cs +++ b/src/neo/SmartContract/Native/TrimmedBlock.cs @@ -8,12 +8,29 @@ namespace Neo.SmartContract.Native { + /// + /// Represents a block which the transactions are trimmed. + /// public class TrimmedBlock : IInteroperable, ISerializable { + /// + /// The header of the block. + /// public Header Header; + + /// + /// The hashes of the transactions of the block. + /// public UInt256[] Hashes; + /// + /// The hash of the block. + /// public UInt256 Hash => Header.Hash; + + /// + /// The index of the block. + /// public uint Index => Header.Index; public int Size => Header.Size + Hashes.GetVarSize(); diff --git a/src/neo/SmartContract/NefFile.cs b/src/neo/SmartContract/NefFile.cs index 7a4ee2e850..e7c64286d5 100644 --- a/src/neo/SmartContract/NefFile.cs +++ b/src/neo/SmartContract/NefFile.cs @@ -8,22 +8,25 @@ namespace Neo.SmartContract { + /* + ┌───────────────────────────────────────────────────────────────────────┐ + │ NEO Executable Format 3 (NEF3) │ + ├──────────┬───────────────┬────────────────────────────────────────────┤ + │ Field │ Type │ Comment │ + ├──────────┼───────────────┼────────────────────────────────────────────┤ + │ Magic │ uint32 │ Magic header │ + │ Compiler │ byte[64] │ Compiler name and version │ + ├──────────┼───────────────┼────────────────────────────────────────────┤ + │ Reserve │ byte[2] │ Reserved for future extensions. Must be 0. │ + │ Tokens │ MethodToken[] │ Method tokens. │ + │ Reserve │ byte[2] │ Reserved for future extensions. Must be 0. │ + │ Script │ byte[] │ Var bytes for the payload │ + ├──────────┼───────────────┼────────────────────────────────────────────┤ + │ Checksum │ uint32 │ First four bytes of double SHA256 hash │ + └──────────┴───────────────┴────────────────────────────────────────────┘ + */ /// - /// ┌───────────────────────────────────────────────────────────────────────┐ - /// │ NEO Executable Format 3 (NEF3) │ - /// ├──────────┬───────────────┬────────────────────────────────────────────┤ - /// │ Field │ Type │ Comment │ - /// ├──────────┼───────────────┼────────────────────────────────────────────┤ - /// │ Magic │ uint32 │ Magic header │ - /// │ Compiler │ byte[64] │ Compiler name and version │ - /// ├──────────┼───────────────┼────────────────────────────────────────────┤ - /// │ Reserve │ byte[2] │ Reserved for future extensions. Must be 0. │ - /// │ Tokens │ MethodToken[] │ Method tokens. │ - /// │ Reserve │ byte[2] │ Reserved for future extensions. Must be 0. │ - /// │ Script │ byte[] │ Var bytes for the payload │ - /// ├──────────┼───────────────┼────────────────────────────────────────────┤ - /// │ Checksum │ uint32 │ First four bytes of double SHA256 hash │ - /// └──────────┴───────────────┴────────────────────────────────────────────┘ + /// Represents the structure of NEO Executable Format. /// public class NefFile : ISerializable { @@ -33,25 +36,28 @@ public class NefFile : ISerializable private const uint Magic = 0x3346454E; /// - /// Compiler name and version + /// The name and version of the compiler that generated this nef file. /// public string Compiler { get; set; } /// - /// Method tokens + /// The methods that to be called statically. /// public MethodToken[] Tokens { get; set; } /// - /// Script + /// The script of the contract. /// public byte[] Script { get; set; } /// - /// Checksum + /// The checksum of the nef file. /// public uint CheckSum { get; set; } + /// + /// The maximum length of the script. + /// public const int MaxScriptLength = 512 * 1024; private const int HeaderSize = @@ -102,15 +108,19 @@ public void Deserialize(BinaryReader reader) } /// - /// Compute checksum for a file + /// Computes the checksum for the specified nef file. /// - /// File - /// Return checksum + /// The specified nef file. + /// The checksum of the nef file. public static uint ComputeChecksum(NefFile file) { return BinaryPrimitives.ReadUInt32LittleEndian(Crypto.Hash256(file.ToArray().AsSpan(..^sizeof(uint)))); } + /// + /// Converts the nef file to a JSON object. + /// + /// The nef file represented by a JSON object. public JObject ToJson() { return new JObject diff --git a/src/neo/SmartContract/NotifyEventArgs.cs b/src/neo/SmartContract/NotifyEventArgs.cs index 532c5080e6..9a7bbb36c4 100644 --- a/src/neo/SmartContract/NotifyEventArgs.cs +++ b/src/neo/SmartContract/NotifyEventArgs.cs @@ -7,13 +7,38 @@ namespace Neo.SmartContract { + /// + /// The of . + /// public class NotifyEventArgs : EventArgs, IInteroperable { + /// + /// The container that containing the executed script. + /// public IVerifiable ScriptContainer { get; } + + /// + /// The script hash of the contract that sends the log. + /// public UInt160 ScriptHash { get; } + + /// + /// The name of the event. + /// public string EventName { get; } + + /// + /// The arguments of the event. + /// public Array State { get; } + /// + /// Initializes a new instance of the class. + /// + /// The container that containing the executed script. + /// The script hash of the contract that sends the log. + /// The name of the event. + /// The arguments of the event. public NotifyEventArgs(IVerifiable container, UInt160 script_hash, string eventName, Array state) { this.ScriptContainer = container; diff --git a/src/neo/SmartContract/StorageContext.cs b/src/neo/SmartContract/StorageContext.cs index f9272269c8..e033b2fce1 100644 --- a/src/neo/SmartContract/StorageContext.cs +++ b/src/neo/SmartContract/StorageContext.cs @@ -1,8 +1,18 @@ namespace Neo.SmartContract { + /// + /// The storage context used to read and write data in smart contracts. + /// public class StorageContext { + /// + /// The id of the contract that owns the context. + /// public int Id; + + /// + /// Indicates whether the context is read-only. + /// public bool IsReadOnly; } } diff --git a/src/neo/SmartContract/StorageItem.cs b/src/neo/SmartContract/StorageItem.cs index add08f943a..690ec27bc7 100644 --- a/src/neo/SmartContract/StorageItem.cs +++ b/src/neo/SmartContract/StorageItem.cs @@ -7,6 +7,9 @@ namespace Neo.SmartContract { + /// + /// Represents the values in contract storage. + /// public class StorageItem : ISerializable { private byte[] value; @@ -14,6 +17,9 @@ public class StorageItem : ISerializable public int Size => Value.GetVarSize(); + /// + /// The byte array value of the . + /// public byte[] Value { get @@ -34,28 +40,51 @@ public byte[] Value } } + /// + /// Initializes a new instance of the class. + /// public StorageItem() { } + /// + /// Initializes a new instance of the class. + /// + /// The byte array value of the . public StorageItem(byte[] value) { this.value = value; } + /// + /// Initializes a new instance of the class. + /// + /// The integer value of the . public StorageItem(BigInteger value) { this.cache = value; } + /// + /// Initializes a new instance of the class. + /// + /// The value of the . public StorageItem(IInteroperable interoperable) { this.cache = interoperable; } + /// + /// Increases the integer value in the store by the specified value. + /// + /// The integer to add. public void Add(BigInteger integer) { Set(this + integer); } + /// + /// Creates a new instance of with the same value as this instance. + /// + /// The created . public StorageItem Clone() { return new StorageItem @@ -69,11 +98,20 @@ public void Deserialize(BinaryReader reader) Value = reader.ReadBytes((int)(reader.BaseStream.Length)); } + /// + /// Copies the value of another instance to this instance. + /// + /// The instance to be copied. public void FromReplica(StorageItem replica) { Value = replica.Value; } + /// + /// Gets an from the storage. + /// + /// The type of the . + /// The in the storage. public T GetInteroperable() where T : IInteroperable, new() { if (cache is null) @@ -86,6 +124,11 @@ public void FromReplica(StorageItem replica) return (T)cache; } + /// + /// Gets a list of from the storage. + /// + /// The type of the . + /// The list of the . public List GetSerializableList() where T : ISerializable, new() { cache ??= new List(value.AsSerializableArray()); @@ -98,6 +141,10 @@ public void Serialize(BinaryWriter writer) writer.Write(Value); } + /// + /// Sets the integer value of the storage. + /// + /// The integer value to set. public void Set(BigInteger integer) { cache = integer; diff --git a/src/neo/SmartContract/StorageKey.cs b/src/neo/SmartContract/StorageKey.cs index 99f4c0387c..74580f20c9 100644 --- a/src/neo/SmartContract/StorageKey.cs +++ b/src/neo/SmartContract/StorageKey.cs @@ -6,13 +6,29 @@ namespace Neo.SmartContract { + /// + /// Represents the keys in contract storage. + /// public class StorageKey : IEquatable, ISerializable { + /// + /// The id of the contract. + /// public int Id; + + /// + /// The key of the storage entry. + /// public byte[] Key; int ISerializable.Size => sizeof(int) + Key.Length; + /// + /// Creates a search prefix for a contract. + /// + /// The id of the contract. + /// The prefix of the keys to search. + /// The created search prefix. public static byte[] CreateSearchPrefix(int id, ReadOnlySpan prefix) { byte[] buffer = new byte[sizeof(int) + prefix.Length]; @@ -40,7 +56,7 @@ public bool Equals(StorageKey other) public override bool Equals(object obj) { - if (!(obj is StorageKey other)) return false; + if (obj is not StorageKey other) return false; return Equals(other); } diff --git a/src/neo/SmartContract/TriggerType.cs b/src/neo/SmartContract/TriggerType.cs index 90b231d502..04b93ffc76 100644 --- a/src/neo/SmartContract/TriggerType.cs +++ b/src/neo/SmartContract/TriggerType.cs @@ -1,30 +1,42 @@ +using Neo.Network.P2P.Payloads; using System; namespace Neo.SmartContract { + /// + /// Represents the triggers for running smart contracts. + /// [Flags] public enum TriggerType : byte { + /// + /// Indicate that the contract is triggered by the system to execute the OnPersist method of the native contracts. + /// OnPersist = 0x01, + + /// + /// Indicate that the contract is triggered by the system to execute the PostPersist method of the native contracts. + /// PostPersist = 0x02, + /// - /// The verification trigger indicates that the contract is being invoked as a verification function. - /// The verification function can accept multiple parameters, and should return a boolean value that indicates the validity of the transaction or block. - /// The entry point of the contract will be invoked if the contract is triggered by Verification: - /// main(...); - /// The entry point of the contract must be able to handle this type of invocation. + /// Indicates that the contract is triggered by the verification of a . /// Verification = 0x20, + /// - /// The application trigger indicates that the contract is being invoked as an application function. - /// The application function can accept multiple parameters, change the states of the blockchain, and return any type of value. - /// The contract can have any form of entry point, but we recommend that all contracts should have the following entry point: - /// public byte[] main(string operation, params object[] args) - /// The functions can be invoked by creating an InvocationTransaction. + /// Indicates that the contract is triggered by the execution of transactions. /// Application = 0x40, + /// + /// The combination of all system triggers. + /// System = OnPersist | PostPersist, + + /// + /// The combination of all triggers. + /// All = OnPersist | PostPersist | Verification | Application } } diff --git a/src/neo/TimeProvider.cs b/src/neo/TimeProvider.cs index b3f1f0472c..8eb8817f67 100644 --- a/src/neo/TimeProvider.cs +++ b/src/neo/TimeProvider.cs @@ -2,11 +2,21 @@ namespace Neo { + /// + /// The time provider for the NEO system. + /// public class TimeProvider { - private static readonly TimeProvider Default = new TimeProvider(); + private static readonly TimeProvider Default = new(); + /// + /// The currently used instance. + /// public static TimeProvider Current { get; internal set; } = Default; + + /// + /// Gets the current time expressed as the Coordinated Universal Time (UTC). + /// public virtual DateTime UtcNow => DateTime.UtcNow; internal static void ResetToDefault() diff --git a/src/neo/UInt160.cs b/src/neo/UInt160.cs index a80ebc4a8f..344edcabbb 100644 --- a/src/neo/UInt160.cs +++ b/src/neo/UInt160.cs @@ -7,14 +7,20 @@ namespace Neo { /// - /// This class stores a 160 bit unsigned int, represented as a 20-byte little-endian byte array - /// It is composed by ulong(64) + ulong(64) + uint(32) = UInt160(160) + /// Represents a 160-bit unsigned integer. /// [StructLayout(LayoutKind.Explicit, Size = 20)] public class UInt160 : IComparable, IEquatable, ISerializable { + /// + /// The length of values. + /// public const int Length = 20; - public static readonly UInt160 Zero = new UInt160(); + + /// + /// Represents 0. + /// + public static readonly UInt160 Zero = new(); [FieldOffset(0)] private ulong value1; [FieldOffset(8)] private ulong value2; @@ -22,23 +28,26 @@ public class UInt160 : IComparable, IEquatable, ISerializable public int Size => Length; + /// + /// Initializes a new instance of the class. + /// public UInt160() { } + /// + /// Initializes a new instance of the class. + /// + /// The value of the . public unsafe UInt160(ReadOnlySpan value) { fixed (ulong* p = &value1) { - Span dst = new Span(p, Length); + Span dst = new(p, Length); value[..Length].CopyTo(dst); } } - /// - /// Method CompareTo returns 1 if this UInt160 is bigger than other UInt160; -1 if it's smaller; 0 if it's equals - /// Example: assume this is 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4, this.CompareTo(02ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) returns 1 - /// public int CompareTo(UInt160 other) { int result = value3.CompareTo(other.value3); @@ -55,18 +64,12 @@ public void Deserialize(BinaryReader reader) value3 = reader.ReadUInt32(); } - /// - /// Method Equals returns true if objects are equal, false otherwise - /// public override bool Equals(object obj) { if (ReferenceEquals(obj, this)) return true; return Equals(obj as UInt160); } - /// - /// Method Equals returns true if objects are equal, false otherwise - /// public bool Equals(UInt160 other) { if (other is null) return false; @@ -81,9 +84,11 @@ public override int GetHashCode() } /// - /// Method Parse receives a big-endian hex string and stores as a UInt160 little-endian 20-bytes array - /// Example: Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01") should create UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 + /// Parses an from the specified . /// + /// An represented by a . + /// The parsed . + /// is not in the correct format. public static UInt160 Parse(string value) { if (!TryParse(value, out var result)) throw new FormatException(); @@ -103,9 +108,11 @@ public override string ToString() } /// - /// Method TryParse tries to parse a big-endian hex string and store it as a UInt160 little-endian 20-bytes array - /// Example: TryParse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01", result) should create result UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 + /// Parses an from the specified . /// + /// An represented by a . + /// The parsed . + /// if an is successfully parsed; otherwise, . public static bool TryParse(string s, out UInt160 result) { if (s == null) @@ -114,7 +121,7 @@ public static bool TryParse(string s, out UInt160 result) return false; } if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) - s = s.Substring(2); + s = s[2..]; if (s.Length != Length * 2) { result = null; @@ -131,9 +138,6 @@ public static bool TryParse(string s, out UInt160 result) return true; } - /// - /// Returns true if left UInt160 is equals to right UInt160 - /// public static bool operator ==(UInt160 left, UInt160 right) { if (ReferenceEquals(left, right)) return true; @@ -141,45 +145,26 @@ public static bool TryParse(string s, out UInt160 result) return left.Equals(right); } - /// - /// Returns true if left UIntBase is not equals to right UIntBase - /// public static bool operator !=(UInt160 left, UInt160 right) { return !(left == right); } - /// - /// Operator > returns true if left UInt160 is bigger than right UInt160 - /// Example: UInt160(01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) > UInt160 (02ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) is true - /// public static bool operator >(UInt160 left, UInt160 right) { return left.CompareTo(right) > 0; } - /// - /// Operator > returns true if left UInt160 is bigger or equals to right UInt160 - /// Example: UInt160(01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) >= UInt160 (02ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) is true - /// public static bool operator >=(UInt160 left, UInt160 right) { return left.CompareTo(right) >= 0; } - /// - /// Operator > returns true if left UInt160 is less than right UInt160 - /// Example: UInt160(02ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) < UInt160 (01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) is true - /// public static bool operator <(UInt160 left, UInt160 right) { return left.CompareTo(right) < 0; } - /// - /// Operator > returns true if left UInt160 is less or equals to right UInt160 - /// Example: UInt160(02ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) < UInt160 (01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) is true - /// public static bool operator <=(UInt160 left, UInt160 right) { return left.CompareTo(right) <= 0; diff --git a/src/neo/UInt256.cs b/src/neo/UInt256.cs index 920a293551..72ac18b017 100644 --- a/src/neo/UInt256.cs +++ b/src/neo/UInt256.cs @@ -7,14 +7,20 @@ namespace Neo { /// - /// This class stores a 256 bit unsigned int, represented as a 32-byte little-endian byte array - /// Composed by ulong(64) + ulong(64) + ulong(64) + ulong(64) = UInt256(256) + /// Represents a 256-bit unsigned integer. /// [StructLayout(LayoutKind.Explicit, Size = 32)] public class UInt256 : IComparable, IEquatable, ISerializable { + /// + /// The length of values. + /// public const int Length = 32; - public static readonly UInt256 Zero = new UInt256(); + + /// + /// Represents 0. + /// + public static readonly UInt256 Zero = new(); [FieldOffset(0)] private ulong value1; [FieldOffset(8)] private ulong value2; @@ -23,23 +29,26 @@ public class UInt256 : IComparable, IEquatable, ISerializable public int Size => Length; + /// + /// Initializes a new instance of the class. + /// public UInt256() { } + /// + /// Initializes a new instance of the class. + /// + /// The value of the . public unsafe UInt256(ReadOnlySpan value) { fixed (ulong* p = &value1) { - Span dst = new Span(p, Length); + Span dst = new(p, Length); value[..Length].CopyTo(dst); } } - /// - /// Method CompareTo returns 1 if this UInt256 is bigger than other UInt256; -1 if it's smaller; 0 if it's equals - /// Example: assume this is 01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4, this.CompareTo(02ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) returns 1 - /// public int CompareTo(UInt256 other) { int result = value4.CompareTo(other.value4); @@ -59,18 +68,12 @@ public void Deserialize(BinaryReader reader) value4 = reader.ReadUInt64(); } - /// - /// Method Equals returns true if objects are equal, false otherwise - /// public override bool Equals(object obj) { if (ReferenceEquals(obj, this)) return true; return Equals(obj as UInt256); } - /// - /// Method Equals returns true if objects are equal, false otherwise - /// public bool Equals(UInt256 other) { if (other is null) return false; @@ -86,9 +89,11 @@ public override int GetHashCode() } /// - /// Method Parse receives a big-endian hex string and stores as a UInt256 little-endian 32-bytes array - /// Example: Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01") should create UInt256 01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 + /// Parses an from the specified . /// + /// An represented by a . + /// The parsed . + /// is not in the correct format. public static UInt256 Parse(string value) { if (!TryParse(value, out var result)) throw new FormatException(); @@ -109,9 +114,11 @@ public override string ToString() } /// - /// Method TryParse tries to parse a big-endian hex string and store it as a UInt256 little-endian 32-bytes array - /// Example: TryParse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01", result) should create result UInt256 01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 + /// Parses an from the specified . /// + /// An represented by a . + /// The parsed . + /// if an is successfully parsed; otherwise, . public static bool TryParse(string s, out UInt256 result) { if (s == null) @@ -120,7 +127,7 @@ public static bool TryParse(string s, out UInt256 result) return false; } if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) - s = s.Substring(2); + s = s[2..]; if (s.Length != Length * 2) { result = null; @@ -137,9 +144,6 @@ public static bool TryParse(string s, out UInt256 result) return true; } - /// - /// Returns true if left UInt256 is equals to right UInt256 - /// public static bool operator ==(UInt256 left, UInt256 right) { if (ReferenceEquals(left, right)) return true; @@ -147,45 +151,26 @@ public static bool TryParse(string s, out UInt256 result) return left.Equals(right); } - /// - /// Returns true if left UIntBase is not equals to right UIntBase - /// public static bool operator !=(UInt256 left, UInt256 right) { return !(left == right); } - /// - /// Operator > returns true if left UInt256 is bigger than right UInt256 - /// Example: UInt256(01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) > UInt256(02ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) is true - /// public static bool operator >(UInt256 left, UInt256 right) { return left.CompareTo(right) > 0; } - /// - /// Operator >= returns true if left UInt256 is bigger or equals to right UInt256 - /// Example: UInt256(01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) >= UInt256(02ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) is true - /// public static bool operator >=(UInt256 left, UInt256 right) { return left.CompareTo(right) >= 0; } - /// - /// Operator < returns true if left UInt256 is less than right UInt256 - /// Example: UInt256(02ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) < UInt256(01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) is true - /// public static bool operator <(UInt256 left, UInt256 right) { return left.CompareTo(right) < 0; } - /// - /// Operator <= returns true if left UInt256 is less or equals to right UInt256 - /// Example: UInt256(02ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a3) <= UInt256(01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4) is true - /// public static bool operator <=(UInt256 left, UInt256 right) { return left.CompareTo(right) <= 0; diff --git a/src/neo/Utility.cs b/src/neo/Utility.cs index 7570b93680..a43c0a5ba3 100644 --- a/src/neo/Utility.cs +++ b/src/neo/Utility.cs @@ -5,6 +5,9 @@ namespace Neo { + /// + /// A utility class that provides common functions. + /// public static class Utility { internal class Logger : ReceiveActor @@ -16,6 +19,9 @@ public Logger() } } + /// + /// A strict UTF8 encoding used in NEO system. + /// public static Encoding StrictUTF8 { get; } static Utility() @@ -25,6 +31,12 @@ static Utility() StrictUTF8.EncoderFallback = EncoderFallback.ExceptionFallback; } + /// + /// Writes a log. + /// + /// The source of the log. Used to identify the producer of the log. + /// The level of the log. + /// The message of the log. public static void Log(string source, LogLevel level, object message) { foreach (ILogPlugin plugin in Plugin.Loggers) diff --git a/src/neo/VM/Helper.cs b/src/neo/VM/Helper.cs index 001e044f8e..7eae7e5d3a 100644 --- a/src/neo/VM/Helper.cs +++ b/src/neo/VM/Helper.cs @@ -13,191 +13,262 @@ namespace Neo.VM { + /// + /// A helper class related to NeoVM. + /// public static class Helper { - public static ScriptBuilder CreateArray(this ScriptBuilder sb, IReadOnlyList list = null) + /// + /// Emits the opcodes for creating an array. + /// + /// The type of the elements of the array. + /// The to be used. + /// The elements of the array. + /// The same instance as . + public static ScriptBuilder CreateArray(this ScriptBuilder builder, IReadOnlyList list = null) { if (list is null || list.Count == 0) - return sb.Emit(OpCode.NEWARRAY0); + return builder.Emit(OpCode.NEWARRAY0); for (int i = list.Count - 1; i >= 0; i--) - sb.EmitPush(list[i]); - sb.EmitPush(list.Count); - return sb.Emit(OpCode.PACK); + builder.EmitPush(list[i]); + builder.EmitPush(list.Count); + return builder.Emit(OpCode.PACK); } - public static ScriptBuilder CreateMap(this ScriptBuilder sb, IEnumerable> map = null) + /// + /// Emits the opcodes for creating a map. + /// + /// The type of the key of the map. + /// The type of the value of the map. + /// The to be used. + /// The key/value pairs of the map. + /// The same instance as . + public static ScriptBuilder CreateMap(this ScriptBuilder builder, IEnumerable> map = null) { - sb.Emit(OpCode.NEWMAP); + builder.Emit(OpCode.NEWMAP); if (map != null) foreach (var p in map) { - sb.Emit(OpCode.DUP); - sb.EmitPush(p.Key); - sb.EmitPush(p.Value); - sb.Emit(OpCode.SETITEM); + builder.Emit(OpCode.DUP); + builder.EmitPush(p.Key); + builder.EmitPush(p.Value); + builder.Emit(OpCode.SETITEM); } - return sb; + return builder; } - public static ScriptBuilder Emit(this ScriptBuilder sb, params OpCode[] ops) + /// + /// Emits the specified opcodes. + /// + /// The to be used. + /// The opcodes to emit. + /// The same instance as . + public static ScriptBuilder Emit(this ScriptBuilder builder, params OpCode[] ops) { foreach (OpCode op in ops) - sb.Emit(op); - return sb; + builder.Emit(op); + return builder; } - public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, params object[] args) + /// + /// Emits the opcodes for calling a contract dynamically. + /// + /// The to be used. + /// The hash of the contract to be called. + /// The method to be called in the contract. + /// The arguments for calling the contract. + /// The same instance as . + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder builder, UInt160 scriptHash, string method, params object[] args) { - return EmitDynamicCall(sb, scriptHash, operation, CallFlags.All, args); + return EmitDynamicCall(builder, scriptHash, method, CallFlags.All, args); } - public static ScriptBuilder EmitDynamicCall(this ScriptBuilder sb, UInt160 scriptHash, string operation, CallFlags flags, params object[] args) + /// + /// Emits the opcodes for calling a contract dynamically. + /// + /// The to be used. + /// The hash of the contract to be called. + /// The method to be called in the contract. + /// The for calling the contract. + /// The arguments for calling the contract. + /// The same instance as . + public static ScriptBuilder EmitDynamicCall(this ScriptBuilder builder, UInt160 scriptHash, string method, CallFlags flags, params object[] args) { - sb.CreateArray(args); - sb.EmitPush(flags); - sb.EmitPush(operation); - sb.EmitPush(scriptHash); - sb.EmitSysCall(ApplicationEngine.System_Contract_Call); - return sb; + builder.CreateArray(args); + builder.EmitPush(flags); + builder.EmitPush(method); + builder.EmitPush(scriptHash); + builder.EmitSysCall(ApplicationEngine.System_Contract_Call); + return builder; } - public static ScriptBuilder EmitPush(this ScriptBuilder sb, ISerializable data) + /// + /// Emits the opcodes for pushing the specified data onto the stack. + /// + /// The to be used. + /// The data to be pushed. + /// The same instance as . + public static ScriptBuilder EmitPush(this ScriptBuilder builder, ISerializable data) { - return sb.EmitPush(data.ToArray()); + return builder.EmitPush(data.ToArray()); } - public static ScriptBuilder EmitPush(this ScriptBuilder sb, ContractParameter parameter) + /// + /// Emits the opcodes for pushing the specified data onto the stack. + /// + /// The to be used. + /// The data to be pushed. + /// The same instance as . + public static ScriptBuilder EmitPush(this ScriptBuilder builder, ContractParameter parameter) { if (parameter.Value is null) - sb.Emit(OpCode.PUSHNULL); + builder.Emit(OpCode.PUSHNULL); else switch (parameter.Type) { case ContractParameterType.Signature: case ContractParameterType.ByteArray: - sb.EmitPush((byte[])parameter.Value); + builder.EmitPush((byte[])parameter.Value); break; case ContractParameterType.Boolean: - sb.EmitPush((bool)parameter.Value); + builder.EmitPush((bool)parameter.Value); break; case ContractParameterType.Integer: if (parameter.Value is BigInteger bi) - sb.EmitPush(bi); + builder.EmitPush(bi); else - sb.EmitPush((BigInteger)typeof(BigInteger).GetConstructor(new[] { parameter.Value.GetType() }).Invoke(new[] { parameter.Value })); + builder.EmitPush((BigInteger)typeof(BigInteger).GetConstructor(new[] { parameter.Value.GetType() }).Invoke(new[] { parameter.Value })); break; case ContractParameterType.Hash160: - sb.EmitPush((UInt160)parameter.Value); + builder.EmitPush((UInt160)parameter.Value); break; case ContractParameterType.Hash256: - sb.EmitPush((UInt256)parameter.Value); + builder.EmitPush((UInt256)parameter.Value); break; case ContractParameterType.PublicKey: - sb.EmitPush((ECPoint)parameter.Value); + builder.EmitPush((ECPoint)parameter.Value); break; case ContractParameterType.String: - sb.EmitPush((string)parameter.Value); + builder.EmitPush((string)parameter.Value); break; case ContractParameterType.Array: { IList parameters = (IList)parameter.Value; for (int i = parameters.Count - 1; i >= 0; i--) - sb.EmitPush(parameters[i]); - sb.EmitPush(parameters.Count); - sb.Emit(OpCode.PACK); + builder.EmitPush(parameters[i]); + builder.EmitPush(parameters.Count); + builder.Emit(OpCode.PACK); } break; case ContractParameterType.Map: { var pairs = (IList>)parameter.Value; - sb.CreateMap(pairs); + builder.CreateMap(pairs); } break; default: - throw new ArgumentException(); + throw new ArgumentException(null, nameof(parameter)); } - return sb; + return builder; } - public static ScriptBuilder EmitPush(this ScriptBuilder sb, object obj) + /// + /// Emits the opcodes for pushing the specified data onto the stack. + /// + /// The to be used. + /// The data to be pushed. + /// The same instance as . + public static ScriptBuilder EmitPush(this ScriptBuilder builder, object obj) { switch (obj) { case bool data: - sb.EmitPush(data); + builder.EmitPush(data); break; case byte[] data: - sb.EmitPush(data); + builder.EmitPush(data); break; case string data: - sb.EmitPush(data); + builder.EmitPush(data); break; case BigInteger data: - sb.EmitPush(data); + builder.EmitPush(data); break; case ISerializable data: - sb.EmitPush(data); + builder.EmitPush(data); break; case sbyte data: - sb.EmitPush(data); + builder.EmitPush(data); break; case byte data: - sb.EmitPush(data); + builder.EmitPush(data); break; case short data: - sb.EmitPush(data); + builder.EmitPush(data); break; case ushort data: - sb.EmitPush(data); + builder.EmitPush(data); break; case int data: - sb.EmitPush(data); + builder.EmitPush(data); break; case uint data: - sb.EmitPush(data); + builder.EmitPush(data); break; case long data: - sb.EmitPush(data); + builder.EmitPush(data); break; case ulong data: - sb.EmitPush(data); + builder.EmitPush(data); break; case Enum data: - sb.EmitPush(BigInteger.Parse(data.ToString("d"))); + builder.EmitPush(BigInteger.Parse(data.ToString("d"))); break; case ContractParameter data: - sb.EmitPush(data); + builder.EmitPush(data); break; case null: - sb.Emit(OpCode.PUSHNULL); + builder.Emit(OpCode.PUSHNULL); break; default: - throw new ArgumentException(); + throw new ArgumentException(null, nameof(obj)); } - return sb; + return builder; } - public static ScriptBuilder EmitSysCall(this ScriptBuilder sb, uint method, params object[] args) + /// + /// Emits the opcodes for invoking an interoperable service. + /// + /// The to be used. + /// The hash of the interoperable service. + /// The arguments for calling the interoperable service. + /// The same instance as . + public static ScriptBuilder EmitSysCall(this ScriptBuilder builder, uint method, params object[] args) { for (int i = args.Length - 1; i >= 0; i--) - EmitPush(sb, args[i]); - return sb.EmitSysCall(method); + EmitPush(builder, args[i]); + return builder.EmitSysCall(method); } /// - /// Generate scripts to call a specific method from a specific contract. + /// Generates the script for calling a contract dynamically. /// - /// contract script hash - /// contract operation - /// operation arguments - /// - public static byte[] MakeScript(this UInt160 scriptHash, string operation, params object[] args) + /// The hash of the contract to be called. + /// The method to be called in the contract. + /// The arguments for calling the contract. + /// The generated script. + public static byte[] MakeScript(this UInt160 scriptHash, string method, params object[] args) { - using ScriptBuilder sb = new ScriptBuilder(); - sb.EmitDynamicCall(scriptHash, operation, args); + using ScriptBuilder sb = new(); + sb.EmitDynamicCall(scriptHash, method, args); return sb.ToArray(); } + /// + /// Converts the to a JSON object. + /// + /// The to convert. + /// The represented by a JSON object. public static JObject ToJson(this StackItem item) { return ToJson(item, null); @@ -205,7 +276,7 @@ public static JObject ToJson(this StackItem item) private static JObject ToJson(StackItem item, HashSet context) { - JObject json = new JObject(); + JObject json = new(); json["type"] = item.Type; switch (item) { @@ -229,7 +300,7 @@ private static JObject ToJson(StackItem item, HashSet context) if (!context.Add(map)) throw new InvalidOperationException(); json["value"] = new JArray(map.Select(p => { - JObject item = new JObject(); + JObject item = new(); item["key"] = ToJson(p.Key, context); item["value"] = ToJson(p.Value, context); return item; @@ -242,6 +313,11 @@ private static JObject ToJson(StackItem item, HashSet context) return json; } + /// + /// Converts the to a . + /// + /// The to convert. + /// The converted . public static ContractParameter ToParameter(this StackItem item) { return ToParameter(item, null); @@ -249,7 +325,7 @@ public static ContractParameter ToParameter(this StackItem item) private static ContractParameter ToParameter(StackItem item, List<(StackItem, ContractParameter)> context) { - if (item is null) throw new ArgumentNullException(); + if (item is null) throw new ArgumentNullException(nameof(item)); ContractParameter parameter = null; switch (item) { @@ -316,6 +392,11 @@ private static ContractParameter ToParameter(StackItem item, List<(StackItem, Co return parameter; } + /// + /// Converts the to a . + /// + /// The to convert. + /// The converted . public static StackItem ToStackItem(this ContractParameter parameter) { return ToStackItem(parameter, null); @@ -323,7 +404,7 @@ public static StackItem ToStackItem(this ContractParameter parameter) private static StackItem ToStackItem(ContractParameter parameter, List<(StackItem, ContractParameter)> context) { - if (parameter is null) throw new ArgumentNullException(); + if (parameter is null) throw new ArgumentNullException(nameof(parameter)); if (parameter.Value is null) return StackItem.Null; StackItem stackItem = null; switch (parameter.Type) @@ -346,7 +427,7 @@ private static StackItem ToStackItem(ContractParameter parameter, List<(StackIte (stackItem, _) = context.FirstOrDefault(p => ReferenceEquals(p.Item2, parameter)); if (stackItem is null) { - Map map = new Map(); + Map map = new(); foreach (var pair in (IList>)parameter.Value) map[(PrimitiveType)ToStackItem(pair.Key, context)] = ToStackItem(pair.Value, context); stackItem = map; diff --git a/src/neo/Wallets/AssetDescriptor.cs b/src/neo/Wallets/AssetDescriptor.cs index fef54a2a15..baf3fa102a 100644 --- a/src/neo/Wallets/AssetDescriptor.cs +++ b/src/neo/Wallets/AssetDescriptor.cs @@ -6,20 +6,44 @@ namespace Neo.Wallets { + /// + /// Represents the descriptor of an asset. + /// public class AssetDescriptor { + /// + /// The id of the asset. + /// public UInt160 AssetId { get; } + + /// + /// The name of the asset. + /// public string AssetName { get; } + + /// + /// The symbol of the asset. + /// public string Symbol { get; } + + /// + /// The number of decimal places of the token. + /// public byte Decimals { get; } + /// + /// Initializes a new instance of the class. + /// + /// The snapshot used to read data. + /// The used by the . + /// The id of the asset. public AssetDescriptor(DataCache snapshot, ProtocolSettings settings, UInt160 asset_id) { var contract = NativeContract.ContractManagement.GetContract(snapshot, asset_id); if (contract is null) throw new ArgumentException(null, nameof(asset_id)); byte[] script; - using (ScriptBuilder sb = new ScriptBuilder()) + using (ScriptBuilder sb = new()) { sb.EmitDynamicCall(asset_id, "decimals", CallFlags.ReadOnly); sb.EmitDynamicCall(asset_id, "symbol", CallFlags.ReadOnly); diff --git a/src/neo/Wallets/Helper.cs b/src/neo/Wallets/Helper.cs index b24c99b112..3183a13600 100644 --- a/src/neo/Wallets/Helper.cs +++ b/src/neo/Wallets/Helper.cs @@ -6,13 +6,29 @@ namespace Neo.Wallets { + /// + /// A helper class related to wallets. + /// public static class Helper { + /// + /// Signs an with the specified private key. + /// + /// The to sign. + /// The private key to be used. + /// The magic number of the NEO network. + /// The signature for the . public static byte[] Sign(this IVerifiable verifiable, KeyPair key, uint magic) { return Crypto.Sign(verifiable.GetSignData(magic), key.PrivateKey, key.PublicKey.EncodePoint(false)[1..]); } + /// + /// Converts the specified script hash to an address. + /// + /// The script hash to convert. + /// The address version. + /// The converted address. public static string ToAddress(this UInt160 scriptHash, byte version) { Span data = stackalloc byte[21]; @@ -21,6 +37,12 @@ public static string ToAddress(this UInt160 scriptHash, byte version) return Base58.Base58CheckEncode(data); } + /// + /// Converts the specified address to a script hash. + /// + /// The address to convert. + /// The address version. + /// The converted script hash. public static UInt160 ToScriptHash(this string address, byte version) { byte[] data = address.Base58CheckDecode(); diff --git a/src/neo/Wallets/KeyPair.cs b/src/neo/Wallets/KeyPair.cs index 875aac79c1..6718e59f56 100644 --- a/src/neo/Wallets/KeyPair.cs +++ b/src/neo/Wallets/KeyPair.cs @@ -1,5 +1,6 @@ using Neo.Cryptography; using Neo.SmartContract; +using Neo.Wallets.NEP6; using Org.BouncyCastle.Crypto.Generators; using System; using System.Security.Cryptography; @@ -8,17 +9,34 @@ namespace Neo.Wallets { + /// + /// Represents a private/public key pair in wallets. + /// public class KeyPair : IEquatable { + /// + /// The private key. + /// public readonly byte[] PrivateKey; + + /// + /// The public key. + /// public readonly Cryptography.ECC.ECPoint PublicKey; + /// + /// The hash of the public key. + /// public UInt160 PublicKeyHash => PublicKey.EncodePoint(true).ToScriptHash(); + /// + /// Initializes a new instance of the class. + /// + /// The private key in the . public KeyPair(byte[] privateKey) { if (privateKey.Length != 32 && privateKey.Length != 96 && privateKey.Length != 104) - throw new ArgumentException(); + throw new ArgumentException(null, nameof(privateKey)); this.PrivateKey = privateKey[^32..]; if (privateKey.Length == 32) { @@ -42,6 +60,10 @@ public override bool Equals(object obj) return Equals(obj as KeyPair); } + /// + /// Exports the private key in WIF format. + /// + /// The private key in WIF format. public string Export() { Span data = stackalloc byte[34]; @@ -53,6 +75,15 @@ public string Export() return wif; } + /// + /// Exports the private key in NEP-2 format. + /// + /// The passphrase of the private key. + /// The address version. + /// The N field of the to be used. + /// The R field of the to be used. + /// The P field of the to be used. + /// The private key in NEP-2 format. public string Export(string passphrase, byte version, int N = 16384, int r = 8, int p = 8) { UInt160 script_hash = Contract.CreateSignatureRedeemScript(PublicKey).ToScriptHash(); diff --git a/src/neo/Wallets/NEP6/NEP6Account.cs b/src/neo/Wallets/NEP6/NEP6Account.cs index 7d68aea23a..1820fc305f 100644 --- a/src/neo/Wallets/NEP6/NEP6Account.cs +++ b/src/neo/Wallets/NEP6/NEP6Account.cs @@ -62,7 +62,7 @@ public KeyPair GetKey(string password) public JObject ToJson() { - JObject account = new JObject(); + JObject account = new(); account["address"] = ScriptHash.ToAddress(ProtocolSettings.AddressVersion); account["label"] = Label; account["isdefault"] = IsDefault; diff --git a/src/neo/Wallets/NEP6/NEP6Contract.cs b/src/neo/Wallets/NEP6/NEP6Contract.cs index af27db6c4c..970e0b8055 100644 --- a/src/neo/Wallets/NEP6/NEP6Contract.cs +++ b/src/neo/Wallets/NEP6/NEP6Contract.cs @@ -24,11 +24,11 @@ public static NEP6Contract FromJson(JObject json) public JObject ToJson() { - JObject contract = new JObject(); + JObject contract = new(); contract["script"] = Convert.ToBase64String(Script); contract["parameters"] = new JArray(ParameterList.Zip(ParameterNames, (type, name) => { - JObject parameter = new JObject(); + JObject parameter = new(); parameter["name"] = name; parameter["type"] = type; return parameter; diff --git a/src/neo/Wallets/NEP6/NEP6Wallet.cs b/src/neo/Wallets/NEP6/NEP6Wallet.cs index bd0961d390..f1440420ae 100644 --- a/src/neo/Wallets/NEP6/NEP6Wallet.cs +++ b/src/neo/Wallets/NEP6/NEP6Wallet.cs @@ -11,6 +11,10 @@ namespace Neo.Wallets.NEP6 { + /// + /// An implementation of the NEP-6 wallet standard. + /// + /// https://github.com/neo-project/proposals/blob/master/nep-6.mediawiki public class NEP6Wallet : Wallet { private string password; @@ -19,10 +23,24 @@ public class NEP6Wallet : Wallet private readonly Dictionary accounts; private readonly JObject extra; + /// + /// The parameters of the SCrypt algorithm used for encrypting and decrypting the private keys in the wallet. + /// public readonly ScryptParameters Scrypt; + public override string Name => name; + + /// + /// The version of the wallet standard. It is currently fixed at 1.0 and will be used for functional upgrades in the future. + /// public override Version Version => version; + /// + /// Loads or creates a wallet at the specified path. + /// + /// The path of the wallet file. + /// The to be used by the wallet. + /// The name of the wallet. If the wallet is loaded from an existing file, this parameter is ignored. public NEP6Wallet(string path, ProtocolSettings settings, string name = null) : base(path, settings) { if (File.Exists(path)) @@ -33,13 +51,19 @@ public NEP6Wallet(string path, ProtocolSettings settings, string name = null) : else { this.name = name; - this.version = Version.Parse("3.0"); + this.version = Version.Parse("1.0"); this.Scrypt = ScryptParameters.Default; this.accounts = new Dictionary(); this.extra = JObject.Null; } } + /// + /// Loads the wallet with the specified JSON string. + /// + /// The path of the wallet. + /// The to be used by the wallet. + /// The JSON object representing the wallet. public NEP6Wallet(string path, ProtocolSettings settings, JObject json) : base(path, settings) { LoadFromJson(json, out Scrypt, out accounts, out extra); @@ -48,8 +72,6 @@ public NEP6Wallet(string path, ProtocolSettings settings, JObject json) : base(p private void LoadFromJson(JObject wallet, out ScryptParameters scrypt, out Dictionary accounts, out JObject extra) { this.version = Version.Parse(wallet["version"].AsString()); - if (this.version.Major < 3) throw new FormatException(); - this.name = wallet["name"]?.AsString(); scrypt = ScryptParameters.FromJson(wallet["scrypt"]); accounts = ((JArray)wallet["accounts"]).Select(p => NEP6Account.FromJson(p, this)).ToDictionary(p => p.ScriptHash); @@ -95,15 +117,15 @@ public override bool Contains(UInt160 scriptHash) public override WalletAccount CreateAccount(byte[] privateKey) { - KeyPair key = new KeyPair(privateKey); - NEP6Contract contract = new NEP6Contract + KeyPair key = new(privateKey); + NEP6Contract contract = new() { Script = Contract.CreateSignatureRedeemScript(key.PublicKey), ParameterList = new[] { ContractParameterType.Signature }, ParameterNames = new[] { "signature" }, Deployed = false }; - NEP6Account account = new NEP6Account(this, contract.ScriptHash, key, password) + NEP6Account account = new(this, contract.ScriptHash, key, password) { Contract = contract }; @@ -113,8 +135,7 @@ public override WalletAccount CreateAccount(byte[] privateKey) public override WalletAccount CreateAccount(Contract contract, KeyPair key = null) { - NEP6Contract nep6contract = contract as NEP6Contract; - if (nep6contract == null) + if (contract is not NEP6Contract nep6contract) { nep6contract = new NEP6Contract { @@ -136,11 +157,16 @@ public override WalletAccount CreateAccount(Contract contract, KeyPair key = nul public override WalletAccount CreateAccount(UInt160 scriptHash) { - NEP6Account account = new NEP6Account(this, scriptHash); + NEP6Account account = new(this, scriptHash); AddAccount(account, true); return account; } + /// + /// Decrypts the specified NEP-2 string with the password of the wallet. + /// + /// The NEP-2 string to decrypt. + /// The decrypted private key. public KeyPair DecryptKey(string nep2key) { return new KeyPair(GetPrivateKeyFromNEP2(nep2key, password, ProtocolSettings.AddressVersion, Scrypt.N, Scrypt.R, Scrypt.P)); @@ -179,14 +205,14 @@ public override WalletAccount Import(X509Certificate2 cert) { key = new KeyPair(ecdsa.ExportParameters(true).D); } - NEP6Contract contract = new NEP6Contract + NEP6Contract contract = new() { Script = Contract.CreateSignatureRedeemScript(key.PublicKey), ParameterList = new[] { ContractParameterType.Signature }, ParameterNames = new[] { "signature" }, Deployed = false }; - NEP6Account account = new NEP6Account(this, contract.ScriptHash, key, password) + NEP6Account account = new(this, contract.ScriptHash, key, password) { Contract = contract }; @@ -196,15 +222,15 @@ public override WalletAccount Import(X509Certificate2 cert) public override WalletAccount Import(string wif) { - KeyPair key = new KeyPair(GetPrivateKeyFromWIF(wif)); - NEP6Contract contract = new NEP6Contract + KeyPair key = new(GetPrivateKeyFromWIF(wif)); + NEP6Contract contract = new() { Script = Contract.CreateSignatureRedeemScript(key.PublicKey), ParameterList = new[] { ContractParameterType.Signature }, ParameterNames = new[] { "signature" }, Deployed = false }; - NEP6Account account = new NEP6Account(this, contract.ScriptHash, key, password) + NEP6Account account = new(this, contract.ScriptHash, key, password) { Contract = contract }; @@ -214,8 +240,8 @@ public override WalletAccount Import(string wif) public override WalletAccount Import(string nep2, string passphrase, int N = 16384, int r = 8, int p = 8) { - KeyPair key = new KeyPair(GetPrivateKeyFromNEP2(nep2, passphrase, ProtocolSettings.AddressVersion, N, r, p)); - NEP6Contract contract = new NEP6Contract + KeyPair key = new(GetPrivateKeyFromNEP2(nep2, passphrase, ProtocolSettings.AddressVersion, N, r, p)); + NEP6Contract contract = new() { Script = Contract.CreateSignatureRedeemScript(key.PublicKey), ParameterList = new[] { ContractParameterType.Signature }, @@ -237,10 +263,18 @@ internal void Lock() password = null; } + /// + /// Migrates the accounts from to a new . + /// + /// The path of the new wallet file. + /// The path of the db3 wallet file. + /// The password of the wallets. + /// The to be used by the wallet. + /// The created new wallet. public static NEP6Wallet Migrate(string path, string db3path, string password, ProtocolSettings settings) { UserWallet wallet_old = UserWallet.Open(db3path, password, settings); - NEP6Wallet wallet_new = new NEP6Wallet(path, settings, wallet_old.Name); + NEP6Wallet wallet_new = new(path, settings, wallet_old.Name); using (wallet_new.Unlock(password)) { foreach (WalletAccount account in wallet_old.GetAccounts()) @@ -251,9 +285,12 @@ public static NEP6Wallet Migrate(string path, string db3path, string password, P return wallet_new; } + /// + /// Saves the wallet to the file. + /// public void Save() { - JObject wallet = new JObject(); + JObject wallet = new(); wallet["name"] = name; wallet["version"] = version.ToString(); wallet["scrypt"] = Scrypt.ToJson(); @@ -262,6 +299,11 @@ public void Save() File.WriteAllText(Path, wallet.ToString()); } + /// + /// Unlocks the wallet with the specified password. + /// + /// The password of the wallet. + /// The object that can be disposed to lock the wallet again. public IDisposable Unlock(string password) { if (!VerifyPassword(password)) diff --git a/src/neo/Wallets/NEP6/ScryptParameters.cs b/src/neo/Wallets/NEP6/ScryptParameters.cs index a987159ad6..f90c17ec68 100644 --- a/src/neo/Wallets/NEP6/ScryptParameters.cs +++ b/src/neo/Wallets/NEP6/ScryptParameters.cs @@ -2,12 +2,37 @@ namespace Neo.Wallets.NEP6 { + /// + /// Represents the parameters of the SCrypt algorithm. + /// public class ScryptParameters { + /// + /// The default parameters used by . + /// public static ScryptParameters Default { get; } = new ScryptParameters(16384, 8, 8); - public readonly int N, R, P; + /// + /// CPU/Memory cost parameter. Must be larger than 1, a power of 2 and less than 2^(128 * r / 8). + /// + public readonly int N; + /// + /// The block size, must be >= 1. + /// + public readonly int R; + + /// + /// Parallelization parameter. Must be a positive integer less than or equal to Int32.MaxValue / (128 * r * 8). + /// + public readonly int P; + + /// + /// Initializes a new instance of the class. + /// + /// CPU/Memory cost parameter. + /// The block size. + /// Parallelization parameter. public ScryptParameters(int n, int r, int p) { this.N = n; @@ -15,14 +40,23 @@ public ScryptParameters(int n, int r, int p) this.P = p; } + /// + /// Converts the parameters from a JSON object. + /// + /// The parameters represented by a JSON object. + /// The converted parameters. public static ScryptParameters FromJson(JObject json) { return new ScryptParameters((int)json["n"].AsNumber(), (int)json["r"].AsNumber(), (int)json["p"].AsNumber()); } + /// + /// Converts the parameters to a JSON object. + /// + /// The parameters represented by a JSON object. public JObject ToJson() { - JObject json = new JObject(); + JObject json = new(); json["n"] = N; json["r"] = R; json["p"] = P; diff --git a/src/neo/Wallets/NEP6/WalletLocker.cs b/src/neo/Wallets/NEP6/WalletLocker.cs index 45574b5cfe..63ef0baf1b 100644 --- a/src/neo/Wallets/NEP6/WalletLocker.cs +++ b/src/neo/Wallets/NEP6/WalletLocker.cs @@ -4,7 +4,7 @@ namespace Neo.Wallets.NEP6 { internal class WalletLocker : IDisposable { - private NEP6Wallet wallet; + private readonly NEP6Wallet wallet; public WalletLocker(NEP6Wallet wallet) { diff --git a/src/neo/Wallets/SQLite/UserWallet.cs b/src/neo/Wallets/SQLite/UserWallet.cs index 12aee5e039..7dec2a93e5 100644 --- a/src/neo/Wallets/SQLite/UserWallet.cs +++ b/src/neo/Wallets/SQLite/UserWallet.cs @@ -15,9 +15,12 @@ namespace Neo.Wallets.SQLite { + /// + /// A wallet implementation that uses SQLite as the underlying storage. + /// public class UserWallet : Wallet { - private readonly object db_lock = new object(); + private readonly object db_lock = new(); private readonly byte[] iv; private readonly byte[] salt; private readonly byte[] masterKey; @@ -40,11 +43,6 @@ public override Version Version } } - /// - /// Open an existing wallet - /// - /// Path - /// Password Key private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings) : base(path, settings) { this.salt = LoadStoredData("Salt"); @@ -62,12 +60,6 @@ private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings) : this.accounts = LoadAccounts(); } - /// - /// Create a new wallet - /// - /// Path - /// Password Key - /// Scrypt initialization value private UserWallet(string path, byte[] passwordKey, ProtocolSettings settings, ScryptParameters scrypt) : base(path, settings) { this.iv = new byte[16]; @@ -112,64 +104,62 @@ private void AddAccount(UserWalletAccount account) accounts[account.ScriptHash] = account; } lock (db_lock) - using (WalletDataContext ctx = new WalletDataContext(Path)) + { + using WalletDataContext ctx = new(Path); + if (account.HasKey) { - if (account.HasKey) + string passphrase = Encoding.UTF8.GetString(masterKey); + Account db_account = ctx.Accounts.FirstOrDefault(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); + if (db_account == null) { - string passphrase = Encoding.UTF8.GetString(masterKey); - Account db_account = ctx.Accounts.FirstOrDefault(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); - if (db_account == null) - { - db_account = ctx.Accounts.Add(new Account - { - Nep2key = account.Key.Export(passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P), - PublicKeyHash = account.Key.PublicKeyHash.ToArray() - }).Entity; - } - else + db_account = ctx.Accounts.Add(new Account { - db_account.Nep2key = account.Key.Export(passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P); - } + Nep2key = account.Key.Export(passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P), + PublicKeyHash = account.Key.PublicKeyHash.ToArray() + }).Entity; } - if (account.Contract != null) + else { - Contract db_contract = ctx.Contracts.FirstOrDefault(p => p.ScriptHash == account.Contract.ScriptHash.ToArray()); - if (db_contract != null) - { - db_contract.PublicKeyHash = account.Key.PublicKeyHash.ToArray(); - } - else + db_account.Nep2key = account.Key.Export(passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P); + } + } + if (account.Contract != null) + { + Contract db_contract = ctx.Contracts.FirstOrDefault(p => p.ScriptHash == account.Contract.ScriptHash.ToArray()); + if (db_contract != null) + { + db_contract.PublicKeyHash = account.Key.PublicKeyHash.ToArray(); + } + else + { + ctx.Contracts.Add(new Contract { - ctx.Contracts.Add(new Contract - { - RawData = ((VerificationContract)account.Contract).ToArray(), - ScriptHash = account.Contract.ScriptHash.ToArray(), - PublicKeyHash = account.Key.PublicKeyHash.ToArray() - }); - } + RawData = ((VerificationContract)account.Contract).ToArray(), + ScriptHash = account.Contract.ScriptHash.ToArray(), + PublicKeyHash = account.Key.PublicKeyHash.ToArray() + }); } - //add address + } + //add address + { + Address db_address = ctx.Addresses.FirstOrDefault(p => p.ScriptHash == account.ScriptHash.ToArray()); + if (db_address == null) { - Address db_address = ctx.Addresses.FirstOrDefault(p => p.ScriptHash == account.ScriptHash.ToArray()); - if (db_address == null) + ctx.Addresses.Add(new Address { - ctx.Addresses.Add(new Address - { - ScriptHash = account.ScriptHash.ToArray() - }); - } + ScriptHash = account.ScriptHash.ToArray() + }); } - ctx.SaveChanges(); } + ctx.SaveChanges(); + } } private void BuildDatabase() { - using (WalletDataContext ctx = new WalletDataContext(Path)) - { - ctx.Database.EnsureDeleted(); - ctx.Database.EnsureCreated(); - } + using WalletDataContext ctx = new(Path); + ctx.Database.EnsureDeleted(); + ctx.Database.EnsureCreated(); } public override bool ChangePassword(string oldPassword, string newPassword) @@ -196,11 +186,27 @@ public override bool Contains(UInt160 scriptHash) } } + /// + /// Creates a new wallet at the specified path. + /// + /// The path of the wallet. + /// The password of the wallet. + /// The to be used by the wallet. + /// The parameters of the SCrypt algorithm used for encrypting and decrypting the private keys in the wallet. + /// The created wallet. public static UserWallet Create(string path, string password, ProtocolSettings settings, ScryptParameters scrypt = null) { return new UserWallet(path, password.ToAesKey(), settings, scrypt ?? ScryptParameters.Default); } + /// + /// Creates a new wallet at the specified path. + /// + /// The path of the wallet. + /// The password of the wallet. + /// The to be used by the wallet. + /// The parameters of the SCrypt algorithm used for encrypting and decrypting the private keys in the wallet. + /// The created wallet. public static UserWallet Create(string path, SecureString password, ProtocolSettings settings, ScryptParameters scrypt = null) { return new UserWallet(path, password.ToAesKey(), settings, scrypt ?? ScryptParameters.Default); @@ -208,13 +214,13 @@ public static UserWallet Create(string path, SecureString password, ProtocolSett public override WalletAccount CreateAccount(byte[] privateKey) { - KeyPair key = new KeyPair(privateKey); - VerificationContract contract = new VerificationContract + KeyPair key = new(privateKey); + VerificationContract contract = new() { Script = SmartContract.Contract.CreateSignatureRedeemScript(key.PublicKey), ParameterList = new[] { ContractParameterType.Signature } }; - UserWalletAccount account = new UserWalletAccount(contract.ScriptHash, ProtocolSettings) + UserWalletAccount account = new(contract.ScriptHash, ProtocolSettings) { Key = key, Contract = contract @@ -225,8 +231,7 @@ public override WalletAccount CreateAccount(byte[] privateKey) public override WalletAccount CreateAccount(SmartContract.Contract contract, KeyPair key = null) { - VerificationContract verification_contract = contract as VerificationContract; - if (verification_contract == null) + if (contract is not VerificationContract verification_contract) { verification_contract = new VerificationContract { @@ -234,7 +239,7 @@ public override WalletAccount CreateAccount(SmartContract.Contract contract, Key ParameterList = contract.ParameterList }; } - UserWalletAccount account = new UserWalletAccount(verification_contract.ScriptHash, ProtocolSettings) + UserWalletAccount account = new(verification_contract.ScriptHash, ProtocolSettings) { Key = key, Contract = verification_contract @@ -245,7 +250,7 @@ public override WalletAccount CreateAccount(SmartContract.Contract contract, Key public override WalletAccount CreateAccount(UInt160 scriptHash) { - UserWalletAccount account = new UserWalletAccount(scriptHash, ProtocolSettings); + UserWalletAccount account = new(scriptHash, ProtocolSettings); AddAccount(account); return account; } @@ -261,25 +266,25 @@ public override bool DeleteAccount(UInt160 scriptHash) if (account != null) { lock (db_lock) - using (WalletDataContext ctx = new WalletDataContext(Path)) + { + using WalletDataContext ctx = new(Path); + if (account.HasKey) { - if (account.HasKey) - { - Account db_account = ctx.Accounts.First(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); - ctx.Accounts.Remove(db_account); - } - if (account.Contract != null) - { - Contract db_contract = ctx.Contracts.First(p => p.ScriptHash == scriptHash.ToArray()); - ctx.Contracts.Remove(db_contract); - } - //delete address - { - Address db_address = ctx.Addresses.First(p => p.ScriptHash == scriptHash.ToArray()); - ctx.Addresses.Remove(db_address); - } - ctx.SaveChanges(); + Account db_account = ctx.Accounts.First(p => p.PublicKeyHash == account.Key.PublicKeyHash.ToArray()); + ctx.Accounts.Remove(db_account); } + if (account.Contract != null) + { + Contract db_contract = ctx.Contracts.First(p => p.ScriptHash == scriptHash.ToArray()); + ctx.Contracts.Remove(db_contract); + } + //delete address + { + Address db_address = ctx.Addresses.First(p => p.ScriptHash == scriptHash.ToArray()); + ctx.Addresses.Remove(db_address); + } + ctx.SaveChanges(); + } return true; } return false; @@ -305,34 +310,44 @@ public override IEnumerable GetAccounts() private Dictionary LoadAccounts() { - using (WalletDataContext ctx = new WalletDataContext(Path)) + using WalletDataContext ctx = new(Path); + string passphrase = Encoding.UTF8.GetString(masterKey); + Dictionary accounts = ctx.Addresses.Select(p => p.ScriptHash).AsEnumerable().Select(p => new UserWalletAccount(new UInt160(p), ProtocolSettings)).ToDictionary(p => p.ScriptHash); + foreach (Contract db_contract in ctx.Contracts.Include(p => p.Account)) { - string passphrase = Encoding.UTF8.GetString(masterKey); - Dictionary accounts = ctx.Addresses.Select(p => p.ScriptHash).AsEnumerable().Select(p => new UserWalletAccount(new UInt160(p), ProtocolSettings)).ToDictionary(p => p.ScriptHash); - foreach (Contract db_contract in ctx.Contracts.Include(p => p.Account)) - { - VerificationContract contract = db_contract.RawData.AsSerializable(); - UserWalletAccount account = accounts[contract.ScriptHash]; - account.Contract = contract; - account.Key = new KeyPair(GetPrivateKeyFromNEP2(db_contract.Account.Nep2key, passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P)); - } - return accounts; + VerificationContract contract = db_contract.RawData.AsSerializable(); + UserWalletAccount account = accounts[contract.ScriptHash]; + account.Contract = contract; + account.Key = new KeyPair(GetPrivateKeyFromNEP2(db_contract.Account.Nep2key, passphrase, ProtocolSettings.AddressVersion, scrypt.N, scrypt.R, scrypt.P)); } + return accounts; } private byte[] LoadStoredData(string name) { - using (WalletDataContext ctx = new WalletDataContext(Path)) - { - return ctx.Keys.FirstOrDefault(p => p.Name == name)?.Value; - } + using WalletDataContext ctx = new(Path); + return ctx.Keys.FirstOrDefault(p => p.Name == name)?.Value; } + /// + /// Opens a wallet at the specified path. + /// + /// The path of the wallet. + /// The password of the wallet. + /// The to be used by the wallet. + /// The opened wallet. public static UserWallet Open(string path, string password, ProtocolSettings settings) { return new UserWallet(path, password.ToAesKey(), settings); } + /// + /// Opens a wallet at the specified path. + /// + /// The path of the wallet. + /// The password of the wallet. + /// The to be used by the wallet. + /// The opened wallet. public static UserWallet Open(string path, SecureString password, ProtocolSettings settings) { return new UserWallet(path, password.ToAesKey(), settings); @@ -348,11 +363,11 @@ private void SaveStoredData(string name, int value) private void SaveStoredData(string name, byte[] value) { lock (db_lock) - using (WalletDataContext ctx = new WalletDataContext(Path)) - { - SaveStoredData(ctx, name, value); - ctx.SaveChanges(); - } + { + using WalletDataContext ctx = new(Path); + SaveStoredData(ctx, name, value); + ctx.SaveChanges(); + } } private static void SaveStoredData(WalletDataContext ctx, string name, byte[] value) diff --git a/src/neo/Wallets/SQLite/VerificationContract.cs b/src/neo/Wallets/SQLite/VerificationContract.cs index 8c325b3211..7b8df238c2 100644 --- a/src/neo/Wallets/SQLite/VerificationContract.cs +++ b/src/neo/Wallets/SQLite/VerificationContract.cs @@ -6,7 +6,7 @@ namespace Neo.Wallets.SQLite { - public class VerificationContract : SmartContract.Contract, IEquatable, ISerializable + class VerificationContract : SmartContract.Contract, IEquatable, ISerializable { public int Size => ParameterList.GetVarSize() + Script.GetVarSize(); diff --git a/src/neo/Wallets/SQLite/WalletDataContext.cs b/src/neo/Wallets/SQLite/WalletDataContext.cs index efc22b58d9..3ed9a497c4 100644 --- a/src/neo/Wallets/SQLite/WalletDataContext.cs +++ b/src/neo/Wallets/SQLite/WalletDataContext.cs @@ -20,7 +20,7 @@ public WalletDataContext(string filename) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); - SqliteConnectionStringBuilder sb = new SqliteConnectionStringBuilder + SqliteConnectionStringBuilder sb = new() { DataSource = filename }; diff --git a/src/neo/Wallets/TransferOutput.cs b/src/neo/Wallets/TransferOutput.cs index ad3d202871..e8cc7d71f3 100644 --- a/src/neo/Wallets/TransferOutput.cs +++ b/src/neo/Wallets/TransferOutput.cs @@ -1,10 +1,28 @@ namespace Neo.Wallets { + /// + /// Represents an output of a transfer. + /// public class TransferOutput { + /// + /// The id of the asset to transfer. + /// public UInt160 AssetId; + + /// + /// The amount of the asset to transfer. + /// public BigDecimal Value; + + /// + /// The account to transfer to. + /// public UInt160 ScriptHash; + + /// + /// The object to be passed to the transfer method of NEP-17. + /// public object Data; } } diff --git a/src/neo/Wallets/Wallet.cs b/src/neo/Wallets/Wallet.cs index 6700b99f84..c6ecb567ac 100644 --- a/src/neo/Wallets/Wallet.cs +++ b/src/neo/Wallets/Wallet.cs @@ -5,6 +5,7 @@ using Neo.SmartContract; using Neo.SmartContract.Native; using Neo.VM; +using Neo.Wallets.NEP6; using Org.BouncyCastle.Crypto.Generators; using System; using System.Collections.Generic; @@ -19,28 +20,103 @@ namespace Neo.Wallets { + /// + /// The base class of wallets. + /// public abstract class Wallet { + /// + /// The to be used by the wallet. + /// public ProtocolSettings ProtocolSettings { get; } + + /// + /// The name of the wallet. + /// public abstract string Name { get; } + + /// + /// The path of the wallet. + /// public string Path { get; } + + /// + /// The version of the wallet. + /// public abstract Version Version { get; } + /// + /// Changes the password of the wallet. + /// + /// The old password of the wallet. + /// The new password to be used. + /// if the password is changed successfully; otherwise, . public abstract bool ChangePassword(string oldPassword, string newPassword); + + /// + /// Determines whether the specified account is included in the wallet. + /// + /// The hash of the account. + /// if the account is included in the wallet; otherwise, . public abstract bool Contains(UInt160 scriptHash); + + /// + /// Creates a standard account with the specified private key. + /// + /// The private key of the account. + /// The created account. public abstract WalletAccount CreateAccount(byte[] privateKey); + + /// + /// Creates a contract account for the wallet. + /// + /// The contract of the account. + /// The private key of the account. + /// The created account. public abstract WalletAccount CreateAccount(Contract contract, KeyPair key = null); + + /// + /// Creates a watch-only account for the wallet. + /// + /// The hash of the account. + /// The created account. public abstract WalletAccount CreateAccount(UInt160 scriptHash); + + /// + /// Deletes an account from the wallet. + /// + /// The hash of the account. + /// if the account is removed; otherwise, . public abstract bool DeleteAccount(UInt160 scriptHash); + + /// + /// Gets the account with the specified hash. + /// + /// The hash of the account. + /// The account with the specified hash. public abstract WalletAccount GetAccount(UInt160 scriptHash); + + /// + /// Gets all the accounts from the wallet. + /// + /// All accounts in the wallet. public abstract IEnumerable GetAccounts(); + /// + /// Initializes a new instance of the class. + /// + /// The path of the wallet file. + /// The to be used by the wallet. protected Wallet(string path, ProtocolSettings settings) { this.ProtocolSettings = settings; this.Path = path; } + /// + /// Creates a standard account for the wallet. + /// + /// The created account. public WalletAccount CreateAccount() { byte[] privateKey = new byte[32]; @@ -53,13 +129,19 @@ public WalletAccount CreateAccount() return account; } + /// + /// Creates a contract account for the wallet. + /// + /// The contract of the account. + /// The private key of the account. + /// The created account. public WalletAccount CreateAccount(Contract contract, byte[] privateKey) { if (privateKey == null) return CreateAccount(contract); return CreateAccount(contract, new KeyPair(privateKey)); } - private List<(UInt160 Account, BigInteger Value)> FindPayingAccounts(List<(UInt160 Account, BigInteger Value)> orderedAccounts, BigInteger amount) + private static List<(UInt160 Account, BigInteger Value)> FindPayingAccounts(List<(UInt160 Account, BigInteger Value)> orderedAccounts, BigInteger amount) { var result = new List<(UInt160 Account, BigInteger Value)>(); BigInteger sum_balance = orderedAccounts.Select(p => p.Value).Sum(); @@ -117,21 +199,39 @@ public WalletAccount CreateAccount(Contract contract, byte[] privateKey) return result; } + /// + /// Gets the account with the specified public key. + /// + /// The public key of the account. + /// The account with the specified public key. public WalletAccount GetAccount(ECPoint pubkey) { return GetAccount(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash()); } + /// + /// Gets the available balance for the specified asset in the wallet. + /// + /// The snapshot used to read data. + /// The id of the asset. + /// The available balance for the specified asset. public BigDecimal GetAvailable(DataCache snapshot, UInt160 asset_id) { UInt160[] accounts = GetAccounts().Where(p => !p.WatchOnly).Select(p => p.ScriptHash).ToArray(); return GetBalance(snapshot, asset_id, accounts); } + /// + /// Gets the balance for the specified asset in the wallet. + /// + /// The snapshot used to read data. + /// The id of the asset. + /// The accounts to be counted. + /// The balance for the specified asset. public BigDecimal GetBalance(DataCache snapshot, UInt160 asset_id, params UInt160[] accounts) { byte[] script; - using (ScriptBuilder sb = new ScriptBuilder()) + using (ScriptBuilder sb = new()) { sb.EmitPush(0); foreach (UInt160 account in accounts) @@ -160,6 +260,16 @@ private static byte[] Decrypt(byte[] data, byte[] key) return decryptor.TransformFinalBlock(data, 0, data.Length); } + /// + /// Decodes a private key from the specified NEP-2 string. + /// + /// The NEP-2 string to be decoded. + /// The passphrase of the private key. + /// The address version of NEO system. + /// The N field of the to be used. + /// The R field of the to be used. + /// The P field of the to be used. + /// The decoded private key. public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, byte version, int N = 16384, int r = 8, int p = 8) { if (nep2 == null) throw new ArgumentNullException(nameof(nep2)); @@ -189,9 +299,14 @@ public static byte[] GetPrivateKeyFromNEP2(string nep2, string passphrase, byte return prikey; } + /// + /// Decodes a private key from the specified WIF string. + /// + /// The WIF string to be decoded. + /// The decoded private key. public static byte[] GetPrivateKeyFromWIF(string wif) { - if (wif == null) throw new ArgumentNullException(); + if (wif is null) throw new ArgumentNullException(nameof(wif)); byte[] data = wif.Base58CheckDecode(); if (data.Length != 34 || data[0] != 0x80 || data[33] != 0x01) throw new FormatException(); @@ -208,7 +323,7 @@ private static Signer[] GetSigners(UInt160 sender, Signer[] cosigners) if (cosigners[i].Account.Equals(sender)) { if (i == 0) return cosigners; - List list = new List(cosigners); + List list = new(cosigners); list.RemoveAt(i); list.Insert(0, cosigners[i]); return list.ToArray(); @@ -221,6 +336,11 @@ private static Signer[] GetSigners(UInt160 sender, Signer[] cosigners) }).ToArray(); } + /// + /// Imports an account from a . + /// + /// The to import. + /// The imported account. public virtual WalletAccount Import(X509Certificate2 cert) { byte[] privateKey; @@ -233,6 +353,11 @@ public virtual WalletAccount Import(X509Certificate2 cert) return account; } + /// + /// Imports an account from the specified WIF string. + /// + /// The WIF string to import. + /// The imported account. public virtual WalletAccount Import(string wif) { byte[] privateKey = GetPrivateKeyFromWIF(wif); @@ -241,6 +366,15 @@ public virtual WalletAccount Import(string wif) return account; } + /// + /// Imports an account from the specified NEP-2 string. + /// + /// The NEP-2 string to import. + /// The passphrase of the private key. + /// The N field of the to be used. + /// The R field of the to be used. + /// The P field of the to be used. + /// The imported account. public virtual WalletAccount Import(string nep2, string passphrase, int N = 16384, int r = 8, int p = 8) { byte[] privateKey = GetPrivateKeyFromNEP2(nep2, passphrase, ProtocolSettings.AddressVersion, N, r, p); @@ -249,6 +383,14 @@ public virtual WalletAccount Import(string nep2, string passphrase, int N = 1638 return account; } + /// + /// Makes a transaction to transfer assets. + /// + /// The snapshot used to read data. + /// The array of that contain the asset, amount, and targets of the transfer. + /// The account to transfer from. + /// The cosigners to be added to the transction. + /// The created transction. public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, UInt160 from = null, Signer[] cosigners = null) { UInt160[] accounts; @@ -263,23 +405,21 @@ public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, Dictionary cosignerList = cosigners?.ToDictionary(p => p.Account) ?? new Dictionary(); byte[] script; List<(UInt160 Account, BigInteger Value)> balances_gas = null; - using (ScriptBuilder sb = new ScriptBuilder()) + using (ScriptBuilder sb = new()) { foreach (var (assetId, group, sum) in outputs.GroupBy(p => p.AssetId, (k, g) => (k, g, g.Select(p => p.Value.Value).Sum()))) { var balances = new List<(UInt160 Account, BigInteger Value)>(); foreach (UInt160 account in accounts) - using (ScriptBuilder sb2 = new ScriptBuilder()) - { - sb2.EmitDynamicCall(assetId, "balanceOf", CallFlags.ReadOnly, account); - using (ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, settings: ProtocolSettings)) - { - if (engine.State != VMState.HALT) - throw new InvalidOperationException($"Execution for {assetId}.balanceOf('{account}' fault"); - BigInteger value = engine.ResultStack.Pop().GetInteger(); - if (value.Sign > 0) balances.Add((account, value)); - } - } + { + using ScriptBuilder sb2 = new(); + sb2.EmitDynamicCall(assetId, "balanceOf", CallFlags.ReadOnly, account); + using ApplicationEngine engine = ApplicationEngine.Run(sb2.ToArray(), snapshot, settings: ProtocolSettings); + if (engine.State != VMState.HALT) + throw new InvalidOperationException($"Execution for {assetId}.balanceOf('{account}' fault"); + BigInteger value = engine.ResultStack.Pop().GetInteger(); + if (value.Sign > 0) balances.Add((account, value)); + } BigInteger sum_balance = balances.Select(p => p.Value).Sum(); if (sum_balance < sum) throw new InvalidOperationException($"It does not have enough balance, expected: {sum} found: {sum_balance}"); @@ -317,6 +457,16 @@ public Transaction MakeTransaction(DataCache snapshot, TransferOutput[] outputs, return MakeTransaction(snapshot, script, cosignerList.Values.ToArray(), Array.Empty(), balances_gas); } + /// + /// Makes a transaction to run a smart contract. + /// + /// The snapshot used to read data. + /// The script to be loaded in the transaction. + /// The sender of the transaction. + /// The cosigners to be added to the transction. + /// The attributes to be added to the transction. + /// The maximum gas that can be spent to execute the script. + /// The created transction. public Transaction MakeTransaction(DataCache snapshot, byte[] script, UInt160 sender = null, Signer[] cosigners = null, TransactionAttribute[] attributes = null, long maxGas = ApplicationEngine.TestModeGas) { UInt160[] accounts; @@ -334,10 +484,10 @@ public Transaction MakeTransaction(DataCache snapshot, byte[] script, UInt160 se private Transaction MakeTransaction(DataCache snapshot, byte[] script, Signer[] cosigners, TransactionAttribute[] attributes, List<(UInt160 Account, BigInteger Value)> balances_gas, long maxGas = ApplicationEngine.TestModeGas) { - Random rand = new Random(); + Random rand = new(); foreach (var (account, value) in balances_gas) { - Transaction tx = new Transaction + Transaction tx = new() { Version = 0, Nonce = (uint)rand.Next(), @@ -363,6 +513,12 @@ private Transaction MakeTransaction(DataCache snapshot, byte[] script, Signer[] throw new InvalidOperationException("Insufficient GAS"); } + /// + /// Calculates the network fee for the specified transaction. + /// + /// The snapshot used to read data. + /// The transaction to calculate. + /// The network fee of the transaction. public long CalculateNetworkFee(DataCache snapshot, Transaction tx) { UInt160[] hashes = tx.GetScriptHashesForVerifying(snapshot); @@ -430,6 +586,11 @@ public long CalculateNetworkFee(DataCache snapshot, Transaction tx) return networkFee; } + /// + /// Signs the in the specified with the wallet. + /// + /// The to be used. + /// if the signature is successfully added to the context; otherwise, . public bool Sign(ContractParametersContext context) { bool fSuccess = false; @@ -488,6 +649,11 @@ public bool Sign(ContractParametersContext context) return fSuccess; } + /// + /// Checks that the specified password is correct for the wallet. + /// + /// The password to be checked. + /// if the password is correct; otherwise, . public abstract bool VerifyPassword(string password); } } diff --git a/src/neo/Wallets/WalletAccount.cs b/src/neo/Wallets/WalletAccount.cs index a62a7c1fd1..e00f07934b 100644 --- a/src/neo/Wallets/WalletAccount.cs +++ b/src/neo/Wallets/WalletAccount.cs @@ -2,21 +2,67 @@ namespace Neo.Wallets { + /// + /// Represents an account in a wallet. + /// public abstract class WalletAccount { + /// + /// The to be used by the wallet. + /// protected readonly ProtocolSettings ProtocolSettings; + + /// + /// The hash of the account. + /// public readonly UInt160 ScriptHash; + + /// + /// The label of the account. + /// public string Label; + + /// + /// Indicates whether the account is the default account in the wallet. + /// public bool IsDefault; + + /// + /// Indicates whether the account is locked. + /// public bool Lock; + + /// + /// The contract of the account. + /// public Contract Contract; + /// + /// The address of the account. + /// public string Address => ScriptHash.ToAddress(ProtocolSettings.AddressVersion); + + /// + /// Indicates whether the account contains a private key. + /// public abstract bool HasKey { get; } + + /// + /// Indicates whether the account is a watch-only account. + /// public bool WatchOnly => Contract == null; + /// + /// Gets the private key of the account. + /// + /// The private key of the account. Or if there is no private key in the account. public abstract KeyPair GetKey(); + /// + /// Initializes a new instance of the class. + /// + /// The hash of the account. + /// The to be used by the wallet. protected WalletAccount(UInt160 scriptHash, ProtocolSettings settings) { this.ProtocolSettings = settings; diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 36686d28cf..94ee630100 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -18,6 +18,7 @@ Neo The Neo Project Neo + true diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs index 98ade07841..204876e82c 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.NEO.cs @@ -260,25 +260,23 @@ public void TestStorage_Find() [TestMethod] public void TestIterator_Next() { - var engine = GetEngine(); var arr = new VMArray { new byte[]{ 0x01 }, new byte[]{ 0x02 } }; - engine.IteratorNext(new ArrayWrapper(arr)).Should().BeTrue(); + ApplicationEngine.IteratorNext(new ArrayWrapper(arr)).Should().BeTrue(); } [TestMethod] public void TestIterator_Value() { - var engine = GetEngine(); var arr = new VMArray { new byte[]{ 0x01 }, new byte[]{ 0x02 } }; var wrapper = new ArrayWrapper(arr); wrapper.Next(); - engine.IteratorValue(wrapper).GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); + ApplicationEngine.IteratorValue(wrapper).GetSpan().ToHexString().Should().Be(new byte[] { 0x01 }.ToHexString()); } [TestMethod] diff --git a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs index d4a2e69197..cdf22d6720 100644 --- a/tests/neo.UnitTests/SmartContract/UT_InteropService.cs +++ b/tests/neo.UnitTests/SmartContract/UT_InteropService.cs @@ -236,7 +236,7 @@ public void TestContract_GetCallFlags() [TestMethod] public void TestRuntime_Platform() { - GetEngine().GetPlatform().Should().Be("NEO"); + ApplicationEngine.GetPlatform().Should().Be("NEO"); } [TestMethod] @@ -522,14 +522,13 @@ public void TestStorage_Delete() [TestMethod] public void TestStorageContext_AsReadOnly() { - var engine = GetEngine(); var state = TestUtils.GetContract(); var storageContext = new StorageContext { Id = state.Id, IsReadOnly = false }; - engine.AsReadOnly(storageContext).IsReadOnly.Should().BeTrue(); + ApplicationEngine.AsReadOnly(storageContext).IsReadOnly.Should().BeTrue(); } [TestMethod] @@ -588,9 +587,8 @@ public void TestContract_Destroy() [TestMethod] public void TestContract_CreateStandardAccount() { - var engine = GetEngine(true, true); ECPoint pubkey = ECPoint.Parse("024b817ef37f2fc3d4a33fe36687e592d9f30fe24b3e28187dc8f12b3b3b2b839e", ECCurve.Secp256r1); - engine.CreateStandardAccount(pubkey).ToArray().ToHexString().Should().Be("a78796ab56598585c80dbe95059324eabde764db"); + ApplicationEngine.CreateStandardAccount(pubkey).ToArray().ToHexString().Should().Be("a78796ab56598585c80dbe95059324eabde764db"); } public static void LogEvent(object sender, LogEventArgs args) diff --git a/tests/neo.UnitTests/TestUtils.cs b/tests/neo.UnitTests/TestUtils.cs index 2fa51ecf58..bb125d3875 100644 --- a/tests/neo.UnitTests/TestUtils.cs +++ b/tests/neo.UnitTests/TestUtils.cs @@ -92,11 +92,11 @@ public static NEP6Wallet GenerateTestWallet() { JObject wallet = new JObject(); wallet["name"] = "noname"; - wallet["version"] = new Version("3.0").ToString(); + wallet["version"] = new Version("1.0").ToString(); wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = null; - wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"3.0\",\"scrypt\":{\"n\":2,\"r\":1,\"p\":1},\"accounts\":[],\"extra\":null}"); + wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"1.0\",\"scrypt\":{\"n\":2,\"r\":1,\"p\":1},\"accounts\":[],\"extra\":null}"); return new NEP6Wallet(null, ProtocolSettings.Default, wallet); } diff --git a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs index 19c605c64d..bf1783eddc 100644 --- a/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs +++ b/tests/neo.UnitTests/Wallets/NEP6/UT_NEP6Wallet.cs @@ -54,7 +54,7 @@ private string CreateWalletFile() rootPath = GetRandomPath(); if (!Directory.Exists(rootPath)) Directory.CreateDirectory(rootPath); string path = Path.Combine(rootPath, "wallet.json"); - File.WriteAllText(path, "{\"name\":\"name\",\"version\":\"3.0\",\"scrypt\":{\"n\":2,\"r\":1,\"p\":1},\"accounts\":[],\"extra\":{}}"); + File.WriteAllText(path, "{\"name\":\"name\",\"version\":\"1.0\",\"scrypt\":{\"n\":2,\"r\":1,\"p\":1},\"accounts\":[],\"extra\":{}}"); return path; } @@ -77,7 +77,7 @@ public void TestChangePassword() { JObject wallet = new JObject(); wallet["name"] = "name"; - wallet["version"] = new System.Version("3.0").ToString(); + wallet["version"] = new System.Version("1.0").ToString(); wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); @@ -98,11 +98,11 @@ public void TestConstructorWithPathAndName() NEP6Wallet wallet = new NEP6Wallet(wPath, ProtocolSettings.Default); Assert.AreEqual("name", wallet.Name); Assert.AreEqual(new ScryptParameters(2, 1, 1).ToJson().ToString(), wallet.Scrypt.ToJson().ToString()); - Assert.AreEqual(new Version("3.0").ToString(), wallet.Version.ToString()); + Assert.AreEqual(new Version("1.0").ToString(), wallet.Version.ToString()); wallet = new NEP6Wallet("", ProtocolSettings.Default, "test"); Assert.AreEqual("test", wallet.Name); Assert.AreEqual(ScryptParameters.Default.ToJson().ToString(), wallet.Scrypt.ToJson().ToString()); - Assert.AreEqual(Version.Parse("3.0"), wallet.Version); + Assert.AreEqual(Version.Parse("1.0"), wallet.Version); } [TestMethod] @@ -110,14 +110,14 @@ public void TestConstructorWithJObject() { JObject wallet = new JObject(); wallet["name"] = "test"; - wallet["version"] = Version.Parse("3.0").ToString(); + wallet["version"] = Version.Parse("1.0").ToString(); wallet["scrypt"] = ScryptParameters.Default.ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); - wallet.ToString().Should().Be("{\"name\":\"test\",\"version\":\"3.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":{}}"); + wallet.ToString().Should().Be("{\"name\":\"test\",\"version\":\"1.0\",\"scrypt\":{\"n\":16384,\"r\":8,\"p\":8},\"accounts\":[],\"extra\":{}}"); NEP6Wallet w = new NEP6Wallet(null, ProtocolSettings.Default, wallet); Assert.AreEqual("test", w.Name); - Assert.AreEqual(Version.Parse("3.0").ToString(), w.Version.ToString()); + Assert.AreEqual(Version.Parse("1.0").ToString(), w.Version.ToString()); } [TestMethod] @@ -129,7 +129,7 @@ public void TestGetName() [TestMethod] public void TestGetVersion() { - Assert.AreEqual(new System.Version("3.0").ToString(), uut.Version.ToString()); + Assert.AreEqual(new System.Version("1.0").ToString(), uut.Version.ToString()); } [TestMethod] @@ -316,7 +316,7 @@ public void TestImportNep2() Assert.AreEqual(false, result); JObject wallet = new JObject(); wallet["name"] = "name"; - wallet["version"] = new Version("3.0").ToString(); + wallet["version"] = new Version("1.0").ToString(); wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); @@ -360,7 +360,7 @@ public void TestSave() { JObject wallet = new JObject(); wallet["name"] = "name"; - wallet["version"] = new System.Version("3.0").ToString(); + wallet["version"] = new System.Version("1.0").ToString(); wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); @@ -402,7 +402,7 @@ public void TestVerifyPassword() Assert.AreEqual(false, uut.Contains(testScriptHash)); JObject wallet = new JObject(); wallet["name"] = "name"; - wallet["version"] = new Version("3.0").ToString(); + wallet["version"] = new Version("1.0").ToString(); wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); wallet["accounts"] = new JArray(); wallet["extra"] = new JObject(); @@ -417,7 +417,7 @@ public void TestVerifyPassword() public void Test_NEP6Wallet_Json() { uut.Name.Should().Be("noname"); - uut.Version.Should().Be(new Version("3.0")); + uut.Version.Should().Be(new Version("1.0")); uut.Scrypt.Should().NotBeNull(); uut.Scrypt.N.Should().Be(new ScryptParameters(2, 1, 1).N); } From 12b7984d63f11e6519f23f7e83e7d7178c06bca0 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Wed, 17 Mar 2021 22:50:56 +0800 Subject: [PATCH 63/63] RC1 (#2402) --- src/neo/neo.csproj | 8 ++++---- tests/neo.UnitTests/UT_Helper.cs | 5 +---- tests/neo.UnitTests/neo.UnitTests.csproj | 12 ++++++------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj index 94ee630100..97862f6edc 100644 --- a/src/neo/neo.csproj +++ b/src/neo/neo.csproj @@ -4,7 +4,7 @@ 2015-2021 The Neo Project Neo 3.0.0 - preview5 + rc1 The Neo Project net5.0 true @@ -26,11 +26,11 @@ - + - - + + diff --git a/tests/neo.UnitTests/UT_Helper.cs b/tests/neo.UnitTests/UT_Helper.cs index dde8ccbd5e..c22d303191 100644 --- a/tests/neo.UnitTests/UT_Helper.cs +++ b/tests/neo.UnitTests/UT_Helper.cs @@ -170,15 +170,12 @@ public void TestToHexString() [TestMethod] public void TestGetVersion() { - string version = typeof(TestMethodAttribute).Assembly.GetVersion(); - version.Should().Be("14.0.4908.02"); - // assembly without version var asm = AppDomain.CurrentDomain.GetAssemblies() .Where(u => u.FullName == "Anonymously Hosted DynamicMethods Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null") .FirstOrDefault(); - version = asm?.GetVersion() ?? ""; + string version = asm?.GetVersion() ?? ""; version.Should().Be("0.0.0"); } diff --git a/tests/neo.UnitTests/neo.UnitTests.csproj b/tests/neo.UnitTests/neo.UnitTests.csproj index dfa7212163..411a6f83cf 100644 --- a/tests/neo.UnitTests/neo.UnitTests.csproj +++ b/tests/neo.UnitTests/neo.UnitTests.csproj @@ -9,13 +9,13 @@ - - + + - - - - + + + +