Skip to content

Commit

Permalink
Oracle: Set the right Tx version (neo-project#1542)
Browse files Browse the repository at this point in the history
* Set the right Tx version

* Remove Error types from TX and VM

* Neo.Oracle.Get now only return the result or null
  • Loading branch information
shargon committed Apr 17, 2020
1 parent 74125d6 commit 3177b80
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 110 deletions.
29 changes: 29 additions & 0 deletions src/neo/Network/P2P/Payloads/Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class Transaction : IEquatable<Transaction>, IInventory, IInteroperable
private Cosigner[] cosigners;
private byte[] script;
private Witness[] witnesses;
private UInt256 oracleRequestTx;

public const int HeaderSize =
sizeof(TransactionVersion) + //Version
Expand All @@ -61,6 +62,12 @@ public Cosigner[] Cosigners
set { cosigners = value; _hash = null; _size = 0; }
}

public UInt256 OracleRequestTx
{
get => oracleRequestTx;
set { oracleRequestTx = value; _hash = null; _size = 0; }
}

/// <summary>
/// The <c>NetworkFee</c> for the transaction divided by its <c>Size</c>.
/// <para>Note that this property must be used with care. Getting the value of this property multiple times will return the same result. The value of this property can only be obtained after the transaction has been completely built (no longer modified).</para>
Expand Down Expand Up @@ -121,6 +128,11 @@ public int Size
Cosigners.GetVarSize() + //Cosigners
Script.GetVarSize() + //Script
Witnesses.GetVarSize(); //Witnesses

if (Version == TransactionVersion.OracleResponse)
{
_size += UInt256.Length;
}
}
return _size;
}
Expand Down Expand Up @@ -181,6 +193,7 @@ public void DeserializeUnsigned(BinaryReader reader)
if (Cosigners.Select(u => u.Account).Distinct().Count() != Cosigners.Length) throw new FormatException();
Script = reader.ReadVarBytes(ushort.MaxValue);
if (Script.Length == 0) throw new FormatException();
OracleRequestTx = Version == TransactionVersion.OracleResponse ? reader.ReadSerializable<UInt256>() : null;
}

public bool Equals(Transaction other)
Expand Down Expand Up @@ -224,6 +237,10 @@ void IVerifiable.SerializeUnsigned(BinaryWriter writer)
writer.Write(Attributes);
writer.Write(Cosigners);
writer.WriteVarBytes(Script);
if (Version == TransactionVersion.OracleResponse)
{
writer.Write(OracleRequestTx);
}
}

public JObject ToJson()
Expand All @@ -241,6 +258,10 @@ public JObject ToJson()
json["cosigners"] = Cosigners.Select(p => p.ToJson()).ToArray();
json["script"] = Convert.ToBase64String(Script);
json["witnesses"] = Witnesses.Select(p => p.ToJson()).ToArray();
if (Version == TransactionVersion.OracleResponse)
{
json["oracle_response_tx"] = OracleRequestTx.ToString();
}
return json;
}

Expand All @@ -258,6 +279,14 @@ public static Transaction FromJson(JObject json)
tx.Cosigners = ((JArray)json["cosigners"]).Select(p => Cosigner.FromJson(p)).ToArray();
tx.Script = Convert.FromBase64String(json["script"].AsString());
tx.Witnesses = ((JArray)json["witnesses"]).Select(p => Witness.FromJson(p)).ToArray();
if (tx.Version == TransactionVersion.OracleResponse)
{
tx.OracleRequestTx = UInt256.Parse(json["oracle_response_tx"].AsString());
}
else
{
tx.OracleRequestTx = null;
}
return tx;
}

Expand Down
66 changes: 31 additions & 35 deletions src/neo/Oracle/OracleResponse.cs~refs/tags/v3.0.0-preview2
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ using Neo.IO;
using Neo.Network.P2P;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using Neo.SmartContract;
using Neo.VM;
using Neo.VM.Types;
using System;
using System.IO;
using System.Text;

