/// <summary> /// Reads a range of the storage value for the given storage key at the provided <paramref name="traceIndex"/>. /// NOTE: Offset and size are indexed from the left hand side, but actually operated on from the right hand side. /// </summary> /// <param name="traceIndex">The index of the trace point in the current execution trace which we wish to read storage at.</param> /// <param name="key">The key used to obtain the storage value.</param> /// <param name="offset">The offset to begin reading from in the obtained storage value.</param> /// <param name="size">The size of the data to read from the obtained storage value.</param> /// <returns>Returns the desired range of the value for the provided storage key at the set <paramref name="traceIndex"/>.</returns> public Memory <byte> ReadStorageSlot(int traceIndex, Memory <byte> key, int offset, int size) { // Obtain our trace point ExecutionTracePoint tracePoint = ExecutionTrace.Tracepoints[traceIndex]; // If storage doesn't contain our key, we return zero. bool succeeded = tracePoint.Storage.TryGetValue(key, out var storageValue); if (!succeeded) { return(new byte[UInt256.SIZE]); } // Otherwise we slice our data Memory <byte> valueMemory = storageValue; // Adjust our offset to account from the right hand side. offset = valueMemory.Length - size - offset; // Return our slice of storage. return(valueMemory.Slice(offset, size)); }
internal static ExecutionTrace CoreExecutionTraceJsonExecutionTrace(Meadow.EVM.Debugging.Tracing.ExecutionTrace coreExecutionTrace) { // If the execution trace is null, we return null if (coreExecutionTrace == null) { return(null); } // Create our array of trace points ExecutionTracePoint[] tracepoints = new ExecutionTracePoint[coreExecutionTrace.Tracepoints.Count]; // Populate all tracepoint items. for (int i = 0; i < tracepoints.Length; i++) { // Obtain our trace point. var executionTracePoint = coreExecutionTrace.Tracepoints[i]; // Define our memory and stack Data[] executionMemory = null; Data[] executionStack = executionTracePoint.Stack.Select(s => new Data(s)).ToArray(); if (executionTracePoint.Memory != null) { int wordCount = (int)EVMDefinitions.GetWordCount(executionTracePoint.Memory.Length); Span <byte> memorySpan = executionTracePoint.Memory; executionMemory = new Data[wordCount]; for (int x = 0; x < executionMemory.Length; x++) { executionMemory[x] = new Data(memorySpan.Slice(x * EVMDefinitions.WORD_SIZE, EVMDefinitions.WORD_SIZE)); } } // Obtain our memory tracepoints[i] = new ExecutionTracePoint() { CallData = executionTracePoint.CallData, Code = executionTracePoint.Code, ContractAddress = executionTracePoint.ContractAddress == null ? (Address?)null : new Address(executionTracePoint.ContractAddress.ToByteArray()), ContractDeployed = executionTracePoint.ContractDeployed, Opcode = executionTracePoint.Opcode, GasRemaining = (UInt256)executionTracePoint.GasRemaining, GasCost = (UInt256)executionTracePoint.GasCost, PC = executionTracePoint.PC, Depth = executionTracePoint.Depth, Stack = executionStack, Memory = executionMemory, Storage = executionTracePoint.Storage }; } // Create our array of exceptions ExecutionTraceException[] exceptions = new ExecutionTraceException[coreExecutionTrace.Exceptions.Count]; // Populate all exception items for (int i = 0; i < exceptions.Length; i++) { // Set our item. exceptions[i] = new ExecutionTraceException() { TraceIndex = coreExecutionTrace.Exceptions[i].TraceIndex, Message = coreExecutionTrace.Exceptions[i].Exception.Message }; } // Obtain our execution trace analysis. ExecutionTrace executionTrace = new ExecutionTrace() { Tracepoints = tracepoints, Exceptions = exceptions }; // Return our execution trace. return(executionTrace); }