From af071803d5618b811a34c7e7675fbbdb6ca6ac63 Mon Sep 17 00:00:00 2001 From: Erik Zhang Date: Fri, 12 Jun 2020 12:54:10 +0800 Subject: [PATCH] Refactor NativeContract (#1693) --- src/neo/SmartContract/ApplicationEngine.cs | 76 ++++++------ .../InteropParameterDescriptor.cs | 4 + .../Native/ContractMethodAttribute.cs | 8 +- .../Native/ContractMethodMetadata.cs | 38 +++++- .../SmartContract/Native/NativeContract.cs | 108 +++++++++++------- .../SmartContract/Native/PolicyContract.cs | 57 +++------ .../SmartContract/Native/Tokens/GasToken.cs | 7 +- .../SmartContract/Native/Tokens/NeoToken.cs | 94 +++++---------- .../SmartContract/Native/Tokens/Nep5Token.cs | 47 +------- .../Native/Tokens/UT_Nep5Token.cs | 16 +-- .../SmartContract/Native/UT_NativeContract.cs | 15 +-- 11 files changed, 202 insertions(+), 268 deletions(-) diff --git a/src/neo/SmartContract/ApplicationEngine.cs b/src/neo/SmartContract/ApplicationEngine.cs index 11708d96095..711a9c8dc9e 100644 --- a/src/neo/SmartContract/ApplicationEngine.cs +++ b/src/neo/SmartContract/ApplicationEngine.cs @@ -71,7 +71,7 @@ public ExecutionContext LoadScript(Script script, CallFlags callFlags, int rvcou return context; } - private StackItem ConvertReturnValue(object value) + internal StackItem Convert(object value) { return value switch { @@ -85,18 +85,51 @@ private StackItem ConvertReturnValue(object value) uint i => i, long i => i, ulong i => i, - Enum e => ConvertReturnValue(Convert.ChangeType(e, e.GetTypeCode())), + Enum e => Convert(System.Convert.ChangeType(e, e.GetTypeCode())), byte[] data => data, string s => s, - UInt160 i => i.ToArray(), - UInt256 i => i.ToArray(), + BigInteger i => i, IInteroperable interoperable => interoperable.ToStackItem(ReferenceCounter), - IInteroperable[] array => new VMArray(ReferenceCounter, array.Select(p => p.ToStackItem(ReferenceCounter))), + ISerializable i => i.ToArray(), StackItem item => item, + (object a, object b) => new Struct(ReferenceCounter) { Convert(a), Convert(b) }, + Array array => new VMArray(ReferenceCounter, array.OfType().Select(p => Convert(p))), _ => StackItem.FromInterface(value) }; } + internal object Convert(StackItem item, InteropParameterDescriptor descriptor) + { + if (descriptor.IsArray) + { + Array av; + if (item is VMArray array) + { + av = Array.CreateInstance(descriptor.Type.GetElementType(), array.Count); + for (int i = 0; i < av.Length; i++) + av.SetValue(descriptor.Converter(array[i]), i); + } + else + { + int count = (int)item.GetBigInteger(); + if (count > MaxStackSize) throw new InvalidOperationException(); + av = Array.CreateInstance(descriptor.Type.GetElementType(), count); + for (int i = 0; i < av.Length; i++) + av.SetValue(descriptor.Converter(Pop()), i); + } + return av; + } + else + { + object value = descriptor.Converter(item); + if (descriptor.IsEnum) + value = System.Convert.ChangeType(value, descriptor.Type); + else if (descriptor.IsInterface) + value = ((InteropInterface)value).GetInterface(); + return value; + } + } + public override void Dispose() { foreach (IDisposable disposable in disposables) @@ -120,39 +153,10 @@ protected override bool OnSysCall(uint method) ? new List() : null; foreach (var pd in descriptor.Parameters) - { - StackItem item = Pop(); - object value; - if (pd.IsArray) - { - Array av; - if (item is VMArray array) - { - av = Array.CreateInstance(pd.Type.GetElementType(), array.Count); - for (int i = 0; i < av.Length; i++) - av.SetValue(pd.Converter(array[i]), i); - } - else - { - av = Array.CreateInstance(pd.Type.GetElementType(), (int)item.GetBigInteger()); - for (int i = 0; i < av.Length; i++) - av.SetValue(pd.Converter(Pop()), i); - } - value = av; - } - else - { - value = pd.Converter(item); - if (pd.IsEnum) - value = Convert.ChangeType(value, pd.Type); - else if (pd.IsInterface) - value = ((InteropInterface)value).GetInterface(); - } - parameters.Add(value); - } + parameters.Add(Convert(Pop(), pd)); object returnValue = descriptor.Handler.Invoke(this, parameters?.ToArray()); if (descriptor.Handler.ReturnType != typeof(void)) - Push(ConvertReturnValue(returnValue)); + Push(Convert(returnValue)); return true; } diff --git a/src/neo/SmartContract/InteropParameterDescriptor.cs b/src/neo/SmartContract/InteropParameterDescriptor.cs index e7ee51cf28e..124f1e27e44 100644 --- a/src/neo/SmartContract/InteropParameterDescriptor.cs +++ b/src/neo/SmartContract/InteropParameterDescriptor.cs @@ -3,12 +3,14 @@ using Neo.VM.Types; using System; using System.Collections.Generic; +using System.Numerics; using System.Reflection; namespace Neo.SmartContract { internal class InteropParameterDescriptor { + public string Name { get; } public Type Type { get; } public Func Converter { get; } public bool IsEnum => Type.IsEnum; @@ -28,6 +30,7 @@ internal class InteropParameterDescriptor [typeof(uint)] = p => (uint)p.GetBigInteger(), [typeof(long)] = p => (long)p.GetBigInteger(), [typeof(ulong)] = p => (ulong)p.GetBigInteger(), + [typeof(BigInteger)] = p => p.GetBigInteger(), [typeof(byte[])] = p => p.IsNull ? null : p.GetSpan().ToArray(), [typeof(string)] = p => p.IsNull ? null : p.GetString(), [typeof(UInt160)] = p => p.IsNull ? null : new UInt160(p.GetSpan()), @@ -37,6 +40,7 @@ internal class InteropParameterDescriptor public InteropParameterDescriptor(ParameterInfo parameterInfo) { + Name = parameterInfo.Name; Type = parameterInfo.ParameterType; if (IsEnum) { diff --git a/src/neo/SmartContract/Native/ContractMethodAttribute.cs b/src/neo/SmartContract/Native/ContractMethodAttribute.cs index 6cb745fee58..4952fc27812 100644 --- a/src/neo/SmartContract/Native/ContractMethodAttribute.cs +++ b/src/neo/SmartContract/Native/ContractMethodAttribute.cs @@ -2,20 +2,16 @@ namespace Neo.SmartContract.Native { - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false)] internal class ContractMethodAttribute : Attribute { public string Name { get; set; } public long Price { get; } - public ContractParameterType ReturnType { get; } - public ContractParameterType[] ParameterTypes { get; set; } = Array.Empty(); - public string[] ParameterNames { get; set; } = Array.Empty(); public CallFlags RequiredCallFlags { get; } - public ContractMethodAttribute(long price, ContractParameterType returnType, CallFlags requiredCallFlags) + public ContractMethodAttribute(long price, CallFlags requiredCallFlags) { this.Price = price; - this.ReturnType = returnType; this.RequiredCallFlags = requiredCallFlags; } } diff --git a/src/neo/SmartContract/Native/ContractMethodMetadata.cs b/src/neo/SmartContract/Native/ContractMethodMetadata.cs index 228252f0930..563040721eb 100644 --- a/src/neo/SmartContract/Native/ContractMethodMetadata.cs +++ b/src/neo/SmartContract/Native/ContractMethodMetadata.cs @@ -1,13 +1,41 @@ -using Neo.VM.Types; +using Neo.Persistence; using System; -using VMArray = Neo.VM.Types.Array; +using System.Linq; +using System.Reflection; namespace Neo.SmartContract.Native { internal class ContractMethodMetadata { - public Func Delegate; - public long Price; - public CallFlags RequiredCallFlags; + public string Name { get; } + public MethodInfo Handler { get; } + public InteropParameterDescriptor[] Parameters { get; } + public bool NeedApplicationEngine { get; } + public bool NeedSnapshot { get; } + public long Price { get; } + public CallFlags RequiredCallFlags { get; } + + public ContractMethodMetadata(MemberInfo member, ContractMethodAttribute attribute) + { + this.Name = attribute.Name ?? member.Name.ToLower()[0] + member.Name[1..]; + this.Handler = member switch + { + MethodInfo m => m, + PropertyInfo p => p.GetMethod, + _ => throw new ArgumentException(nameof(member)) + }; + ParameterInfo[] parameterInfos = this.Handler.GetParameters(); + if (parameterInfos.Length > 0) + { + NeedApplicationEngine = parameterInfos[0].ParameterType.IsAssignableFrom(typeof(ApplicationEngine)); + NeedSnapshot = parameterInfos[0].ParameterType.IsAssignableFrom(typeof(StoreView)); + } + if (NeedApplicationEngine || NeedSnapshot) + this.Parameters = parameterInfos.Skip(1).Select(p => new InteropParameterDescriptor(p)).ToArray(); + else + this.Parameters = parameterInfos.Select(p => new InteropParameterDescriptor(p)).ToArray(); + this.Price = attribute.Price; + this.RequiredCallFlags = attribute.RequiredCallFlags; + } } } diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index c42d7eb4392..a18b26abd76 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -1,5 +1,3 @@ -#pragma warning disable IDE0060 - using Neo.IO; using Neo.Ledger; using Neo.SmartContract.Manifest; @@ -9,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using System.Reflection; using Array = Neo.VM.Types.Array; @@ -26,11 +25,13 @@ public abstract class NativeContract public static GasToken GAS { get; } = new GasToken(); public static PolicyContract Policy { get; } = new PolicyContract(); + [ContractMethod(0, CallFlags.None)] public abstract string Name { get; } public byte[] Script { get; } public UInt160 Hash { get; } public abstract int Id { get; } public ContractManifest Manifest { get; } + [ContractMethod(0, CallFlags.None)] public virtual string[] SupportedStandards { get; } = { "NEP-10" }; protected NativeContract() @@ -44,24 +45,19 @@ protected NativeContract() this.Hash = Script.ToScriptHash(); List descriptors = new List(); List safeMethods = new List(); - foreach (MethodInfo method in GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) + foreach (MemberInfo member in GetType().GetMembers(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { - ContractMethodAttribute attribute = method.GetCustomAttribute(); + ContractMethodAttribute attribute = member.GetCustomAttribute(); if (attribute is null) continue; - string name = attribute.Name ?? (method.Name.ToLower()[0] + method.Name.Substring(1)); + ContractMethodMetadata metadata = new ContractMethodMetadata(member, attribute); descriptors.Add(new ContractMethodDescriptor { - Name = name, - ReturnType = attribute.ReturnType, - Parameters = attribute.ParameterTypes.Zip(attribute.ParameterNames, (t, n) => new ContractParameterDefinition { Type = t, Name = n }).ToArray() - }); - if (!attribute.RequiredCallFlags.HasFlag(CallFlags.AllowModifyStates)) safeMethods.Add(name); - methods.Add(name, new ContractMethodMetadata - { - Delegate = (Func)method.CreateDelegate(typeof(Func), this), - Price = attribute.Price, - RequiredCallFlags = attribute.RequiredCallFlags + Name = metadata.Name, + ReturnType = ToParameterType(metadata.Handler.ReturnType), + Parameters = metadata.Parameters.Select(p => new ContractParameterDefinition { Type = ToParameterType(p.Type), Name = p.Name }).ToArray() }); + if (!attribute.RequiredCallFlags.HasFlag(CallFlags.AllowModifyStates)) safeMethods.Add(metadata.Name); + methods.Add(metadata.Name, metadata); } this.Manifest = new ContractManifest { @@ -69,11 +65,11 @@ protected NativeContract() Abi = new ContractAbi() { Hash = Hash, - Events = new ContractEventDescriptor[0], + Events = System.Array.Empty(), Methods = descriptors.ToArray() }, Features = ContractFeatures.NoProperty, - Groups = new ContractGroup[0], + Groups = System.Array.Empty(), SafeMethods = WildcardContainer.Create(safeMethods.ToArray()), Trusts = WildcardContainer.Create(), Extra = null, @@ -114,19 +110,24 @@ public static NativeContract GetContract(string name) internal bool Invoke(ApplicationEngine engine) { - if (!engine.CurrentScriptHash.Equals(Hash)) - return false; - string operation = engine.CurrentContext.EvaluationStack.Pop().GetString(); - Array args = (Array)engine.CurrentContext.EvaluationStack.Pop(); - if (!methods.TryGetValue(operation, out ContractMethodMetadata method)) - return false; + if (!engine.CurrentScriptHash.Equals(Hash)) return false; + if (!engine.TryPop(out string operation)) return false; + if (!engine.TryPop(out Array args)) return false; + if (!methods.TryGetValue(operation, out ContractMethodMetadata method)) return false; ExecutionContextState state = engine.CurrentContext.GetState(); - if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) - return false; - if (!engine.AddGas(method.Price)) - return false; - StackItem result = method.Delegate(engine, args); - engine.CurrentContext.EvaluationStack.Push(result); + if (!state.CallFlags.HasFlag(method.RequiredCallFlags)) return false; + if (!engine.AddGas(method.Price)) return false; + List parameters = new List(); + if (method.NeedApplicationEngine) parameters.Add(engine); + if (method.NeedSnapshot) parameters.Add(engine.Snapshot); + for (int i = 0; i < method.Parameters.Length; i++) + { + StackItem item = i < args.Count ? args[i] : StackItem.Null; + parameters.Add(engine.Convert(item, method.Parameters[i])); + } + object returnValue = method.Handler.Invoke(this, parameters.ToArray()); + if (method.Handler.ReturnType != typeof(void)) + engine.Push(engine.Convert(returnValue)); return true; } @@ -139,22 +140,11 @@ internal virtual void Initialize(ApplicationEngine engine) { } - [ContractMethod(0, ContractParameterType.Boolean, CallFlags.AllowModifyStates)] - protected StackItem OnPersist(ApplicationEngine engine, Array args) + [ContractMethod(0, CallFlags.AllowModifyStates)] + protected virtual void OnPersist(ApplicationEngine engine) { - if (engine.Trigger != TriggerType.System) return false; - return OnPersist(engine); - } - - protected virtual bool OnPersist(ApplicationEngine engine) - { - return true; - } - - [ContractMethod(0, ContractParameterType.Array, CallFlags.None, Name = "supportedStandards")] - protected StackItem SupportedStandardsMethod(ApplicationEngine engine, Array args) - { - return new Array(engine.ReferenceCounter, SupportedStandards.Select(p => (StackItem)p)); + if (engine.Trigger != TriggerType.System) + throw new InvalidOperationException(); } public ApplicationEngine TestCall(string operation, params object[] args) @@ -165,5 +155,35 @@ public ApplicationEngine TestCall(string operation, params object[] args) return ApplicationEngine.Run(sb.ToArray(), testMode: true); } } + + private static ContractParameterType ToParameterType(Type type) + { + if (type == typeof(void)) return ContractParameterType.Void; + if (type == typeof(bool)) return ContractParameterType.Boolean; + if (type == typeof(sbyte)) return ContractParameterType.Integer; + if (type == typeof(byte)) return ContractParameterType.Integer; + if (type == typeof(short)) return ContractParameterType.Integer; + if (type == typeof(ushort)) return ContractParameterType.Integer; + if (type == typeof(int)) return ContractParameterType.Integer; + if (type == typeof(uint)) return ContractParameterType.Integer; + if (type == typeof(long)) return ContractParameterType.Integer; + if (type == typeof(ulong)) return ContractParameterType.Integer; + if (type == typeof(BigInteger)) return ContractParameterType.Integer; + if (type == typeof(byte[])) return ContractParameterType.ByteArray; + if (type == typeof(string)) return ContractParameterType.ByteArray; + if (type == typeof(VM.Types.Boolean)) return ContractParameterType.Boolean; + if (type == typeof(Integer)) return ContractParameterType.Integer; + if (type == typeof(ByteString)) return ContractParameterType.ByteArray; + if (type == typeof(VM.Types.Buffer)) return ContractParameterType.ByteArray; + if (type == typeof(Array)) return ContractParameterType.Array; + if (type == typeof(Struct)) return ContractParameterType.Array; + if (type == typeof(Map)) return ContractParameterType.Map; + if (type == typeof(StackItem)) return ContractParameterType.Any; + if (typeof(IInteroperable).IsAssignableFrom(type)) return ContractParameterType.Array; + if (typeof(ISerializable).IsAssignableFrom(type)) return ContractParameterType.ByteArray; + if (type.IsArray) return ContractParameterType.Array; + if (type.IsEnum) return ContractParameterType.Integer; + return ContractParameterType.Any; + } } } diff --git a/src/neo/SmartContract/Native/PolicyContract.cs b/src/neo/SmartContract/Native/PolicyContract.cs index 2bd2244a965..2cef3843061 100644 --- a/src/neo/SmartContract/Native/PolicyContract.cs +++ b/src/neo/SmartContract/Native/PolicyContract.cs @@ -1,17 +1,13 @@ #pragma warning disable IDE0051 -#pragma warning disable IDE0060 using Neo.IO; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; -using Neo.VM; -using Neo.VM.Types; using System; using System.Collections.Generic; using System.Linq; -using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native { @@ -64,86 +60,62 @@ internal override void Initialize(ApplicationEngine engine) }); } - [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] - private StackItem GetMaxTransactionsPerBlock(ApplicationEngine engine, Array args) - { - return GetMaxTransactionsPerBlock(engine.Snapshot); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public uint GetMaxTransactionsPerBlock(StoreView snapshot) { return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxTransactionsPerBlock)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] - private StackItem GetMaxBlockSize(ApplicationEngine engine, Array args) - { - return GetMaxBlockSize(engine.Snapshot); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public uint GetMaxBlockSize(StoreView snapshot) { return BitConverter.ToUInt32(snapshot.Storages[CreateStorageKey(Prefix_MaxBlockSize)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] - private StackItem GetFeePerByte(ApplicationEngine engine, Array args) - { - return GetFeePerByte(engine.Snapshot); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public long GetFeePerByte(StoreView snapshot) { return BitConverter.ToInt64(snapshot.Storages[CreateStorageKey(Prefix_FeePerByte)].Value, 0); } - [ContractMethod(0_01000000, ContractParameterType.Array, CallFlags.AllowStates)] - private StackItem GetBlockedAccounts(ApplicationEngine engine, Array args) - { - return new Array(engine.ReferenceCounter, GetBlockedAccounts(engine.Snapshot).Select(p => (StackItem)p.ToArray())); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public UInt160[] GetBlockedAccounts(StoreView snapshot) { return snapshot.Storages[CreateStorageKey(Prefix_BlockedAccounts)].Value.AsSerializableArray(); } - [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] - private StackItem SetMaxBlockSize(ApplicationEngine engine, Array args) + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool SetMaxBlockSize(ApplicationEngine engine, uint value) { if (!CheckCommittees(engine)) return false; - uint value = (uint)args[0].GetBigInteger(); if (Network.P2P.Message.PayloadMaxSize <= value) return false; StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxBlockSize)); storage.Value = BitConverter.GetBytes(value); return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] - private StackItem SetMaxTransactionsPerBlock(ApplicationEngine engine, Array args) + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool SetMaxTransactionsPerBlock(ApplicationEngine engine, uint value) { if (!CheckCommittees(engine)) return false; - uint value = (uint)args[0].GetBigInteger(); StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_MaxTransactionsPerBlock)); storage.Value = BitConverter.GetBytes(value); return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "value" })] - private StackItem SetFeePerByte(ApplicationEngine engine, Array args) + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool SetFeePerByte(ApplicationEngine engine, long value) { if (!CheckCommittees(engine)) return false; - long value = (long)args[0].GetBigInteger(); StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_FeePerByte)); storage.Value = BitConverter.GetBytes(value); return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] - private StackItem BlockAccount(ApplicationEngine engine, Array args) + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool BlockAccount(ApplicationEngine engine, UInt160 account) { if (!CheckCommittees(engine)) return false; - UInt160 account = new UInt160(args[0].GetSpan()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; SortedSet accounts = new SortedSet(storage.Value.AsSerializableArray()); @@ -153,11 +125,10 @@ private StackItem BlockAccount(ApplicationEngine engine, Array args) return true; } - [ContractMethod(0_03000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] - private StackItem UnblockAccount(ApplicationEngine engine, Array args) + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + private bool UnblockAccount(ApplicationEngine engine, UInt160 account) { if (!CheckCommittees(engine)) return false; - UInt160 account = new UInt160(args[0].GetSpan()); StorageKey key = CreateStorageKey(Prefix_BlockedAccounts); StorageItem storage = engine.Snapshot.Storages[key]; SortedSet accounts = new SortedSet(storage.Value.AsSerializableArray()); diff --git a/src/neo/SmartContract/Native/Tokens/GasToken.cs b/src/neo/SmartContract/Native/Tokens/GasToken.cs index 4a8cd5e1bf6..37a5098f04f 100644 --- a/src/neo/SmartContract/Native/Tokens/GasToken.cs +++ b/src/neo/SmartContract/Native/Tokens/GasToken.cs @@ -1,5 +1,3 @@ -#pragma warning disable IDE0051 - using Neo.Cryptography.ECC; using Neo.Ledger; using Neo.Network.P2P.Payloads; @@ -24,15 +22,14 @@ internal override void Initialize(ApplicationEngine engine) Mint(engine, account, 30_000_000 * Factor); } - protected override bool OnPersist(ApplicationEngine engine) + protected override void OnPersist(ApplicationEngine engine) { - if (!base.OnPersist(engine)) return false; + base.OnPersist(engine); foreach (Transaction tx in engine.Snapshot.PersistingBlock.Transactions) Burn(engine, tx.Sender, tx.SystemFee + tx.NetworkFee); ECPoint[] validators = NEO.GetNextBlockValidators(engine.Snapshot); UInt160 primary = Contract.CreateSignatureRedeemScript(validators[engine.Snapshot.PersistingBlock.ConsensusData.PrimaryIndex]).ToScriptHash(); Mint(engine, primary, engine.Snapshot.PersistingBlock.Transactions.Sum(p => p.NetworkFee)); - return true; } } } diff --git a/src/neo/SmartContract/Native/Tokens/NeoToken.cs b/src/neo/SmartContract/Native/Tokens/NeoToken.cs index 1c63485b5ce..494bf4afc60 100644 --- a/src/neo/SmartContract/Native/Tokens/NeoToken.cs +++ b/src/neo/SmartContract/Native/Tokens/NeoToken.cs @@ -1,5 +1,4 @@ #pragma warning disable IDE0051 -#pragma warning disable IDE0060 using Neo.Cryptography.ECC; using Neo.IO; @@ -11,7 +10,6 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -using Array = Neo.VM.Types.Array; namespace Neo.SmartContract.Native.Tokens { @@ -50,12 +48,12 @@ protected override void OnBalanceChanging(ApplicationEngine engine, UInt160 acco private void DistributeGas(ApplicationEngine engine, UInt160 account, NeoAccountState state) { - BigInteger gas = CalculateBonus(engine.Snapshot, state.Balance, state.BalanceHeight, engine.Snapshot.PersistingBlock.Index); + BigInteger gas = CalculateBonus(state.Balance, state.BalanceHeight, engine.Snapshot.PersistingBlock.Index); state.BalanceHeight = engine.Snapshot.PersistingBlock.Index; GAS.Mint(engine, account, gas); } - private BigInteger CalculateBonus(StoreView snapshot, BigInteger value, uint start, uint end) + private BigInteger CalculateBonus(BigInteger value, uint start, uint end) { if (value.IsZero || start >= end) return BigInteger.Zero; if (value.Sign < 0) throw new ArgumentOutOfRangeException(nameof(value)); @@ -93,91 +91,74 @@ internal override void Initialize(ApplicationEngine engine) for (int i = 0; i < Blockchain.StandbyCommittee.Length; i++) { ECPoint pubkey = Blockchain.StandbyCommittee[i]; - RegisterCandidate(engine.Snapshot, pubkey); + RegisterCandidateInternal(engine.Snapshot, pubkey); BigInteger balance = TotalAmount / 2 / (Blockchain.StandbyValidators.Length * 2 + (Blockchain.StandbyCommittee.Length - Blockchain.StandbyValidators.Length)); if (i < Blockchain.StandbyValidators.Length) balance *= 2; UInt160 account = Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash(); Mint(engine, account, balance); - Vote(engine.Snapshot, account, pubkey); + VoteInternal(engine.Snapshot, account, pubkey); amount -= balance; } Mint(engine, Blockchain.GetConsensusAddress(Blockchain.StandbyValidators), amount); } - protected override bool OnPersist(ApplicationEngine engine) + protected override void OnPersist(ApplicationEngine engine) { - if (!base.OnPersist(engine)) return false; + base.OnPersist(engine); StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_NextValidators), () => new StorageItem()); storage.Value = GetValidators(engine.Snapshot).ToByteArray(); - return true; - } - - [ContractMethod(0_03000000, ContractParameterType.Integer, CallFlags.AllowStates, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "account", "end" })] - private StackItem UnclaimedGas(ApplicationEngine engine, Array args) - { - UInt160 account = new UInt160(args[0].GetSpan()); - uint end = (uint)args[1].GetBigInteger(); - return UnclaimedGas(engine.Snapshot, account, end); } + [ContractMethod(0_03000000, CallFlags.AllowStates)] public BigInteger UnclaimedGas(StoreView snapshot, UInt160 account, uint end) { StorageItem storage = snapshot.Storages.TryGet(CreateAccountKey(account)); if (storage is null) return BigInteger.Zero; NeoAccountState state = storage.GetInteroperable(); - return CalculateBonus(snapshot, state.Balance, state.BalanceHeight, end); + return CalculateBonus(state.Balance, state.BalanceHeight, end); } - [ContractMethod(0_05000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] - private StackItem RegisterCandidate(ApplicationEngine engine, Array args) + [ContractMethod(0_05000000, CallFlags.AllowModifyStates)] + private bool RegisterCandidate(ApplicationEngine engine, ECPoint pubkey) { - ECPoint pubkey = args[0].GetSpan().AsSerializable(); if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) return false; - return RegisterCandidate(engine.Snapshot, pubkey); + RegisterCandidateInternal(engine.Snapshot, pubkey); + return true; } - private bool RegisterCandidate(StoreView snapshot, ECPoint pubkey) + private void RegisterCandidateInternal(StoreView snapshot, ECPoint pubkey) { StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey); StorageItem item = snapshot.Storages.GetAndChange(key, () => new StorageItem(new CandidateState())); CandidateState state = item.GetInteroperable(); state.Registered = true; - return true; } - [ContractMethod(0_05000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.PublicKey }, ParameterNames = new[] { "pubkey" })] - private StackItem UnregisterCandidate(ApplicationEngine engine, Array args) + [ContractMethod(0_05000000, CallFlags.AllowModifyStates)] + private bool UnregisterCandidate(ApplicationEngine engine, ECPoint pubkey) { - ECPoint pubkey = args[0].GetSpan().AsSerializable(); if (!engine.CheckWitnessInternal(Contract.CreateSignatureRedeemScript(pubkey).ToScriptHash())) return false; - return UnregisterCandidate(engine.Snapshot, pubkey); - } - - private bool UnregisterCandidate(StoreView snapshot, ECPoint pubkey) - { StorageKey key = CreateStorageKey(Prefix_Candidate, pubkey); - if (snapshot.Storages.TryGet(key) is null) return true; - StorageItem item = snapshot.Storages.GetAndChange(key); + if (engine.Snapshot.Storages.TryGet(key) is null) return true; + StorageItem item = engine.Snapshot.Storages.GetAndChange(key); CandidateState state = item.GetInteroperable(); if (state.Votes.IsZero) - snapshot.Storages.Delete(key); + engine.Snapshot.Storages.Delete(key); else state.Registered = false; return true; } - [ContractMethod(5_00000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Array }, ParameterNames = new[] { "account", "pubkeys" })] - private StackItem Vote(ApplicationEngine engine, Array args) + [ContractMethod(5_00000000, CallFlags.AllowModifyStates)] + private bool Vote(ApplicationEngine engine, UInt160 account, ECPoint voteTo) { - UInt160 account = new UInt160(args[0].GetSpan()); - ECPoint voteTo = args[1].IsNull ? null : args[1].GetSpan().AsSerializable(); if (!engine.CheckWitnessInternal(account)) return false; - return Vote(engine.Snapshot, account, voteTo); + return VoteInternal(engine.Snapshot, account, voteTo); } - private bool Vote(StoreView snapshot, UInt160 account, ECPoint voteTo) + private bool VoteInternal(StoreView snapshot, UInt160 account, ECPoint voteTo) { StorageKey key_account = CreateAccountKey(account); if (snapshot.Storages.TryGet(key_account) is null) return false; @@ -205,13 +186,13 @@ private bool Vote(StoreView snapshot, UInt160 account, ECPoint voteTo) return true; } - [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] - private StackItem GetCandidates(ApplicationEngine engine, Array args) + [ContractMethod(1_00000000, CallFlags.AllowStates)] + public (ECPoint PublicKey, BigInteger Votes)[] GetCandidates(StoreView snapshot) { - return new Array(engine.ReferenceCounter, GetCandidates(engine.Snapshot).Select(p => new Struct(engine.ReferenceCounter, new StackItem[] { p.PublicKey.ToArray(), p.Votes }))); + return GetCandidatesInternal(snapshot).ToArray(); } - public IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetCandidates(StoreView snapshot) + private IEnumerable<(ECPoint PublicKey, BigInteger Votes)> GetCandidatesInternal(StoreView snapshot) { byte[] prefix_key = StorageKey.CreateSearchPrefix(Id, new[] { Prefix_Candidate }); return snapshot.Storages.Find(prefix_key).Select(p => @@ -221,23 +202,13 @@ private StackItem GetCandidates(ApplicationEngine engine, Array args) )).Where(p => p.Item2.Registered).Select(p => (p.Item1, p.Item2.Votes)); } - [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] - private StackItem GetValidators(ApplicationEngine engine, Array args) - { - return new Array(engine.ReferenceCounter, GetValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray())); - } - + [ContractMethod(1_00000000, CallFlags.AllowStates)] public ECPoint[] GetValidators(StoreView snapshot) { return GetCommitteeMembers(snapshot, ProtocolSettings.Default.MaxValidatorsCount).OrderBy(p => p).ToArray(); } - [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] - private StackItem GetCommittee(ApplicationEngine engine, Array args) - { - return new Array(engine.ReferenceCounter, GetCommittee(engine.Snapshot).Select(p => (StackItem)p.ToArray())); - } - + [ContractMethod(1_00000000, CallFlags.AllowStates)] public ECPoint[] GetCommittee(StoreView snapshot) { return GetCommitteeMembers(snapshot, ProtocolSettings.Default.MaxCommitteeMembersCount).OrderBy(p => p).ToArray(); @@ -251,15 +222,10 @@ public UInt160 GetCommitteeAddress(StoreView snapshot) private IEnumerable GetCommitteeMembers(StoreView snapshot, int count) { - return GetCandidates(snapshot).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count); - } - - [ContractMethod(1_00000000, ContractParameterType.Array, CallFlags.AllowStates)] - private StackItem GetNextBlockValidators(ApplicationEngine engine, Array args) - { - return new Array(engine.ReferenceCounter, GetNextBlockValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray())); + return GetCandidatesInternal(snapshot).OrderByDescending(p => p.Votes).ThenBy(p => p.PublicKey).Select(p => p.PublicKey).Take(count); } + [ContractMethod(1_00000000, CallFlags.AllowStates)] public ECPoint[] GetNextBlockValidators(StoreView snapshot) { StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_NextValidators)); diff --git a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs index 855d864c597..f8b190ef92e 100644 --- a/src/neo/SmartContract/Native/Tokens/Nep5Token.cs +++ b/src/neo/SmartContract/Native/Tokens/Nep5Token.cs @@ -1,10 +1,7 @@ -#pragma warning disable IDE0060 - using Neo.IO; using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract.Manifest; -using Neo.VM; using Neo.VM.Types; using System; using System.Collections.Generic; @@ -17,7 +14,9 @@ public abstract class Nep5Token : NativeContract where TState : AccountState, new() { public override string[] SupportedStandards { get; } = { "NEP-5", "NEP-10" }; + [ContractMethod(0, CallFlags.None)] public abstract string Symbol { get; } + [ContractMethod(0, CallFlags.None)] public abstract byte Decimals { get; } public BigInteger Factor { get; } @@ -103,30 +102,7 @@ internal protected virtual void Burn(ApplicationEngine engine, UInt160 account, engine.SendNotification(Hash, new Array(new StackItem[] { "Transfer", account.ToArray(), StackItem.Null, amount })); } - [ContractMethod(0, ContractParameterType.String, CallFlags.None, Name = "name")] - protected StackItem NameMethod(ApplicationEngine engine, Array args) - { - return Name; - } - - [ContractMethod(0, ContractParameterType.String, CallFlags.None, Name = "symbol")] - protected StackItem SymbolMethod(ApplicationEngine engine, Array args) - { - return Symbol; - } - - [ContractMethod(0, ContractParameterType.Integer, CallFlags.None, Name = "decimals")] - protected StackItem DecimalsMethod(ApplicationEngine engine, Array args) - { - return (uint)Decimals; - } - - [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates)] - protected StackItem TotalSupply(ApplicationEngine engine, Array args) - { - return TotalSupply(engine.Snapshot); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public virtual BigInteger TotalSupply(StoreView snapshot) { StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_TotalSupply)); @@ -134,12 +110,7 @@ public virtual BigInteger TotalSupply(StoreView snapshot) return new BigInteger(storage.Value); } - [ContractMethod(0_01000000, ContractParameterType.Integer, CallFlags.AllowStates, ParameterTypes = new[] { ContractParameterType.Hash160 }, ParameterNames = new[] { "account" })] - protected StackItem BalanceOf(ApplicationEngine engine, Array args) - { - return BalanceOf(engine.Snapshot, new UInt160(args[0].GetSpan())); - } - + [ContractMethod(0_01000000, CallFlags.AllowStates)] public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) { StorageItem storage = snapshot.Storages.TryGet(CreateAccountKey(account)); @@ -147,15 +118,7 @@ public virtual BigInteger BalanceOf(StoreView snapshot, UInt160 account) return storage.GetInteroperable().Balance; } - [ContractMethod(0_08000000, ContractParameterType.Boolean, CallFlags.AllowModifyStates, ParameterTypes = new[] { ContractParameterType.Hash160, ContractParameterType.Hash160, ContractParameterType.Integer }, ParameterNames = new[] { "from", "to", "amount" })] - protected StackItem Transfer(ApplicationEngine engine, Array args) - { - UInt160 from = new UInt160(args[0].GetSpan()); - UInt160 to = new UInt160(args[1].GetSpan()); - BigInteger amount = args[2].GetBigInteger(); - return Transfer(engine, from, to, amount); - } - + [ContractMethod(0_08000000, CallFlags.AllowModifyStates)] protected virtual bool Transfer(ApplicationEngine engine, UInt160 from, UInt160 to, BigInteger amount) { if (amount.Sign < 0) throw new ArgumentOutOfRangeException(nameof(amount)); diff --git a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs index 8c6d8eefef0..57b2a254918 100644 --- a/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs +++ b/tests/neo.UnitTests/SmartContract/Native/Tokens/UT_Nep5Token.cs @@ -2,10 +2,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Neo.Ledger; -using Neo.SmartContract; using Neo.SmartContract.Native.Tokens; -using Neo.VM; -using Neo.VM.Types; using System; using System.Numerics; @@ -37,9 +34,7 @@ public void TestTotalSupply() key.Id = test.Id; snapshot.Storages.Add(key, item); - ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); - StackItem stackItem = test.TotalSupply(ae, null); - stackItem.GetBigInteger().Should().Be(1); + test.TotalSupply(snapshot).Should().Be(1); } [TestMethod] @@ -60,9 +55,7 @@ public void TestTotalSupplyDecimal() snapshot.Storages.Add(key, item); - ApplicationEngine ae = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); - StackItem stackItem = test.TotalSupply(ae, null); - stackItem.GetBigInteger().Should().Be(10_000_000_000_000_000); + test.TotalSupply(snapshot).Should().Be(10_000_000_000_000_000); } public StorageKey CreateStorageKey(byte prefix, byte[] key = null) @@ -87,10 +80,5 @@ public class TestNep5Token : Nep5Token public override string Symbol => throw new NotImplementedException(); public override byte Decimals => 8; - - public new StackItem TotalSupply(ApplicationEngine engine, VM.Types.Array args) - { - return base.TotalSupply(engine, args); - } } } diff --git a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs index 656332aabc8..aa58ba157c8 100644 --- a/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs +++ b/tests/neo.UnitTests/SmartContract/Native/UT_NativeContract.cs @@ -31,7 +31,7 @@ public void TestInitialize() public void TestInvoke() { var snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + ApplicationEngine engine = new ApplicationEngine(TriggerType.System, null, snapshot, 0); engine.LoadScript(testNativeContract.Script); ByteString method1 = new ByteString(System.Text.Encoding.Default.GetBytes("wrongMethod")); @@ -51,15 +51,12 @@ public void TestInvoke() public void TestOnPersistWithArgs() { var snapshot = Blockchain.Singleton.GetSnapshot(); - ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); - VMArray args = new VMArray(); - VM.Types.Boolean result1 = new VM.Types.Boolean(false); - testNativeContract.TestOnPersist(engine1, args).Should().Be(result1); + ApplicationEngine engine1 = new ApplicationEngine(TriggerType.Application, null, snapshot, 0); + Assert.ThrowsException(() => testNativeContract.TestOnPersist(engine1)); ApplicationEngine engine2 = new ApplicationEngine(TriggerType.System, null, snapshot, 0); - VM.Types.Boolean result2 = new VM.Types.Boolean(true); - testNativeContract.TestOnPersist(engine2, args).Should().Be(result2); + testNativeContract.TestOnPersist(engine2); } [TestMethod] @@ -76,9 +73,9 @@ public class TestNativeContract : NativeContract public override int Id => 0x10000006; - public StackItem TestOnPersist(ApplicationEngine engine, VMArray args) + public void TestOnPersist(ApplicationEngine engine) { - return OnPersist(engine, args); + OnPersist(engine); } } }