namespace Neo.Oracle
{
public class OracleResponse : IInteroperable, IVerifiable
public class OracleResponse : IVerifiable
{
private UInt160 _hash;

Expand All @@ -22,14 +19,14 @@ namespace Neo.Oracle
public UInt160 RequestHash { get; set; }

/// <summary>
/// Error
/// Result
/// </summary>
public OracleResultError Error { get; set; }
public byte[] Result { get; set; }

/// <summary>
/// Result
/// Error
/// </summary>
public byte[] Result { get; set; }
public bool Error => Result == null;

/// <summary>
/// Filter cost paid by Oracle and must be claimed to the user
Expand Down Expand Up @@ -69,13 +66,9 @@ namespace Neo.Oracle
/// <returns>OracleResult</returns>
public static OracleResponse CreateError(UInt160 requestHash, OracleResultError error, long filterCost = 0)
{
return new OracleResponse()
{
RequestHash = requestHash,
Error = error,
Result = new byte[0],
FilterCost = filterCost
};
// TODO: We should log the error if we want, but in order to reduce the indeterminism, we will only say that the download was unsuccessful

return CreateResult(requestHash, (byte[])null, filterCost);
}

/// <summary>
Expand All @@ -102,7 +95,6 @@ namespace Neo.Oracle
return new OracleResponse()
{
RequestHash = requestHash,
Error = OracleResultError.None,
Result = result,
FilterCost = filterCost
};
Expand All @@ -111,10 +103,19 @@ namespace Neo.Oracle
public void SerializeUnsigned(BinaryWriter writer)
{
writer.Write(RequestHash);
writer.Write((byte)Error);
if (Error == OracleResultError.None)
writer.WriteVarBytes(Result);
writer.Write(FilterCost);

if (Result != null)
{
writer.Write((byte)0x01);
writer.WriteVarBytes(Result);
}
else
{
// Error result

writer.Write((byte)0x00);
}
}

public void Serialize(BinaryWriter writer)
Expand All @@ -125,10 +126,19 @@ namespace Neo.Oracle
public void DeserializeUnsigned(BinaryReader reader)
{
RequestHash = reader.ReadSerializable<UInt160>();
Error = (OracleResultError)reader.ReadByte();
Result = Error == OracleResultError.None ? reader.ReadVarBytes(ushort.MaxValue) : new byte[0];
FilterCost = reader.ReadInt64();
if (FilterCost < 0) throw new FormatException(nameof(FilterCost));

if (reader.ReadByte() == 0x01)
{
Result = reader.ReadVarBytes(ushort.MaxValue);
}
else
{
// Error result

Result = null;
}
}

public void Deserialize(BinaryReader reader)
Expand All @@ -140,19 +150,5 @@ namespace Neo.Oracle
{
return new UInt160[] { new UInt160(Crypto.Hash160(this.GetHashData())) };
}

/// <summary>
/// Get Stack item for IInteroperable
/// </summary>
/// <returns>StackItem</returns>
public StackItem ToStackItem(ReferenceCounter referenceCounter)
{
return new VM.Types.Array(referenceCounter, new StackItem[]
{
new ByteString(RequestHash.ToArray()),
new Integer((byte)Error),
new ByteString(Result)
});
}
}
}
27 changes: 18 additions & 9 deletions src/neo/Oracle/OracleService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Neo.Ledger;
using Neo.Network.P2P.Payloads;
using Neo.Oracle.Protocols.Https;
using Neo.Persistence;
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.VM;
Expand Down Expand Up @@ -215,7 +216,7 @@ private void ProcessTransaction(Transaction tx)
{
// Send oracle result

var responseTx = CreateResponseTransaction(oracle, tx);
var responseTx = CreateResponseTransaction(snapshot, oracle, tx);
Sign(responseTx, out var signature);

var response = new OracleServiceResponse()
Expand All @@ -238,27 +239,35 @@ private void ProcessTransaction(Transaction tx)
/// Create Oracle response transaction
/// We need to create a deterministic TX for this result/oracleRequest
/// </summary>
/// <param name="snapshot">Snapshot</param>
/// <param name="oracle">Oracle</param>
/// <param name="requestTx">Request Hash</param>
/// <returns>Transaction</returns>
public static Transaction CreateResponseTransaction(OracleExecutionCache oracle, Transaction requestTx)
public static Transaction CreateResponseTransaction(StoreView snapshot, OracleExecutionCache oracle, Transaction requestTx)
{
var sender = NativeContract.Oracle.GetOracleMultiSigAddress(snapshot);
using ScriptBuilder script = new ScriptBuilder();

script.EmitAppCall(NativeContract.Oracle.Hash, "setOracleResponse", requestTx.Hash, oracle.ToArray());

return new Transaction()
{
Attributes = new TransactionAttribute[]
Version = TransactionVersion.OracleResponse,
Sender = sender,
Nonce = requestTx.Nonce,
ValidUntilBlock = requestTx.ValidUntilBlock,
OracleRequestTx = requestTx.Hash,
Cosigners = new Cosigner[]
{
// DependsOn = hash
new Cosigner()
{
Account = sender,
AllowedContracts = new UInt160[]{ NativeContract.Oracle.Hash },
Scopes = WitnessScope.CustomContracts
}
},
Cosigners = new Cosigner[0],
NetworkFee = 1_000_000, // TODO: Define fee
SystemFee = 1_000_000,
Sender = UInt160.Zero, // <- OracleSender
Nonce = requestTx.Nonce,
ValidUntilBlock = requestTx.ValidUntilBlock,
SystemFee = 1_000_000, // TODO: Define fee
Witnesses = new Witness[0],
Script = script.ToArray()
};
Expand Down
2 changes: 1 addition & 1 deletion src/neo/SmartContract/InteropService.Oracle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ private static bool Oracle_Get(ApplicationEngine engine)

if (engine.OracleCache.TryGet(request, out var response))
{
engine.Push(response.ToStackItem(engine.ReferenceCounter));
engine.Push(response.Result ?? StackItem.Null);
return true;
}

Expand Down
66 changes: 36 additions & 30 deletions src/neo/Wallets/Wallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,7 @@ private Transaction MakeTransaction(StoreView snapshot, byte[] script, Transacti
{
Transaction tx = new Transaction
{
Version = 0,
//TODO: x.Version = TransactionType.Oracle; <- if oracleQueries.Count != 0
Version = oracleCache?.Count > 0 ? TransactionVersion.OracleRequest : TransactionVersion.Transaction,
Nonce = (uint)rand.Next(),
Script = script,
Sender = account,
Expand All @@ -344,48 +343,55 @@ private Transaction MakeTransaction(StoreView snapshot, byte[] script, Transacti

// Change the Transaction type because it's an oracle request

if (oracleRequests.Count > 0 && oracle == OracleWalletBehaviour.OracleWithAssert)
if (oracleRequests.Count > 0)
{
// If we want the same result for accept the response, we need to create asserts at the begining of the script

var assertScript = new ScriptBuilder();

foreach (var oracleRequest in oracleRequests)
if (oracle == OracleWalletBehaviour.OracleWithAssert)
{
// Do the request in order to cache the result
// If we want the same result for accept the response, we need to create asserts at the begining of the script

if (oracleRequest is OracleHttpsRequest https)
{
assertScript.EmitSysCall(InteropService.Oracle.Neo_Oracle_Get, https.URL.ToString(), https.Filter?.ContractHash, https.Filter?.FilterMethod);
}
else
var assertScript = new ScriptBuilder();

foreach (var oracleRequest in oracleRequests)
{
throw new NotImplementedException();
// Do the request in order to cache the result

if (oracleRequest is OracleHttpsRequest https)
{
assertScript.EmitSysCall(InteropService.Oracle.Neo_Oracle_Get, https.URL.ToString(), https.Filter?.ContractHash, https.Filter?.FilterMethod);
}
else
{
throw new NotImplementedException();
}
}
}

// Clear the stack
// Clear the stack

assertScript.Emit(OpCode.CLEAR);
assertScript.Emit(OpCode.CLEAR);

// Check that the hash of the whole responses are exactly the same
// Check that the hash of the whole responses are exactly the same

assertScript.EmitSysCall(InteropService.Oracle.Neo_Oracle_Hash);
assertScript.EmitPush(oracleCache.Hash.ToArray());
assertScript.Emit(OpCode.EQUAL);
assertScript.Emit(OpCode.ASSERT);
assertScript.EmitSysCall(InteropService.Oracle.Neo_Oracle_Hash);
assertScript.EmitPush(oracleCache.Hash.ToArray());
assertScript.Emit(OpCode.EQUAL);
assertScript.Emit(OpCode.ASSERT);

// Concat two scripts [OracleAsserts+Script]
// Concat two scripts [OracleAsserts+Script]

script = assertScript.ToArray().Concat(script).ToArray();
oracle = OracleWalletBehaviour.OracleWithoutAssert;
script = assertScript.ToArray().Concat(script).ToArray();
oracle = OracleWalletBehaviour.OracleWithoutAssert;

// We need to remove new oracle calls (OracleService.Process)
// We need to remove new oracle calls (OracleService.Process)

oracleCache = new OracleExecutionCache(oracleCache.Responses);
oracleCache = new OracleExecutionCache(oracleCache.Responses);

// We need to compute the gas again with the right script
goto Start;
// We need to compute the gas again with the right script
goto Start;
}
else
{
tx.Version = TransactionVersion.OracleRequest;
}
}
}

Expand Down
Loading

0 comments on commit 3177b80

Please sign in to comment.