diff --git a/src/neo/SmartContract/Native/NativeContract.cs b/src/neo/SmartContract/Native/NativeContract.cs index b28f55be14..86eedc68b0 100644 --- a/src/neo/SmartContract/Native/NativeContract.cs +++ b/src/neo/SmartContract/Native/NativeContract.cs @@ -4,6 +4,7 @@ using Neo.Ledger; using Neo.Persistence; using Neo.SmartContract.Manifest; +using Neo.SmartContract.Native.Oracle; using Neo.SmartContract.Native.Tokens; using Neo.VM; using Neo.VM.Types; @@ -24,6 +25,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 41d80ba88c..d68ca0f3f7 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 25aa788129..a045d5c12f 100644 --- a/tests/neo.UnitTests/neo.UnitTests.csproj +++ b/tests/neo.UnitTests/neo.UnitTests.csproj @@ -1,4 +1,4 @@ - + Exe @@ -31,4 +31,5 @@ +