diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs
index b7c7f85251..812de7591e 100644
--- a/src/neo/SmartContract/Native/NativeContract.cs
+++ b/src/neo/SmartContract/Native/NativeContract.cs
@@ -3,6 +3,7 @@
using Neo.IO;
using Neo.Ledger;
using Neo.SmartContract.Manifest;
+using Neo.SmartContract.Native.Oracle;
using Neo.SmartContract.Native.Tokens;
using Neo.VM;
using Neo.VM.Types;
@@ -23,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();
public abstract string ServiceName { get; }
public uint ServiceHash { get; }
diff --git a/src/neo/SmartContract/Native/Oracle/HttpConfig.cs b/src/neo/SmartContract/Native/Oracle/HttpConfig.cs
new file mode 100644
index 0000000000..877467c0f7
--- /dev/null
+++ b/src/neo/SmartContract/Native/Oracle/HttpConfig.cs
@@ -0,0 +1,7 @@
+namespace Neo.SmartContract.Native.Oracle
+{
+ public class HttpConfig
+ {
+ public const string Timeout = "HttpTimeout";
+ }
+}
diff --git a/src/neo/SmartContract/Native/Oracle/OracleContract.cs b/src/neo/SmartContract/Native/Oracle/OracleContract.cs
new file mode 100644
index 0000000000..c1cbd62932
--- /dev/null
+++ b/src/neo/SmartContract/Native/Oracle/OracleContract.cs
@@ -0,0 +1,235 @@
+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;
+using System.Numerics;
+using System.Text;
+using Array = Neo.VM.Types.Array;
+
+namespace Neo.SmartContract.Native.Oracle
+{
+ public sealed class OracleContract : NativeContract
+ {
+ public override string ServiceName => "Neo.Native.Oracle";
+
+ public override int Id => -4;
+
+ private const byte Prefix_Validator = 24;
+ private const byte Prefix_Config = 25;
+ private const byte Prefix_PerRequestFee = 26;
+
+ public OracleContract()
+ {
+ Manifest.Features = ContractFeatures.HasStorage;
+ }
+
+ internal override bool Initialize(ApplicationEngine engine)
+ {
+ if (!base.Initialize(engine)) return false;
+ if (GetPerRequestFee(engine.Snapshot) != 0) return false;
+
+ engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_Config, Encoding.UTF8.GetBytes(HttpConfig.Timeout)), new StorageItem
+ {
+ Value = new ByteArray(BitConverter.GetBytes(5000)).GetSpan().ToArray()
+ });
+ engine.Snapshot.Storages.Add(CreateStorageKey(Prefix_PerRequestFee), new StorageItem
+ {
+ Value = BitConverter.GetBytes(1000)
+ });
+ return true;
+ }
+
+ ///
+ /// Consensus node can delegate third party to operate Oracle nodes
+ ///
+ /// VM
+ /// Parameter Array
+ /// Returns true if the execution is successful, otherwise returns false
+ [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.ByteArray, ContractParameterType.ByteArray }, ParameterNames = new[] { "consignorPubKey", "consigneePubKey" })]
+ private StackItem DelegateOracleValidator(ApplicationEngine engine, Array args)
+ {
+ StoreView snapshot = engine.Snapshot;
+ ECPoint consignorPubKey = args[0].GetSpan().AsSerializable();
+ ECPoint consigneePubKey = args[1].GetSpan().AsSerializable();
+ ECPoint[] cnPubKeys = NativeContract.NEO.GetValidators(snapshot);
+ if (!cnPubKeys.Contains(consignorPubKey)) return false;
+ UInt160 account = Contract.CreateSignatureRedeemScript(consignorPubKey).ToScriptHash();
+ if (!InteropService.Runtime.CheckWitnessInternal(engine, account)) return false;
+ StorageKey key = CreateStorageKey(Prefix_Validator, consignorPubKey);
+ StorageItem item = snapshot.Storages.GetAndChange(key, () => new StorageItem());
+ item.Value = consigneePubKey.ToArray();
+
+ byte[] prefixKey = StorageKey.CreateSearchPrefix(Id, new[] { Prefix_Validator });
+ List delegatedOracleValidators = snapshot.Storages.Find(prefixKey).Select(p =>
+ (
+ p.Key.Key.AsSerializable(1)
+ )).ToList();
+ foreach (var validator in delegatedOracleValidators)
+ {
+ if (!cnPubKeys.Contains(validator))
+ {
+ snapshot.Storages.Delete(CreateStorageKey(Prefix_Validator, validator));
+ }
+ }
+ return true;
+ }
+
+ ///
+ /// Get current authorized Oracle validator.
+ ///
+ /// VM
+ /// Parameter Array
+ /// Authorized Oracle validator
+ [ContractMethod(0_01000000, ContractParameterType.Array)]
+ private StackItem GetOracleValidators(ApplicationEngine engine, Array args)
+ {
+ return new Array(engine.ReferenceCounter, GetOracleValidators(engine.Snapshot).Select(p => (StackItem)p.ToArray()));
+ }
+
+ ///
+ /// Get current authorized Oracle validator
+ ///
+ /// snapshot
+ /// Authorized Oracle validator
+ public ECPoint[] GetOracleValidators(StoreView snapshot)
+ {
+ ECPoint[] cnPubKeys = NativeContract.NEO.GetValidators(snapshot);
+ ECPoint[] oraclePubKeys = new ECPoint[cnPubKeys.Length];
+ System.Array.Copy(cnPubKeys, oraclePubKeys, cnPubKeys.Length);
+ for (int index = 0; index < oraclePubKeys.Length; index++)
+ {
+ var oraclePubKey = oraclePubKeys[index];
+ StorageKey key = CreateStorageKey(Prefix_Validator, oraclePubKey);
+ ECPoint delegatePubKey = snapshot.Storages.TryGet(key)?.Value.AsSerializable();
+ if (delegatePubKey != null) { oraclePubKeys[index] = delegatePubKey; }
+ }
+ return oraclePubKeys;
+ }
+
+ ///
+ /// Get number of current authorized Oracle validator
+ ///
+ /// VM
+ /// Parameter Array
+ /// The number of authorized Oracle validator
+ [ContractMethod(0_01000000, ContractParameterType.Integer)]
+ private StackItem GetOracleValidatorsCount(ApplicationEngine engine, Array args)
+ {
+ return GetOracleValidatorsCount(engine.Snapshot);
+ }
+
+ ///
+ /// Get number of current authorized Oracle validator
+ ///
+ /// snapshot
+ /// The number of authorized Oracle validator
+ public BigInteger GetOracleValidatorsCount(StoreView snapshot)
+ {
+ return GetOracleValidators(snapshot).Length;
+ }
+
+ ///
+ /// Create a Oracle multisignature address
+ ///
+ /// snapshot
+ /// Oracle multisignature address
+ public UInt160 GetOracleMultiSigAddress(StoreView snapshot)
+ {
+ ECPoint[] cnPubKeys = NativeContract.NEO.GetValidators(snapshot);
+ return Contract.CreateMultiSigRedeemScript(cnPubKeys.Length - (cnPubKeys.Length - 1) / 3, cnPubKeys).ToScriptHash();
+ }
+
+ ///
+ /// Set HttpConfig
+ ///
+ /// VM
+ /// Parameter Array
+ /// Returns true if the execution is successful, otherwise returns false
+ [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.String, ContractParameterType.ByteArray }, ParameterNames = new[] { "configKey", "configValue" })]
+ private StackItem SetConfig(ApplicationEngine engine, Array args)
+ {
+ StoreView snapshot = engine.Snapshot;
+ UInt160 account = GetOracleMultiSigAddress(snapshot);
+ if (!InteropService.Runtime.CheckWitnessInternal(engine, account)) return false;
+ string key = args[0].GetString();
+ ByteArray value = args[1].GetSpan().ToArray();
+ StorageItem storage = snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Config, Encoding.UTF8.GetBytes(key)));
+ storage.Value = value.GetSpan().ToArray();
+ return true;
+ }
+
+ ///
+ /// Get HttpConfig
+ ///
+ /// VM
+ /// value
+ [ContractMethod(0_01000000, ContractParameterType.Array, ParameterTypes = new[] { ContractParameterType.String }, ParameterNames = new[] { "configKey" })]
+ private StackItem GetConfig(ApplicationEngine engine, Array args)
+ {
+ StoreView snapshot = engine.Snapshot;
+ string key = args[0].GetString();
+ return GetConfig(snapshot, key);
+ }
+
+ ///
+ /// Get HttpConfig
+ ///
+ /// snapshot
+ /// key
+ /// value
+ public ByteArray GetConfig(StoreView snapshot, string key)
+ {
+ StorageItem storage = snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_Config, Encoding.UTF8.GetBytes(key)));
+ return storage.Value;
+ }
+
+ ///
+ /// Set PerRequestFee
+ ///
+ /// VM
+ /// Parameter Array
+ /// Returns true if the execution is successful, otherwise returns false
+ [ContractMethod(0_03000000, ContractParameterType.Boolean, ParameterTypes = new[] { ContractParameterType.Integer }, ParameterNames = new[] { "fee" })]
+ private StackItem SetPerRequestFee(ApplicationEngine engine, Array args)
+ {
+ StoreView snapshot = engine.Snapshot;
+ UInt160 account = GetOracleMultiSigAddress(snapshot);
+ if (!InteropService.Runtime.CheckWitnessInternal(engine, account)) return false;
+ int perRequestFee = (int)args[0].GetBigInteger();
+ if (perRequestFee <= 0) return false;
+ StorageItem storage = snapshot.Storages.GetAndChange(CreateStorageKey(Prefix_PerRequestFee));
+ storage.Value = BitConverter.GetBytes(perRequestFee);
+ return true;
+ }
+
+ ///
+ /// Get PerRequestFee
+ ///
+ /// VM
+ /// Parameter Array
+ /// Value
+ [ContractMethod(0_01000000, ContractParameterType.Integer, SafeMethod = true)]
+ private StackItem GetPerRequestFee(ApplicationEngine engine, Array args)
+ {
+ return new Integer(GetPerRequestFee(engine.Snapshot));
+ }
+
+ ///
+ /// Get PerRequestFee
+ ///
+ /// snapshot
+ /// Value
+ public int GetPerRequestFee(StoreView snapshot)
+ {
+ StorageItem storage = snapshot.Storages.TryGet(CreateStorageKey(Prefix_PerRequestFee));
+ if (storage is null) return 0;
+ return BitConverter.ToInt32(storage.Value);
+ }
+ }
+}
diff --git a/src/neo/neo.csproj b/src/neo/neo.csproj
index 075d17fb7f..7bc96cce8c 100644
--- a/src/neo/neo.csproj
+++ b/src/neo/neo.csproj
@@ -1,4 +1,4 @@
-
+
2015-2019 The Neo Project
@@ -30,4 +30,4 @@
-
+
\ No newline at end of file
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..330bce23a2
--- /dev/null
+++ b/tests/neo.UnitTests/SmartContract/Native/Oracle/UT_OracleContract.cs
@@ -0,0 +1,354 @@
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Neo.Cryptography.ECC;
+using Neo.IO;
+using Neo.Ledger;
+using Neo.Network.P2P.Payloads;
+using Neo.SmartContract;
+using Neo.SmartContract.Native;
+using Neo.SmartContract.Native.Tokens;
+using Neo.UnitTests.Wallets;
+using Neo.VM;
+using Neo.Wallets;
+using System;
+using System.Linq;
+using System.Numerics;
+using static Neo.UnitTests.Extensions.Nep5NativeContractExtensions;
+
+namespace Neo.UnitTests.Oracle
+{
+ [TestClass]
+ public class UT_OracleContract
+ {
+ [TestInitialize]
+ public void TestSetup()
+ {
+ TestBlockchain.InitializeMockNeoSystem();
+ }
+
+ [TestMethod]
+ public void TestInitialize()
+ {
+ var snapshot = Blockchain.Singleton.GetSnapshot();
+ var engine = new ApplicationEngine(TriggerType.System, null, snapshot, 0, true);
+
+ snapshot.Storages.Delete(CreateStorageKey(11));
+ snapshot.PersistingBlock = Blockchain.GenesisBlock;
+ engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true);
+ NativeContract.Oracle.Initialize(engine).Should().BeFalse(); // already registered
+ }
+ 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, "getPerRequestFee");
+ 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, 1000);
+ }
+
+ [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.Oracle.GetOracleMultiSigAddress(snapshot);
+ var value = 12345;
+
+ // Set
+ var script = new ScriptBuilder();
+ script.EmitAppCall(NativeContract.Oracle.Hash, "setPerRequestFee", new ContractParameter(ContractParameterType.Integer) { Value = 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).ToBoolean());
+
+ // Set (wrong witness)
+ script = new ScriptBuilder();
+ script.EmitAppCall(NativeContract.Oracle.Hash, "setPerRequestFee", new ContractParameter(ContractParameterType.Integer) { Value = 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).ToBoolean());
+
+ // Set wrong (negative)
+
+ script = new ScriptBuilder();
+ script.EmitAppCall(NativeContract.Oracle.Hash, "setPerRequestFee", new ContractParameter(ContractParameterType.Integer) { Value = -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).ToBoolean());
+
+ // Get
+
+ script = new ScriptBuilder();
+ script.EmitAppCall(NativeContract.Oracle.Hash, "getPerRequestFee");
+ 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_GetHttpConfig()
+ {
+ var snapshot = Blockchain.Singleton.GetSnapshot();
+ var script = new ScriptBuilder();
+ script.EmitAppCall(NativeContract.Oracle.Hash, "getConfig", new ContractParameter(ContractParameterType.String) { Value = "HttpTimeout" });
+ 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.ByteArray));
+ Assert.AreEqual(result.GetBigInteger(), 5000);
+ }
+
+ [TestMethod]
+ public void Test_SetConfig()
+ {
+ var snapshot = Blockchain.Singleton.GetSnapshot().Clone();
+
+ // Init
+
+ var engine = new ApplicationEngine(TriggerType.Application, null, snapshot, 0, true);
+ var from = NativeContract.Oracle.GetOracleMultiSigAddress(snapshot);
+ var key = "HttpTimeout";
+ var value = BitConverter.GetBytes(12345);
+
+ // Set (wrong witness)
+ var script = new ScriptBuilder();
+ script.EmitAppCall(NativeContract.Oracle.Hash, "setConfig", new ContractParameter(ContractParameterType.String) { Value = key }, new ContractParameter(ContractParameterType.ByteArray) { Value = value });
+ engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(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.Boolean));
+ Assert.IsFalse((result as VM.Types.Boolean).ToBoolean());
+
+ // Set good
+
+ script = new ScriptBuilder();
+ script.EmitAppCall(NativeContract.Oracle.Hash, "setConfig", new ContractParameter(ContractParameterType.String) { Value = key }, new ContractParameter(ContractParameterType.ByteArray) { Value = value });
+ 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.IsTrue((result as VM.Types.Boolean).ToBoolean());
+
+ // Get
+
+ script = new ScriptBuilder();
+ script.EmitAppCall(NativeContract.Oracle.Hash, "getConfig", new ContractParameter(ContractParameterType.String) { Value = key });
+ 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.ByteArray));
+ Assert.AreEqual(result.GetBigInteger(), new BigInteger(value));
+ }
+
+ [TestMethod]
+ public void Test_GetOracleValidators()
+ {
+ var snapshot = Blockchain.Singleton.GetSnapshot();
+
+ // Fake a oracle validator has cosignee.
+ ECPoint[] oraclePubKeys = NativeContract.Oracle.GetOracleValidators(snapshot);
+
+ ECPoint pubkey0 = oraclePubKeys[0]; // Validator0 is the cosignor
+ ECPoint cosignorPubKey = oraclePubKeys[1]; // Validator1 is the cosignee
+ var validator0Key = NativeContract.Oracle.CreateStorageKey(24, pubkey0); // 24 = Prefix_Validator
+ var validator0Value = new StorageItem()
+ {
+ Value = cosignorPubKey.ToArray()
+ };
+ snapshot.Storages.Add(validator0Key, validator0Value);
+
+ var script = new ScriptBuilder();
+ script.EmitAppCall(NativeContract.Oracle.Hash, "getOracleValidators");
+ 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.Array));
+ Assert.AreEqual(((VM.Types.Array)result).Count, 7);
+
+ // The validator0's cosignee should be the validator1
+ var validators = (VM.Types.Array)result;
+ var cosignee0Bytes = (VM.Types.ByteArray)validators[0];
+ var cosignee1Bytes = (VM.Types.ByteArray)validators[1];
+ Assert.AreEqual(cosignee0Bytes, cosignee1Bytes);
+ VM.Types.ByteArray validator1Bytes = cosignorPubKey.ToArray();
+ Assert.AreEqual(cosignee1Bytes, validator1Bytes);
+
+ // clear data
+ snapshot.Storages.Delete(validator0Key);
+ }
+
+ [TestMethod]
+ public void Test_GetOracleValidatorsCount()
+ {
+ var snapshot = Blockchain.Singleton.GetSnapshot();
+ var script = new ScriptBuilder();
+ script.EmitAppCall(NativeContract.Oracle.Hash, "getOracleValidatorsCount");
+ 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, 7);
+ }
+
+ [TestMethod]
+ public void Test_DelegateOracleValidator()
+ {
+ var snapshot = Blockchain.Singleton.GetSnapshot();
+
+ ECPoint[] oraclePubKeys = NativeContract.Oracle.GetOracleValidators(snapshot);
+
+ ECPoint pubkey0 = oraclePubKeys[0];
+
+ byte[] privateKey1 = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+ KeyPair keyPair1 = new KeyPair(privateKey1);
+ ECPoint pubkey1 = keyPair1.PublicKey;
+
+ byte[] privateKey2 = { 0x02,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+ KeyPair keyPair2 = new KeyPair(privateKey2);
+ ECPoint pubkey2 = keyPair2.PublicKey;
+
+ using ScriptBuilder sb = new ScriptBuilder();
+ sb.EmitAppCall(NativeContract.Oracle.Hash, "delegateOracleValidator", new ContractParameter
+ {
+ Type = ContractParameterType.ByteArray,
+ Value = pubkey0.ToArray()
+ }, new ContractParameter
+ {
+ Type = ContractParameterType.ByteArray,
+ Value = pubkey1.ToArray()
+ });
+
+ MyWallet wallet = new MyWallet();
+ WalletAccount account = wallet.CreateAccount(privateKey1);
+
+ // Fake balance
+ var key = NativeContract.GAS.CreateStorageKey(20, account.ScriptHash);
+ var entry = snapshot.Storages.GetAndChange(key, () => new StorageItem
+ {
+ Value = new Nep5AccountState().ToByteArray()
+ });
+ entry.Value = new Nep5AccountState()
+ {
+ Balance = 1000000 * NativeContract.GAS.Factor
+ }
+ .ToByteArray();
+
+ snapshot.Commit();
+
+ // Fake an nonexist validator in delegatedOracleValidators
+ byte[] fakerPrivateKey = { 0x01,0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02};
+ KeyPair fakerKeyPair = new KeyPair(fakerPrivateKey);
+ ECPoint fakerPubkey = fakerKeyPair.PublicKey;
+ var invalidOracleValidatorKey = NativeContract.Oracle.CreateStorageKey(24, fakerPubkey); // 24 = Prefix_Validator
+ var invalidOracleValidatorValue = new StorageItem()
+ {
+ Value = fakerPubkey.ToArray()
+ };
+ snapshot.Storages.Add(invalidOracleValidatorKey, invalidOracleValidatorValue);
+
+ var tx = wallet.MakeTransaction(sb.ToArray(), account.ScriptHash, new TransactionAttribute[] { });
+ ContractParametersContext context = new ContractParametersContext(tx);
+ wallet.Sign(context);
+ tx.Witnesses = context.GetWitnesses();
+
+ // wrong witness
+ var engine = new ApplicationEngine(TriggerType.Application, tx, snapshot, 0, true);
+ engine.LoadScript(tx.Script);
+ var state = engine.Execute();
+ state.Should().Be(VMState.HALT);
+ var result = engine.ResultStack.Pop();
+ result.Should().BeOfType(typeof(VM.Types.Boolean));
+ Assert.IsFalse((result as VM.Types.Boolean).ToBoolean());
+
+ //wrong witness
+ using ScriptBuilder sb2 = new ScriptBuilder();
+ sb2.EmitAppCall(NativeContract.Oracle.Hash, "delegateOracleValidator", new ContractParameter
+ {
+ Type = ContractParameterType.ByteArray,
+ Value = pubkey1.ToArray()
+ }, new ContractParameter
+ {
+ Type = ContractParameterType.ByteArray,
+ Value = pubkey2.ToArray()
+ });
+
+ var from = Contract.CreateSignatureContract(pubkey1).ScriptHash;
+
+ engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(from), snapshot, 0, true);
+ engine.LoadScript(sb2.ToArray());
+ state = engine.Execute();
+ state.Should().Be(VMState.HALT);
+ result = engine.ResultStack.Pop();
+ result.Should().BeOfType(typeof(VM.Types.Boolean));
+ Assert.IsFalse((result as VM.Types.Boolean).ToBoolean());
+
+ //correct
+ from = Contract.CreateSignatureContract(pubkey0).ScriptHash;
+
+ engine = new ApplicationEngine(TriggerType.Application, new ManualWitness(from), snapshot, 0, true);
+ engine.LoadScript(tx.Script.ToArray());
+
+ engine.Execute().Should().Be(VMState.HALT);
+ result = engine.ResultStack.Pop();
+ result.Should().BeOfType(typeof(VM.Types.Boolean));
+ Assert.IsTrue((result as VM.Types.Boolean).ToBoolean());
+
+ // The invalid oracle validator should be removed
+ Assert.IsNull(snapshot.Storages.TryGet(invalidOracleValidatorKey));
+
+ Test_GetOracleValidators();
+ }
+ }
+}
diff --git a/tests/neo.UnitTests/neo.UnitTests.csproj b/tests/neo.UnitTests/neo.UnitTests.csproj
index 5857ea504f..1e40bfea79 100644
--- a/tests/neo.UnitTests/neo.UnitTests.csproj
+++ b/tests/neo.UnitTests/neo.UnitTests.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -31,4 +31,5 @@
+