Skip to content

Commit

Permalink
Asynchronous Oracle (neo-project#1738)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzhang authored and Shawn committed Jan 8, 2021
1 parent a75516e commit 16082b8
Show file tree
Hide file tree
Showing 9 changed files with 415 additions and 15 deletions.
40 changes: 27 additions & 13 deletions src/neo/Ledger/Blockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class RelayResult { public IInventory Inventory; public VerifyResult Resu
Transactions = new[] { DeployNativeContracts() }
};

private readonly static Script onPersistNativeContractScript;
private readonly static Script onPersistScript, postPersistScript;
private const int MaxTxToReverifyPerIdle = 10;
private static readonly object lockObj = new object();
private readonly NeoSystem system;
Expand Down Expand Up @@ -87,16 +87,23 @@ static Blockchain()
{
GenesisBlock.RebuildMerkleRoot();

NativeContract[] contracts = { NativeContract.GAS, NativeContract.NEO };
using (ScriptBuilder sb = new ScriptBuilder())
{
foreach (NativeContract contract in contracts)
foreach (NativeContract contract in new NativeContract[] { NativeContract.GAS, NativeContract.NEO })
{
sb.EmitAppCall(contract.Hash, "onPersist");
sb.Emit(OpCode.DROP);
}

onPersistNativeContractScript = sb.ToArray();
onPersistScript = sb.ToArray();
}
using (ScriptBuilder sb = new ScriptBuilder())
{
foreach (NativeContract contract in new NativeContract[] { NativeContract.Oracle })
{
sb.EmitAppCall(contract.Hash, "postPersist");
sb.Emit(OpCode.DROP);
}
postPersistScript = sb.ToArray();
}
}

Expand Down Expand Up @@ -418,14 +425,12 @@ private void Persist(Block block)
snapshot.PersistingBlock = block;
if (block.Index > 0)
{
using (ApplicationEngine engine = ApplicationEngine.Create(TriggerType.System, null, snapshot))
{
engine.LoadScript(onPersistNativeContractScript);
if (engine.Execute() != VMState.HALT) throw new InvalidOperationException();
ApplicationExecuted application_executed = new ApplicationExecuted(engine);
Context.System.EventStream.Publish(application_executed);
all_application_executed.Add(application_executed);
}
using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.System, null, snapshot);
engine.LoadScript(onPersistScript);
if (engine.Execute() != VMState.HALT) throw new InvalidOperationException();
ApplicationExecuted application_executed = new ApplicationExecuted(engine);
Context.System.EventStream.Publish(application_executed);
all_application_executed.Add(application_executed);
}
snapshot.Blocks.Add(block.Hash, block.Trim());
StoreView clonedSnapshot = snapshot.Clone();
Expand Down Expand Up @@ -459,6 +464,15 @@ private void Persist(Block block)
}
}
snapshot.BlockHashIndex.GetAndChange().Set(block);
if (block.Index > 0)
{
using ApplicationEngine engine = ApplicationEngine.Create(TriggerType.System, null, snapshot);
engine.LoadScript(postPersistScript);
if (engine.Execute() != VMState.HALT) throw new InvalidOperationException();
ApplicationExecuted application_executed = new ApplicationExecuted(engine);
Context.System.EventStream.Publish(application_executed);
all_application_executed.Add(application_executed);
}
foreach (IPersistencePlugin plugin in Plugin.PersistencePlugins)
plugin.OnPersist(snapshot, all_application_executed);
snapshot.Commit();
Expand Down
77 changes: 77 additions & 0 deletions src/neo/Network/P2P/Payloads/OracleResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using Neo.IO;
using Neo.IO.Json;
using Neo.Ledger;
using Neo.Persistence;
using Neo.SmartContract.Native;
using Neo.SmartContract.Native.Oracle;
using Neo.VM;
using System;
using System.IO;
using System.Linq;

