From 2bdf1a411a828808b4b08cca38ef52e8f072eb86 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:35:38 +0200 Subject: [PATCH 1/8] create fork manager + implement 2935 with it --- src/blockchain/blockchain.zig | 18 ++++++------ src/blockchain/fork.zig | 23 +++++++++++++++ src/blockchain/forks/base.zig | 50 +++++++++++++++++++++++++++++++++ src/blockchain/forks/prague.zig | 41 +++++++++++++++++++++++++++ src/blockchain/types.zig | 3 +- src/blockchain/vm.zig | 6 +--- src/engine_api/engine_api.zig | 18 ++++++------ src/main.zig | 3 +- src/tests/custom_tests.zig | 3 +- src/tests/spec_tests.zig | 3 +- 10 files changed, 141 insertions(+), 27 deletions(-) create mode 100644 src/blockchain/fork.zig create mode 100644 src/blockchain/forks/base.zig create mode 100644 src/blockchain/forks/prague.zig diff --git a/src/blockchain/blockchain.zig b/src/blockchain/blockchain.zig index ca8583a..be85ba7 100644 --- a/src/blockchain/blockchain.zig +++ b/src/blockchain/blockchain.zig @@ -29,14 +29,15 @@ const Log = types.Log; const TxSigner = signer.TxSigner; const VM = vm.VM; const Keccak256 = std.crypto.hash.sha3.Keccak256; +pub const Fork = @import("fork.zig"); pub const Blockchain = struct { allocator: Allocator, chain_id: config.ChainId, state: *StateDB, prev_block: BlockHeader, - last_256_blocks_hashes: [256]Hash32, // ordered in asc order tx_signer: TxSigner, + fork: *Fork, // init initializes a blockchain. // The caller **does not** transfer ownership of prev_block. @@ -45,14 +46,14 @@ pub const Blockchain = struct { chain_id: config.ChainId, state: *StateDB, prev_block: BlockHeader, - last_256_blocks_hashes: [256]Hash32, + fork: *Fork, ) !Blockchain { return .{ .allocator = allocator, .chain_id = chain_id, .state = state, .prev_block = try prev_block.clone(allocator), - .last_256_blocks_hashes = last_256_blocks_hashes, + .fork = fork, .tx_signer = try signer.TxSigner.init(@intFromEnum(chain_id)), }; } @@ -66,6 +67,9 @@ pub const Blockchain = struct { defer arena.deinit(); const allocator = arena.allocator(); + // Add the current block to the last 256 block hashes. + try self.fork.update_parent_block_hash(block.header.block_number - 1, block.header.parent_hash); + // Execute block. var result = try applyBody(allocator, self, self.state, block, self.tx_signer); @@ -85,12 +89,6 @@ pub const Blockchain = struct { if (!std.mem.eql(u8, &result.withdrawals_root, &block.header.withdrawals_root)) return error.InvalidWithdrawalsRoot; - // Add the current block to the last 256 block hashes. - // TODO: this can be done more efficiently with some ring buffer to avoid copying the slice - // to make room for the new block hash. - std.mem.copyForwards(Hash32, &self.last_256_blocks_hashes, self.last_256_blocks_hashes[1..255]); - self.last_256_blocks_hashes[255] = try common.encodeToRLPAndHash(BlockHeader, allocator, block.header, null); - // Note that we free and clone with the Blockchain allocator, and not the arena allocator. // This is required since Blockchain field lifetimes are longer than the block execution processing. self.prev_block.deinit(self.allocator); @@ -164,8 +162,8 @@ pub const Blockchain = struct { const tx_info = try checkTransaction(allocator, tx, block.header.base_fee_per_gas, gas_available, tx_signer); const env: Environment = .{ + .fork = chain.fork, .origin = tx_info.sender_address, - .block_hashes = chain.last_256_blocks_hashes, .coinbase = block.header.fee_recipient, .number = block.header.block_number, .gas_limit = block.header.gas_limit, diff --git a/src/blockchain/fork.zig b/src/blockchain/fork.zig new file mode 100644 index 0000000..1f0517f --- /dev/null +++ b/src/blockchain/fork.zig @@ -0,0 +1,23 @@ +const lib = @import("../lib.zig"); +const Hash32 = lib.types.Hash32; +const Fork = @This(); +pub const base = @import("./forks/base.zig"); +pub const prague = @import("./forks/prague.zig"); + +vtable: *const VTable, + +pub const VTable = struct { + update_parent_block_hash: *const fn (self: *Fork, num: u64, hash: Hash32) anyerror!void, + get_parent_block_hash: *const fn (self: *Fork, index: u64) anyerror!Hash32, +}; + +// Used to update the parent hash at the end of a block execution +pub fn update_parent_block_hash(self: *Fork, num: u64, hash: Hash32) !void { + return self.vtable.update_parent_block_hash(self, num, hash); +} + +// Used to get the block hash of a parent when implementing the +// BLOCKHASH instruction. +pub fn get_parent_block_hash(self: *Fork, index: u64) !Hash32 { + return self.vtable.get_parent_block_hash(self, index); +} diff --git a/src/blockchain/forks/base.zig b/src/blockchain/forks/base.zig new file mode 100644 index 0000000..e59a4bf --- /dev/null +++ b/src/blockchain/forks/base.zig @@ -0,0 +1,50 @@ +const std = @import("std"); +const Fork = @import("../fork.zig"); +const lib = @import("../../lib.zig"); +const Hash32 = lib.types.Hash32; + +const base_fork_vtable = Fork.VTable{ + .update_parent_block_hash = update_parent_block_hash, + .get_parent_block_hash = get_parent_block_hash, +}; + +const BaseFork = struct { + const Self = @This(); + + fork: Fork = .{ + .vtable = &base_fork_vtable, + }, + + written: u64 = 0, + block_hashes: [256]Hash32 = [_]Hash32{[_]u8{0} ** 32} ** 256, + + fn init(self: *Self) void { + self.written = 0; + self.fork.vtable = &base_fork_vtable; + } +}; + +fn update_parent_block_hash(self: *Fork, num: u64, hash: Hash32) anyerror!void { + var base_fork: *BaseFork = @fieldParentPtr("fork", self); + if (num != base_fork.written) { + return error.NonSequentialParentUpdate; + } + + base_fork.block_hashes[base_fork.written % base_fork.block_hashes.len] = hash; + base_fork.written += 1; +} + +fn get_parent_block_hash(self: *Fork, index: u64) !Hash32 { + const base_fork: *BaseFork = @fieldParentPtr("fork", self); + if (index > base_fork.written or index + base_fork.block_hashes.len < base_fork.written) { + return std.mem.zeroes(Hash32); + } + + return base_fork.block_hashes[index % base_fork.block_hashes.len]; +} + +pub fn newBaseFork(allocator: std.mem.Allocator) *Fork { + var base_fork = allocator.create(BaseFork) catch @panic("could not allocate fork"); + base_fork.init(); + return &base_fork.fork; +} diff --git a/src/blockchain/forks/prague.zig b/src/blockchain/forks/prague.zig new file mode 100644 index 0000000..7ebc30f --- /dev/null +++ b/src/blockchain/forks/prague.zig @@ -0,0 +1,41 @@ +const Fork = @import("../fork.zig"); +const lib = @import("../../lib.zig"); +const Hash32 = lib.types.Hash32; +const StateDB = lib.state.StateDB; +const self = @This(); +const Address = lib.types.Address; + +const system_addr: Address = [_]u8{0xff} ** 19 ++ [_]u8{0xfe}; +const history_size: u64 = 8192; +var state_db: ?*StateDB = null; + +const vtable = Fork.VTable{ + .update_parent_block_hash = update_parent_block_hash, + .get_parent_block_hash = get_parent_block_hash, +}; + +fn update_parent_block_hash(_: *Fork, num: u64, hash: Hash32) anyerror!void { + if (self.state_db) |state_db_ptr| { + const slot: u256 = @intCast(num % history_size); + try state_db_ptr.setStorage(self.system_addr, slot, hash); + } + + return error.UninitializedStateDB; +} + +fn get_parent_block_hash(_: *Fork, index: u64) !Hash32 { + if (self.state_db) |state_db_ptr| { + const slot: u256 = @intCast(index % history_size); + return state_db_ptr.getStorage(self.system_addr, slot); + } + + return error.UninitializedStateDB; +} + +// This method takes a parent fork and activate all the +// Prague-specific methods, superseding the previous fork. +pub fn enablePrague(_: *StateDB, _: *Fork) Fork { + return Fork{ + .vtable = &vtable, + }; +} diff --git a/src/blockchain/types.zig b/src/blockchain/types.zig index 452e576..8c24aa1 100644 --- a/src/blockchain/types.zig +++ b/src/blockchain/types.zig @@ -1,6 +1,7 @@ const types = @import("../types/types.zig"); const config = @import("../config/config.zig"); const common = @import("../common/common.zig"); +const Fork = @import("fork.zig"); const StateDB = @import("../state/state.zig").StateDB; const Address = types.Address; const Hash32 = types.Hash32; @@ -10,7 +11,7 @@ const AddresssKey = common.AddressKey; const AddressKeySet = common.AddressKeySet; pub const Environment = struct { - block_hashes: [256]Hash32, + fork: *Fork, origin: Address, coinbase: Address, number: u64, diff --git a/src/blockchain/vm.zig b/src/blockchain/vm.zig index f3e4a63..8f217c8 100644 --- a/src/blockchain/vm.zig +++ b/src/blockchain/vm.zig @@ -151,11 +151,7 @@ const EVMOneHost = struct { const vm: *VM = @as(*VM, @alignCast(@ptrCast(ctx.?))); const idx = vm.env.number - @as(u64, @intCast(block_number)); - if (idx < 0 or idx >= vm.env.block_hashes.len) { - return std.mem.zeroes(evmc.evmc_bytes32); - } - - return .{ .bytes = vm.env.block_hashes[idx] }; + return .{ .bytes = vm.env.fork.get_parent_block_hash(idx) catch @panic("unhandled error getting parent hash") }; } fn account_exists(ctx: ?*evmc.struct_evmc_host_context, addr: [*c]const evmc.evmc_address) callconv(.C) bool { diff --git a/src/engine_api/engine_api.zig b/src/engine_api/engine_api.zig index b786f7a..333faa8 100644 --- a/src/engine_api/engine_api.zig +++ b/src/engine_api/engine_api.zig @@ -6,6 +6,8 @@ const Allocator = std.mem.Allocator; const Withdrawal = types.Withdrawal; const Tx = types.Tx; const ExecutionPayload = execution_payload.ExecutionPayload; +const lib = @import("../lib.zig"); +const Fork = lib.blockchain.Fork; pub const execution_payload = @import("execution_payload.zig"); @@ -85,21 +87,19 @@ pub const EngineAPIRequest = struct { test "deserialize sample engine_newPayloadV2" { const json = std.json; const expect = std.testing.expect; - const lib = @import("./../lib.zig"); const StateDB = lib.state.StateDB; const Blockchain = lib.blockchain.Blockchain; const AccountState = lib.state.AccountState; const BlockHeader = lib.blockchain.BlockHeader; - const Hash32 = lib.types.Hash32; var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); + const allocator = arena.allocator(); const fileContent = @embedFile("./test_req.json"); - - const payload = try json.parseFromSlice(EngineAPIRequest, arena.allocator(), fileContent, .{ .ignore_unknown_fields = true }); + const payload = try json.parseFromSlice(EngineAPIRequest, allocator, fileContent, .{ .ignore_unknown_fields = true }); defer payload.deinit(); - var statedb = try StateDB.init(arena.allocator(), &[0]AccountState{}); + var statedb = try StateDB.init(allocator, &[0]AccountState{}); defer statedb.deinit(); const parent_header = BlockHeader{ .parent_hash = [_]u8{0} ** 32, @@ -120,11 +120,13 @@ test "deserialize sample engine_newPayloadV2" { .base_fee_per_gas = 7, .withdrawals_root = [_]u8{0} ** 32, }; - var blockchain = try Blockchain.init(arena.allocator(), .Testing, &statedb, parent_header, [_]Hash32{[_]u8{0} ** 32} ** 256); + // TODO pick the fork based on chain config + block number + time stamp + const base_fork = lib.blockchain.Fork.base.newBaseFork(allocator); + var blockchain = try Blockchain.init(allocator, .Testing, &statedb, parent_header, base_fork); try expect(std.mem.eql(u8, payload.value.method, "engine_newPayloadV2")); const execution_payload_json = payload.value.params[0]; - var ep = try execution_payload_json.to_execution_payload(arena.allocator()); - defer ep.deinit(arena.allocator()); + var ep = try execution_payload_json.to_execution_payload(allocator); + defer ep.deinit(allocator); try execution_payload.newPayloadV2Handler(&blockchain, &ep); } diff --git a/src/main.zig b/src/main.zig index 708318f..f847f99 100644 --- a/src/main.zig +++ b/src/main.zig @@ -19,6 +19,7 @@ const json = std.json; const simargs = @import("simargs"); const version = @import("version.zig").version; const Blockchain = lib.blockchain.Blockchain; +const Fork = lib.blockchain.Fork; fn engineAPIHandler(blockchain: *Blockchain, req: *httpz.Request, res: *httpz.Response) !void { if (try req.json(engine_api.EngineAPIRequest)) |payload| { @@ -98,7 +99,7 @@ pub fn main() !void { .base_fee_per_gas = 0, .withdrawals_root = [_]u8{0} ** 32, }; - var blockchain = try Blockchain.init(allocator, config.chainId, &statedb, parent_header, [_]Hash32{[_]u8{0} ** 32} ** 256); + var blockchain = try Blockchain.init(allocator, config.chainId, &statedb, parent_header, Fork.base.fork); var engine_api_server = try httpz.ServerApp(*Blockchain).init(allocator, .{ .port = port, diff --git a/src/tests/custom_tests.zig b/src/tests/custom_tests.zig index 3629c42..db3e966 100644 --- a/src/tests/custom_tests.zig +++ b/src/tests/custom_tests.zig @@ -12,6 +12,7 @@ const Hash32 = types.Hash32; const Address = types.Address; const StateDB = state.StateDB; const ChainID = config.ChainId; +const Fork = blockchain.Fork; test "create contract" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); @@ -25,7 +26,7 @@ test "create contract" { // Configure an EVM execution enviroment for a block from this coinbase. const env: Environment = .{ - .block_hashes = [_]Hash32{std.mem.zeroes(Hash32)} ** 256, + .fork = Fork.base.newBaseFork(allocator), .origin = coinbase, .coinbase = coinbase, .number = 100, diff --git a/src/tests/spec_tests.zig b/src/tests/spec_tests.zig index b4a0ccf..98af67b 100644 --- a/src/tests/spec_tests.zig +++ b/src/tests/spec_tests.zig @@ -19,6 +19,7 @@ const VM = vm.VM; const StateDB = state.StateDB; const AccountState = state.AccountState; const log = std.log.scoped(.execspectests); +const Fork = blockchain.Fork; const HexString = []const u8; @@ -78,7 +79,7 @@ pub const FixtureTest = struct { var out = try allocator.alloc(u8, self.genesisRLP.len / 2); var rlp_bytes = try std.fmt.hexToBytes(out, self.genesisRLP[2..]); const parent_block = try Block.decode(allocator, rlp_bytes); - var chain = try blockchain.Blockchain.init(allocator, config.ChainId.Mainnet, &statedb, parent_block.header, std.mem.zeroes([256]Hash32)); + var chain = try blockchain.Blockchain.init(allocator, config.ChainId.Mainnet, &statedb, parent_block.header, Fork.base.newBaseFork(allocator)); // Execute blocks. for (self.blocks) |encoded_block| { From 59ea8c7fd924570225b26adc39ef6e818a21e212 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:34:45 +0200 Subject: [PATCH 2/8] review feedback --- src/blockchain/fork.zig | 5 +++++ src/blockchain/forks/base.zig | 28 ++++++++++++++++++---------- src/blockchain/forks/prague.zig | 3 +++ src/engine_api/engine_api.zig | 5 +++-- src/tests/custom_tests.zig | 2 +- src/tests/spec_tests.zig | 2 +- 6 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/blockchain/fork.zig b/src/blockchain/fork.zig index 1f0517f..db8c921 100644 --- a/src/blockchain/fork.zig +++ b/src/blockchain/fork.zig @@ -9,6 +9,7 @@ vtable: *const VTable, pub const VTable = struct { update_parent_block_hash: *const fn (self: *Fork, num: u64, hash: Hash32) anyerror!void, get_parent_block_hash: *const fn (self: *Fork, index: u64) anyerror!Hash32, + deinit: *const fn (self: *Fork) void, }; // Used to update the parent hash at the end of a block execution @@ -21,3 +22,7 @@ pub fn update_parent_block_hash(self: *Fork, num: u64, hash: Hash32) !void { pub fn get_parent_block_hash(self: *Fork, index: u64) !Hash32 { return self.vtable.get_parent_block_hash(self, index); } + +pub fn deinit(self: *Fork) void { + self.vtable.deinit(self); +} diff --git a/src/blockchain/forks/base.zig b/src/blockchain/forks/base.zig index e59a4bf..04db1aa 100644 --- a/src/blockchain/forks/base.zig +++ b/src/blockchain/forks/base.zig @@ -6,6 +6,7 @@ const Hash32 = lib.types.Hash32; const base_fork_vtable = Fork.VTable{ .update_parent_block_hash = update_parent_block_hash, .get_parent_block_hash = get_parent_block_hash, + .deinit = deinit, }; const BaseFork = struct { @@ -15,36 +16,43 @@ const BaseFork = struct { .vtable = &base_fork_vtable, }, - written: u64 = 0, + allocator: std.mem.Allocator, + next_block_hash_index: u64 = 0, // index of the next block hash to be written block_hashes: [256]Hash32 = [_]Hash32{[_]u8{0} ** 32} ** 256, fn init(self: *Self) void { - self.written = 0; + self.next_block_hash_index = 0; self.fork.vtable = &base_fork_vtable; } }; -fn update_parent_block_hash(self: *Fork, num: u64, hash: Hash32) anyerror!void { +fn update_parent_block_hash(self: *Fork, block_num: u64, hash: Hash32) anyerror!void { var base_fork: *BaseFork = @fieldParentPtr("fork", self); - if (num != base_fork.written) { + if (block_num != base_fork.next_block_hash_index) { return error.NonSequentialParentUpdate; } - base_fork.block_hashes[base_fork.written % base_fork.block_hashes.len] = hash; - base_fork.written += 1; + base_fork.block_hashes[base_fork.next_block_hash_index % base_fork.block_hashes.len] = hash; + base_fork.next_block_hash_index += 1; } -fn get_parent_block_hash(self: *Fork, index: u64) !Hash32 { +fn get_parent_block_hash(self: *Fork, block_num: u64) !Hash32 { const base_fork: *BaseFork = @fieldParentPtr("fork", self); - if (index > base_fork.written or index + base_fork.block_hashes.len < base_fork.written) { + if (block_num > base_fork.next_block_hash_index or block_num + base_fork.block_hashes.len < base_fork.next_block_hash_index) { return std.mem.zeroes(Hash32); } - return base_fork.block_hashes[index % base_fork.block_hashes.len]; + return base_fork.block_hashes[block_num % base_fork.block_hashes.len]; } -pub fn newBaseFork(allocator: std.mem.Allocator) *Fork { +pub fn newBaseFork(allocator: std.mem.Allocator) !*Fork { var base_fork = allocator.create(BaseFork) catch @panic("could not allocate fork"); base_fork.init(); + base_fork.allocator = allocator; return &base_fork.fork; } + +fn deinit(self: *Fork) void { + var base_fork: *BaseFork = @fieldParentPtr("fork", self); + base_fork.allocator.destroy(base_fork); +} diff --git a/src/blockchain/forks/prague.zig b/src/blockchain/forks/prague.zig index 7ebc30f..3791563 100644 --- a/src/blockchain/forks/prague.zig +++ b/src/blockchain/forks/prague.zig @@ -12,6 +12,7 @@ var state_db: ?*StateDB = null; const vtable = Fork.VTable{ .update_parent_block_hash = update_parent_block_hash, .get_parent_block_hash = get_parent_block_hash, + .deinit = deinit, }; fn update_parent_block_hash(_: *Fork, num: u64, hash: Hash32) anyerror!void { @@ -39,3 +40,5 @@ pub fn enablePrague(_: *StateDB, _: *Fork) Fork { .vtable = &vtable, }; } + +fn deinit(_: *Fork) void {} diff --git a/src/engine_api/engine_api.zig b/src/engine_api/engine_api.zig index 333faa8..69d6211 100644 --- a/src/engine_api/engine_api.zig +++ b/src/engine_api/engine_api.zig @@ -120,8 +120,9 @@ test "deserialize sample engine_newPayloadV2" { .base_fee_per_gas = 7, .withdrawals_root = [_]u8{0} ** 32, }; - // TODO pick the fork based on chain config + block number + time stamp - const base_fork = lib.blockchain.Fork.base.newBaseFork(allocator); + // TODO pick the fork based on chain config + block number + timestamp + const base_fork = try lib.blockchain.Fork.base.newBaseFork(allocator); + defer base_fork.deinit(); var blockchain = try Blockchain.init(allocator, .Testing, &statedb, parent_header, base_fork); try expect(std.mem.eql(u8, payload.value.method, "engine_newPayloadV2")); diff --git a/src/tests/custom_tests.zig b/src/tests/custom_tests.zig index db3e966..98430d5 100644 --- a/src/tests/custom_tests.zig +++ b/src/tests/custom_tests.zig @@ -26,7 +26,7 @@ test "create contract" { // Configure an EVM execution enviroment for a block from this coinbase. const env: Environment = .{ - .fork = Fork.base.newBaseFork(allocator), + .fork = try Fork.base.newBaseFork(allocator), .origin = coinbase, .coinbase = coinbase, .number = 100, diff --git a/src/tests/spec_tests.zig b/src/tests/spec_tests.zig index 98af67b..cbfc8a6 100644 --- a/src/tests/spec_tests.zig +++ b/src/tests/spec_tests.zig @@ -79,7 +79,7 @@ pub const FixtureTest = struct { var out = try allocator.alloc(u8, self.genesisRLP.len / 2); var rlp_bytes = try std.fmt.hexToBytes(out, self.genesisRLP[2..]); const parent_block = try Block.decode(allocator, rlp_bytes); - var chain = try blockchain.Blockchain.init(allocator, config.ChainId.Mainnet, &statedb, parent_block.header, Fork.base.newBaseFork(allocator)); + var chain = try blockchain.Blockchain.init(allocator, config.ChainId.Mainnet, &statedb, parent_block.header, try Fork.base.newBaseFork(allocator)); // Execute blocks. for (self.blocks) |encoded_block| { From 4fb3421192266d2d4fae6e5b530b3c6c6b523de4 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:45:46 +0200 Subject: [PATCH 3/8] fix ci build --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index f847f99..aff58b9 100644 --- a/src/main.zig +++ b/src/main.zig @@ -99,7 +99,7 @@ pub fn main() !void { .base_fee_per_gas = 0, .withdrawals_root = [_]u8{0} ** 32, }; - var blockchain = try Blockchain.init(allocator, config.chainId, &statedb, parent_header, Fork.base.fork); + var blockchain = try Blockchain.init(allocator, config.chainId, &statedb, parent_header, try Fork.base.newBaseFork(allocator)); var engine_api_server = try httpz.ServerApp(*Blockchain).init(allocator, .{ .port = port, From b4d69714ce0502a0cf4b1674fc33c07ab35cdf7d Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:47:31 +0200 Subject: [PATCH 4/8] more review feedback co-authored-by: Ignacio Hagopian --- src/engine_api/engine_api.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine_api/engine_api.zig b/src/engine_api/engine_api.zig index 69d6211..61797a8 100644 --- a/src/engine_api/engine_api.zig +++ b/src/engine_api/engine_api.zig @@ -121,7 +121,7 @@ test "deserialize sample engine_newPayloadV2" { .withdrawals_root = [_]u8{0} ** 32, }; // TODO pick the fork based on chain config + block number + timestamp - const base_fork = try lib.blockchain.Fork.base.newBaseFork(allocator); + const base_fork = try Fork.base.newBaseFork(allocator); defer base_fork.deinit(); var blockchain = try Blockchain.init(allocator, .Testing, &statedb, parent_header, base_fork); From 11b01701ad55459940eaec36f2624ea769ae02c0 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 30 Aug 2024 20:07:49 +0200 Subject: [PATCH 5/8] remove panic --- src/blockchain/forks/base.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/blockchain/forks/base.zig b/src/blockchain/forks/base.zig index 04db1aa..a0564d6 100644 --- a/src/blockchain/forks/base.zig +++ b/src/blockchain/forks/base.zig @@ -46,7 +46,7 @@ fn get_parent_block_hash(self: *Fork, block_num: u64) !Hash32 { } pub fn newBaseFork(allocator: std.mem.Allocator) !*Fork { - var base_fork = allocator.create(BaseFork) catch @panic("could not allocate fork"); + var base_fork = try allocator.create(BaseFork); base_fork.init(); base_fork.allocator = allocator; return &base_fork.fork; From e0281923b645ff75bc2608d8612b53b9365ab8c5 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 30 Aug 2024 20:08:26 +0200 Subject: [PATCH 6/8] fix a few issues for the 2935 implementation --- src/blockchain/forks/prague.zig | 42 ++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/blockchain/forks/prague.zig b/src/blockchain/forks/prague.zig index 3791563..bab4ee5 100644 --- a/src/blockchain/forks/prague.zig +++ b/src/blockchain/forks/prague.zig @@ -1,13 +1,12 @@ +const std = @import("std"); const Fork = @import("../fork.zig"); const lib = @import("../../lib.zig"); const Hash32 = lib.types.Hash32; const StateDB = lib.state.StateDB; -const self = @This(); const Address = lib.types.Address; const system_addr: Address = [_]u8{0xff} ** 19 ++ [_]u8{0xfe}; const history_size: u64 = 8192; -var state_db: ?*StateDB = null; const vtable = Fork.VTable{ .update_parent_block_hash = update_parent_block_hash, @@ -15,30 +14,35 @@ const vtable = Fork.VTable{ .deinit = deinit, }; -fn update_parent_block_hash(_: *Fork, num: u64, hash: Hash32) anyerror!void { - if (self.state_db) |state_db_ptr| { - const slot: u256 = @intCast(num % history_size); - try state_db_ptr.setStorage(self.system_addr, slot, hash); - } +const PragueFork = struct { + fork: Fork = Fork{ + .vtable = &vtable, + }, - return error.UninitializedStateDB; -} + state_db: *StateDB, + allocator: std.mem.Allocator, +}; -fn get_parent_block_hash(_: *Fork, index: u64) !Hash32 { - if (self.state_db) |state_db_ptr| { - const slot: u256 = @intCast(index % history_size); - return state_db_ptr.getStorage(self.system_addr, slot); - } +fn update_parent_block_hash(self: *Fork, num: u64, hash: Hash32) anyerror!void { + const prague_fork: *PragueFork = @fieldParentPtr("fork", self); + const slot: u256 = @intCast(num % history_size); + try prague_fork.state_db.setStorage(system_addr, slot, hash); +} - return error.UninitializedStateDB; +fn get_parent_block_hash(self: *Fork, index: u64) !Hash32 { + const prague_fork: *PragueFork = @fieldParentPtr("fork", self); + const slot: u256 = @intCast(index % history_size); + return prague_fork.state_db.getStorage(system_addr, slot); } // This method takes a parent fork and activate all the // Prague-specific methods, superseding the previous fork. -pub fn enablePrague(_: *StateDB, _: *Fork) Fork { - return Fork{ - .vtable = &vtable, - }; +pub fn enablePrague(state_db: *StateDB, _: ?*Fork, allocator: std.mem.Allocator) !*Fork { + var prague_fork = try allocator.create(PragueFork); + prague_fork.allocator = allocator; + prague_fork.state_db = state_db; + prague_fork.fork = Fork{ .vtable = &vtable }; + return &prague_fork.fork; } fn deinit(_: *Fork) void {} From cafa3b1c87cd7ad24636c0a8d1f4a307343003f1 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 30 Aug 2024 21:13:58 +0200 Subject: [PATCH 7/8] activate Prague fork in tests --- src/blockchain/forks/prague.zig | 11 ++++++++++- src/engine_api/engine_api.zig | 7 ++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/blockchain/forks/prague.zig b/src/blockchain/forks/prague.zig index bab4ee5..612d1b5 100644 --- a/src/blockchain/forks/prague.zig +++ b/src/blockchain/forks/prague.zig @@ -45,4 +45,13 @@ pub fn enablePrague(state_db: *StateDB, _: ?*Fork, allocator: std.mem.Allocator) return &prague_fork.fork; } -fn deinit(_: *Fork) void {} +fn deinit(self: *Fork) void { + const prague_fork: *PragueFork = @fieldParentPtr("fork", self); + prague_fork.allocator.destroy(prague_fork); +} + +// a helper function to deploy the 2935 contract on a testnet. +pub fn deployContract(self: *Fork) !void { + const prague_fork: *PragueFork = @fieldParentPtr("fork", self); + try prague_fork.state_db.db.put(system_addr, try lib.state.AccountState.init(prague_fork.allocator, system_addr, 1, 0, &[0]u8{})); +} diff --git a/src/engine_api/engine_api.zig b/src/engine_api/engine_api.zig index 61797a8..ef45d21 100644 --- a/src/engine_api/engine_api.zig +++ b/src/engine_api/engine_api.zig @@ -121,9 +121,10 @@ test "deserialize sample engine_newPayloadV2" { .withdrawals_root = [_]u8{0} ** 32, }; // TODO pick the fork based on chain config + block number + timestamp - const base_fork = try Fork.base.newBaseFork(allocator); - defer base_fork.deinit(); - var blockchain = try Blockchain.init(allocator, .Testing, &statedb, parent_header, base_fork); + const prague_fork = try Fork.prague.enablePrague(&statedb, null, allocator); + defer prague_fork.deinit(); + try Fork.prague.deployContract(prague_fork); + var blockchain = try Blockchain.init(allocator, .Testing, &statedb, parent_header, prague_fork); try expect(std.mem.eql(u8, payload.value.method, "engine_newPayloadV2")); const execution_payload_json = payload.value.params[0]; From a6ee8bad53337fd7dc6fb17378728f6f0076e5ea Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:08:12 +0200 Subject: [PATCH 8/8] rename "base" fork to "fontier" Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- src/blockchain/fork.zig | 2 +- src/blockchain/forks/{base.zig => frontier.zig} | 12 ++++++------ src/main.zig | 2 +- src/tests/custom_tests.zig | 2 +- src/tests/spec_tests.zig | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) rename src/blockchain/forks/{base.zig => frontier.zig} (81%) diff --git a/src/blockchain/fork.zig b/src/blockchain/fork.zig index db8c921..af91e13 100644 --- a/src/blockchain/fork.zig +++ b/src/blockchain/fork.zig @@ -1,7 +1,7 @@ const lib = @import("../lib.zig"); const Hash32 = lib.types.Hash32; const Fork = @This(); -pub const base = @import("./forks/base.zig"); +pub const frontier = @import("./forks/frontier.zig"); pub const prague = @import("./forks/prague.zig"); vtable: *const VTable, diff --git a/src/blockchain/forks/base.zig b/src/blockchain/forks/frontier.zig similarity index 81% rename from src/blockchain/forks/base.zig rename to src/blockchain/forks/frontier.zig index a0564d6..2ee0524 100644 --- a/src/blockchain/forks/base.zig +++ b/src/blockchain/forks/frontier.zig @@ -9,7 +9,7 @@ const base_fork_vtable = Fork.VTable{ .deinit = deinit, }; -const BaseFork = struct { +const FrontierFork = struct { const Self = @This(); fork: Fork = .{ @@ -27,7 +27,7 @@ const BaseFork = struct { }; fn update_parent_block_hash(self: *Fork, block_num: u64, hash: Hash32) anyerror!void { - var base_fork: *BaseFork = @fieldParentPtr("fork", self); + var base_fork: *FrontierFork = @fieldParentPtr("fork", self); if (block_num != base_fork.next_block_hash_index) { return error.NonSequentialParentUpdate; } @@ -37,7 +37,7 @@ fn update_parent_block_hash(self: *Fork, block_num: u64, hash: Hash32) anyerror! } fn get_parent_block_hash(self: *Fork, block_num: u64) !Hash32 { - const base_fork: *BaseFork = @fieldParentPtr("fork", self); + const base_fork: *FrontierFork = @fieldParentPtr("fork", self); if (block_num > base_fork.next_block_hash_index or block_num + base_fork.block_hashes.len < base_fork.next_block_hash_index) { return std.mem.zeroes(Hash32); } @@ -45,14 +45,14 @@ fn get_parent_block_hash(self: *Fork, block_num: u64) !Hash32 { return base_fork.block_hashes[block_num % base_fork.block_hashes.len]; } -pub fn newBaseFork(allocator: std.mem.Allocator) !*Fork { - var base_fork = try allocator.create(BaseFork); +pub fn newFrontierFork(allocator: std.mem.Allocator) !*Fork { + var base_fork = try allocator.create(FrontierFork); base_fork.init(); base_fork.allocator = allocator; return &base_fork.fork; } fn deinit(self: *Fork) void { - var base_fork: *BaseFork = @fieldParentPtr("fork", self); + var base_fork: *FrontierFork = @fieldParentPtr("fork", self); base_fork.allocator.destroy(base_fork); } diff --git a/src/main.zig b/src/main.zig index aff58b9..5d217f3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -99,7 +99,7 @@ pub fn main() !void { .base_fee_per_gas = 0, .withdrawals_root = [_]u8{0} ** 32, }; - var blockchain = try Blockchain.init(allocator, config.chainId, &statedb, parent_header, try Fork.base.newBaseFork(allocator)); + var blockchain = try Blockchain.init(allocator, config.chainId, &statedb, parent_header, try Fork.frontier.newFrontierFork(allocator)); var engine_api_server = try httpz.ServerApp(*Blockchain).init(allocator, .{ .port = port, diff --git a/src/tests/custom_tests.zig b/src/tests/custom_tests.zig index 98430d5..e2172f9 100644 --- a/src/tests/custom_tests.zig +++ b/src/tests/custom_tests.zig @@ -26,7 +26,7 @@ test "create contract" { // Configure an EVM execution enviroment for a block from this coinbase. const env: Environment = .{ - .fork = try Fork.base.newBaseFork(allocator), + .fork = try Fork.frontier.newFrontierFork(allocator), .origin = coinbase, .coinbase = coinbase, .number = 100, diff --git a/src/tests/spec_tests.zig b/src/tests/spec_tests.zig index cbfc8a6..e66e1fb 100644 --- a/src/tests/spec_tests.zig +++ b/src/tests/spec_tests.zig @@ -79,7 +79,7 @@ pub const FixtureTest = struct { var out = try allocator.alloc(u8, self.genesisRLP.len / 2); var rlp_bytes = try std.fmt.hexToBytes(out, self.genesisRLP[2..]); const parent_block = try Block.decode(allocator, rlp_bytes); - var chain = try blockchain.Blockchain.init(allocator, config.ChainId.Mainnet, &statedb, parent_block.header, try Fork.base.newBaseFork(allocator)); + var chain = try blockchain.Blockchain.init(allocator, config.ChainId.Mainnet, &statedb, parent_block.header, try Fork.frontier.newFrontierFork(allocator)); // Execute blocks. for (self.blocks) |encoded_block| {