Skip to content

Commit

Permalink
progressive version: fairy wallet for each session; default fairy wal…
Browse files Browse the repository at this point in the history
…let; performance enhancement by cancelling runtimeArgs copying
  • Loading branch information
Hecate2 committed Nov 25, 2022
1 parent cf81c01 commit 2184a22
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 69 deletions.
61 changes: 38 additions & 23 deletions Fairy.Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.VM;
using System.Collections.Concurrent;
using Neo.Wallets;
using System.Numerics;
using System.Reflection;

Expand All @@ -15,25 +15,33 @@ public partial class Fairy
public class FairyEngine : ApplicationEngine
{
readonly Fairy fairy;
protected FairyEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas, IDiagnostic diagnostic, FairyEngine? oldEngine = null, Fairy? fairy = null) : base(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic)
protected FairyEngine(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock, ProtocolSettings settings, long gas, IDiagnostic diagnostic, FairyEngine? oldEngine = null, Fairy? fairy = null, bool copyRuntimeArgs = false) : base(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic)
{
if (fairy != null)
this.fairy = fairy;
if (oldEngine != null)
{
this.services = oldEngine.services;
this.serviceArgs = oldEngine.serviceArgs;
if (copyRuntimeArgs)
{
this.services = oldEngine.services.ToDictionary(kvpair => kvpair.Key, kvpair => kvpair.Value); // clone
this.runtimeArgs = (RuntimeArgs)oldEngine.runtimeArgs.Clone();
}
else
{
this.services = oldEngine.services;
this.runtimeArgs = oldEngine.runtimeArgs;
}
}
else
{
this.services = ApplicationEngine.Services.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
this.serviceArgs = new ServiceArgs();
this.services = ApplicationEngine.Services.ToDictionary(kvpair => kvpair.Key, kvpair => kvpair.Value);
this.runtimeArgs = new RuntimeArgs(fairy.defaultFairyWallet);
}
if (fairy != null)
this.fairy = fairy;
}

public static FairyEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, ProtocolSettings settings = null, long gas = TestModeGas, IDiagnostic diagnostic = null, FairyEngine? oldEngine = null, Fairy? fairy = null)
public static FairyEngine Create(TriggerType trigger, IVerifiable container, DataCache snapshot, Block persistingBlock = null, ProtocolSettings settings = null, long gas = TestModeGas, IDiagnostic diagnostic = null, FairyEngine? oldEngine = null, Fairy? fairy = null, bool copyRuntimeArgs = false)
{
return new FairyEngine(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, oldEngine: oldEngine, fairy: fairy);
return new FairyEngine(trigger, container, snapshot, persistingBlock, settings, gas, diagnostic, oldEngine: oldEngine, fairy: fairy, copyRuntimeArgs: copyRuntimeArgs);
}

public new VMState State
Expand Down Expand Up @@ -68,24 +76,31 @@ public virtual VMState Execute()
return State;
}

public static FairyEngine Run(ReadOnlyMemory<byte> script, DataCache snapshot, IVerifiable container = null, Block persistingBlock = null, ProtocolSettings settings = null, int offset = 0, long gas = TestModeGas, IDiagnostic diagnostic = null, FairyEngine? oldEngine = null, Fairy? fairy = null)
public static FairyEngine Run(ReadOnlyMemory<byte> script, DataCache snapshot, IVerifiable container = null, Block persistingBlock = null, ProtocolSettings settings = null, int offset = 0, long gas = TestModeGas, IDiagnostic diagnostic = null, FairyEngine? oldEngine = null, Fairy? fairy = null, bool copyRuntimeArgs = false)
{
persistingBlock ??= CreateDummyBlockWithTimestamp(snapshot, settings ?? ProtocolSettings.Default, timestamp: null);
FairyEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock, settings, gas, diagnostic, oldEngine: oldEngine, fairy: fairy);
FairyEngine engine = Create(TriggerType.Application, container, snapshot, persistingBlock, settings, gas, diagnostic, oldEngine: oldEngine, fairy: fairy, copyRuntimeArgs: copyRuntimeArgs);
engine.LoadScript(script, initialPosition: offset);
engine.Execute();
return engine;
}