namespace Neo.Network.P2P.Payloads
{
public class OracleResponse : TransactionAttribute
{
private const int MaxResultSize = 1024;

public static readonly byte[] FixedScript;

public ulong Id;
public OracleResponseCode Code;
public byte[] Result;

public override TransactionAttributeType Type => TransactionAttributeType.OracleResponse;
public override bool AllowMultiple => false;

public override int Size => base.Size +
sizeof(ulong) + //Id
sizeof(OracleResponseCode) + //ResponseCode
Result.GetVarSize(); //Result

static OracleResponse()
{
using ScriptBuilder sb = new ScriptBuilder();
sb.EmitAppCall(NativeContract.Oracle.Hash, "finish");
FixedScript = sb.ToArray();
}

protected override void DeserializeWithoutType(BinaryReader reader)
{
Id = reader.ReadUInt64();
Code = (OracleResponseCode)reader.ReadByte();
if (!Enum.IsDefined(typeof(OracleResponseCode), Code))
throw new FormatException();
Result = reader.ReadVarBytes(MaxResultSize);
if (Code != OracleResponseCode.Success && Result.Length > 0)
throw new FormatException();
}

protected override void SerializeWithoutType(BinaryWriter writer)
{
writer.Write(Id);
writer.Write((byte)Code);
writer.WriteVarBytes(Result);
}

public override JObject ToJson()
{
JObject json = base.ToJson();
json["id"] = Id;
json["code"] = Code;
json["result"] = Convert.ToBase64String(Result);
return json;
}

public override bool Verify(StoreView snapshot, Transaction tx)
{
if (tx.Signers.Any(p => p.Scopes != WitnessScope.None)) return false;
if (!tx.Script.AsSpan().SequenceEqual(FixedScript)) return false;
OracleRequest request = NativeContract.Oracle.GetRequest(snapshot, Id);
if (request is null) return false;
if (tx.NetworkFee + tx.SystemFee != request.GasForResponse) return false;
UInt160 oracleAccount = Blockchain.GetConsensusAddress(NativeContract.Oracle.GetOracleNodes(snapshot));
return tx.Signers.Any(p => p.Account.Equals(oracleAccount));
}
}
}
13 changes: 13 additions & 0 deletions src/neo/Network/P2P/Payloads/OracleResponseCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Neo.Network.P2P.Payloads
{
public enum OracleResponseCode : byte
{
Success = 0x00,

NotFound = 0x10,
Timeout = 0x12,
Forbidden = 0x14,

Error = 0xff
}
}
4 changes: 3 additions & 1 deletion src/neo/Network/P2P/Payloads/TransactionAttributeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace Neo.Network.P2P.Payloads
public enum TransactionAttributeType : byte
{
[ReflectionCache(typeof(HighPriorityAttribute))]
HighPriority = 1
HighPriority = 0x01,
[ReflectionCache(typeof(OracleResponse))]
OracleResponse = 0x11
}
}
15 changes: 14 additions & 1 deletion src/neo/SmartContract/ApplicationEngine.Runtime.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Neo.Cryptography.ECC;
using Neo.IO;
using Neo.Network.P2P.Payloads;
using Neo.SmartContract.Native;
using Neo.SmartContract.Native.Oracle;
using Neo.VM.Types;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -104,7 +106,18 @@ protected internal bool CheckWitnessInternal(UInt160 hash)
{
if (ScriptContainer is Transaction tx)
{
Signer signer = tx.Signers.FirstOrDefault(p => p.Account.Equals(hash));
Signer[] signers;
OracleResponse response = tx.GetAttribute<OracleResponse>();
if (response is null)
{
signers = tx.Signers;
}
else
{
OracleRequest request = NativeContract.Oracle.GetRequest(Snapshot, response.Id);
signers = Snapshot.GetTransaction(request.OriginalTxid).Signers;
}
Signer signer = signers.FirstOrDefault(p => p.Account.Equals(hash));
if (signer is null) return false;
if (signer.Scopes == WitnessScope.Global) return true;
if (signer.Scopes.HasFlag(WitnessScope.CalledByEntry))
Expand Down
9 changes: 9 additions & 0 deletions src/neo/SmartContract/Native/NativeContract.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Neo.IO;
using Neo.SmartContract.Manifest;
using Neo.SmartContract.Native.Oracle;
using Neo.SmartContract.Native.Tokens;
using Neo.VM;
using Neo.VM.Types;
Expand All @@ -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();

[ContractMethod(0, CallFlags.None)]
public abstract string Name { get; }
Expand Down Expand Up @@ -140,6 +142,13 @@ protected virtual void OnPersist(ApplicationEngine engine)
throw new InvalidOperationException();
}

[ContractMethod(0, CallFlags.AllowModifyStates)]
protected virtual void PostPersist(ApplicationEngine engine)
{
if (engine.Trigger != TriggerType.System)
throw new InvalidOperationException();
}

public ApplicationEngine TestCall(string operation, params object[] args)
{
using (ScriptBuilder sb = new ScriptBuilder())
Expand Down
40 changes: 40 additions & 0 deletions src/neo/SmartContract/Native/Oracle/OracleContract.Lists.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Neo.Cryptography.ECC;
using Neo.IO;
using Neo.VM;
using Neo.VM.Types;
using System.Collections.Generic;
using System.Linq;

namespace Neo.SmartContract.Native.Oracle
{
partial class OracleContract
{
private class IdList : List<ulong>, IInteroperable
{
public void FromStackItem(StackItem stackItem)
{
foreach (StackItem item in (Array)stackItem)
Add((ulong)item.GetInteger());
}

public StackItem ToStackItem(ReferenceCounter referenceCounter)
{
return new Array(referenceCounter, this.Select(p => (Integer)p));
}
}

private class NodeList : List<ECPoint>, IInteroperable
{
public void FromStackItem(StackItem stackItem)
{
foreach (StackItem item in (Array)stackItem)
Add(ECPoint.DecodePoint(item.GetSpan(), ECCurve.Secp256r1));
}

public StackItem ToStackItem(ReferenceCounter referenceCounter)
{
return new Array(referenceCounter, this.Select(p => (StackItem)p.ToArray()));
}
}
}
}
Loading

0 comments on commit 16082b8

Please sign in to comment.