Skip to content

Commit

Permalink
Test OracleService (#3475)
Browse files Browse the repository at this point in the history
* trigger https oracle

* make it internal

* return Task.CompletedTask

---------

Co-authored-by: Jimmy <[email protected]>
Co-authored-by: Jimmy <[email protected]>
Co-authored-by: Shargon <[email protected]>
  • Loading branch information
4 people committed Sep 6, 2024
1 parent 3d663f2 commit 2b118b4
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 45 deletions.
22 changes: 11 additions & 11 deletions src/Plugins/OracleService/OracleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class OracleService : Plugin, ICommittingHandler, IServiceAddedHandler, I
private readonly ConcurrentDictionary<ulong, OracleTask> pendingQueue = new ConcurrentDictionary<ulong, OracleTask>();
private readonly ConcurrentDictionary<ulong, DateTime> finishedCache = new ConcurrentDictionary<ulong, DateTime>();
private Timer timer;
private readonly CancellationTokenSource cancelSource = new CancellationTokenSource();
internal readonly CancellationTokenSource cancelSource = new CancellationTokenSource();
private OracleStatus status = OracleStatus.Unstarted;
private IWalletProvider walletProvider;
private int counter;
Expand Down Expand Up @@ -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 Task.CompletedTask;

if (wallet is null)
{
ConsoleHelper.Warning("Please open wallet first!");
return;
return Task.CompletedTask;
}

if (!CheckOracleAvaiblable(_system.StoreView, out ECPoint[] oracles))
if (!CheckOracleAvailable(_system.StoreView, out ECPoint[] oracles))
{
ConsoleHelper.Warning("The oracle service is unavailable");
return;
return Task.CompletedTask;
}
if (!CheckOracleAccount(wallet, oracles))
{
ConsoleHelper.Warning("There is no oracle account in wallet");
return;
return Task.CompletedTask;
}

this.wallet = wallet;
Expand All @@ -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")]
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -325,7 +325,7 @@ private async Task ProcessRequestAsync(DataCache snapshot, OracleRequest req)
}
}

private async void ProcessRequestsAsync()
private async Task ProcessRequestsAsync()
{
while (!cancelSource.IsCancellationRequested)
{
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/Plugins/OracleService/OracleService.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@
</None>
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="Neo.Plugins.OracleService.Tests" />
</ItemGroup>

</Project>
3 changes: 3 additions & 0 deletions src/Plugins/OracleService/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<string>()).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);
Expand Down
104 changes: 104 additions & 0 deletions tests/Neo.Plugins.OracleService.Tests/E2E_Https.cs
Original file line number Diff line number Diff line change
@@ -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<TransactionAttribute>(),
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();
}
}
}
62 changes: 57 additions & 5 deletions tests/Neo.Plugins.OracleService.Tests/TestBlockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TransactionAttribute>(),
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()
Expand Down
25 changes: 25 additions & 0 deletions tests/Neo.Plugins.OracleService.Tests/TestUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
}
}
}
6 changes: 0 additions & 6 deletions tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
26 changes: 3 additions & 23 deletions tests/Neo.Plugins.OracleService.Tests/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
}
},
"ProtocolConfiguration": {
"Network": 860833102,
"Network": 5195086,
"AddressVersion": 53,
"MillisecondsPerBlock": 15000,
"MaxTransactionsPerBlock": 512,
Expand All @@ -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",
Expand Down

0 comments on commit 2b118b4

Please sign in to comment.