From ca2776cee8fb8bf469f9fddebf8558bf723b8375 Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Mon, 2 Sep 2024 15:03:21 +0800 Subject: [PATCH 1/3] trigger https oracle --- src/Plugins/OracleService/OracleService.cs | 22 ++-- src/Plugins/OracleService/Settings.cs | 3 + .../E2E_Https.cs | 104 ++++++++++++++++++ .../TestBlockchain.cs | 62 ++++++++++- .../TestUtils.cs | 25 +++++ .../UT_OracleService.cs | 6 - .../config.json | 26 +---- 7 files changed, 203 insertions(+), 45 deletions(-) create mode 100644 tests/Neo.Plugins.OracleService.Tests/E2E_Https.cs diff --git a/src/Plugins/OracleService/OracleService.cs b/src/Plugins/OracleService/OracleService.cs index 27ced4e04c..e474b559ea 100644 --- a/src/Plugins/OracleService/OracleService.cs +++ b/src/Plugins/OracleService/OracleService.cs @@ -53,7 +53,7 @@ public class OracleService : Plugin, ICommittingHandler, IServiceAddedHandler, I private readonly ConcurrentDictionary pendingQueue = new ConcurrentDictionary(); private readonly ConcurrentDictionary finishedCache = new ConcurrentDictionary(); private Timer timer; - private readonly CancellationTokenSource cancelSource = new CancellationTokenSource(); + public readonly CancellationTokenSource cancelSource = new CancellationTokenSource(); private OracleStatus status = OracleStatus.Unstarted; private IWalletProvider walletProvider; private int counter; @@ -123,25 +123,25 @@ private void OnStart() Start(walletProvider?.GetWallet()); } - public void Start(Wallet wallet) + public Task Start(Wallet wallet) { - if (status == OracleStatus.Running) return; + if (status == OracleStatus.Running) return null; if (wallet is null) { ConsoleHelper.Warning("Please open wallet first!"); - return; + return null; } - if (!CheckOracleAvaiblable(_system.StoreView, out ECPoint[] oracles)) + if (!CheckOracleAvailable(_system.StoreView, out ECPoint[] oracles)) { ConsoleHelper.Warning("The oracle service is unavailable"); - return; + return null; } if (!CheckOracleAccount(wallet, oracles)) { ConsoleHelper.Warning("There is no oracle account in wallet"); - return; + return null; } this.wallet = wallet; @@ -150,7 +150,7 @@ public void Start(Wallet wallet) status = OracleStatus.Running; timer = new Timer(OnTimer, null, RefreshIntervalMilliSeconds, Timeout.Infinite); ConsoleHelper.Info($"Oracle started"); - ProcessRequestsAsync(); + return ProcessRequestsAsync(); } [ConsoleCommand("stop oracle", Category = "Oracle", Description = "Stop oracle service")] @@ -180,7 +180,7 @@ void ICommittingHandler.Blockchain_Committing_Handler(NeoSystem system, Block bl OnStart(); } if (status != OracleStatus.Running) return; - if (!CheckOracleAvaiblable(snapshot, out ECPoint[] oracles) || !CheckOracleAccount(wallet, oracles)) + if (!CheckOracleAvailable(snapshot, out ECPoint[] oracles) || !CheckOracleAccount(wallet, oracles)) OnStop(); } @@ -325,7 +325,7 @@ private async Task ProcessRequestAsync(DataCache snapshot, OracleRequest req) } } - private async void ProcessRequestsAsync() + private async Task ProcessRequestsAsync() { while (!cancelSource.IsCancellationRequested) { @@ -553,7 +553,7 @@ private bool CheckTxSign(DataCache snapshot, Transaction tx, ConcurrentDictionar return false; } - private static bool CheckOracleAvaiblable(DataCache snapshot, out ECPoint[] oracles) + private static bool CheckOracleAvailable(DataCache snapshot, out ECPoint[] oracles) { uint height = NativeContract.Ledger.CurrentIndex(snapshot) + 1; oracles = NativeContract.RoleManagement.GetDesignatedByRole(snapshot, Role.Oracle, height); diff --git a/src/Plugins/OracleService/Settings.cs b/src/Plugins/OracleService/Settings.cs index db93c1c400..08c074d363 100644 --- a/src/Plugins/OracleService/Settings.cs +++ b/src/Plugins/OracleService/Settings.cs @@ -9,6 +9,7 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Akka.Util.Internal; using Microsoft.Extensions.Configuration; using System; using System.Linq; @@ -59,6 +60,8 @@ private Settings(IConfigurationSection section) : base(section) MaxOracleTimeout = TimeSpan.FromMilliseconds(section.GetValue("MaxOracleTimeout", 15000)); AllowPrivateHost = section.GetValue("AllowPrivateHost", false); AllowedContentTypes = section.GetSection("AllowedContentTypes").GetChildren().Select(p => p.Get()).ToArray(); + if (AllowedContentTypes.Count() == 0) + AllowedContentTypes = AllowedContentTypes.Concat("application/json").ToArray(); Https = new HttpsSettings(section.GetSection("Https")); NeoFS = new NeoFSSettings(section.GetSection("NeoFS")); AutoStart = section.GetValue("AutoStart", false); diff --git a/tests/Neo.Plugins.OracleService.Tests/E2E_Https.cs b/tests/Neo.Plugins.OracleService.Tests/E2E_Https.cs new file mode 100644 index 0000000000..c404f3f982 --- /dev/null +++ b/tests/Neo.Plugins.OracleService.Tests/E2E_Https.cs @@ -0,0 +1,104 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// UT_OracleService.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Akka.Actor; +using Akka.TestKit.Xunit2; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Neo.Cryptography; +using Neo.Cryptography.ECC; +using Neo.Network.P2P.Payloads; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.Wallets; +using System; +using System.Linq; +using System.Threading.Tasks; +using static Neo.Plugins.OracleService.Tests.TestBlockchain; +using static Neo.Plugins.OracleService.Tests.TestUtils; + +namespace Neo.Plugins.OracleService.Tests +{ + [TestClass] + public class E2E_Https : TestKit + { + UInt160 customContract; + + [TestInitialize] + public void TestSetup() + { + customContract = InitializeContract(); + } + + [TestMethod] + public void TestE2EHttps() + { + byte[] script; + using (ScriptBuilder sb = new()) + { + sb.EmitDynamicCall(NativeContract.RoleManagement.Hash, "designateAsRole", + [Role.Oracle, + new ContractParameter() + { + Type = ContractParameterType.Array, + Value = settings.StandbyCommittee.Select( + p => new ContractParameter() { Type = ContractParameterType.PublicKey, Value = p }).ToList() + }]); + // Expected result: 12685221 + sb.EmitDynamicCall(customContract, "createRequest", + ["https://api.github.com/orgs/neo-project", "$.id", "callback", new byte[] { }, 1_0000_0000]); + script = sb.ToArray(); + } + Transaction[] txs = [ + new Transaction + { + Nonce = 233, + ValidUntilBlock = NativeContract.Ledger.CurrentIndex(s_theNeoSystem.GetSnapshotCache()) + s_theNeoSystem.Settings.MaxValidUntilBlockIncrement, + Signers = [new Signer() { Account = MultisigScriptHash, Scopes = WitnessScope.CalledByEntry }], + Attributes = Array.Empty(), + Script = script, + NetworkFee = 1000_0000, + SystemFee = 2_0000_0000, + } + ]; + byte[] signature = txs[0].Sign(s_walletAccount.GetKey(), settings.Network); + txs[0].Witnesses = [new Witness + { + InvocationScript = new byte[] { (byte)OpCode.PUSHDATA1, (byte)signature.Length }.Concat(signature).ToArray(), + VerificationScript = MultisigScript, + }]; + Block block = new Block + { + Header = new Header + { + Version = 0, + PrevHash = s_theNeoSystem.GenesisBlock.Hash, + Timestamp = s_theNeoSystem.GenesisBlock.Timestamp + 15_000, + Index = 1, + NextConsensus = s_theNeoSystem.GenesisBlock.NextConsensus, + }, + Transactions = txs, + }; + block.Header.MerkleRoot ??= MerkleTree.ComputeRoot(block.Transactions.Select(t => t.Hash).ToArray()); + signature = block.Sign(s_walletAccount.GetKey(), settings.Network); + block.Header.Witness = new Witness + { + InvocationScript = new byte[] { (byte)OpCode.PUSHDATA1, (byte)signature.Length }.Concat(signature).ToArray(), + VerificationScript = MultisigScript, + }; + s_theNeoSystem.Blockchain.Ask(block).Wait(); + Task t = s_oracle.Start(s_wallet); + t.Wait(TimeSpan.FromMilliseconds(900)); + s_oracle.cancelSource.Cancel(); + t.Wait(); + } + } +} diff --git a/tests/Neo.Plugins.OracleService.Tests/TestBlockchain.cs b/tests/Neo.Plugins.OracleService.Tests/TestBlockchain.cs index a4cc2f6fbf..160ac0407e 100644 --- a/tests/Neo.Plugins.OracleService.Tests/TestBlockchain.cs +++ b/tests/Neo.Plugins.OracleService.Tests/TestBlockchain.cs @@ -11,31 +11,83 @@ using Akka.Actor; using Neo.Ledger; +using Neo.Network.P2P.Payloads; using Neo.Persistence; +using Neo.SmartContract; +using Neo.SmartContract.Native; +using Neo.VM; +using Neo.VM.Types; +using Neo.Wallets; +using Neo.Wallets.NEP6; using System; namespace Neo.Plugins.OracleService.Tests { public static class TestBlockchain { - private static readonly NeoSystem s_theNeoSystem; - private static readonly MemoryStore s_store = new(); + public static readonly NeoSystem s_theNeoSystem; + public static readonly MemoryStore s_store = new(); + public static readonly NEP6Wallet s_wallet; + public static readonly WalletAccount s_walletAccount; + public static readonly OracleService s_oracle; private class StoreProvider : IStoreProvider { public string Name => "TestProvider"; - public IStore GetStore(string path) => s_store; } static TestBlockchain() { Console.WriteLine("initialize NeoSystem"); - s_theNeoSystem = new NeoSystem(ProtocolSettings.Load("config.json"), new StoreProvider()); + StoreProvider _memoryStoreProvider = new(); + s_oracle = new(); + s_theNeoSystem = new NeoSystem(TestUtils.settings, _memoryStoreProvider); + s_wallet = TestUtils.GenerateTestWallet("123"); + s_walletAccount = s_wallet.Import("KxuRSsHgJMb3AMSN6B9P3JHNGMFtxmuimqgR9MmXPcv3CLLfusTd"); } - public static void InitializeMockNeoSystem() + public static UInt160 InitializeContract() { + string _oracleContractSrc = """ +using System.Numerics;using Neo.SmartContract.Framework;using Neo.SmartContract.Framework.Native;using Neo.SmartContract.Framework.Services; +namespace oracle_demo{public class OracleDemo:SmartContract{ +const byte PREFIX_COUNT = 0xcc; +const byte PREFIX_DATA = 0xdd; +public static string GetRequstData() => Storage.Get(Storage.CurrentContext, new byte[] { PREFIX_DATA }); +public static BigInteger GetRequstCount() => (BigInteger)Storage.Get(Storage.CurrentContext, new byte[] { PREFIX_COUNT }); +public static void CreateRequest(string url, string filter, string callback, byte[] userData, long gasForResponse) => Oracle.Request(url, filter, callback, userData, gasForResponse); +public static void Callback(string url, byte[] userData, int code, byte[] result) +{ + ExecutionEngine.Assert(Runtime.CallingScriptHash == Oracle.Hash, "Unauthorized!"); + StorageContext currentContext = Storage.CurrentContext; + Storage.Put(currentContext, new byte[] { PREFIX_DATA }, (ByteString)result); + Storage.Put(currentContext, new byte[] { PREFIX_COUNT }, + (BigInteger)Storage.Get(currentContext, new byte[] { PREFIX_DATA }) + 1); +}}} +"""; + string base64NefFile = "TkVGM05lby5Db21waWxlci5DU2hhcnAgMy43LjQrNjAzNGExODIxY2E3MDk0NjBlYzMxMzZjNzBjMmRjYzNiZWEuLi4AAAFYhxcRfgqoEHKvq3HS3Yn+fEuS/gdyZXF1ZXN0BQAADwAAmAwB3dswQZv2Z85Bkl3oMUAMAczbMEGb9mfOQZJd6DFK2CYERRDbIUBXAAV8e3p5eDcAAEBXAQRBOVNuPAwUWIcXEX4KqBByr6tx0t2J/nxLkv6XDA1VbmF1dGhvcml6ZWQh4UGb9mfOcHvbKAwB3dswaEHmPxiEDAHd2zBoQZJd6DFK2CYERRDbIRGeDAHM2zBoQeY/GIRAnIyFhg=="; + string manifest = """{"name":"OracleDemo","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"getRequstData","parameters":[],"returntype":"String","offset":0,"safe":false},{"name":"getRequstCount","parameters":[],"returntype":"Integer","offset":16,"safe":false},{"name":"createRequest","parameters":[{"name":"url","type":"String"},{"name":"filter","type":"String"},{"name":"callback","type":"String"},{"name":"userData","type":"ByteArray"},{"name":"gasForResponse","type":"Integer"}],"returntype":"Void","offset":40,"safe":false},{"name":"callback","parameters":[{"name":"url","type":"String"},{"name":"userData","type":"ByteArray"},{"name":"code","type":"Integer"},{"name":"result","type":"ByteArray"}],"returntype":"Void","offset":52,"safe":false}],"events":[]},"permissions":[{"contract":"0xfe924b7cfe89ddd271abaf7210a80a7e11178758","methods":["request"]}],"trusts":[],"extra":{"nef":{"optimization":"All"}}}"""; + byte[] script; + using (ScriptBuilder sb = new()) + { + sb.EmitDynamicCall(NativeContract.ContractManagement.Hash, "deploy", Convert.FromBase64String(base64NefFile), manifest); + script = sb.ToArray(); + } + SnapshotCache snapshot = s_theNeoSystem.GetSnapshotCache(); + Transaction? tx = new Transaction + { + Nonce = 233, + ValidUntilBlock = NativeContract.Ledger.CurrentIndex(snapshot) + s_theNeoSystem.Settings.MaxValidUntilBlockIncrement, + Signers = [new Signer() { Account = TestUtils.ValidatorScriptHash, Scopes = WitnessScope.CalledByEntry }], + Attributes = System.Array.Empty(), + Script = script, + Witnesses = null, + }; + var engine = ApplicationEngine.Run(tx.Script, snapshot, container: tx, settings: s_theNeoSystem.Settings, gas: 1200_0000_0000); + engine.SnapshotCache.Commit(); + var result = (Neo.VM.Types.Array)engine.ResultStack.Peek(); + return new UInt160(result[2].GetSpan()); } internal static void ResetStore() diff --git a/tests/Neo.Plugins.OracleService.Tests/TestUtils.cs b/tests/Neo.Plugins.OracleService.Tests/TestUtils.cs index 7f1c3a58d5..a82be3ff9b 100644 --- a/tests/Neo.Plugins.OracleService.Tests/TestUtils.cs +++ b/tests/Neo.Plugins.OracleService.Tests/TestUtils.cs @@ -9,14 +9,27 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using FluentAssertions; using Neo.IO; +using Neo.Json; using Neo.SmartContract; using Neo.SmartContract.Native; +using Neo.Wallets; +using Neo.Wallets.NEP6; +using System; namespace Neo.Plugins.OracleService.Tests { public static class TestUtils { + public static readonly ProtocolSettings settings = ProtocolSettings.Load("config.json"); + public static readonly byte[] ValidatorScript = Contract.CreateSignatureRedeemScript(settings.StandbyCommittee[0]); + public static readonly UInt160 ValidatorScriptHash = ValidatorScript.ToScriptHash(); + public static readonly string ValidatorAddress = ValidatorScriptHash.ToAddress(ProtocolSettings.Default.AddressVersion); + public static readonly byte[] MultisigScript = Contract.CreateMultiSigRedeemScript(1, settings.StandbyCommittee); + public static readonly UInt160 MultisigScriptHash = MultisigScript.ToScriptHash(); + public static readonly string MultisigAddress = MultisigScriptHash.ToAddress(ProtocolSettings.Default.AddressVersion); + public static StorageKey CreateStorageKey(this NativeContract contract, byte prefix, ISerializable key) { var k = new KeyBuilder(contract.Id, prefix); @@ -28,5 +41,17 @@ public static StorageKey CreateStorageKey(this NativeContract contract, byte pre { return new KeyBuilder(contract.Id, prefix).AddBigEndian(value); } + + public static NEP6Wallet GenerateTestWallet(string password) + { + JObject wallet = new JObject(); + wallet["name"] = "noname"; + wallet["version"] = new Version("1.0").ToString(); + wallet["scrypt"] = new ScryptParameters(2, 1, 1).ToJson(); + wallet["accounts"] = new JArray(); + wallet["extra"] = null; + wallet.ToString().Should().Be("{\"name\":\"noname\",\"version\":\"1.0\",\"scrypt\":{\"n\":2,\"r\":1,\"p\":1},\"accounts\":[],\"extra\":null}"); + return new NEP6Wallet(null, password, settings, wallet); + } } } diff --git a/tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs b/tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs index 33d82c618c..3276c40c2f 100644 --- a/tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs +++ b/tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs @@ -21,12 +21,6 @@ namespace Neo.Plugins.OracleService.Tests [TestClass] public class UT_OracleService : TestKit { - [TestInitialize] - public void TestSetup() - { - TestBlockchain.InitializeMockNeoSystem(); - } - [TestMethod] public void TestFilter() { diff --git a/tests/Neo.Plugins.OracleService.Tests/config.json b/tests/Neo.Plugins.OracleService.Tests/config.json index 67bc3a62fa..edcd76127a 100644 --- a/tests/Neo.Plugins.OracleService.Tests/config.json +++ b/tests/Neo.Plugins.OracleService.Tests/config.json @@ -28,7 +28,7 @@ } }, "ProtocolConfiguration": { - "Network": 860833102, + "Network": 5195086, "AddressVersion": 53, "MillisecondsPerBlock": 15000, "MaxTransactionsPerBlock": 512, @@ -39,29 +39,9 @@ "HF_Basilisk": 4120000 }, "InitialGasDistribution": 5200000000000000, - "ValidatorsCount": 7, + "ValidatorsCount": 1, "StandbyCommittee": [ - "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c", - "02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093", - "03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a", - "02ca0e27697b9c248f6f16e085fd0061e26f44da85b58ee835c110caa5ec3ba554", - "024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d", - "02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e", - "02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70", - "023a36c72844610b4d34d1968662424011bf783ca9d984efa19a20babf5582f3fe", - "03708b860c1de5d87f5b151a12c2a99feebd2e8b315ee8e7cf8aa19692a9e18379", - "03c6aa6e12638b36e88adc1ccdceac4db9929575c3e03576c617c49cce7114a050", - "03204223f8c86b8cd5c89ef12e4f0dbb314172e9241e30c9ef2293790793537cf0", - "02a62c915cf19c7f19a50ec217e79fac2439bbaad658493de0c7d8ffa92ab0aa62", - "03409f31f0d66bdc2f70a9730b66fe186658f84a8018204db01c106edc36553cd0", - "0288342b141c30dc8ffcde0204929bb46aed5756b41ef4a56778d15ada8f0c6654", - "020f2887f41474cfeb11fd262e982051c1541418137c02a0f4961af911045de639", - "0222038884bbd1d8ff109ed3bdef3542e768eef76c1247aea8bc8171f532928c30", - "03d281b42002647f0113f36c7b8efb30db66078dfaaa9ab3ff76d043a98d512fde", - "02504acbc1f4b3bdad1d86d6e1a08603771db135a73e61c9d565ae06a1938cd2ad", - "0226933336f1b75baa42d42b71d9091508b638046d19abd67f4e119bf64a7cfb4d", - "03cdcea66032b82f5c30450e381e5295cae85c5e6943af716cc6b646352a6067dc", - "02cd5a5547119e24feaa7c2a0f37b8c9366216bab7054de0065c9be42084003c8a" + "0278ed78c917797b637a7ed6e7a9d94e8c408444c41ee4c0a0f310a256b9271eda" ], "SeedList": [ "seed1.neo.org:10333", From 70f7100216bf477cfff92c5119892f32da3eff8c Mon Sep 17 00:00:00 2001 From: Jimmy Date: Tue, 3 Sep 2024 15:58:04 +0800 Subject: [PATCH 2/3] make it internal --- src/Plugins/OracleService/OracleService.cs | 2 +- src/Plugins/OracleService/OracleService.csproj | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Plugins/OracleService/OracleService.cs b/src/Plugins/OracleService/OracleService.cs index e474b559ea..be7fe5c70f 100644 --- a/src/Plugins/OracleService/OracleService.cs +++ b/src/Plugins/OracleService/OracleService.cs @@ -53,7 +53,7 @@ public class OracleService : Plugin, ICommittingHandler, IServiceAddedHandler, I private readonly ConcurrentDictionary pendingQueue = new ConcurrentDictionary(); private readonly ConcurrentDictionary finishedCache = new ConcurrentDictionary(); private Timer timer; - public readonly CancellationTokenSource cancelSource = new CancellationTokenSource(); + internal readonly CancellationTokenSource cancelSource = new CancellationTokenSource(); private OracleStatus status = OracleStatus.Unstarted; private IWalletProvider walletProvider; private int counter; diff --git a/src/Plugins/OracleService/OracleService.csproj b/src/Plugins/OracleService/OracleService.csproj index bb5cde6754..c77ddb75d8 100644 --- a/src/Plugins/OracleService/OracleService.csproj +++ b/src/Plugins/OracleService/OracleService.csproj @@ -24,4 +24,8 @@ + + + + From 413b79c3ae565b50bea3cd51cfe080eeaf3ea5da Mon Sep 17 00:00:00 2001 From: Hecate2 <2474101468@qq.com> Date: Tue, 3 Sep 2024 18:29:24 +0800 Subject: [PATCH 3/3] return Task.CompletedTask --- src/Plugins/OracleService/OracleService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Plugins/OracleService/OracleService.cs b/src/Plugins/OracleService/OracleService.cs index be7fe5c70f..fbdee148db 100644 --- a/src/Plugins/OracleService/OracleService.cs +++ b/src/Plugins/OracleService/OracleService.cs @@ -125,23 +125,23 @@ private void OnStart() public Task Start(Wallet wallet) { - if (status == OracleStatus.Running) return null; + if (status == OracleStatus.Running) return Task.CompletedTask; if (wallet is null) { ConsoleHelper.Warning("Please open wallet first!"); - return null; + return Task.CompletedTask; } if (!CheckOracleAvailable(_system.StoreView, out ECPoint[] oracles)) { ConsoleHelper.Warning("The oracle service is unavailable"); - return null; + return Task.CompletedTask; } if (!CheckOracleAccount(wallet, oracles)) { ConsoleHelper.Warning("There is no oracle account in wallet"); - return null; + return Task.CompletedTask; } this.wallet = wallet;