public Dictionary<uint, InteropDescriptor> services;

public class ServiceArgs
public class RuntimeArgs: ICloneable
{
public RuntimeArgs(Wallet fairyWallet)
{
this.fairyWallet = fairyWallet;
}
public Wallet fairyWallet;
public ulong? timestamp = null;
public BigInteger? designatedRandom = null;

public object Clone() => MemberwiseClone();
//public uint? blockIndex = null;
}
public ServiceArgs serviceArgs;
public RuntimeArgs runtimeArgs;

protected override void OnSysCall(uint method)
{
Expand All @@ -107,9 +122,9 @@ public InteropDescriptor Register(string name, MethodInfo method, uint hash, lon
}

public new BigInteger GetRandom() => base.GetRandom();
public BigInteger GetFairyRandom() => serviceArgs.designatedRandom != null ? (BigInteger)serviceArgs.designatedRandom : base.GetRandom();
public BigInteger GetFairyRandom() => runtimeArgs.designatedRandom != null ? (BigInteger)runtimeArgs.designatedRandom : base.GetRandom();
public new ulong GetTime() => base.GetTime();
public ulong GetFairyTime() => serviceArgs.timestamp != null ? (ulong)serviceArgs.timestamp : GetTime();
public ulong GetFairyTime() => runtimeArgs.timestamp != null ? (ulong)runtimeArgs.timestamp : GetTime();
//public ulong GetFairyBlockIndex() => serviceArgs.blockIndex != null ? (uint)serviceArgs.blockIndex : NativeContract.Ledger.CurrentIndex(this.Snapshot);
}

Expand Down Expand Up @@ -213,43 +228,43 @@ public class FairySession

public ulong? timestamp
{
get => engine.serviceArgs.timestamp;
get => engine.runtimeArgs.timestamp;
set
{
if (value == null)
{
engine.Register("System.Runtime.GetTime", typeof(FairyEngine).GetMethod(nameof(FairyEngine.GetTime)), ApplicationEngine.System_Runtime_GetTime.Hash, 1 << 3, CallFlags.None);
engine.serviceArgs.timestamp = null;
engine.runtimeArgs.timestamp = null;
}
else
{
engine.serviceArgs.timestamp = value;
engine.runtimeArgs.timestamp = value;
engine.Register("System.Runtime.GetTime", typeof(FairyEngine).GetMethod(nameof(FairyEngine.GetFairyTime)), ApplicationEngine.System_Runtime_GetTime.Hash, 1 << 3, CallFlags.None);
}
}
}

public BigInteger? designatedRandom
{
get => engine.serviceArgs.designatedRandom;
get => engine.runtimeArgs.designatedRandom;
set
{
if (value == null)
{
engine.Register("System.Runtime.GetRandom", typeof(FairyEngine).GetMethod(nameof(FairyEngine.GetRandom)), ApplicationEngine.System_Runtime_GetRandom.Hash, 0, CallFlags.None);
engine.serviceArgs.designatedRandom = null;
engine.runtimeArgs.designatedRandom = null;
}
else
{
engine.serviceArgs.designatedRandom = value;
engine.runtimeArgs.designatedRandom = value;
engine.Register("System.Runtime.GetRandom", typeof(FairyEngine).GetMethod(nameof(FairyEngine.GetFairyRandom)), ApplicationEngine.System_Runtime_GetRandom.Hash, 0, CallFlags.None);
}
}
}

public void ResetServices()
{
engine.serviceArgs = new();
engine.runtimeArgs = new(fairy.defaultFairyWallet);
engine.services = ApplicationEngine.Services.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
ResetExpiration();
}
Expand Down
2 changes: 1 addition & 1 deletion Fairy.Tester.Snapshot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public FairySession NewTestSession()

