代码之家  ›  专栏  ›  技术社区  ›  xchrisbradley

使用Zig签名对功能选择器进行编码

  •  0
  • xchrisbradley  · 技术社区  · 4 周前

    我正在进行一个Zig项目,该项目涉及根据ABI编码规则对参数进行编码。该项目包括一个函数abiEncodeWithSignature,旨在获取一个函数签名和一组参数,然后返回ABI编码的字节。该函数使用switch语句来处理不同的参数类型,包括256位无符号整数(@“u256”)和无符号字节片([]const u8和[]u8)的特定情况。

    在对该函数运行单元测试时会出现此问题。第一个测试对一个不带参数的函数签名进行编码,它顺利通过。然而,当我开始添加参数时,第二个测试失败了。

    是否有人对如何解决此问题以确保所有参数类型都得到正确支持和编码有见解或建议?

    供参考: https://noxx.substack.com/p/evm-deep-dives-the-path-to-shadowy?s=r

    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 {
        // Convert the function signature to a Keccak-256 hash
        var hash: [32]u8 = undefined;
        crypto.hash.sha3.Keccak256.hash(signature, &hash, .{});
    
        // Take the first 4 bytes of the hash as the function selector
        const selector = hash[0..4];
    
        // Create a list to store the encoded arguments
        var encoded_args = std.ArrayList(u8).init(std.heap.page_allocator);
        defer encoded_args.deinit();
    
        // Encode each argument according to the ABI encoding rules
        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});
                    // Left-pad the address with 12 zero bytes to make it 32 bytes long
                    var padded_address: [32]u8 = undefined;
                    for (padded_address[0..12]) |*byte| {
                        byte.* = 0; // Pad with zeros
                    }
                    std.mem.copy(u8, padded_address[12..], arg[0..20]);
                    try encoded_args.appendSlice(padded_address[0..]); // Append the padded address
                },
                *[32]u8 => {
                    std.debug.print("Appending u256 argument: {any}\n", .{arg});
                    try encoded_args.appendSlice(arg[0..]); // Convert the array to a slice
                },
                else => {
                    std.debug.print("Unsupported argument type at index {}: {s}\n", .{ i, @typeName(arg_type) });
                    return error.UnsupportedArgumentType;
                },
            }
        }
    
        // Concatenate the function selector and the encoded arguments
        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);
    
        // Expected result: 6057361d
        const expected = [_]u8{ 0x60, 0x57, 0x36, 0x1d };
    
        try testing.expectEqualSlices(u8, &expected, encoded);
    }
    
    test "abiEncodeWithSignature.store(uint256, address)" {
        const signature = "store(uint256,address)";
    
        // Adjust amount_bytes to be a [32]u8 array, representing a uint256.
        var amount_bytes: [32]u8 = undefined;
        amount_bytes[31] = 0x01; // Assuming little-endian, place the value at the end.
    
        const address_bytes = [_]u8{ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90 };
    
        const encoded = try abiEncodeWithSignature(signature, .{ @as([]const u8, &amount_bytes), @as([]const u8, &address_bytes) });
        defer std.heap.page_allocator.free(encoded);
    
        const expected = [_]u8{
            0xce, 0xaa, 0x31, 0x82, // Function selector
            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);
    }
    
    Test [2/2] test.abiEncodeWithSignature.store(uint256, address)... Argument at index 0: type=[]const u8, value={ 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 1 }
    Appending address argument: { 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 1 }
    Argument at index 1: type=[]const u8, value={ 18, 52, 86, 120, 144, 18, 52, 86, 120, 144, 18, 52, 86, 120, 144, 18, 52, 86, 120, 144 }
    Appending address argument: { 18, 52, 86, 120, 144, 18, 52, 86, 120, 144, 18, 52, 86, 120, 144, 18, 52, 86, 120, 144 }
    slices differ. first difference occurs at index 16 (0x10)
    
    ============ expected this output: =============  len: 68 (0x44)
    
    CE AA 31 82 00 00 00 00  00 00 00 00 00 00 00 00  ..1.............
    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
    00 00 00 01 00 00 00 00  00 00 00 00 00 00 00 00  ................
    12 34 56 78 90 12 34 56  78 90 12 34 56 78 90 12  .4Vx..4Vx..4Vx..
    34 56 78 90                                       4Vx.
    
    ============= instead found this: ==============  len: 68 (0x44)
    
    CE AA 31 82 00 00 00 00  00 00 00 00 00 00 00 00  ..1.............
    AA AA AA AA AA AA AA AA  AA AA AA AA AA AA AA AA  ................
    AA AA AA AA 00 00 00 00  00 00 00 00 00 00 00 00  ................
    12 34 56 78 90 12 34 56  78 90 12 34 56 78 90 12  .4Vx..4Vx..4Vx..
    34 56 78 90                                       4Vx.
    
    ================================================
    
    Test [2/2] test.abiEncodeWithSignature.store(uint256, address)... FAIL (TestExpectedEqual)
    /opt/homebrew/Cellar/zig/0.11.0/lib/zig/std/testing.zig:380:5: 0x100ecf113 in expectEqualSlices__anon_1697 (test)
        return error.TestExpectedEqual;
        ^
    /Users/christopher.bradley/boringlabs/zigsol/src/main.zig:89:5: 0x100ed0613 in test.abiEncodeWithSignature.store(uint256, address) (test)
        try testing.expectEqualSlices(u8, &expected, encoded);
        ^
    1 passed; 0 skipped; 1 failed.
    error: the following test command failed with exit code 1:
    /Users/christopher.bradley/boringlabs/zigsol/zig-cache/o/29627c5bf39569b46dda51a33e32e0e7/test
    
    1 回复  |  直到 4 周前
        1
  •  1
  •   sigod    4 周前

    amount_bytes address_bytes 不是 slices ,它们是 arrays :

    @compileLog(@TypeOf(amount_bytes)); // @as(type, [1]u8)
    @compileLog(@TypeOf(address_bytes)); // @as(type, [20]u8)
    

    通常情况下,铸造成薄片非常简单 &array array[0..] ,但在这种情况下,您使用 anytype ,所以你必须明确: @as([]const u8, &array) 。像这样:

    const encoded = try abiEncodeWithSignature(signature, .{ @as([]const u8, &amount_bytes), @as([]const u8, &address_bytes) });