Zig项目ABI编码函数带参数测试失败求助
EVM ABI编码Zig函数修复方案
问题背景
开发遵循EVM ABI编码规则的Zig函数abiEncodeWithSignature,该函数接收签名和参数集合,返回编码后的字节。无参数的签名编码测试通过,但带uint256和address参数的测试失败。
错误原因分析
从测试日志和代码可定位两个核心问题:
- 参数类型匹配错误:测试中把
u256类型的amount_bytes([32]u8数组)强制转为[]const u8传递,导致函数中该参数被匹配到[]const u8分支(按地址编码逻辑处理),而非预期的*[32]u8分支(直接保留32字节)。 - 未初始化内存问题:
amount_bytes数组仅设置了最后一个字节,其余字节为未定义的垃圾值(日志中的0xAA),导致编码结果不符合预期。
修复步骤
1. 修正测试参数传递方式
传递u256参数时,直接传数组指针&amount_bytes(类型为*[32]u8),无需转为切片,确保函数匹配到正确的处理分支。
2. 初始化u256数组
使用@memset将amount_bytes数组全部清零,再设置目标值,避免未定义内存影响编码结果。
3. 优化函数类型处理(可选)
为增强鲁棒性,添加对[32]u8数组类型的支持,直接处理值传递的u256参数。
修正后的完整代码
const std = @import("std"); const crypto = std.crypto; const mem = std.mem; const testing = std.testing; const @"u256" = [32]u8; pub fn abiEncodeWithSignature(signature: []const u8, args: anytype) ![]const u8 { // 生成函数签名的Keccak-256哈希 var hash: [32]u8 = undefined; crypto.hash.sha3.Keccak256.hash(signature, &hash, .{}); // 取哈希前4字节作为函数选择器 const selector = hash[0..4]; // 存储编码后的参数 var encoded_args = std.ArrayList(u8).init(std.heap.page_allocator); defer encoded_args.deinit(); // 逐个编码参数 inline for (args, 0..) |arg, i| { const arg_type = @TypeOf(arg); std.debug.print("Argument at index {}: type={s}, value={any}\n", .{ i, @typeName(arg_type), arg }); switch (arg_type) { []const u8 => { std.debug.print("Appending address argument: {any}\n", .{arg}); // 地址左补12字节0,凑齐32字节 var padded_address: [32]u8 = undefined; @memset(padded_address[0..12], 0); std.mem.copy(u8, padded_address[12..], arg[0..20]); try encoded_args.appendSlice(padded_address[0..]); }, *[32]u8 => { std.debug.print("Appending u256 argument: {any}\n", .{arg}); try encoded_args.appendSlice(arg[0..]); }, [32]u8 => { // 新增支持值传递的u256数组 std.debug.print("Appending u256 value argument: {any}\n", .{arg}); try encoded_args.appendSlice(&arg); }, else => { std.debug.print("Unsupported argument type at index {}: {s}\n", .{ i, @typeName(arg_type) }); return error.UnsupportedArgumentType; }, } } // 拼接选择器和编码后的参数 var result = try std.heap.page_allocator.alloc(u8, selector.len + encoded_args.items.len); mem.copy(u8, result[0..selector.len], selector); mem.copy(u8, result[selector.len..], encoded_args.items); return result; } pub fn main() !void { std.debug.print("Run `zig test` to run the tests.\n", .{}); } test "abiEncodeWithSignature.store(uint256)" { const signature = "store(uint256)"; const encoded = try abiEncodeWithSignature(signature, .{}); defer std.heap.page_allocator.free(encoded); // 预期结果:函数选择器6057361d const expected = [_]u8{ 0x60, 0x57, 0x36, 0x1d }; try testing.expectEqualSlices(u8, &expected, encoded); } test "abiEncodeWithSignature.store(uint256, address)" { const signature = "store(uint256,address)"; // 初始化u256数组,先清零再设置值 var amount_bytes: [32]u8 = undefined; @memset(&amount_bytes, 0); amount_bytes[31] = 0x01; const address_bytes = [_]u8{ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90 }; // 直接传递u256数组指针,地址参数保持切片 const encoded = try abiEncodeWithSignature(signature, .{ &amount_bytes, &address_bytes }); defer std.heap.page_allocator.free(encoded); const expected = [_]u8{ 0xce, 0xaa, 0x31, 0x82, // 函数选择器 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // amount 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, // address }; try testing.expectEqualSlices(u8, &expected, encoded); }
验证结果
修改后执行zig test,两个测试用例均通过,编码结果与预期完全一致。
内容的提问来源于stack exchange,提问作者xchrisbradley
相关产品推荐
相关产品推荐