private FairyEngine BuildSnapshotWithDummyScript(FairyEngine engine = null)
{
return FairyEngine.Run(new byte[] { 0x40 }, engine != null ? engine.Snapshot.CreateSnapshot() : system.StoreView, settings: system.Settings, gas: settings.MaxGasInvoke, oldEngine: engine, fairy: this);
return FairyEngine.Run(new byte[] { 0x40 }, engine != null ? engine.Snapshot.CreateSnapshot() : system.StoreView, settings: system.Settings, gas: settings.MaxGasInvoke, oldEngine: engine, fairy: this, copyRuntimeArgs: true);
}

[RpcMethod]
Expand Down
41 changes: 27 additions & 14 deletions Fairy.Tester.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using Neo.IO;
using Neo.Json;
using Neo.Network.P2P.Payloads;
using Neo.Persistence;
using Neo.SmartContract;
using Neo.SmartContract.Native;
using Neo.VM;
using Neo.Wallets;
using System.Numerics;
using System.Collections.Concurrent;
using Neo.Persistence;

namespace Neo.Plugins
{
Expand Down Expand Up @@ -150,7 +151,7 @@ private JObject GetInvokeResultWithSession(string session, bool writeSnapshot, b
}
foreach (LogEventArgs log in logs)
{
string contractName = NativeContract.ContractManagement.GetContract(newEngine.Snapshot, log.ScriptHash)?.Manifest.Name;
string? contractName = NativeContract.ContractManagement.GetContract(newEngine.Snapshot, log.ScriptHash)?.Manifest.Name;
traceback += $"\r\n[{log.ScriptHash}] {contractName}: {log.Message}";
}
json["traceback"] = traceback;
Expand All @@ -165,14 +166,22 @@ private JObject GetInvokeResultWithSession(string session, bool writeSnapshot, b
}
if (newEngine.State != VMState.FAULT)
{
ProcessInvokeWithWalletAndSnapshot(oldEngine.Snapshot, json, signers, block: CreateDummyBlockWithTimestamp(testSession.engine.Snapshot, system.Settings, timestamp: testSession.timestamp));
if (witnesses == null)
ProcessInvokeWithWalletAndSnapshot(oldEngine, script, json, signers, block: CreateDummyBlockWithTimestamp(testSession.engine.Snapshot, system.Settings, timestamp: testSession.timestamp));
else
{
Wallet signatureWallet = oldEngine.runtimeArgs.fairyWallet == null ? defaultFairyWallet : oldEngine.runtimeArgs.fairyWallet;
json["tx"] = Convert.ToBase64String(tx.ToArray());
json["networkfee"] = defaultFairyWallet.CalculateNetworkFee(oldEngine.Snapshot, tx).ToString();
}
}
return json;
}

