diff --git a/README.md b/README.md index f9ad8e0..52d6884 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,39 @@ You can execute your own **fairy transactions** or deploy your own **fairy contr No GAS fee is needed for your fairy transactions. This is a great help when your contract heavily manipulates GAS and you want to ensure correct GAS transferring. If you do need to compute the GAS system fee and network fee, just read `["networkfee"]` and `["gasconsumed"]` in the result of invoked fairy transactions. Network fee is calculated only when the correct wallet is opened and the transaction can be validly signed by the opened wallet. +Detailed traceback is returned in result`["traceback"]` if there is any fault. Sample: + +``` +"ASSERT is executed with false result." +CallingScriptHash=0x5c1068339fae89eb1a743909d0213e1d99dc5dc9 +CurrentScriptHash=0x5c1068339fae89eb1a743909d0213e1d99dc5dc9 +EntryScriptHash=0xbfe1ac44ec31bdac17591588c0bbba0c63b7be99 + at Neo.VM.ExecutionEngine.ExecuteInstruction() in C:\Users\RhantolkYtriHistoria\NEO\neo-vm\src\neo-vm\ExecutionEngine.cs:line 374 + at Neo.VM.ExecutionEngine.ExecuteNext() in C:\Users\RhantolkYtriHistoria\NEO\neo-vm\src\neo-vm\ExecutionEngine.cs:line 1436 +InstructionPointer=2281, OpCode ASSERT, Script Length=8518 +InstructionPointer=4458, OpCode JMP, Script Length=8518 +InstructionPointer=502, OpCode STLOC2, Script Length=806 +InstructionPointer=21375, OpCode RET, Script Length=21375 +-------Logs-------(1) +[0x5c1068339fae89eb1a743909d0213e1d99dc5dc9] AnyUpdateShortSafe: Transfer failed +``` + +``` +"ASSERT is executed with false result." +CallingScriptHash=0x5c1068339fae89eb1a743909d0213e1d99dc5dc9 +CurrentScriptHash=0x5c1068339fae89eb1a743909d0213e1d99dc5dc9 +EntryScriptHash=0x4250ef2854561ee4ecd3567135cc2da65910938b + at Neo.VM.ExecutionEngine.ExecuteInstruction() in C:\Users\RhantolkYtriHistoria\NEO\neo-vm\src\neo-vm\ExecutionEngine.cs:line 374 + at Neo.VM.ExecutionEngine.ExecuteNext() in C:\Users\RhantolkYtriHistoria\NEO\neo-vm\src\neo-vm\ExecutionEngine.cs:line 1436 +InstructionPointer=2281, OpCode ASSERT, Script Length=8518 +InstructionPointer=5699, OpCode LDLOC5, Script Length=8518 +InstructionPointer=5365, OpCode JMP, Script Length=8518 +InstructionPointer=502, OpCode STLOC2, Script Length=806 +InstructionPointer=21390, OpCode RET, Script Length=21390 +-------Logs-------(1) +[0x5c1068339fae89eb1a743909d0213e1d99dc5dc9] AnyUpdateShortSafe: No enough token to lend +``` + Non official client: https://github.com/Hecate2/neo-test-client diff --git a/RpcServer.WithSession.cs b/RpcServer.WithSession.cs index b0d9f69..049f5b1 100644 --- a/RpcServer.WithSession.cs +++ b/RpcServer.WithSession.cs @@ -21,6 +21,7 @@ public partial class RpcServer { Dictionary sessionToEngine = new(); Dictionary sessionToTimestamp = new(); + List logs = new(); public UInt160 neoScriptHash = UInt160.Parse("0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5"); public UInt160 gasScriptHash = UInt160.Parse("0xd2a4cff31913016155e38e474a2c06d08be276cf"); @@ -334,6 +335,11 @@ private ApplicationEngine BuildSnapshotWithDummyScript(ApplicationEngine engine return ApplicationEngine.Run(new byte[] { 0x40 }, engine != null ? engine.Snapshot.CreateSnapshot() : system.StoreView, settings: system.Settings, gas: settings.MaxGasInvoke); } + private void CacheLog(object sender, LogEventArgs logEventArgs) + { + logs.Add(logEventArgs); + } + private JObject GetInvokeResultWithSession(string session, bool writeSnapshot, byte[] script, Signers signers = null) { Transaction tx = signers == null ? null : new Transaction @@ -347,6 +353,8 @@ private JObject GetInvokeResultWithSession(string session, bool writeSnapshot, b sessionToTimestamp[session] = 0; ApplicationEngine oldEngine, newEngine; DataCache validSnapshotBase; + logs.Clear(); + ApplicationEngine.Log += CacheLog; if (timestamp == 0) { if (sessionToEngine.TryGetValue(session, out oldEngine)) @@ -366,6 +374,7 @@ private JObject GetInvokeResultWithSession(string session, bool writeSnapshot, b validSnapshotBase = oldEngine.Snapshot; newEngine = ApplicationEngine.Run(script, oldEngine.Snapshot.CreateSnapshot(), persistingBlock: CreateDummyBlockWithTimestamp(oldEngine.Snapshot, system.Settings, timestamp: timestamp), container: tx, settings: system.Settings, gas: settings.MaxGasInvoke); } + ApplicationEngine.Log -= CacheLog; if (writeSnapshot && newEngine.State == VMState.HALT) sessionToEngine[session] = newEngine; JObject json = new(); @@ -381,6 +390,15 @@ private JObject GetInvokeResultWithSession(string session, bool writeSnapshot, b { traceback += $"\r\nInstructionPointer={context.InstructionPointer}, OpCode {context.CurrentInstruction.OpCode}, Script Length={context.Script.Length}"; } + if(logs.Count > 0) + { + traceback += $"\r\n-------Logs-------({logs.Count})"; + } + foreach (LogEventArgs log in logs) + { + string contractName = NativeContract.ContractManagement.GetContract(newEngine.Snapshot, log.ScriptHash).Manifest.Name; + traceback += $"\r\n[{log.ScriptHash}] {contractName}: {log.Message}"; + } json["traceback"] = traceback; } try