From f81891ec9dce7858a8ff064b5fb2c98509a6cb01 Mon Sep 17 00:00:00 2001 From: doubiliu Date: Wed, 1 Jul 2020 17:10:02 +0800 Subject: [PATCH 1/6] init --- src/neo/Ledger/Blockchain.cs | 3 +- src/neo/Network/P2P/MessageCommand.cs | 2 + src/neo/Network/P2P/Payloads/InventoryType.cs | 3 +- src/neo/Network/P2P/Payloads/OraclePayload.cs | 72 ++++ .../P2P/Payloads/OracleResponseAttribute.cs | 59 +++ src/neo/Network/P2P/Payloads/Transaction.cs | 14 + .../P2P/Payloads/TransactionAttributeType.cs | 5 +- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 3 + .../SmartContract/Native/NativeContract.cs | 1 + .../Native/Oracle/OracleContract.cs | 224 ++++++++++ .../Native/Oracle/OracleRequest.cs | 81 ++++ .../Native/Oracle/RequestStatusType.cs | 10 + .../Native/Oracle/UT_OracleContract.cs | 404 ++++++++++++++++++ tests/neo.UnitTests/neo.UnitTests.csproj | 2 +- 14 files changed, 878 insertions(+), 5 deletions(-) create mode 100644 src/neo/Network/P2P/Payloads/OraclePayload.cs create mode 100644 src/neo/Network/P2P/Payloads/OracleResponseAttribute.cs create mode 100644 src/neo/SmartContract/Native/Oracle/OracleContract.cs create mode 100644 src/neo/SmartContract/Native/Oracle/OracleRequest.cs create mode 100644 src/neo/SmartContract/Native/Oracle/RequestStatusType.cs create mode 100644 tests/neo.UnitTests/SmartContract/Native/Oracle/UT_OracleContract.cs diff --git a/src/neo/Ledger/Blockchain.cs b/src/neo/Ledger/Blockchain.cs index 50cee9509d..fb2fbdc78e 100644 --- a/src/neo/Ledger/Blockchain.cs +++ b/src/neo/Ledger/Blockchain.cs @@ -86,8 +86,7 @@ public static Blockchain Singleton static Blockchain() { GenesisBlock.RebuildMerkleRoot(); - - NativeContract[] contracts = { NativeContract.GAS, NativeContract.NEO }; + NativeContract[] contracts = { NativeContract.GAS, NativeContract.NEO, NativeContract.Oracle }; using (ScriptBuilder sb = new ScriptBuilder()) { foreach (NativeContract contract in contracts) diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index 89c21597cf..3ec8b4ff26 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -40,6 +40,8 @@ public enum MessageCommand : byte Block = 0x2c, [ReflectionCache(typeof(ConsensusPayload))] Consensus = 0x2d, + [ReflectionCache(typeof(OraclePayload))] + Oracle = 0x2e, Reject = 0x2f, //SPV protocol diff --git a/src/neo/Network/P2P/Payloads/InventoryType.cs b/src/neo/Network/P2P/Payloads/InventoryType.cs index 0a1b831d12..6bc5d7bd26 100644 --- a/src/neo/Network/P2P/Payloads/InventoryType.cs +++ b/src/neo/Network/P2P/Payloads/InventoryType.cs @@ -4,6 +4,7 @@ public enum InventoryType : byte { TX = MessageCommand.Transaction, Block = MessageCommand.Block, - Consensus = MessageCommand.Consensus + Consensus = MessageCommand.Consensus, + Oracle = MessageCommand.Oracle, } } diff --git a/src/neo/Network/P2P/Payloads/OraclePayload.cs b/src/neo/Network/P2P/Payloads/OraclePayload.cs new file mode 100644 index 0000000000..ded1ba6c73 --- /dev/null +++ b/src/neo/Network/P2P/Payloads/OraclePayload.cs @@ -0,0 +1,72 @@ +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using System; +using System.IO; +using System.Linq; + +namespace Neo.Network.P2P.Payloads +{ + public class OraclePayload : IInventory + { + private const long MaxWitnessGas = 0_02000000; + + public ECPoint OraclePub; + public UInt256 RequestTxHash; + public byte[] ResponseTxSignature; + + public Witness[] Witnesses { get; set; } + + public int Size => + ResponseTxSignature.GetVarSize() + // Oracle Response Transaction Signature + OraclePub.Size + // Oracle Node Public key + Witnesses.GetVarSize() + // Witnesses + UInt256.Length; // RequestTx Hash + + public UInt256 Hash => new UInt256(Crypto.Hash256(this.GetHashData())); + + public InventoryType InventoryType => InventoryType.Oracle; + + void ISerializable.Deserialize(BinaryReader reader) + { + ((IVerifiable)this).DeserializeUnsigned(reader); + Witnesses = reader.ReadSerializableArray(1); + if (Witnesses.Length != 1) throw new FormatException(); + } + + void IVerifiable.DeserializeUnsigned(BinaryReader reader) + { + OraclePub = reader.ReadSerializable(); + RequestTxHash = reader.ReadSerializable(); + ResponseTxSignature = reader.ReadFixedBytes(64); + } + + public virtual void Serialize(BinaryWriter writer) + { + ((IVerifiable)this).SerializeUnsigned(writer); + writer.Write(Witnesses); + } + + void IVerifiable.SerializeUnsigned(BinaryWriter writer) + { + writer.Write(OraclePub); + writer.Write(RequestTxHash); + writer.Write(ResponseTxSignature); + } + + UInt160[] IVerifiable.GetScriptHashesForVerifying(StoreView snapshot) + { + return new[] { Contract.CreateSignatureRedeemScript(OraclePub).ToScriptHash() }; + } + + public bool Verify(StoreView snapshot) + { + ECPoint[] validators = NativeContract.Oracle.GetOracleValidators(snapshot); + if (!validators.Any(u => u.Equals(OraclePub))) return false; + return this.VerifyWitnesses(snapshot, MaxWitnessGas); + } + } +} diff --git a/src/neo/Network/P2P/Payloads/OracleResponseAttribute.cs b/src/neo/Network/P2P/Payloads/OracleResponseAttribute.cs new file mode 100644 index 0000000000..66f99715f5 --- /dev/null +++ b/src/neo/Network/P2P/Payloads/OracleResponseAttribute.cs @@ -0,0 +1,59 @@ +using Neo.IO; +using Neo.SmartContract; +using Neo.VM; +using Neo.VM.Types; +using System.IO; + +namespace Neo.Network.P2P.Payloads +{ + public class OracleResponseAttribute : TransactionAttribute, IInteroperable + { + public UInt256 RequestTxHash; + public long FilterCost; + public byte[] Data; + + public override int Size => + base.Size + // Base size + UInt256.Length + // Request tx hash + sizeof(long) + // Filter cost + (Data is null ? 1 : Data.GetVarSize()); // Data + + public override TransactionAttributeType Type => TransactionAttributeType.OracleResponse; + + public override bool AllowMultiple => false; + + protected override void DeserializeWithoutType(BinaryReader reader) + { + RequestTxHash = new UInt256(reader.ReadBytes(UInt256.Length)); + Data = reader.ReadByte() == 0x01 ? reader.ReadVarBytes(ushort.MaxValue) : null; + FilterCost = reader.ReadInt64(); + } + + protected override void SerializeWithoutType(BinaryWriter writer) + { + writer.Write(RequestTxHash); + if (Data != null) + { + writer.Write((byte)0x01); + writer.WriteVarBytes(Data); + } + else + { + writer.Write((byte)0x00); + } + writer.Write(FilterCost); + } + + public void FromStackItem(StackItem stackItem) => throw new System.NotImplementedException(); + + public StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Struct(referenceCounter) + { + RequestTxHash.ToArray(), + Data, + FilterCost + }; + } + } +} diff --git a/src/neo/Network/P2P/Payloads/Transaction.cs b/src/neo/Network/P2P/Payloads/Transaction.cs index 10591659ca..f231980915 100644 --- a/src/neo/Network/P2P/Payloads/Transaction.cs +++ b/src/neo/Network/P2P/Payloads/Transaction.cs @@ -5,6 +5,7 @@ using Neo.Persistence; using Neo.SmartContract; using Neo.SmartContract.Native; +using Neo.SmartContract.Native.Tokens; using Neo.VM; using Neo.VM.Types; using Neo.Wallets; @@ -290,6 +291,19 @@ public virtual VerifyResult Verify(StoreView snapshot, BigInteger totalSenderFee if (size > MaxTransactionSize) return VerifyResult.Invalid; long net_fee = NetworkFee - size * NativeContract.Policy.GetFeePerByte(snapshot); if (net_fee < 0) return VerifyResult.InsufficientFunds; + + var oracleResponse = attributes.OfType().FirstOrDefault(); + if (oracleResponse != null) + { + if (Sender != NativeContract.Oracle.Hash || oracleResponse.FilterCost < 0) + return VerifyResult.Invalid; + var request = NativeContract.Oracle.GetRequest(snapshot, oracleResponse.RequestTxHash); + if (request is null || request.Status != RequestStatusType.Request) + return VerifyResult.Invalid; + if (request.OracleFee < oracleResponse.FilterCost + NativeContract.Oracle.GetRequestBaseFee(snapshot) + NetworkFee + SystemFee) + return VerifyResult.Invalid; + } + if (!this.VerifyWitnesses(snapshot, net_fee)) return VerifyResult.Invalid; return VerifyResult.Succeed; } diff --git a/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs b/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs index f1e2d704da..49f6a0451e 100644 --- a/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs +++ b/src/neo/Network/P2P/Payloads/TransactionAttributeType.cs @@ -5,6 +5,9 @@ namespace Neo.Network.P2P.Payloads public enum TransactionAttributeType : byte { [ReflectionCache(typeof(Cosigner))] - Cosigner = 0x01 + Cosigner = 0x01, + + [ReflectionCache(typeof(OracleResponseAttribute))] + OracleResponse = 0x02 } } diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 9e73809cc1..0339a94a9a 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -107,6 +107,9 @@ private void OnMessage(Message msg) if (msg.Payload.Size <= Transaction.MaxTransactionSize) OnInventoryReceived((Transaction)msg.Payload); break; + case MessageCommand.Oracle: + OnInventoryReceived((OraclePayload)msg.Payload); + break; case MessageCommand.Verack: case MessageCommand.Version: throw new ProtocolViolationException(); diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index 4d649ac627..c5313c7373 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -24,6 +24,7 @@ public abstract class NativeContract public static NeoToken NEO { get; } = new NeoToken(); public static GasToken GAS { get; } = new GasToken(); public static PolicyContract Policy { get; } = new PolicyContract(); + public static OracleContract Oracle { get; } = new OracleContract(); [ContractMethod(0, CallFlags.None)] public abstract string Name { get; } diff --git a/src/neo/SmartContract/Native/Oracle/OracleContract.cs b/src/neo/SmartContract/Native/Oracle/OracleContract.cs new file mode 100644 index 0000000000..c9dfbf90e6 --- /dev/null +++ b/src/neo/SmartContract/Native/Oracle/OracleContract.cs @@ -0,0 +1,224 @@ +using Neo.Cryptography.ECC; +using Neo.IO; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native.Tokens; +using Neo.VM.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using Array = Neo.VM.Types.Array; + +namespace Neo.SmartContract.Native +{ + public class OracleContract : NativeContract + { + public override string Name => "Oracle"; + public override int Id => -5; + + private const byte Prefix_Validator = 37; + private const byte Prefix_RequestBaseFee = 13; + private const byte Prefix_RequestMaxValidHeight = 33; + private const byte Prefix_Request = 21; + private const byte Prefix_Response = 27; + + private const long ResponseTxMinFee = 1000; + private string[] SupportedProtocol = new string[] { "http", "https" }; + + public OracleContract() + { + Manifest.Features = ContractFeatures.HasStorage | ContractFeatures.Payable; + var events = new List(Manifest.Abi.Events) + { + new ContractEventDescriptor() + { + Name = "Request", + Parameters = new ContractParameterDefinition[] + { + new ContractParameterDefinition() + { + Name = "request", + Type = ContractParameterType.InteropInterface + } + } + } + }; + Manifest.Abi.Events = events.ToArray(); + } + + [ContractMethod(0_01000000, CallFlags.AllowStates)] + public bool Verify(ApplicationEngine engine) + { + UInt160 oracleAddress = GetOracleMultiSigAddress(engine.Snapshot); + return engine.CheckWitnessInternal(oracleAddress); + } + + [ContractMethod(0_01000000, CallFlags.AllowStates)] + public bool SetOracleValidators(ApplicationEngine engine, byte[] data) + { + ECPoint[] validators = data.AsSerializableArray(); + UInt160 committeeAddress = NEO.GetCommitteeAddress(engine.Snapshot); + if (validators.Length == 0 || !engine.CheckWitnessInternal(committeeAddress)) return false; + var storageItem = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Validator), () => new StorageItem()); + storageItem.Value = validators.ToByteArray(); + return true; + } + + [ContractMethod(0_01000000, CallFlags.AllowStates)] + public ECPoint[] GetOracleValidators(StoreView snapshot) + { + StorageKey key = CreateStorageKey(Prefix_Validator); + StorageItem item = snapshot.Storages.TryGet(key); + return item?.Value.AsSerializableArray(); + } + + public UInt160 GetOracleMultiSigAddress(StoreView snapshot) + { + ECPoint[] oracleValidators = GetOracleValidators(snapshot); + return Contract.CreateMultiSigContract(oracleValidators.Length - (oracleValidators.Length - 1) / 3, oracleValidators).ScriptHash; + } + + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + public bool SetRequestBaseFee(ApplicationEngine engine, long requestBaseFee) + { + UInt160 account = NEO.GetCommitteeAddress(engine.Snapshot); + if (!engine.CheckWitnessInternal(account)) return false; + if (requestBaseFee <= 0) return false; + StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_RequestBaseFee), () => new StorageItem()); + storage.Value = BitConverter.GetBytes(requestBaseFee); + return true; + } + + [ContractMethod(0_01000000, CallFlags.AllowStates)] + public long GetRequestBaseFee(StoreView snapshot) + { + StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_RequestBaseFee)); + if (storage is null) return 0; + return BitConverter.ToInt64(storage.Value); + } + + [ContractMethod(0_03000000, CallFlags.AllowModifyStates)] + public bool SetRequestMaxValidHeight(ApplicationEngine engine, uint ValidHeight) + { + UInt160 committeeAddress = NEO.GetCommitteeAddress(engine.Snapshot); + if (!engine.CheckWitnessInternal(committeeAddress)) return false; + StorageItem storage = engine.Snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_RequestMaxValidHeight), () => new StorageItem()); + storage.Value = BitConverter.GetBytes(ValidHeight); + return true; + } + + [ContractMethod(0_01000000, CallFlags.AllowStates)] + public uint GetRequestMaxValidHeight(StoreView snapshot) + { + StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_RequestMaxValidHeight)); + if (storage is null) return 0; + return BitConverter.ToUInt32(storage.Value); + } + + [ContractMethod(0_01000000, CallFlags.All)] + public bool Request(ApplicationEngine engine, string url, string filterPath, string callbackMethod, long oracleFee) + { + Transaction tx = (Transaction)engine.GetScriptContainer(); + var requestKey = CreateRequestKey(tx.Hash); + if (engine.Snapshot.Storages.TryGet(requestKey) != null) throw new ArgumentException("One transaction can only request once"); + if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) throw new ArgumentException("It's not a valid request"); + if (!SupportedProtocol.Contains(uri.Scheme.ToLowerInvariant())) throw new ArgumentException($"The scheme '{uri.Scheme}' is not allowed"); + if (oracleFee < GetRequestBaseFee(engine.Snapshot) + ResponseTxMinFee) throw new InvalidOperationException("OracleFee is not enough"); + + // OracleFee = RequestBaseFee + FilterCost + ResponseTxFee + // FilterCost = Size of the requested data * GasPerByte + // ResponseTxFee = ResponseTx.NetwrokFee + ResponseTx.SystemFee + + engine.AddGas(oracleFee); + GAS.Mint(engine, Hash, oracleFee - GetRequestBaseFee(engine.Snapshot)); // pay response tx + + OracleRequest request = new OracleRequest() + { + Url = url, + FilterPath = filterPath, + CallbackContract = engine.CallingScriptHash, + CallbackMethod = callbackMethod, + OracleFee = oracleFee, + RequestTxHash = tx.Hash, + ValidHeight = engine.GetBlockchainHeight() + GetRequestMaxValidHeight(engine.Snapshot), + Status = RequestStatusType.Request + }; + engine.Snapshot.Storages.Add(requestKey, new StorageItem(request)); + engine.SendNotification(Hash, "Request", new Array() { StackItem.FromInterface(request) }); + return true; + } + + [ContractMethod(0_01000000, CallFlags.AllowStates)] + public OracleRequest GetRequest(StoreView snapshot, UInt256 requestTxHash) + { + return snapshot.Storages.TryGet(CreateRequestKey(requestTxHash))?.GetInteroperable(); + } + + [ContractMethod(0_01000000, CallFlags.AllowStates)] + public OracleResponseAttribute GetResponse(ApplicationEngine engine, UInt256 requestTxHash) + { + var item = engine.Snapshot.Storages.TryGet(CreateStorageKey(Prefix_Response, requestTxHash)); + if (item is null || item.Value is null) throw new ArgumentException("Response dose not exist"); + var responseTxHash = new UInt256(item.Value); + return engine.Snapshot.Transactions.TryGet(responseTxHash).Transaction.Attributes.OfType().First(); + } + + private bool Response(ApplicationEngine engine, UInt256 responseTxHash, OracleResponseAttribute response) + { + OracleRequest request = engine.Snapshot.Storages.TryGet(CreateRequestKey(response.RequestTxHash))?.GetInteroperable(); + if (request is null || request.Status != RequestStatusType.Request || request.ValidHeight < engine.Snapshot.Height) return false; + request.Status = RequestStatusType.Ready; + engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Response, response.RequestTxHash), new StorageItem() { Value = responseTxHash.ToArray() }); + return true; + } + + [ContractMethod(0_01000000, CallFlags.All)] + public void Callback(ApplicationEngine engine) + { + UInt160 oracleAddress = GetOracleMultiSigAddress(engine.Snapshot); + if (!engine.CheckWitnessInternal(oracleAddress)) throw new InvalidOperationException(); + Transaction tx = (Transaction)engine.ScriptContainer; + if (tx is null) throw new InvalidOperationException(); + OracleResponseAttribute response = tx.Attributes.OfType().FirstOrDefault(); + if (response is null) throw new InvalidOperationException(); + StorageKey requestKey = CreateRequestKey(response.RequestTxHash); + OracleRequest request = engine.Snapshot.Storages.GetAndChange(requestKey)?.GetInteroperable(); + if (request is null || request.Status != RequestStatusType.Ready) throw new InvalidOperationException(); + + engine.CallFromNativeContract(() => + { + request.Status = RequestStatusType.Successed; + }, request.CallbackContract, request.CallbackMethod, response.Data); + } + + protected override void OnPersist(ApplicationEngine engine) + { + base.OnPersist(engine); + foreach (Transaction tx in engine.Snapshot.PersistingBlock.Transactions) + { + OracleResponseAttribute response = tx.Attributes.OfType().FirstOrDefault(); + if (response is null) continue; + if (Response(engine, tx.Hash, response)) + { + UInt160[] oracleNodes = GetOracleValidators(engine.Snapshot).Select(p => Contract.CreateSignatureContract(p).ScriptHash).ToArray(); + long nodeReward = (response.FilterCost + GetRequestBaseFee(engine.Snapshot)) / oracleNodes.Length; + foreach (UInt160 account in oracleNodes) + GAS.Mint(engine, account, nodeReward); + + OracleRequest request = engine.Snapshot.Storages.TryGet(CreateRequestKey(response.RequestTxHash))?.GetInteroperable(); + long refund = request.OracleFee - response.FilterCost - GetRequestBaseFee(engine.Snapshot) - tx.NetworkFee - tx.SystemFee; + Transaction requestTx = engine.Snapshot.Transactions.TryGet(request.RequestTxHash).Transaction; + GAS.Mint(engine, requestTx.Sender, refund); + GAS.Burn(engine, Hash, refund + response.FilterCost); + } + } + } + + private StorageKey CreateRequestKey(UInt256 requestTxHash) + { + return CreateStorageKey(Prefix_Request, requestTxHash.ToArray()); + } + } +} diff --git a/src/neo/SmartContract/Native/Oracle/OracleRequest.cs b/src/neo/SmartContract/Native/Oracle/OracleRequest.cs new file mode 100644 index 0000000000..03edb4955a --- /dev/null +++ b/src/neo/SmartContract/Native/Oracle/OracleRequest.cs @@ -0,0 +1,81 @@ +using Neo.IO; +using Neo.VM; +using Neo.VM.Types; +using System.IO; + +namespace Neo.SmartContract.Native.Tokens +{ + public class OracleRequest : IInteroperable, ISerializable + { + public UInt256 RequestTxHash; + public string FilterPath; + public UInt160 CallbackContract; + public string CallbackMethod; + public uint ValidHeight; + public long OracleFee; + public string Url; + public RequestStatusType Status; + + public virtual int Size => + UInt256.Length + // Request Tx Hash + FilterPath.GetVarSize() + // Filter Path + UInt160.Length + // Callback Contract + CallbackMethod.GetVarSize() + // Callback Method + sizeof(uint) + // Valid Height + sizeof(long) + // Oracle Fee + Url.GetVarSize() + // Url + sizeof(byte); // Status + + public virtual void Serialize(BinaryWriter writer) + { + writer.Write(RequestTxHash); + writer.WriteVarString(FilterPath); + writer.Write(CallbackContract); + writer.WriteVarString(CallbackMethod); + writer.Write(ValidHeight); + writer.Write(OracleFee); + writer.WriteVarString(Url); + writer.Write((byte)Status); + } + + public virtual void Deserialize(BinaryReader reader) + { + RequestTxHash = new UInt256(reader.ReadBytes(UInt160.Length)); + FilterPath = reader.ReadVarString(); + CallbackContract = new UInt160(reader.ReadBytes(UInt160.Length)); + CallbackMethod = reader.ReadVarString(); + ValidHeight = reader.ReadUInt32(); + OracleFee = reader.ReadInt64(); + Url = reader.ReadVarString(); + Status = (RequestStatusType)reader.ReadByte(); + } + + public virtual void FromStackItem(StackItem stackItem) + { + Struct @struct = (Struct)stackItem; + RequestTxHash = @struct[0].GetSpan().AsSerializable(); + FilterPath = @struct[1].GetString(); + CallbackContract = @struct[2].GetSpan().AsSerializable(); + CallbackMethod = @struct[3].GetString(); + ValidHeight = (uint)@struct[4].GetInteger(); + OracleFee = (long)@struct[5].GetInteger(); + Url = ((Struct)stackItem)[6].GetString(); + Status = (RequestStatusType)@struct[7].GetSpan().ToArray()[0]; + } + + public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) + { + return new Struct(referenceCounter) + { + RequestTxHash.ToArray(), + FilterPath, + CallbackContract.ToArray(), + CallbackMethod, + ValidHeight, + OracleFee, + Url, + new byte[]{ (byte)Status } + }; + } + } +} diff --git a/src/neo/SmartContract/Native/Oracle/RequestStatusType.cs b/src/neo/SmartContract/Native/Oracle/RequestStatusType.cs new file mode 100644 index 0000000000..620809c79d --- /dev/null +++ b/src/neo/SmartContract/Native/Oracle/RequestStatusType.cs @@ -0,0 +1,10 @@ +namespace Neo.SmartContract.Native.Tokens +{ + public enum RequestStatusType : byte + { + Request = 0x00, + Ready = 0x01, + Successed = 0x02, + Failed = 0x03 + } +} diff --git a/tests/neo.UnitTests/SmartContract/Native/Oracle/UT_OracleContract.cs b/tests/neo.UnitTests/SmartContract/Native/Oracle/UT_OracleContract.cs new file mode 100644 index 0000000000..c72f98b178 --- /dev/null +++ b/tests/neo.UnitTests/SmartContract/Native/Oracle/UT_OracleContract.cs @@ -0,0 +1,404 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.IO; +using Neo.Ledger; +using Neo.Network.P2P.Payloads; +using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native; +using Neo.SmartContract.Native.Tokens; +using Neo.VM; +using Neo.VM.Types; +using Neo.Wallets; +using System; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using static Neo.UnitTests.Extensions.Nep5NativeContractExtensions; + +namespace Neo.UnitTests.SmartContract.Native +{ + [TestClass] + public class UT_OracleContract + { + [TestInitialize] + public void TestSetup() + { + TestBlockchain.InitializeMockNeoSystem(); + } + + internal static StorageKey CreateStorageKey(byte prefix, byte[] key = null) + { + StorageKey storageKey = new StorageKey + { + Id = NativeContract.Oracle.Id, + Key = new byte[sizeof(byte) + (key?.Length ?? 0)] + }; + storageKey.Key[0] = prefix; + key?.CopyTo(storageKey.Key.AsSpan(1)); + return storageKey; + } + + [TestMethod] + public void Test_GetPerRequestFee() + { + var snapshot = Blockchain.Singleton.GetSnapshot(); + var script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "getRequestBaseFee"); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Integer)); + Assert.AreEqual(result, 0); + } + + [TestMethod] + public void Test_SetPerRequestFee() + { + var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); + + // Init + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + var from = NativeContract.NEO.GetCommitteeAddress(engine.Snapshot); + long value = 12345; + + // Set + var script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "setRequestBaseFee", value); + engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(from), snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Boolean)); + Assert.IsTrue((result as VM.Types.Boolean).GetBoolean()); + + // Set (wrong witness) + script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "setRequestBaseFee", value); + engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(null), snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Boolean)); + Assert.IsFalse((result as VM.Types.Boolean).GetBoolean()); + + // Set wrong (negative) + script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "setRequestBaseFee", -1); + engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(from), snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Boolean)); + Assert.IsFalse((result as VM.Types.Boolean).GetBoolean()); + + // Get + script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "getRequestBaseFee"); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Integer)); + Assert.AreEqual(result, value); + } + + [TestMethod] + public void Test_GetValidHeight() + { + var snapshot = Blockchain.Singleton.GetSnapshot(); + var script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "getRequestMaxValidHeight"); + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Integer)); + Assert.AreEqual(result, 0); + } + + [TestMethod] + public void Test_SetValidHeight() + { + var snapshot = Blockchain.Singleton.GetSnapshot().Clone(); + + // Init + var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + var from = NativeContract.NEO.GetCommitteeAddress(snapshot); + uint value = 123; + + // Set + var script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "setRequestMaxValidHeight", value); + engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(from), snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Boolean)); + Assert.IsTrue((result as VM.Types.Boolean).GetBoolean()); + + // Set (wrong witness) + script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "setRequestMaxValidHeight", value); + engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(null), snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Boolean)); + Assert.IsFalse((result as VM.Types.Boolean).GetBoolean()); + + // Get + script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "getRequestMaxValidHeight"); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Integer)); + Assert.AreEqual(result, value); + } + + [TestMethod] + public void Test_GetOracleValidators() + { + var snapshot = Blockchain.Singleton.GetSnapshot(); + + var from = NativeContract.NEO.GetCommitteeAddress(snapshot); + Neo.Cryptography.ECC.ECPoint[] defaultNodes = NativeContract.NEO.GetCommittee(snapshot); + var script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "setOracleValidators", defaultNodes.ToByteArray()); + var engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(from), snapshot, 0, true); + engine.LoadScript(script.ToArray()); + engine.Execute(); + script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "getOracleValidators"); + engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true); + engine.LoadScript(script.ToArray()); + + engine.Execute().Should().Be(VMState.HALT); + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Array)); + Assert.AreEqual(21, ((VM.Types.Array)result).Count); + } + + [TestMethod] + public void Check_Request() + { + var snapshot = Blockchain.Singleton.GetSnapshot(); + var request = new OracleRequest() + { + Url = "https://www.baidu.com/", + FilterPath = "dotest", + OracleFee = 10000 + }; + var ret_Request = Check_Request(snapshot, request, out UInt256 requestTxHash, out Transaction tx); + ret_Request.Result.GetBoolean().Should().BeTrue(); + ret_Request.State.Should().BeTrue(); + } + + internal static (bool State, StackItem Result) Check_Request(StoreView snapshot, OracleRequest request, out UInt256 requestTxHash, out Transaction tx) + { + var from = NativeContract.NEO.GetCommitteeAddress(snapshot); + Neo.Cryptography.ECC.ECPoint[] defaultNodes = NativeContract.NEO.GetCommittee(snapshot); + var script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, "setOracleValidators", defaultNodes.ToByteArray()); + var engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(from), snapshot, 0, true); + engine.LoadScript(script.ToArray()); + engine.Execute(); + + snapshot.PersistingBlock = new Block() { Index = 1000 }; + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + KeyPair keyPair = new KeyPair(privateKey); + UInt160 account = Contract.CreateSignatureRedeemScript(keyPair.PublicKey).ToScriptHash(); + + script = new ScriptBuilder(); + script.EmitAppCall(NativeContract.Oracle.Hash, + "request", + request.Url, + request.FilterPath, + "back", + request.OracleFee); + script.Emit(OpCode.RET); + script.Emit(OpCode.DROP); + script.Emit(OpCode.RET); + ContractManifest manifest = new ContractManifest() + { + Permissions = new[] { ContractPermission.DefaultPermission }, + Abi = new ContractAbi() + { + Hash = script.ToArray().ToScriptHash(), + Events = new ContractEventDescriptor[0], + Methods = new ContractMethodDescriptor[0] + }, + Features = ContractFeatures.NoProperty, + Groups = new ContractGroup[0], + SafeMethods = WildcardContainer.Create(), + Trusts = WildcardContainer.Create(), + Extra = null, + }; + manifest.Abi.Methods = new ContractMethodDescriptor[] + { + new ContractMethodDescriptor() + { + Name = "testInvoke", + Parameters = new ContractParameterDefinition[0], + ReturnType = ContractParameterType.Void, + Offset=0x00 + }, + new ContractMethodDescriptor() + { + Name = "back", + Parameters =new ContractParameterDefinition[]{ + new ContractParameterDefinition(){ + Name="data", + Type=ContractParameterType.ByteArray + } + }, + ReturnType = ContractParameterType.Void, + Offset=script.ToArray().Length-2 + } + }; + ContractState contractState = new ContractState + { + Id = 0x43000000, + Script = script.ToArray(), + Manifest = manifest + }; + snapshot.Contracts.Add(contractState.ScriptHash, contractState); + + ScriptBuilder builder = new ScriptBuilder(); + builder.EmitAppCall(contractState.ScriptHash, "testInvoke"); + tx = new Transaction + { + Version = 0, + Nonce = (uint)1000, + Script = builder.ToArray(), + Sender = account, + ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement, + Attributes = new TransactionAttribute[0], + Witnesses = new Witness[] { new Witness + { + InvocationScript = System.Array.Empty(), + VerificationScript = Contract.CreateSignatureRedeemScript(keyPair.PublicKey) + }} + }; + var data = new ContractParametersContext(tx); + byte[] sig = data.Verifiable.Sign(keyPair); + tx.Witnesses[0].InvocationScript = sig; + requestTxHash = tx.Hash; + engine = ApplicationEngine.Run(builder.ToArray(), snapshot, tx, null, 0, true); + if (engine.State == VMState.FAULT) + { + return (false, false); + } + + var result = engine.ResultStack.Pop(); + result.Should().BeOfType(typeof(VM.Types.Boolean)); + return (true, result); + } + + [TestMethod] + public void Check_CallBack() + { + var snapshot = Blockchain.Singleton.GetSnapshot(); + var request = new OracleRequest() + { + Url = "https://www.baidu.com/", + FilterPath = "dotest", + CallbackMethod = "back", + OracleFee = 1000L + }; + var ret_Request = Check_Request(snapshot, request, out UInt256 requestTxHash, out Transaction tx); + ret_Request.Result.GetBoolean().Should().Be(true); + ret_Request.State.Should().BeTrue(); + snapshot.Transactions.Add(tx.Hash, new TransactionState() { Transaction = tx, VMState = VMState.HALT, BlockIndex = snapshot.PersistingBlock.Index }); + + byte[] privateKey = new byte[32]; + using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(privateKey); + } + KeyPair keyPair = new KeyPair(privateKey); + + OracleResponseAttribute response = new OracleResponseAttribute(); + response.RequestTxHash = requestTxHash; + response.Data = keyPair.PublicKey.ToArray(); + response.FilterCost = 0; + Transaction responsetx = CreateResponseTransaction(snapshot, response); + Console.WriteLine(responsetx.SystemFee); + } + + private static Transaction CreateResponseTransaction(StoreView initsnapshot, OracleResponseAttribute response) + { + StoreView snapshot = initsnapshot.Clone(); + + var oracleAddress = NativeContract.Oracle.GetOracleMultiSigAddress(snapshot); + ScriptBuilder sb = new ScriptBuilder(); + sb.EmitAppCall(NativeContract.Oracle.Hash, "onPersist"); + + var tx = new Transaction() + { + Version = 0, + ValidUntilBlock = snapshot.Height + Transaction.MaxValidUntilBlockIncrement, + Attributes = new TransactionAttribute[]{ + new Cosigner() + { + Account = oracleAddress, + AllowedContracts = new UInt160[]{ NativeContract.Oracle.Hash }, + Scopes = WitnessScope.CustomContracts + }, + response + }, + Sender = oracleAddress, + Witnesses = new Witness[0], + Script = sb.ToArray(), + NetworkFee = 0, + Nonce = 0, + SystemFee = 0 + }; + + snapshot.PersistingBlock = new Block() { Index = snapshot.Height + 1, Transactions = new Transaction[] { tx } }; + //commit response + var engine = new ApplicationEngine(TriggerType.System, null, snapshot, 0, true); + engine.LoadScript(sb.ToArray()); + if (engine.Execute() != VMState.HALT) throw new InvalidOperationException(); + + var sb2 = new ScriptBuilder(); + sb2.EmitAppCall(NativeContract.Oracle.Hash, "callback"); + + var state = new TransactionState + { + BlockIndex = snapshot.PersistingBlock.Index, + Transaction = tx + }; + snapshot.Transactions.Add(tx.Hash, state); + + var engine2 = ApplicationEngine.Run(sb2.ToArray(), snapshot, tx, testMode: true); + if (engine2.State != VMState.HALT) throw new ApplicationException(); + tx.SystemFee = engine2.GasConsumed; + // Calculate network fee + int size = tx.Size; + var oracleValidators = NativeContract.Oracle.GetOracleValidators(snapshot); + var oracleMultiContract = Contract.CreateMultiSigContract(oracleValidators.Length - (oracleValidators.Length - 1) / 3, oracleValidators); + tx.NetworkFee += Wallet.CalculateNetworkFee(oracleMultiContract.Script, ref size); + tx.NetworkFee += size * NativeContract.Policy.GetFeePerByte(snapshot); + return tx; + } + + } +} diff --git a/tests/neo.UnitTests/neo.UnitTests.csproj b/tests/neo.UnitTests/neo.UnitTests.csproj index c1d5a7200d..a588c7da5a 100644 --- a/tests/neo.UnitTests/neo.UnitTests.csproj +++ b/tests/neo.UnitTests/neo.UnitTests.csproj @@ -1,4 +1,4 @@ - + Exe From 9a3d9e8b73aef1f8802a5274e308b4e8fa215208 Mon Sep 17 00:00:00 2001 From: Tommo-L Date: Wed, 1 Jul 2020 17:18:33 +0800 Subject: [PATCH 2/6] fix response attr --- src/neo/Network/P2P/Payloads/OracleResponseAttribute.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/neo/Network/P2P/Payloads/OracleResponseAttribute.cs b/src/neo/Network/P2P/Payloads/OracleResponseAttribute.cs index 66f99715f5..cd886ceaa4 100644 --- a/src/neo/Network/P2P/Payloads/OracleResponseAttribute.cs +++ b/src/neo/Network/P2P/Payloads/OracleResponseAttribute.cs @@ -16,6 +16,7 @@ public class OracleResponseAttribute : TransactionAttribute, IInteroperable base.Size + // Base size UInt256.Length + // Request tx hash sizeof(long) + // Filter cost + 1 + // Data type, 0x01 means normal data, 0x00 means null (Data is null ? 1 : Data.GetVarSize()); // Data public override TransactionAttributeType Type => TransactionAttributeType.OracleResponse; From 17e25acf1486f70340e216d555561202eee902b6 Mon Sep 17 00:00:00 2001 From: doubiliu Date: Wed, 1 Jul 2020 23:12:50 +0800 Subject: [PATCH 3/6] Remove OraclePayload --- src/neo/Network/P2P/MessageCommand.cs | 2 - src/neo/Network/P2P/Payloads/InventoryType.cs | 1 - src/neo/Network/P2P/Payloads/OraclePayload.cs | 72 ------------------- .../Network/P2P/RemoteNode.ProtocolHandler.cs | 3 - 4 files changed, 78 deletions(-) delete mode 100644 src/neo/Network/P2P/Payloads/OraclePayload.cs diff --git a/src/neo/Network/P2P/MessageCommand.cs b/src/neo/Network/P2P/MessageCommand.cs index 3ec8b4ff26..89c21597cf 100644 --- a/src/neo/Network/P2P/MessageCommand.cs +++ b/src/neo/Network/P2P/MessageCommand.cs @@ -40,8 +40,6 @@ public enum MessageCommand : byte Block = 0x2c, [ReflectionCache(typeof(ConsensusPayload))] Consensus = 0x2d, - [ReflectionCache(typeof(OraclePayload))] - Oracle = 0x2e, Reject = 0x2f, //SPV protocol diff --git a/src/neo/Network/P2P/Payloads/InventoryType.cs b/src/neo/Network/P2P/Payloads/InventoryType.cs index 6bc5d7bd26..a68e773f10 100644 --- a/src/neo/Network/P2P/Payloads/InventoryType.cs +++ b/src/neo/Network/P2P/Payloads/InventoryType.cs @@ -5,6 +5,5 @@ public enum InventoryType : byte TX = MessageCommand.Transaction, Block = MessageCommand.Block, Consensus = MessageCommand.Consensus, - Oracle = MessageCommand.Oracle, } } diff --git a/src/neo/Network/P2P/Payloads/OraclePayload.cs b/src/neo/Network/P2P/Payloads/OraclePayload.cs deleted file mode 100644 index ded1ba6c73..0000000000 --- a/src/neo/Network/P2P/Payloads/OraclePayload.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Neo.Cryptography; -using Neo.Cryptography.ECC; -using Neo.IO; -using Neo.Persistence; -using Neo.SmartContract; -using Neo.SmartContract.Native; -using System; -using System.IO; -using System.Linq; - -namespace Neo.Network.P2P.Payloads -{ - public class OraclePayload : IInventory - { - private const long MaxWitnessGas = 0_02000000; - - public ECPoint OraclePub; - public UInt256 RequestTxHash; - public byte[] ResponseTxSignature; - - public Witness[] Witnesses { get; set; } - - public int Size => - ResponseTxSignature.GetVarSize() + // Oracle Response Transaction Signature - OraclePub.Size + // Oracle Node Public key - Witnesses.GetVarSize() + // Witnesses - UInt256.Length; // RequestTx Hash - - public UInt256 Hash => new UInt256(Crypto.Hash256(this.GetHashData())); - - public InventoryType InventoryType => InventoryType.Oracle; - - void ISerializable.Deserialize(BinaryReader reader) - { - ((IVerifiable)this).DeserializeUnsigned(reader); - Witnesses = reader.ReadSerializableArray(1); - if (Witnesses.Length != 1) throw new FormatException(); - } - - void IVerifiable.DeserializeUnsigned(BinaryReader reader) - { - OraclePub = reader.ReadSerializable(); - RequestTxHash = reader.ReadSerializable(); - ResponseTxSignature = reader.ReadFixedBytes(64); - } - - public virtual void Serialize(BinaryWriter writer) - { - ((IVerifiable)this).SerializeUnsigned(writer); - writer.Write(Witnesses); - } - - void IVerifiable.SerializeUnsigned(BinaryWriter writer) - { - writer.Write(OraclePub); - writer.Write(RequestTxHash); - writer.Write(ResponseTxSignature); - } - - UInt160[] IVerifiable.GetScriptHashesForVerifying(StoreView snapshot) - { - return new[] { Contract.CreateSignatureRedeemScript(OraclePub).ToScriptHash() }; - } - - public bool Verify(StoreView snapshot) - { - ECPoint[] validators = NativeContract.Oracle.GetOracleValidators(snapshot); - if (!validators.Any(u => u.Equals(OraclePub))) return false; - return this.VerifyWitnesses(snapshot, MaxWitnessGas); - } - } -} diff --git a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs index 0339a94a9a..9e73809cc1 100644 --- a/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs +++ b/src/neo/Network/P2P/RemoteNode.ProtocolHandler.cs @@ -107,9 +107,6 @@ private void OnMessage(Message msg) if (msg.Payload.Size <= Transaction.MaxTransactionSize) OnInventoryReceived((Transaction)msg.Payload); break; - case MessageCommand.Oracle: - OnInventoryReceived((OraclePayload)msg.Payload); - break; case MessageCommand.Verack: case MessageCommand.Version: throw new ProtocolViolationException(); From fca5cc7df6d92bb402856ff63dfc5bfae9ec1cb0 Mon Sep 17 00:00:00 2001 From: doubiliu Date: Thu, 2 Jul 2020 10:41:55 +0800 Subject: [PATCH 4/6] Fix bug --- .../Native/Oracle/OracleContract.cs | 2 +- .../Native/Oracle/OracleRequest.cs | 26 +------------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/src/neo/SmartContract/Native/Oracle/OracleContract.cs b/src/neo/SmartContract/Native/Oracle/OracleContract.cs index c9dfbf90e6..26c1535d34 100644 --- a/src/neo/SmartContract/Native/Oracle/OracleContract.cs +++ b/src/neo/SmartContract/Native/Oracle/OracleContract.cs @@ -160,7 +160,7 @@ public OracleRequest GetRequest(StoreView snapshot, UInt256 requestTxHash) public OracleResponseAttribute GetResponse(ApplicationEngine engine, UInt256 requestTxHash) { var item = engine.Snapshot.Storages.TryGet(CreateStorageKey(Prefix_Response, requestTxHash)); - if (item is null || item.Value is null) throw new ArgumentException("Response dose not exist"); + if (item is null || item.Value is null) throw new ArgumentException("Response does not exist"); var responseTxHash = new UInt256(item.Value); return engine.Snapshot.Transactions.TryGet(responseTxHash).Transaction.Attributes.OfType().First(); } diff --git a/src/neo/SmartContract/Native/Oracle/OracleRequest.cs b/src/neo/SmartContract/Native/Oracle/OracleRequest.cs index 03edb4955a..88cc2ffc19 100644 --- a/src/neo/SmartContract/Native/Oracle/OracleRequest.cs +++ b/src/neo/SmartContract/Native/Oracle/OracleRequest.cs @@ -5,7 +5,7 @@ namespace Neo.SmartContract.Native.Tokens { - public class OracleRequest : IInteroperable, ISerializable + public class OracleRequest : IInteroperable { public UInt256 RequestTxHash; public string FilterPath; @@ -26,30 +26,6 @@ public class OracleRequest : IInteroperable, ISerializable Url.GetVarSize() + // Url sizeof(byte); // Status - public virtual void Serialize(BinaryWriter writer) - { - writer.Write(RequestTxHash); - writer.WriteVarString(FilterPath); - writer.Write(CallbackContract); - writer.WriteVarString(CallbackMethod); - writer.Write(ValidHeight); - writer.Write(OracleFee); - writer.WriteVarString(Url); - writer.Write((byte)Status); - } - - public virtual void Deserialize(BinaryReader reader) - { - RequestTxHash = new UInt256(reader.ReadBytes(UInt160.Length)); - FilterPath = reader.ReadVarString(); - CallbackContract = new UInt160(reader.ReadBytes(UInt160.Length)); - CallbackMethod = reader.ReadVarString(); - ValidHeight = reader.ReadUInt32(); - OracleFee = reader.ReadInt64(); - Url = reader.ReadVarString(); - Status = (RequestStatusType)reader.ReadByte(); - } - public virtual void FromStackItem(StackItem stackItem) { Struct @struct = (Struct)stackItem; From 4d4e09b7c99f6b3d3f2c8e1cdaa45b3c15f1c7ce Mon Sep 17 00:00:00 2001 From: doubiliu Date: Thu, 2 Jul 2020 10:46:06 +0800 Subject: [PATCH 5/6] little change --- src/neo/SmartContract/Native/Oracle/OracleRequest.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/neo/SmartContract/Native/Oracle/OracleRequest.cs b/src/neo/SmartContract/Native/Oracle/OracleRequest.cs index 88cc2ffc19..b5ad8473d3 100644 --- a/src/neo/SmartContract/Native/Oracle/OracleRequest.cs +++ b/src/neo/SmartContract/Native/Oracle/OracleRequest.cs @@ -16,16 +16,6 @@ public class OracleRequest : IInteroperable public string Url; public RequestStatusType Status; - public virtual int Size => - UInt256.Length + // Request Tx Hash - FilterPath.GetVarSize() + // Filter Path - UInt160.Length + // Callback Contract - CallbackMethod.GetVarSize() + // Callback Method - sizeof(uint) + // Valid Height - sizeof(long) + // Oracle Fee - Url.GetVarSize() + // Url - sizeof(byte); // Status - public virtual void FromStackItem(StackItem stackItem) { Struct @struct = (Struct)stackItem; From c4214a585bc203a0f255071c839db4bc3cf6dbfa Mon Sep 17 00:00:00 2001 From: doubiliu Date: Thu, 2 Jul 2020 11:00:59 +0800 Subject: [PATCH 6/6] little change --- .../SmartContract/Native/Oracle/OracleRequest.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/neo/SmartContract/Native/Oracle/OracleRequest.cs b/src/neo/SmartContract/Native/Oracle/OracleRequest.cs index b5ad8473d3..b22e3e6aed 100644 --- a/src/neo/SmartContract/Native/Oracle/OracleRequest.cs +++ b/src/neo/SmartContract/Native/Oracle/OracleRequest.cs @@ -8,24 +8,24 @@ namespace Neo.SmartContract.Native.Tokens public class OracleRequest : IInteroperable { public UInt256 RequestTxHash; + public string Url; public string FilterPath; public UInt160 CallbackContract; public string CallbackMethod; public uint ValidHeight; public long OracleFee; - public string Url; public RequestStatusType Status; public virtual void FromStackItem(StackItem stackItem) { Struct @struct = (Struct)stackItem; RequestTxHash = @struct[0].GetSpan().AsSerializable(); - FilterPath = @struct[1].GetString(); - CallbackContract = @struct[2].GetSpan().AsSerializable(); - CallbackMethod = @struct[3].GetString(); - ValidHeight = (uint)@struct[4].GetInteger(); - OracleFee = (long)@struct[5].GetInteger(); - Url = ((Struct)stackItem)[6].GetString(); + Url = ((Struct)stackItem)[1].GetString(); + FilterPath = @struct[2].GetString(); + CallbackContract = @struct[3].GetSpan().AsSerializable(); + CallbackMethod = @struct[4].GetString(); + ValidHeight = (uint)@struct[5].GetInteger(); + OracleFee = (long)@struct[6].GetInteger(); Status = (RequestStatusType)@struct[7].GetSpan().ToArray()[0]; } @@ -34,12 +34,12 @@ public virtual StackItem ToStackItem(ReferenceCounter referenceCounter) return new Struct(referenceCounter) { RequestTxHash.ToArray(), + Url, FilterPath, CallbackContract.ToArray(), CallbackMethod, ValidHeight, OracleFee, - Url, new byte[]{ (byte)Status } }; }