diff --git a/src/neo/Network/P2P/Payloads/OracleResponse.cs b/src/neo/Network/P2P/Payloads/OracleResponse.cs index a05824dd69..9313d9fb63 100644 --- a/src/neo/Network/P2P/Payloads/OracleResponse.cs +++ b/src/neo/Network/P2P/Payloads/OracleResponse.cs @@ -4,6 +4,7 @@ using Neo.Persistence; using Neo.SmartContract.Native; using Neo.SmartContract.Native.Oracle; +using Neo.SmartContract.Native.Designate; using Neo.VM; using System; using System.IO; @@ -70,7 +71,7 @@ public override bool Verify(StoreView snapshot, Transaction tx) OracleRequest request = NativeContract.Oracle.GetRequest(snapshot, Id); if (request is null) return false; if (tx.NetworkFee + tx.SystemFee != request.GasForResponse) return false; - UInt160 oracleAccount = Blockchain.GetConsensusAddress(NativeContract.Oracle.GetOracleNodes(snapshot)); + UInt160 oracleAccount = Blockchain.GetConsensusAddress(NativeContract.Designate.GetDesignatedByRole(snapshot, Role.Oracle)); return tx.Signers.Any(p => p.Account.Equals(oracleAccount)); } } diff --git a/src/neo/SmartContract/Native/Designate/DesignateContract.cs b/src/neo/SmartContract/Native/Designate/DesignateContract.cs new file mode 100644 index 0000000000..437f7d95b9 --- /dev/null +++ b/src/neo/SmartContract/Native/Designate/DesignateContract.cs @@ -0,0 +1,70 @@ +#pragma warning disable IDE0051 + +using Neo.Cryptography; +using Neo.Cryptography.ECC; +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; +using System.Linq; + +namespace Neo.SmartContract.Native.Designate +{ + public sealed class DesignateContract : NativeContract + { + public override string Name => "Designation"; + public override int Id => -5; + + internal DesignateContract() + { + Manifest.Features = ContractFeatures.HasStorage; + } + + internal override void Initialize(ApplicationEngine engine) + { + foreach (byte role in Enum.GetValues(typeof(Role))) + { + engine.Snapshot.Storages.Add(CreateStorageKey(role), new StorageItem(new NodeList())); + } + } + + [ContractMethod(0_01000000, CallFlags.AllowStates)] + public ECPoint[] GetDesignatedByRole(StoreView snapshot, Role role) + { + if (!Enum.IsDefined(typeof(Role), role)) + throw new ArgumentOutOfRangeException(nameof(role)); + return snapshot.Storages[CreateStorageKey((byte)role)].GetInteroperable().ToArray(); + } + + [ContractMethod(0, CallFlags.AllowModifyStates)] + private void DesignateAsRole(ApplicationEngine engine, Role role, ECPoint[] nodes) + { + if (nodes.Length == 0) throw new ArgumentException(nameof(nodes)); + if (!Enum.IsDefined(typeof(Role), role)) + throw new ArgumentOutOfRangeException(nameof(role)); + if (!CheckCommittee(engine)) throw new InvalidOperationException(); + NodeList list = engine.Snapshot.Storages.GetAndChange(CreateStorageKey((byte)role)).GetInteroperable(); + list.Clear(); + list.AddRange(nodes); + list.Sort(); + } + + private class NodeList : List, IInteroperable + { + public void FromStackItem(StackItem stackItem) + { + foreach (StackItem item in (VM.Types.Array)stackItem) + Add(ECPoint.DecodePoint(item.GetSpan(), ECCurve.Secp256r1)); + } + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new VM.Types.Array(referenceCounter, this.Select(p => (StackItem)p.ToArray())); + } + } + } +} diff --git a/src/neo/SmartContract/Native/Designate/Role.cs b/src/neo/SmartContract/Native/Designate/Role.cs new file mode 100644 index 0000000000..f14af7962e --- /dev/null +++ b/src/neo/SmartContract/Native/Designate/Role.cs @@ -0,0 +1,8 @@ +namespace Neo.SmartContract.Native.Designate +{ + public enum Role : byte + { + StateValidator = 4, + Oracle = 8 + } +} diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 1de83bddac..cc0ab8f531 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -1,5 +1,6 @@ using Neo.IO; using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native.Designate; using Neo.SmartContract.Native.Oracle; using Neo.SmartContract.Native.Tokens; using Neo.VM; @@ -25,6 +26,7 @@ public abstract class NativeContract public static GasToken GAS { get; } = new GasToken(); public static PolicyContract Policy { get; } = new PolicyContract(); public static OracleContract Oracle { get; } = new OracleContract(); + public static DesignateContract Designate { get; } = new DesignateContract(); [ContractMethod(0, CallFlags.None)] public abstract string Name { get; } diff --git a/src/neo/SmartContract/Native/Oracle/OracleContract.Lists.cs b/src/neo/SmartContract/Native/Oracle/OracleContract.Lists.cs deleted file mode 100644 index de239bcb9c..0000000000 --- a/src/neo/SmartContract/Native/Oracle/OracleContract.Lists.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Neo.Cryptography.ECC; -using Neo.IO; -using Neo.VM; -using Neo.VM.Types; -using System.Collections.Generic; -using System.Linq; - -namespace Neo.SmartContract.Native.Oracle -{ - partial class OracleContract - { - private class IdList : List, IInteroperable - { - public void FromStackItem(StackItem stackItem) - { - foreach (StackItem item in (Array)stackItem) - Add((ulong)item.GetInteger()); - } - - public StackItem ToStackItem(ReferenceCounter referenceCounter) - { - return new Array(referenceCounter, this.Select(p => (Integer)p)); - } - } - - private class NodeList : List, IInteroperable - { - public void FromStackItem(StackItem stackItem) - { - foreach (StackItem item in (Array)stackItem) - Add(ECPoint.DecodePoint(item.GetSpan(), ECCurve.Secp256r1)); - } - - public StackItem ToStackItem(ReferenceCounter referenceCounter) - { - return new Array(referenceCounter, this.Select(p => (StackItem)p.ToArray())); - } - } - } -} diff --git a/src/neo/SmartContract/Native/Oracle/OracleContract.cs b/src/neo/SmartContract/Native/Oracle/OracleContract.cs index 79189a5274..1d91654f35 100644 --- a/src/neo/SmartContract/Native/Oracle/OracleContract.cs +++ b/src/neo/SmartContract/Native/Oracle/OracleContract.cs @@ -1,11 +1,12 @@ #pragma warning disable IDE0051 using Neo.Cryptography; -using Neo.Cryptography.ECC; using Neo.Ledger; using Neo.Network.P2P.Payloads; using Neo.Persistence; using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native.Designate; +using Neo.VM; using Neo.VM.Types; using System; using System.Collections.Generic; @@ -14,14 +15,13 @@ namespace Neo.SmartContract.Native.Oracle { - public sealed partial class OracleContract : NativeContract + public sealed class OracleContract : NativeContract { private const int MaxUrlLength = 256; private const int MaxFilterLength = 128; private const int MaxCallbackLength = 32; private const int MaxUserDataLength = 512; - private const byte Prefix_NodeList = 8; private const byte Prefix_RequestId = 9; private const byte Prefix_Request = 7; private const byte Prefix_IdList = 6; @@ -48,12 +48,6 @@ private void Finish(ApplicationEngine engine) engine.CallFromNativeContract(null, request.CallbackContract, request.CallbackMethod, request.Url, userData, (int)response.Code, response.Result); } - [ContractMethod(0_01000000, CallFlags.AllowStates)] - public ECPoint[] GetOracleNodes(StoreView snapshot) - { - return snapshot.Storages[CreateStorageKey(Prefix_NodeList)].GetInteroperable().ToArray(); - } - private UInt256 GetOriginalTxid(ApplicationEngine engine) { Transaction tx = (Transaction)engine.ScriptContainer; @@ -88,7 +82,6 @@ private static byte[] GetUrlHash(string url) internal override void Initialize(ApplicationEngine engine) { - engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_NodeList), new StorageItem(new NodeList())); engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_RequestId), new StorageItem(BitConverter.GetBytes(0ul))); } @@ -114,7 +107,7 @@ protected override void PostPersist(ApplicationEngine engine) if (list.Count == 0) engine.Snapshot.Storages.Delete(key); //Mint GAS for oracle nodes - nodes ??= GetOracleNodes(engine.Snapshot).Select(p => (Contract.CreateSignatureRedeemScript(p).ToScriptHash(), BigInteger.Zero)).ToArray(); + nodes ??= NativeContract.Designate.GetDesignatedByRole(engine.Snapshot, Role.Oracle).Select(p => (Contract.CreateSignatureRedeemScript(p).ToScriptHash(), BigInteger.Zero)).ToArray(); if (nodes.Length > 0) { int index = (int)(response.Id % (ulong)nodes.Length); @@ -167,22 +160,25 @@ private void Request(ApplicationEngine engine, string url, string filter, string engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_IdList).Add(GetUrlHash(url)), () => new StorageItem(new IdList())).GetInteroperable().Add(id); } - [ContractMethod(0, CallFlags.AllowModifyStates)] - private void SetOracleNodes(ApplicationEngine engine, ECPoint[] nodes) - { - if (nodes.Length == 0) throw new ArgumentException(); - if (!CheckCommittee(engine)) throw new InvalidOperationException(); - NodeList list = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_NodeList)).GetInteroperable(); - list.Clear(); - list.AddRange(nodes); - list.Sort(); - } - [ContractMethod(0_01000000, CallFlags.None)] private bool Verify(ApplicationEngine engine) { Transaction tx = (Transaction)engine.ScriptContainer; return tx?.GetAttribute() != null; } + + private class IdList : List, IInteroperable + { + public void FromStackItem(StackItem stackItem) + { + foreach (StackItem item in (VM.Types.Array)stackItem) + Add((ulong)item.GetInteger()); + } + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new VM.Types.Array(referenceCounter, this.Select(p => (Integer)p)); + } + } } }