private void Step() { // Verify we're not at the end of the stream if (ExecutionState.PC >= Code.Length) { // If we reached the end, exit with the remainder of the gas and a success status. ExecutionState.Result = new EVMExecutionResult(this, null, true); return; } // Set our position back to the start of the instruction, grab our opcode and verify it. InstructionOpcode opcode = (InstructionOpcode)Code.Span[(int)ExecutionState.PC]; // Obtain our base cost for this opcode. If we fail to, the instruction isn't implemented yet. uint?instructionBaseGasCost = GasDefinitions.GetInstructionBaseGasCost(State.Configuration.Version, opcode); if (instructionBaseGasCost == null) { throw new EVMException($"Invalid opcode {opcode.ToString()} read when executing!"); } // If we just jumped, then this next opcode should be a JUMPDEST. if (ExecutionState.JumpedLastInstruction && opcode != InstructionOpcode.JUMPDEST) { throw new EVMException($"Invalid jump to offset {ExecutionState.PC} in code!"); } // Obtain our instruction implementation for this opcode var opcodeDescriptor = opcode.GetDescriptor(); InstructionBase instruction = opcodeDescriptor.GetInstructionImplementation(this); // Record our code coverage for this execution. CoverageMap?.RecordExecution(instruction.Offset, (ExecutionState.PC - instruction.Offset)); // Record our instruction execution tracing if (State.Configuration.DebugConfiguration.IsTracing) { State.Configuration.DebugConfiguration.ExecutionTrace?.RecordExecution(this, instruction, GasState.Gas, (BigInteger)instructionBaseGasCost); } // Deduct base gas cost GasState.Deduct((BigInteger)instructionBaseGasCost); // Debug: Print out instruction execution information. //if (opcode == InstructionOpcode.JUMPDEST) // Console.WriteLine($"\r\n---------------------------------------------------------------\r\n"); //Console.WriteLine($"0x{instruction.Offset.ToString("X4")}: {instruction}"); //Console.WriteLine($"Stack: {ExecutionState.Stack}"); // Execute the instruction instruction.Execute(); }
public void TestBaseGasCostUpdates() { // Make static assertions about updates made during Tangerine Whistle release. EthereumRelease[] releases = (EthereumRelease[])Enum.GetValues(typeof(EthereumRelease)); foreach (EthereumRelease release in releases) { if (release < EthereumRelease.TangerineWhistle) { Assert.Equal <uint>(20, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.BALANCE)); Assert.Equal <uint>(20, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.EXTCODESIZE)); Assert.Equal <uint>(20, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.EXTCODECOPY)); Assert.Equal <uint>(50, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.SLOAD)); Assert.Equal <uint>(40, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.CALL)); Assert.Equal <uint>(40, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.CALLCODE)); // DELEGATE CALL WAS INTRODUCED IN HOMESTEAD WITH 40 BASE GAS, NULL BEFORE. if (release >= EthereumRelease.Homestead) { Assert.Equal <uint>(40, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.DELEGATECALL)); } else { Assert.Null(GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.DELEGATECALL)); } // STATIC CALL WAS INTRODUCED IN BYZANTIUM WITH 700 BASE GAS, NULL BEFORE if (release >= EthereumRelease.Byzantium) { Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.STATICCALL)); } else { Assert.Null(GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.STATICCALL)); } Assert.Equal <uint>(0, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.SELFDESTRUCT)); } else { Assert.Equal <uint>(400, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.BALANCE)); Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.EXTCODESIZE)); Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.EXTCODECOPY)); Assert.Equal <uint>(200, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.SLOAD)); Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.CALL)); Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.CALLCODE)); Assert.Equal <uint>(700, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.DELEGATECALL)); Assert.Equal <uint>(5000, (uint)GasDefinitions.GetInstructionBaseGasCost(release, InstructionOpcode.SELFDESTRUCT)); } } }