diff --git a/.circleci/config.yml b/.circleci/config.yml index 1f90fd2..9b5d252 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -19,6 +19,7 @@ jobs: - run: name: Run unit tests command: nix develop --command zig build test --summary all + # TODO: Test evmc compatibility of libethzvm. compile_test_cases: executor: nix steps: @@ -59,6 +60,8 @@ jobs: root: ./ paths: - ./benchmarks/*.benchmark + - store_artifacts: + path: zig-out/ benchmark_evm: executor: base steps: diff --git a/build.zig b/build.zig index 575789b..b749b5b 100644 --- a/build.zig +++ b/build.zig @@ -8,6 +8,9 @@ pub fn build(b: *std.Build) void { const evm_module = b.createModule(.{ .source_file = .{ .path = "src/evm/opcodes.zig" }, }); + const evm_vm_module = b.createModule(.{ + .source_file = .{ .path = "src/evm/vm.zig" }, + }); const exe = b.addExecutable(.{ .name = "eth-zvm", @@ -28,6 +31,16 @@ pub fn build(b: *std.Build) void { mnemonic_exe.addIncludePath(.{ .path = evmc_include_path }); b.installArtifact(mnemonic_exe); + const eth_zvm_lib = b.addSharedLibrary(.{ + .name = "ethzvm", + .root_source_file = .{ .path = "src/evmc/main.zig" }, + .target = target, + .optimize = optimize, + }); + eth_zvm_lib.addIncludePath(.{ .path = evmc_include_path }); + eth_zvm_lib.addModule("evm", evm_vm_module); + b.installArtifact(eth_zvm_lib); + const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); diff --git a/src/evmc/main.zig b/src/evmc/main.zig new file mode 100644 index 0000000..b6227a9 --- /dev/null +++ b/src/evmc/main.zig @@ -0,0 +1,74 @@ +const std = @import("std"); +const evm = @import("evm"); +const evmc = @cImport({ + @cInclude("evmc/evmc.h"); +}); + +fn execute( + _: ?*evmc.evmc_vm, + _: ?*const evmc.evmc_host_interface, + _: ?*evmc.evmc_host_context, + _: evmc.evmc_revision, + _: ?*const evmc.evmc_message, + bytecode_c: [*c]const u8, + bytecode_c_size: usize, +) callconv(.C) evmc.evmc_result { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + defer _ = gpa.deinit(); + + var vm = evm.VM{}; + vm.init(allocator); + defer vm.deinit(); + + const fail_result = evmc.evmc_result{ + .status_code = evmc.EVMC_FAILURE, + .gas_left = 0, + .gas_refund = 0, + .output_data = null, + .output_size = 0, + .release = null, + .create_address = evmc.evmc_address{ + .bytes = [20]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + }, + .padding = [4]u8{ 0, 0, 0, 0 }, + }; + + const bytecode = bytecode_c[0..bytecode_c_size]; + + vm.run(bytecode) catch return fail_result; + + return evmc.evmc_result{ + .status_code = evmc.EVMC_SUCCESS, + .gas_left = 0, + .gas_refund = 0, + .output_data = vm.returnValue.ptr, + .output_size = vm.returnValue.len, + .release = null, + .create_address = evmc.evmc_address{ + .bytes = [20]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + }, + .padding = [4]u8{ 0, 0, 0, 0 }, + }; +} + +fn get_capabilities(_: ?*evmc.evmc_vm) callconv(.C) evmc.evmc_capabilities_flagset { + return evmc.EVMC_CAPABILITY_EVM1; +} + +fn destroy(_: ?*evmc.evmc_vm) callconv(.C) void { + return; +} + +export fn evmc_create() callconv(.C) *evmc.evmc_vm { + var vm: evmc.evmc_vm = evmc.evmc_vm{ + .abi_version = evmc.EVMC_ABI_VERSION, + .name = "eth-zvm", + .version = "0.0.1", + .execute = &execute, + .get_capabilities = &get_capabilities, + .set_option = null, + .destroy = &destroy, + }; + return &vm; +}