From 9c16e7188212542509169475bf3af0d3e6522dd9 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 30 Oct 2023 21:02:34 -0300 Subject: [PATCH 01/10] use zig-eth-secp256k1 module Signed-off-by: Ignacio Hagopian --- build.zig | 6 ++++++ build.zig.zon | 4 ++++ src/types/transaction.zig | 4 +++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index adff67e..e2d6296 100644 --- a/build.zig +++ b/build.zig @@ -12,6 +12,8 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const mod_rlp = b.dependency("zig-rlp", .{}).module("zig-rlp"); + const depSecp256k1 = b.dependency("zig-eth-secp256k1", .{}); + const mod_secp256k1 = depSecp256k1.module("zig-eth-secp256k1"); // Standard optimization options allow the person running `zig build` to select // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not @@ -96,6 +98,8 @@ pub fn build(b: *std.Build) void { exe.linkLibrary(evmone); exe.linkLibC(); exe.addModule("zig-rlp", mod_rlp); + exe.linkLibrary(depSecp256k1.artifact("secp256k1")); + exe.addModule("zig-eth-secp256k1", mod_secp256k1); // This declares intent for the executable to be installed into the // standard location when the user invokes the "install" step (the default @@ -146,6 +150,8 @@ pub fn build(b: *std.Build) void { unit_tests.linkLibrary(evmone); unit_tests.linkLibC(); unit_tests.addModule("zig-rlp", mod_rlp); + unit_tests.linkLibrary(depSecp256k1.artifact("secp256k1")); + unit_tests.addModule("zig-eth-secp256k1", mod_secp256k1); const run_unit_tests = b.addRunArtifact(unit_tests); run_unit_tests.has_side_effects = true; diff --git a/build.zig.zon b/build.zig.zon index 80f7236..c190abe 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -6,5 +6,9 @@ .url = "https://github.com/gballet/zig-rlp/archive/refs/tags/v0.0.1-beta-0.tar.gz", .hash = "122000e0811d6cb4758f6122b1de2d384efa32b9b2714caec2236f6b34b0529d699c", }, + .@"zig-eth-secp256k1" = .{ + .url = "https://github.com/jsign/zig-eth-secp256k1/archive/refs/tags/v0.1.0-beta-1.tar.gz", + .hash = "12207f816de4045e6cffce82a0e8242c61cf93911cfeaf4adfb4e2f763b03b7dd4bb", + }, }, } diff --git a/src/types/transaction.zig b/src/types/transaction.zig index a09a71f..52fa4cd 100644 --- a/src/types/transaction.zig +++ b/src/types/transaction.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const secp256k1 = @import("zig-eth-secp256k1"); const types = @import("types.zig"); const Address = types.Address; @@ -26,8 +27,9 @@ pub fn init(type_: u8, chain_id: u256, nonce: u64, gas_price: u256, value: u256, }; } -// TODO(jsign): use some secp256k1 library. pub fn get_from(_: *const @This()) Address { + var secp = secp256k1.Secp256k1.init(); + _ = secp; const from: Address = comptime blk: { var buf: Address = undefined; _ = std.fmt.hexToBytes(&buf, "a94f5374Fce5edBC8E2a8697C15331677e6EbF0B") catch unreachable; From b522b8a61f0009507563d1b0bf89712b8d78e2b8 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Wed, 1 Nov 2023 22:58:01 -0300 Subject: [PATCH 02/10] use non-cimported secp256k1 Signed-off-by: Ignacio Hagopian --- build.zig.zon | 4 ++-- src/types/transaction.zig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index c190abe..1e1c013 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -7,8 +7,8 @@ .hash = "122000e0811d6cb4758f6122b1de2d384efa32b9b2714caec2236f6b34b0529d699c", }, .@"zig-eth-secp256k1" = .{ - .url = "https://github.com/jsign/zig-eth-secp256k1/archive/refs/tags/v0.1.0-beta-1.tar.gz", - .hash = "12207f816de4045e6cffce82a0e8242c61cf93911cfeaf4adfb4e2f763b03b7dd4bb", + .url = "https://github.com/jsign/zig-eth-secp256k1/archive/refs/tags/v0.1.0-beta-2.tar.gz", + .hash = "1220eac83d1b17e9eadbde506731c923fe6f075858687d86628142d0c6b15cd4e534", }, }, } diff --git a/src/types/transaction.zig b/src/types/transaction.zig index 52fa4cd..20597a3 100644 --- a/src/types/transaction.zig +++ b/src/types/transaction.zig @@ -28,7 +28,7 @@ pub fn init(type_: u8, chain_id: u256, nonce: u64, gas_price: u256, value: u256, } pub fn get_from(_: *const @This()) Address { - var secp = secp256k1.Secp256k1.init(); + var secp = secp256k1.Secp256k1.init() catch unreachable; _ = secp; const from: Address = comptime blk: { var buf: Address = undefined; From 94337188289633b4942d06459cc126cf9413baae Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Wed, 1 Nov 2023 23:07:58 -0300 Subject: [PATCH 03/10] use target and optimize flags in dependencies Signed-off-by: Ignacio Hagopian --- build.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index e2d6296..1071df5 100644 --- a/build.zig +++ b/build.zig @@ -11,15 +11,15 @@ pub fn build(b: *std.Build) void { // for restricting supported target set are available. const target = b.standardTargetOptions(.{}); - const mod_rlp = b.dependency("zig-rlp", .{}).module("zig-rlp"); - const depSecp256k1 = b.dependency("zig-eth-secp256k1", .{}); - const mod_secp256k1 = depSecp256k1.module("zig-eth-secp256k1"); - // Standard optimization options allow the person running `zig build` to select // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); + const mod_rlp = b.dependency("zig-rlp", .{ .target = target, .optimize = optimize }).module("zig-rlp"); + const depSecp256k1 = b.dependency("zig-eth-secp256k1", .{ .target = target, .optimize = optimize }); + const mod_secp256k1 = depSecp256k1.module("zig-eth-secp256k1"); + const ethash = b.addStaticLibrary(.{ .name = "ethash", .optimize = optimize, From 8fa55e2a6d026911c029a15742e8e7896481bd53 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 4 Dec 2023 10:36:27 -0300 Subject: [PATCH 04/10] build: allow to cImport ethash Signed-off-by: Ignacio Hagopian --- build.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/build.zig b/build.zig index 1071df5..d240df1 100644 --- a/build.zig +++ b/build.zig @@ -138,6 +138,7 @@ pub fn build(b: *std.Build) void { }); unit_tests.addIncludePath(LazyPath{ .path = "evmone/include/evmone" }); unit_tests.addIncludePath(LazyPath{ .path = "evmone/evmc/include" }); + unit_tests.addIncludePath(LazyPath{ .path = "ethash/include" }); if (target.getCpuArch() == .x86_64) { // On x86_64, some functions are missing from the static library, // so we define dummy functions to make sure that it compiles. From 65a742e1cfcac98fd7bd259d7fe08c8d4eaf3d0f Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 4 Dec 2023 10:37:01 -0300 Subject: [PATCH 05/10] update zig-eth-secp256k1 module Signed-off-by: Ignacio Hagopian --- build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 1e1c013..8f55e45 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -7,8 +7,8 @@ .hash = "122000e0811d6cb4758f6122b1de2d384efa32b9b2714caec2236f6b34b0529d699c", }, .@"zig-eth-secp256k1" = .{ - .url = "https://github.com/jsign/zig-eth-secp256k1/archive/refs/tags/v0.1.0-beta-2.tar.gz", - .hash = "1220eac83d1b17e9eadbde506731c923fe6f075858687d86628142d0c6b15cd4e534", + .url = "https://github.com/jsign/zig-eth-secp256k1/archive/refs/tags/v0.1.0-beta-3.tar.gz", + .hash = "1220fcf062f8ee89b343e1588ac3cc002f37ee3f72841dd7f9493d9c09acad7915a3", }, }, } From af100494430e941e936af84b0f3fe4641f78c2e3 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 4 Dec 2023 10:37:48 -0300 Subject: [PATCH 06/10] crypto: add package with ecdsa and hasher implementations Signed-off-by: Ignacio Hagopian --- src/crypto/ecdsa.zig | 50 +++++++++++++++++++++++++++++++++++++++++++ src/crypto/hasher.zig | 8 +++++++ 2 files changed, 58 insertions(+) create mode 100644 src/crypto/ecdsa.zig create mode 100644 src/crypto/hasher.zig diff --git a/src/crypto/ecdsa.zig b/src/crypto/ecdsa.zig new file mode 100644 index 0000000..9f2f9bd --- /dev/null +++ b/src/crypto/ecdsa.zig @@ -0,0 +1,50 @@ +const std = @import("std"); +const secp256k1 = @import("zig-eth-secp256k1"); + +pub const Signature = [65]u8; +pub const Message = [32]u8; +pub const PrivateKey = [32]u8; +pub const CompressedPublicKey = [33]u8; +pub const UncompressedPublicKey = [65]u8; + +pub const Signer = struct { + sec: secp256k1.Secp256k1, + + pub fn init() !Signer { + return Signer{ + .sec = try secp256k1.Secp256k1.init(), + }; + } + + pub fn erecover(self: Signer, sig: Signature, msg: Message) !UncompressedPublicKey { + return try self.sec.recoverPubkey(msg, sig); + } + + pub fn sign(self: Signer, msg: Message, privkey: PrivateKey) !Signature { + return try self.sec.sign(msg, privkey); + } +}; + +// The following test values where generated using geth, as a reference. +const hashed_msg = hexToBytes("0x05e0e0ff09b01e5626daac3165b82afa42be29197b82e8a5a8800740ee7519d2"); +const private_key = hexToBytes("0xf457cd3bd0186e342d243ea40ad78fe8e81743f90852e87074e68d8c94c2a42e"); +const signature = hexToBytes("0x5a62891eb3e26f3a2344f93a7bad7fe5e670dc45cbdbf0e5bbdba4399238b5e6614caf592f96ee273a2bf018a976e7bf4b63777f9e53ce819d96c5035611400600"); +const uncompressed_pubkey = hexToBytes("0x04682bade67348db99074fcaaffef29394192e7e227a2bdb49f930c74358060c6a42df70f7ef8aadd94854abe646e047142fad42811e325afbec4753342d630b1e"); +const compressed_pubkey = hexToBytes("0x02682bade67348db99074fcaaffef29394192e7e227a2bdb49f930c74358060c6a"); + +test "erecover" { + const signer = try Signer.init(); + const got_pubkey = try signer.erecover(signature, hashed_msg); + try std.testing.expectEqual(uncompressed_pubkey, got_pubkey); +} + +// TODO: must to hexutils when a current PR gets merged. +fn hexToBytes(comptime hex: []const u8) [hex.len / 2 - if (std.mem.startsWith(u8, hex, "0x")) 1 else 0]u8 { + var target = hex; + if (std.mem.startsWith(u8, hex, "0x")) { + target = hex[2..]; + } + var ret: [target.len / 2]u8 = undefined; + _ = std.fmt.hexToBytes(&ret, target) catch unreachable; + return ret; +} diff --git a/src/crypto/hasher.zig b/src/crypto/hasher.zig new file mode 100644 index 0000000..e010a67 --- /dev/null +++ b/src/crypto/hasher.zig @@ -0,0 +1,8 @@ +const ethash = @cImport({ + @cInclude("ethash/keccak.h"); +}); + +pub fn keccak256(data: []const u8) [32]u8 { + const ret = ethash.ethash_keccak256(data.ptr, data.len); + return ret.bytes; +} From 62276b923493c0b9dbb98f425f06affb880a616b Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 4 Dec 2023 10:38:28 -0300 Subject: [PATCH 07/10] signer: add txn signer Signed-off-by: Ignacio Hagopian --- src/signer/signer.zig | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/signer/signer.zig diff --git a/src/signer/signer.zig b/src/signer/signer.zig new file mode 100644 index 0000000..e3efff9 --- /dev/null +++ b/src/signer/signer.zig @@ -0,0 +1,40 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const ecdsa = @import("../crypto/ecdsa.zig"); +const Transaction = @import("../types/transaction.zig"); +const Address = @import("../types/types.zig").Address; +const hasher = @import("../crypto/hasher.zig"); + +// TODO: TxnSigner should be generalized to: +// - Only accept correct transactions types depending on the fork we're in. +// - Handle "v" correctly depending on transaction type. +// For now it's a post London signer, and only support 1559 txns. +pub const TxnSigner = struct { + ecdsa_signer: ecdsa.Signer, + + pub fn init() !TxnSigner { + return TxnSigner{ + .ecdsa_signer = try ecdsa.Signer.init(), + }; + } + + pub fn sign(self: TxnSigner, allocator: Allocator, txn: Transaction, privkey: ecdsa.PrivateKey) !struct { r: u256, s: u256, v: u8 } { + const txn_hash = try txn.hash(allocator); + + const ecdsa_sig = try self.ecdsa_signer.sign(txn_hash, privkey); + const r = std.mem.readIntSlice(u256, ecdsa_sig[0..32], std.builtin.Endian.Big); + const s = std.mem.readIntSlice(u256, ecdsa_sig[32..64], std.builtin.Endian.Big); + const v = ecdsa_sig[64]; + return .{ .r = r, .s = s, .v = v }; + } + + pub fn get_sender(self: TxnSigner, allocator: Allocator, txn: Transaction) !Address { + const txn_hash = try txn.hash(allocator); + var sig: ecdsa.Signature = undefined; + std.mem.writeIntSlice(u256, sig[0..32], txn.r, std.builtin.Endian.Big); + std.mem.writeIntSlice(u256, sig[32..64], txn.s, std.builtin.Endian.Big); + sig[64] = txn.v; + const pubkey = try self.ecdsa_signer.erecover(sig, txn_hash); + return hasher.keccak256(pubkey[1..])[12..].*; + } +}; From 88544115b4fd8089e32cafb0d8d9f3c0fc985ba2 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 4 Dec 2023 10:38:46 -0300 Subject: [PATCH 08/10] evolve code to use signed transactions Signed-off-by: Ignacio Hagopian --- src/exec-spec-tests/execspectests.zig | 17 ++++-- src/main.zig | 1 + src/types/transaction.zig | 86 ++++++++++++++++++--------- src/vm/vm.zig | 40 +++++++------ 4 files changed, 92 insertions(+), 52 deletions(-) diff --git a/src/exec-spec-tests/execspectests.zig b/src/exec-spec-tests/execspectests.zig index 693203a..191f8f7 100644 --- a/src/exec-spec-tests/execspectests.zig +++ b/src/exec-spec-tests/execspectests.zig @@ -10,6 +10,8 @@ const BlockHeader = types.BlockHeader; const vm = @import("../vm/vm.zig"); const VM = vm.VM; const StateDB = vm.StateDB; +const TxnSigner = @import("../signer/signer.zig").TxnSigner; +const ecdsa = @import("../crypto/ecdsa.zig"); const log = std.log.scoped(.execspectests); const HexString = []const u8; @@ -69,6 +71,7 @@ pub const FixtureTest = struct { var evm = VM.init(&db); // 2. Execute blocks. + const txn_signer = try TxnSigner.init(); for (self.blocks) |encoded_block| { var out = try allocator.alloc(u8, encoded_block.rlp.len / 2); defer allocator.free(out); @@ -79,10 +82,10 @@ pub const FixtureTest = struct { var txns = try allocator.alloc(Transaction, encoded_block.transactions.len); defer allocator.free(txns); for (encoded_block.transactions, 0..) |tx_hex, i| { - txns[i] = try tx_hex.to_vm_transaction(allocator); + txns[i] = try tx_hex.to_vm_transaction(allocator, txn_signer); } - try evm.run_block(block, txns); + try evm.run_block(allocator, txn_signer, block, txns); } // 3. Verify that the post state matches what the fixture `postState` claims is true. @@ -145,7 +148,7 @@ pub const TransactionHex = struct { data: HexString, gasLimit: HexString, - pub fn to_vm_transaction(self: *const TransactionHex, allocator: Allocator) !Transaction { + pub fn to_vm_transaction(self: TransactionHex, allocator: Allocator, txn_signer: TxnSigner) !Transaction { const type_ = try std.fmt.parseInt(u8, self.type[2..], 16); const chain_id = try std.fmt.parseInt(u256, self.chainId[2..], 16); const nonce = try std.fmt.parseUnsigned(u64, self.nonce[2..], 16); @@ -160,7 +163,13 @@ pub const TransactionHex = struct { _ = try std.fmt.hexToBytes(data, self.data[2..]); const gas_limit = try std.fmt.parseUnsigned(u64, self.gasLimit[2..], 16); - return Transaction.init(type_, chain_id, nonce, gas_price, value, to, data, gas_limit); + var txn = Transaction.init(type_, chain_id, nonce, gas_price, value, to, data, gas_limit); + var privkey: ecdsa.PrivateKey = undefined; + _ = try std.fmt.hexToBytes(&privkey, self.secretKey[2..]); + const sig = try txn_signer.sign(allocator, txn, privkey); + txn.setSignature(sig.v, sig.r, sig.s); + + return txn; } }; diff --git a/src/main.zig b/src/main.zig index 6c679fd..47827bb 100644 --- a/src/main.zig +++ b/src/main.zig @@ -77,4 +77,5 @@ test "tests" { _ = @import("exec-spec-tests/execspectests.zig"); _ = @import("types/types.zig"); _ = @import("vm/vm.zig"); + _ = @import("crypto/ecdsa.zig"); } diff --git a/src/types/transaction.zig b/src/types/transaction.zig index 20597a3..646d253 100644 --- a/src/types/transaction.zig +++ b/src/types/transaction.zig @@ -1,41 +1,69 @@ const std = @import("std"); -const secp256k1 = @import("zig-eth-secp256k1"); +const Allocator = std.mem.Allocator; +const rlp = @import("zig-rlp"); +const hasher = @import("../crypto/hasher.zig"); const types = @import("types.zig"); const Address = types.Address; -// TODO(jsign): consider using union to support txntypes -type: u8, -chain_id: u256, -nonce: u64, -gas_price: u256, -value: u256, -to: ?Address, -data: []const u8, -gas_limit: u64, +// TODO(jsign): create Transaction type that is the union of transaction types. +const Txn = @This(); +pub const TxnData = struct { + type: u8, + chain_id: u256, + nonce: u64, + gas_price: u256, + value: u256, + to: ?Address, + data: []const u8, + gas_limit: u64, +}; + +data: TxnData, +v: u8, +r: u256, +s: u256, + +// init initializes a transaction without signature fields. // TODO(jsign): comment about data ownership. -pub fn init(type_: u8, chain_id: u256, nonce: u64, gas_price: u256, value: u256, to: ?Address, data: []const u8, gas_limit: u64) @This() { +pub fn init( + type_: u8, + chain_id: u256, + nonce: u64, + gas_price: u256, + value: u256, + to: ?Address, + data: []const u8, + gas_limit: u64, +) Txn { return @This(){ - .type = type_, - .chain_id = chain_id, - .nonce = nonce, - .gas_price = gas_price, - .value = value, - .to = to, - .data = data, - .gas_limit = gas_limit, + .data = .{ + .type = type_, + .chain_id = chain_id, + .nonce = nonce, + .gas_price = gas_price, + .value = value, + .to = to, + .data = data, + .gas_limit = gas_limit, + }, + .v = 0, + .r = 0, + .s = 0, }; } -pub fn get_from(_: *const @This()) Address { - var secp = secp256k1.Secp256k1.init() catch unreachable; - _ = secp; - const from: Address = comptime blk: { - var buf: Address = undefined; - _ = std.fmt.hexToBytes(&buf, "a94f5374Fce5edBC8E2a8697C15331677e6EbF0B") catch unreachable; - break :blk buf; - }; - return from; +pub fn setSignature(self: *Txn, v: u8, r: u256, s: u256) void { + self.*.v = v; + self.*.r = r; + self.*.s = s; } -// TODO(jsign): add helper to get txn hash. +pub fn hash(self: Txn, allocator: Allocator) !types.Hash32 { + var out = std.ArrayList(u8).init(allocator); + defer out.deinit(); + + try rlp.serialize(TxnData, self.data, &out); + + return hasher.keccak256(out.items); +} diff --git a/src/vm/vm.zig b/src/vm/vm.zig index faac198..2b06ff7 100644 --- a/src/vm/vm.zig +++ b/src/vm/vm.zig @@ -2,9 +2,11 @@ const evmc = @cImport({ @cInclude("evmone.h"); }); const std = @import("std"); +const Allocator = std.mem.Allocator; const util = @import("util.zig"); const types = @import("../types/types.zig"); const Transaction = types.Transaction; +const TxnSigner = @import("../signer/signer.zig").TxnSigner; const Block = types.Block; const AccountState = types.AccountState; const Bytecode = types.Bytecode; @@ -84,7 +86,7 @@ pub const VM = struct { // TODO(jsign): check freeing evmone instance. } - pub fn run_block(self: *VM, block: Block, txns: []const Transaction) !void { + pub fn run_block(self: *VM, allocator: Allocator, txn_signer: TxnSigner, block: Block, txns: []const Transaction) !void { log.debug("run block number={d}", .{block.header.block_number}); // TODO: stashing area. @@ -97,7 +99,7 @@ pub const VM = struct { for (txns) |txn| { // Set the transaction context. - self.context.?.txn = gen_txn_context(txn); + self.context.?.txn = try gen_txn_context(allocator, txn_signer, txn); try self.run_txn(txn); } @@ -117,35 +119,35 @@ pub const VM = struct { }; } - fn gen_txn_context(txn: Transaction) TxnContext { + fn gen_txn_context(allocator: Allocator, txn_signer: TxnSigner, txn: Transaction) !TxnContext { return TxnContext{ - .chain_id = txn.chain_id, - .gas_price = txn.gas_price, - .from = txn.get_from(), + .chain_id = txn.data.chain_id, + .gas_price = txn.data.gas_price, + .from = try txn_signer.get_sender(allocator, txn), }; } fn run_txn(self: *VM, txn: Transaction) !void { - const from_addr = txn.get_from(); + const from_addr = self.*.context.?.txn.from; - var remaining_gas: i64 = @intCast(txn.gas_limit); + var remaining_gas: i64 = @intCast(txn.data.gas_limit); // Sender nonce updating. - if (txn.nonce +% 1 < txn.nonce) { + if (txn.data.nonce +% 1 < txn.data.nonce) { return error.MaxNonce; } - try self.statedb.*.set_nonce(from_addr, txn.nonce + 1); + try self.statedb.*.set_nonce(from_addr, txn.data.nonce + 1); // Charge intrinsic gas costs. // TODO(jsign): this is incomplete. try charge_gas(&remaining_gas, txn_base_gas); - if (txn.to) |to_addr| { + if (txn.data.to) |to_addr| { assert(!std.mem.eql(u8, &to_addr, &std.mem.zeroes(Address))); // Send transaction value to the recipient. - if (txn.value > 0) { // TODO(jsign): incomplete - try self.statedb.add_balance(to_addr, txn.value); + if (txn.data.value > 0) { // TODO(jsign): incomplete + try self.statedb.add_balance(to_addr, txn.data.value); } self.context.?.execution = ExecutionContext{ @@ -156,19 +158,19 @@ pub const VM = struct { .flags = 0, // TODO: STATIC? .depth = 0, .gas = @intCast(remaining_gas), // TODO(jsign): why evmc expects a i64 for gas instead of u64? - .recipient = util.to_evmc_address(txn.to), + .recipient = util.to_evmc_address(txn.data.to), .sender = util.to_evmc_address(from_addr), - .input_data = txn.data.ptr, - .input_size = txn.data.len, + .input_data = txn.data.data.ptr, + .input_size = txn.data.data.len, .value = blk: { var txn_value: [32]u8 = undefined; - std.mem.writeIntSliceBig(u256, &txn_value, txn.value); + std.mem.writeIntSliceBig(u256, &txn_value, txn.data.value); break :blk .{ .bytes = txn_value }; }, .create2_salt = .{ .bytes = [_]u8{0} ** 32, // TODO: fix this. }, - .code_address = util.to_evmc_address(txn.to), // TODO: fix this when .kind is generalized. + .code_address = util.to_evmc_address(txn.data.to), // TODO: fix this when .kind is generalized. }; const result = call(@ptrCast(self), @ptrCast(&msg)); @@ -180,7 +182,7 @@ pub const VM = struct { @panic("TODO contract creation"); } - const gas_used = @as(i64, @intCast(txn.gas_limit)) - remaining_gas; // TODO(jsign): decide on casts. + const gas_used = @as(i64, @intCast(txn.data.gas_limit)) - remaining_gas; // TODO(jsign): decide on casts. // Coinbase rewards. const gas_tip = 0xa - 0x7; // TODO(jsign): fix, pull from tx_context. From d34a4422d2e04064dfb7762973ce2402e0120517 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Mon, 4 Dec 2023 18:38:36 -0300 Subject: [PATCH 09/10] fix main and update zig-rlp Signed-off-by: Ignacio Hagopian --- build.zig | 3 ++- build.zig.zon | 4 ++-- src/main.zig | 28 ++++++++++++++++++---------- src/types/transaction.zig | 2 +- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/build.zig b/build.zig index d240df1..c5aff96 100644 --- a/build.zig +++ b/build.zig @@ -16,7 +16,7 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); - const mod_rlp = b.dependency("zig-rlp", .{ .target = target, .optimize = optimize }).module("zig-rlp"); + const mod_rlp = b.dependency("zig-rlp", .{ .target = target, .optimize = optimize }).module("rlp"); const depSecp256k1 = b.dependency("zig-eth-secp256k1", .{ .target = target, .optimize = optimize }); const mod_secp256k1 = depSecp256k1.module("zig-eth-secp256k1"); @@ -86,6 +86,7 @@ pub fn build(b: *std.Build) void { }); exe.addIncludePath(LazyPath{ .path = "evmone/include/evmone" }); exe.addIncludePath(LazyPath{ .path = "evmone/evmc/include" }); + exe.addIncludePath(LazyPath{ .path = "ethash/include" }); if (target.getCpuArch() == .x86_64) { // On x86_64, some functions are missing from the static library, // so we define dummy functions to make sure that it compiles. diff --git a/build.zig.zon b/build.zig.zon index 8f55e45..f258298 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -3,8 +3,8 @@ .version = "0.0.1-beta-0", .dependencies = .{ .@"zig-rlp" = .{ - .url = "https://github.com/gballet/zig-rlp/archive/refs/tags/v0.0.1-beta-0.tar.gz", - .hash = "122000e0811d6cb4758f6122b1de2d384efa32b9b2714caec2236f6b34b0529d699c", + .url = "https://github.com/gballet/zig-rlp/archive/refs/tags/v0.0.1-beta-3.tar.gz", + .hash = "1220d36c04cfa7040278000a869ebde502ac1bffc77757deef631396c26f7a2bf8b4", }, .@"zig-eth-secp256k1" = .{ .url = "https://github.com/jsign/zig-eth-secp256k1/archive/refs/tags/v0.1.0-beta-3.tar.gz", diff --git a/src/main.zig b/src/main.zig index 47827bb..47b9bd7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,12 +6,14 @@ const VM = @import("vm/vm.zig").VM; const StateDB = @import("vm/statedb.zig"); const Block = types.Block; const Transaction = types.Transaction; +const TxnSigner = @import("signer/signer.zig").TxnSigner; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var allocator = gpa.allocator(); std.log.info("Welcome to phant! 🐘", .{}); + const txn_signer = try TxnSigner.init(); // Create block. const block = Block{ @@ -37,14 +39,19 @@ pub fn main() !void { // Create some dummy transaction. const txn = Transaction{ - .type = 0, - .chain_id = 1, - .nonce = 0, - .gas_price = 10, - .value = 0, - .to = [_]u8{0} ** 18 ++ [_]u8{ 0x41, 0x42 }, - .data = &[_]u8{}, - .gas_limit = 100_000, + .data = .{ + .type = 0, + .chain_id = 1, + .nonce = 0, + .gas_price = 10, + .value = 0, + .to = [_]u8{0} ** 18 ++ [_]u8{ 0x41, 0x42 }, + .data = &[_]u8{}, + .gas_limit = 100_000, + }, + .r = 0, + .s = 0, + .v = 0, }; // Create the corresponding AccountState for txn.to, in particular with relevant bytecode @@ -53,7 +60,8 @@ pub fn main() !void { 0x61, 0x41, 0x42, // PUSH2 0x4142 0x31, // BALANCE }; - var account_state = try AccountState.init(allocator, txn.get_from(), 0, 1_000_000, &code); + const sender_addr = [_]u8{0} ** 20; //try txn_signer.get_sender(allocator, txn); + var account_state = try AccountState.init(allocator, sender_addr, 0, 1_000_000, &code); defer account_state.deinit(); // Create the statedb, with the created account state. @@ -64,7 +72,7 @@ pub fn main() !void { var vm = VM.init(&statedb); // Execute block with txns. - vm.run_block(block, &[_]Transaction{txn}) catch |err| { + vm.run_block(allocator, txn_signer, block, &[_]Transaction{txn}) catch |err| { std.log.err("error executing transaction: {}", .{err}); return; }; diff --git a/src/types/transaction.zig b/src/types/transaction.zig index 646d253..a05dd2b 100644 --- a/src/types/transaction.zig +++ b/src/types/transaction.zig @@ -63,7 +63,7 @@ pub fn hash(self: Txn, allocator: Allocator) !types.Hash32 { var out = std.ArrayList(u8).init(allocator); defer out.deinit(); - try rlp.serialize(TxnData, self.data, &out); + try rlp.serialize(TxnData, allocator, self.data, &out); return hasher.keccak256(out.items); } From 7f70657b8831c0f95177b5d311fc66473aced8e9 Mon Sep 17 00:00:00 2001 From: Ignacio Hagopian Date: Tue, 5 Dec 2023 14:24:43 -0300 Subject: [PATCH 10/10] review feedback Signed-off-by: Ignacio Hagopian --- build.zig | 2 -- src/crypto/hasher.zig | 9 ++++----- src/main.zig | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/build.zig b/build.zig index c5aff96..0cef9a1 100644 --- a/build.zig +++ b/build.zig @@ -86,7 +86,6 @@ pub fn build(b: *std.Build) void { }); exe.addIncludePath(LazyPath{ .path = "evmone/include/evmone" }); exe.addIncludePath(LazyPath{ .path = "evmone/evmc/include" }); - exe.addIncludePath(LazyPath{ .path = "ethash/include" }); if (target.getCpuArch() == .x86_64) { // On x86_64, some functions are missing from the static library, // so we define dummy functions to make sure that it compiles. @@ -139,7 +138,6 @@ pub fn build(b: *std.Build) void { }); unit_tests.addIncludePath(LazyPath{ .path = "evmone/include/evmone" }); unit_tests.addIncludePath(LazyPath{ .path = "evmone/evmc/include" }); - unit_tests.addIncludePath(LazyPath{ .path = "ethash/include" }); if (target.getCpuArch() == .x86_64) { // On x86_64, some functions are missing from the static library, // so we define dummy functions to make sure that it compiles. diff --git a/src/crypto/hasher.zig b/src/crypto/hasher.zig index e010a67..473ef5c 100644 --- a/src/crypto/hasher.zig +++ b/src/crypto/hasher.zig @@ -1,8 +1,7 @@ -const ethash = @cImport({ - @cInclude("ethash/keccak.h"); -}); +const Keccak = @import("std").crypto.hash.sha3.Keccak256; pub fn keccak256(data: []const u8) [32]u8 { - const ret = ethash.ethash_keccak256(data.ptr, data.len); - return ret.bytes; + var ret: [32]u8 = undefined; + Keccak.hash(data, &ret, .{}); + return ret; } diff --git a/src/main.zig b/src/main.zig index 47b9bd7..bec5d42 100644 --- a/src/main.zig +++ b/src/main.zig @@ -60,7 +60,7 @@ pub fn main() !void { 0x61, 0x41, 0x42, // PUSH2 0x4142 0x31, // BALANCE }; - const sender_addr = [_]u8{0} ** 20; //try txn_signer.get_sender(allocator, txn); + const sender_addr = try txn_signer.get_sender(allocator, txn); var account_state = try AccountState.init(allocator, sender_addr, 0, 1_000_000, &code); defer account_state.deinit();