private void ProcessInvokeWithWalletAndSnapshot(DataCache snapshot, JObject result, Signer[]? signers = null, Block block = null)
private void ProcessInvokeWithWalletAndSnapshot(FairyEngine engine, byte[] script, JObject result, Signer[]? signers = null, Block block = null)
{
if (fairyWallet == null || signers == null) return;
Wallet signatureWallet = engine.runtimeArgs.fairyWallet == null ? defaultFairyWallet : engine.runtimeArgs.fairyWallet;
if (signatureWallet == null || signers == null) return;

Signer[] witnessSigners = signers;
if (witnessSigners.Length <= 0) return;
Expand All @@ -181,24 +190,28 @@ private void ProcessInvokeWithWalletAndSnapshot(DataCache snapshot, JObject resu
Transaction tx;
try
{
tx = fairyWallet.MakeTransaction(snapshot.CreateSnapshot(), Convert.FromBase64String(result["script"].AsString()), sender, witnessSigners, maxGas: settings.MaxGasInvoke, persistingBlock: block);
tx = signatureWallet.MakeTransaction(engine.Snapshot.CreateSnapshot(), script, sender, witnessSigners, maxGas: settings.MaxGasInvoke, persistingBlock: block);
}
catch //(Exception e)
{
// result["exception"] = GetExceptionMessage(e);
return;
}
ContractParametersContext context = new(snapshot.CreateSnapshot(), tx, system.Settings.Network);
fairyWallet.Sign(context);
if (context.Completed)
DataCache snapshotForSignature = engine.Snapshot.CreateSnapshot();
ContractParametersContext context = new(snapshotForSignature, tx, system.Settings.Network);
signatureWallet.Sign(context);
try
{
tx.Witnesses = context.GetWitnesses();
byte[] txBytes = tx.ToArray();
result["tx"] = Convert.ToBase64String(txBytes);
long networkfee = (fairyWallet ?? new DummyWallet(system.Settings)).CalculateNetworkFee(system.StoreView, txBytes.AsSerializable<Transaction>());
result["networkfee"] = networkfee.ToString();
}
else
catch
{
// When no valid signature is given, we can only try to simulate a transaction with a single witness
tx.Witnesses = defaultWitness;
}
result["tx"] = Convert.ToBase64String(tx.ToArray());
result["networkfee"] = signatureWallet.CalculateNetworkFee(snapshotForSignature, tx).ToString();
if (!context.Completed)
{
result["pendingsignature"] = context.ToJson();
}
Expand Down
11 changes: 6 additions & 5 deletions Fairy.Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ public partial class Fairy
[RpcMethod]
protected virtual JObject VirtualDeploy(JArray _params)
{
if (fairyWallet == null)
if (defaultFairyWallet == null)
throw new Exception("Please open a wallet before deploying a contract.");
string session = _params[0].AsString();
NefFile nef = Convert.FromBase64String(_params[1].AsString()).AsSerializable<NefFile>();
ContractManifest manifest = ContractManifest.Parse(_params[2].AsString());
Signer[] signers = SignersFromJson((JArray)_params[3], system.Settings);
if (!sessionStringToFairySession.TryGetValue(session, out FairySession testSession))
{
testSession = NewTestSession();
Expand All @@ -39,12 +40,12 @@ protected virtual JObject VirtualDeploy(JArray _params)
JObject json = new();
try
{
Block dummyBlock = CreateDummyBlockWithTimestamp(testSession.engine.Snapshot, system.Settings, timestamp: sessionStringToFairySession[session].engine.serviceArgs.timestamp);
Transaction tx = fairyWallet.MakeTransaction(snapshot.CreateSnapshot(), script, persistingBlock: dummyBlock);
json["gasconsumed"] = tx.SystemFee;
json["networkfee"] = tx.NetworkFee;
Block dummyBlock = CreateDummyBlockWithTimestamp(testSession.engine.Snapshot, system.Settings, timestamp: sessionStringToFairySession[session].engine.runtimeArgs.timestamp);
Transaction tx = defaultFairyWallet.MakeTransaction(sessionStringToFairySession[session].engine.Snapshot, script, sender: signers[0].Account, persistingBlock: dummyBlock);
json["networkfee"] = tx.NetworkFee.ToString();
UInt160 hash = SmartContract.Helper.GetContractHash(tx.Sender, nef.CheckSum, manifest.Name);
sessionStringToFairySession[session].engine = FairyEngine.Run(script, snapshot.CreateSnapshot(), persistingBlock: dummyBlock, container: tx, settings: system.Settings, gas: settings.MaxGasInvoke, oldEngine: sessionStringToFairySession[session].engine, fairy: this);
json["gasconsumed"] = sessionStringToFairySession[session].engine.GasConsumed.ToString();
json[session] = hash.ToString();
}
catch (InvalidOperationException ex)
Expand Down
Loading

0 comments on commit 2184a22

Please sign in to comment.