From 6b712bb1bc030be0aebf5ef339914a5e70e1e7d5 Mon Sep 17 00:00:00 2001 From: Afr Schoe <58883403+q9f@users.noreply.github.com> Date: Wed, 18 Jan 2023 18:21:08 +0100 Subject: [PATCH 1/8] eth/abi: enable packed encoding --- lib/eth/abi.rb | 28 +++-- lib/eth/abi/decoder.rb | 8 +- lib/eth/abi/encoder.rb | 75 +++++++----- spec/eth/abi/encoder_spec.rb | 15 +++ spec/eth/abi_spec.rb | 220 +++++++++++++++++++---------------- 5 files changed, 207 insertions(+), 139 deletions(-) diff --git a/lib/eth/abi.rb b/lib/eth/abi.rb index 145341af..d742170e 100644 --- a/lib/eth/abi.rb +++ b/lib/eth/abi.rb @@ -38,30 +38,41 @@ class ValueOutOfBounds < StandardError; end # # @param types [Array] types to be ABI-encoded. # @param args [Array] values to be ABI-encoded. + # @param packed [Boolean] use custom packed encoding. # @return [String] the encoded ABI data. - def encode(types, args) + def encode(types, args, packed = false) # parse all types parsed_types = types.map { |t| Type === t ? t : Type.parse(t) } # prepare the "head" head_size = (0...args.size) - .map { |i| parsed_types[i].size or 32 } + .map { |i| + if packed + parsed_types[i].sub_type.to_i / 8 + else + parsed_types[i].size or 32 + end + } .reduce(0, &:+) head, tail = "", "" # encode types and arguments args.each_with_index do |arg, i| if parsed_types[i].dynamic? - head += Abi::Encoder.type(Type.size_type, head_size + tail.size) - tail += Abi::Encoder.type(parsed_types[i], arg) + head += Abi::Encoder.type(Type.size_type, head_size + tail.size, packed) + tail += Abi::Encoder.type(parsed_types[i], arg, packed) else - head += Abi::Encoder.type(parsed_types[i], arg) + head += Abi::Encoder.type(parsed_types[i], arg, packed) end end + if tail.size == 0 && packed + tail = head + end + # return the encoded ABI blob - "#{head}#{tail}" + packed ? "#{tail}" : "#{head}#{tail}" end # Decodes Application Binary Interface (ABI) data. It accepts multiple @@ -69,8 +80,9 @@ def encode(types, args) # # @param types [Array] the ABI to be decoded. # @param data [String] ABI data to be decoded. + # @param packed [Boolean] use custom packed decoding. # @return [Array] the decoded ABI data. - def decode(types, data) + def decode(types, data, packed = false) # accept hex abi but decode it first data = Util.hex_to_bin data if Util.hex? data @@ -118,7 +130,7 @@ def decode(types, data) end # return the decoded ABI types and data - parsed_types.zip(outputs).map { |(type, out)| Abi::Decoder.type(type, out) } + parsed_types.zip(outputs).map { |(type, out)| Abi::Decoder.type(type, out, packed) } end end end diff --git a/lib/eth/abi/decoder.rb b/lib/eth/abi/decoder.rb index 5f104c7f..2a1827a1 100644 --- a/lib/eth/abi/decoder.rb +++ b/lib/eth/abi/decoder.rb @@ -28,9 +28,10 @@ module Decoder # # @param type [Eth::Abi::Type] type to be decoded. # @param arg [String] encoded type data string. + # @param packed [Boolean] use custom packed decoding. # @return [String] the decoded data for the type. # @raise [DecodingError] if decoding fails for type. - def type(type, arg) + def type(type, arg, packed = false) if %w(string bytes).include?(type.base_type) and type.sub_type.empty? # Case: decoding a string/bytes if type.dimensions.empty? @@ -77,14 +78,15 @@ def type(type, arg) # # @param type [Eth::Abi::Type] type to be decoded. # @param data [String] encoded primitive type data string. + # @param packed [Boolean] use custom packed decoding. # @return [String] the decoded data for the type. # @raise [DecodingError] if decoding fails for type. - def primitive_type(type, data) + def primitive_type(type, data, packed = false) case type.base_type when "address" # decoded address with 0x-prefix - "0x#{Util.bin_to_hex data[12..-1]}" + Address.new(Util.bin_to_hex data[12..-1]).to_s.downcase when "string", "bytes" if type.sub_type.empty? size = Util.deserialize_big_endian_to_int data[0, 32] diff --git a/lib/eth/abi/encoder.rb b/lib/eth/abi/encoder.rb index b1aa9612..677037f2 100644 --- a/lib/eth/abi/encoder.rb +++ b/lib/eth/abi/encoder.rb @@ -28,26 +28,29 @@ module Encoder # # @param type [Eth::Abi::Type] type to be encoded. # @param arg [String|Number] value to be encoded. + # @param packed [Boolean] use custom packed encoding. # @return [String] the encoded type. # @raise [EncodingError] if value does not match type. - def type(type, arg) + def type(type, arg, packed = false) + + if %w(string bytes).include? type.base_type and type.sub_type.empty? and type.dimensions.empty? raise EncodingError, "Argument must be a String" unless arg.instance_of? String # encodes strings and bytes - size = type Type.size_type, arg.size + size = type(Type.size_type, arg.size, packed) padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size) "#{size}#{arg}#{padding}" elsif type.base_type == "tuple" && type.dimensions.size == 1 && type.dimensions[0] != 0 result = "" result += struct_offsets(type.nested_sub, arg) - result += arg.map { |x| type(type.nested_sub, x) }.join + result += arg.map { |x| type(type.nested_sub, x, packed) }.join result elsif type.dynamic? && arg.is_a?(Array) # encodes dynamic-sized arrays head, tail = "", "" - head += type(Type.size_type, arg.size) + head += type(Type.size_type, arg.size, packed) unless packed nested_sub = type.nested_sub nested_sub_size = type.nested_sub.size @@ -63,25 +66,26 @@ def type(type, arg) offset += total_bytes_length + 32 end - head += type(Type.size_type, offset) + head += type(Type.size_type, offset, packed) end elsif nested_sub.base_type == "tuple" && nested_sub.dynamic? head += struct_offsets(nested_sub, arg) end arg.size.times do |i| - head += type nested_sub, arg[i] + head += type(nested_sub, arg[i], packed) end + "#{head}#{tail}" else if type.dimensions.empty? # encode a primitive type - primitive_type type, arg + primitive_type type, arg, packed else # encode static-size arrays - arg.map { |x| type(type.nested_sub, x) }.join + arg.map { |x| type(type.nested_sub, x, packed) }.join end end end @@ -90,29 +94,30 @@ def type(type, arg) # # @param type [Eth::Abi::Type] type to be encoded. # @param arg [String|Number] value to be encoded. + # @param packed [Boolean] use custom packed encoding. # @return [String] the encoded primitive type. # @raise [EncodingError] if value does not match type. # @raise [ValueOutOfBounds] if value is out of bounds for type. # @raise [EncodingError] if encoding fails for type. - def primitive_type(type, arg) + def primitive_type(type, arg, packed = false) case type.base_type when "uint" - uint arg, type + uint arg, type, packed when "bool" - bool arg + bool arg, packed when "int" - int arg, type - when "ureal", "ufixed" + int arg, type, packed + when "ureal", "ufixed" # TODO: Q9F ufixed arg, type - when "real", "fixed" + when "real", "fixed" # TODO: Q9F fixed arg, type when "string", "bytes" - bytes arg, type - when "tuple" + bytes arg, type, packed + when "tuple" # TODO: Q9F tuple arg, type - when "hash" + when "hash" # TODO: Q9F hash arg, type - when "address" + when "address" # TODO: Q9F address arg else raise EncodingError, "Unhandled type: #{type.base_type} #{type.sub_type}" @@ -122,29 +127,39 @@ def primitive_type(type, arg) private # Properly encodes unsigned integers. - def uint(arg, type) + def uint(arg, type, packed) raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::UINT_MAX or arg < Constant::UINT_MIN real_size = type.sub_type.to_i i = arg.to_i raise ValueOutOfBounds, arg unless i >= 0 and i < 2 ** real_size - Util.zpad_int i + if packed + len = real_size / 8 + return Util.zpad_int(i, len) + else + return Util.zpad_int(i) + end end # Properly encodes signed integers. - def int(arg, type) + def int(arg, type, packed) raise ArgumentError, "Don't know how to handle this input." unless arg.is_a? Numeric raise ValueOutOfBounds, "Number out of range: #{arg}" if arg > Constant::INT_MAX or arg < Constant::INT_MIN real_size = type.sub_type.to_i i = arg.to_i raise ValueOutOfBounds, arg unless i >= -2 ** (real_size - 1) and i < 2 ** (real_size - 1) - Util.zpad_int(i % 2 ** type.sub_type.to_i) + if packed + len = real_size / 8 + return Util.zpad_int(i % 2 ** type.sub_type.to_i, len) + else + return Util.zpad_int(i % 2 ** type.sub_type.to_i) + end end # Properly encodes booleans. - def bool(arg) + def bool(arg, packed) raise EncodingError, "Argument is not bool: #{arg}" unless arg.instance_of? TrueClass or arg.instance_of? FalseClass - Util.zpad_int(arg ? 1 : 0) + Util.zpad_int(arg ? 1 : 0, packed ? 1 : 32) end # Properly encodes unsigned fixed-point numbers. @@ -165,7 +180,7 @@ def fixed(arg, type) end # Properly encodes byte-strings. - def bytes(arg, type) + def bytes(arg, type, packed) raise EncodingError, "Expecting String: #{arg}" unless arg.instance_of? String arg = handle_hex_string arg, type @@ -173,12 +188,16 @@ def bytes(arg, type) size = Util.zpad_int arg.size padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size) + pp size, arg, padding if packed + # variable length string/bytes "#{size}#{arg}#{padding}" else raise ValueOutOfBounds, arg unless arg.size <= type.sub_type.to_i padding = Constant::BYTE_ZERO * (32 - arg.size) + pp arg, padding if packed + # fixed length string/bytes "#{arg}#{padding}" end @@ -257,7 +276,11 @@ def hash(arg, type) # Properly encodes addresses. def address(arg) - if arg.is_a? Integer + if arg.is_a? Address + + # address from eth::address + Util.zpad_hex arg.to_s + elsif arg.is_a? Integer # address from integer Util.zpad_int arg diff --git a/spec/eth/abi/encoder_spec.rb b/spec/eth/abi/encoder_spec.rb index 95cfd629..dd44db63 100644 --- a/spec/eth/abi/encoder_spec.rb +++ b/spec/eth/abi/encoder_spec.rb @@ -96,4 +96,19 @@ expect { Abi::Encoder.primitive_type(t_address, "0x8cb9d52661513ac5490483c79ac715f5dd572bfb0xbd76086b38f2660fcaa65781ff5998f5c18e766d") }.to raise_error Abi::EncodingError expect { Abi::Encoder.primitive_type(Abi::Type.new("foo", 32, []), 12354235345634646546346346345) }.to raise_error Abi::EncodingError end + + describe "packed encoding" do + it "encodes packed types" do + expect(Util.bin_to_hex Abi.encode(["uint8[]"], [[1, 2, 3]], true)).to eq "010203" + expect(Util.bin_to_hex Abi.encode(["uint16[]"], [[1, 2, 3]], true)).to eq "000100020003" + expect(Util.bin_to_hex Abi.encode(["uint32"], [17], true)).to eq "00000011" + expect(Util.bin_to_hex Abi.encode(["uint64"], [17], true)).to eq "0000000000000011" + expect(Util.bin_to_hex Abi.encode(["bool[]"], [[true, false]], true)).to eq "0100" + expect(Util.bin_to_hex Abi.encode(["bool"], [true], true)).to eq "01" + expect(Util.bin_to_hex Abi.encode(["int32[]"], [[1, 2, 3]], true)).to eq "000000010000000200000003" + expect(Util.bin_to_hex Abi.encode(["int64[]"], [[1, 2, 3]], true)).to eq "000000000000000100000000000000020000000000000003" + expect(Util.bin_to_hex Abi.encode(["int64"], [17], true)).to eq "0000000000000011" + expect(Util.bin_to_hex Abi.encode(["int128"], [17], true)).to eq "00000000000000000000000000000011" + end + end end diff --git a/spec/eth/abi_spec.rb b/spec/eth/abi_spec.rb index ae369734..aace42fc 100644 --- a/spec/eth/abi_spec.rb +++ b/spec/eth/abi_spec.rb @@ -8,108 +8,6 @@ # load official ethereum/tests fixtures for ABIs let(:basic_abi_tests_file) { File.read "spec/fixtures/ethereum/tests/ABITests/basic_abi_tests.json" } subject(:basic_abi_tests) { JSON.parse basic_abi_tests_file } - - describe "dynamic encode" do - it "can encode array of string" do - encoded = Util.bin_to_hex(described_class.encode(["string[]"], [["hello", "world"]])) - expected = "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000568656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000" - expect(encoded).to eq(expected) - end - - it "can encode array of uint256" do - encoded = Util.bin_to_hex(described_class.encode(["uint256[]"], [[123, 456]])) - expected = "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c8" - expect(encoded).to eq(expected) - end - - it "can encode mix of array of uint256 and string" do - encoded = Util.bin_to_hex(described_class.encode(["uint256[]", "string[]"], [[123, 456], ["hello", "world"]])) - expected = "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c8000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000568656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000" - expect(encoded).to eq(expected) - - encoded = Util.bin_to_hex(described_class.encode(["uint256[]", "string[]", "string[]", "uint8[]"], [[123, 456], ["hello", "world"], ["ruby", "ethereum"], [8]])) - expected = "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c8000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000568656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000472756279000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008657468657265756d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000008" - expect(encoded).to eq(expected) - end - - it "can encode array of dynamic structs" do - encoded = Util.bin_to_hex(described_class.encode([ - Abi::Type.parse("tuple[3]", [ - { - "type" => "tuple", - "name" => "nuu", - "components" => [ - "type" => "tuple", - "name" => "foo", - "components" => [ - { "type" => "string", "name" => "id" }, - { "type" => "string", "name" => "name" }, - ], - ], - }, - ]), - Abi::Type.parse("tuple[]", [ - { - "type" => "uint256", - "name" => "id", - }, - { - "type" => "uint256", - "name" => "data", - }, - ]), - Abi::Type.parse("tuple[]", [ - { "type" => "string", "name" => "id" }, - { "type" => "string", "name" => "name" }, - ]), - Abi::Type.parse("tuple[]", [ - { - "type" => "tuple", - "name" => "nuu", - "components" => [ - "type" => "tuple", - "name" => "foo", - "components" => [ - { "type" => "string", "name" => "id" }, - { "type" => "string", "name" => "name" }, - ], - ], - }, - ]), - Abi::Type.parse("tuple[3]", [ - { "type" => "string", "name" => "id" }, - { "type" => "string", "name" => "name" }, - ]), - ], [ - [ - { "nuu" => { "foo" => { "id" => "4", "name" => "nestedFoo" } } }, - { "nuu" => { "foo" => { "id" => "", "name" => "" } } }, - { "nuu" => { "foo" => { "id" => "4", "name" => "nestedFoo" } } }, - ], - [ - { "id" => 123, "data" => 123 }, - { "id" => 12, "data" => 33 }, - { "id" => 0, "data" => 0 }, - ], - [ - { "id" => "id", "name" => "name" }, - ], - [ - { "nuu" => { "foo" => { "id" => "4", "name" => "nestedFoo" } } }, - { "nuu" => { "foo" => { "id" => "4", "name" => "nestedFoo" } } }, - { "nuu" => { "foo" => { "id" => "", "name" => "" } } }, - ], - [ - { "id" => "id", "name" => "name" }, - { "id" => "id", "name" => "name" }, - { "id" => "id", "name" => "name" }, - ], - ])) - expected = "00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000004a000000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e6573746564466f6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e6573746564466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002696400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e6573746564466f6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e6573746564466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002696400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002696400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002696400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000" - expect(encoded).to eq(expected) - end - end - it "can encode abi" do basic_abi_tests.each do |test| types = test.last["types"] @@ -310,4 +208,122 @@ expect(Util.bin_to_hex Abi.encode ["bytes10"], ["1234567890".b]).to eq "3132333435363738393000000000000000000000000000000000000000000000" end end + + describe "dynamic encoding" do + it "can encode array of string" do + encoded = Util.bin_to_hex(described_class.encode(["string[]"], [["hello", "world"]])) + expected = "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000568656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000" + expect(encoded).to eq(expected) + end + + it "can encode array of uint256" do + encoded = Util.bin_to_hex(described_class.encode(["uint256[]"], [[123, 456]])) + expected = "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c8" + expect(encoded).to eq(expected) + end + + it "can encode mix of array of uint256 and string" do + encoded = Util.bin_to_hex(described_class.encode(["uint256[]", "string[]"], [[123, 456], ["hello", "world"]])) + expected = "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c8000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000568656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000" + expect(encoded).to eq(expected) + + encoded = Util.bin_to_hex(described_class.encode(["uint256[]", "string[]", "string[]", "uint8[]"], [[123, 456], ["hello", "world"], ["ruby", "ethereum"], [8]])) + expected = "000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000007b00000000000000000000000000000000000000000000000000000000000001c8000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000568656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000472756279000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008657468657265756d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000008" + expect(encoded).to eq(expected) + end + + it "can encode array of dynamic structs" do + encoded = Util.bin_to_hex(described_class.encode([ + Abi::Type.parse("tuple[3]", [ + { + "type" => "tuple", + "name" => "nuu", + "components" => [ + "type" => "tuple", + "name" => "foo", + "components" => [ + { "type" => "string", "name" => "id" }, + { "type" => "string", "name" => "name" }, + ], + ], + }, + ]), + Abi::Type.parse("tuple[]", [ + { + "type" => "uint256", + "name" => "id", + }, + { + "type" => "uint256", + "name" => "data", + }, + ]), + Abi::Type.parse("tuple[]", [ + { "type" => "string", "name" => "id" }, + { "type" => "string", "name" => "name" }, + ]), + Abi::Type.parse("tuple[]", [ + { + "type" => "tuple", + "name" => "nuu", + "components" => [ + "type" => "tuple", + "name" => "foo", + "components" => [ + { "type" => "string", "name" => "id" }, + { "type" => "string", "name" => "name" }, + ], + ], + }, + ]), + Abi::Type.parse("tuple[3]", [ + { "type" => "string", "name" => "id" }, + { "type" => "string", "name" => "name" }, + ]), + ], [ + [ + { "nuu" => { "foo" => { "id" => "4", "name" => "nestedFoo" } } }, + { "nuu" => { "foo" => { "id" => "", "name" => "" } } }, + { "nuu" => { "foo" => { "id" => "4", "name" => "nestedFoo" } } }, + ], + [ + { "id" => 123, "data" => 123 }, + { "id" => 12, "data" => 33 }, + { "id" => 0, "data" => 0 }, + ], + [ + { "id" => "id", "name" => "name" }, + ], + [ + { "nuu" => { "foo" => { "id" => "4", "name" => "nestedFoo" } } }, + { "nuu" => { "foo" => { "id" => "4", "name" => "nestedFoo" } } }, + { "nuu" => { "foo" => { "id" => "", "name" => "" } } }, + ], + [ + { "id" => "id", "name" => "name" }, + { "id" => "id", "name" => "name" }, + { "id" => "id", "name" => "name" }, + ], + ])) + expected = "00000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000003c000000000000000000000000000000000000000000000000000000000000004a000000000000000000000000000000000000000000000000000000000000005a000000000000000000000000000000000000000000000000000000000000008e000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e6573746564466f6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e6573746564466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000007b000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002696400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000026000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e6573746564466f6f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096e6573746564466f6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002696400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002696400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002696400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046e616d6500000000000000000000000000000000000000000000000000000000" + expect(encoded).to eq(expected) + end + end + + describe "packed encoding" do + it "encodes packed types" do + expect(Util.bin_to_hex Abi.encode(["uint8[]"], [[1, 2, 3]], true)).to eq "010203" + expect(Util.bin_to_hex Abi.encode(["uint16[]"], [[1, 2, 3]], true)).to eq "000100020003" + expect(Util.bin_to_hex Abi.encode(["uint32"], [17], true)).to eq "00000011" + expect(Util.bin_to_hex Abi.encode(["uint64"], [17], true)).to eq "0000000000000011" + expect(Util.bin_to_hex Abi.encode(["bool[]"], [[true, false]], true)).to eq "0100" + expect(Util.bin_to_hex Abi.encode(["bool"], [true], true)).to eq "01" + expect(Util.bin_to_hex Abi.encode(["int32[]"], [[1, 2, 3]], true)).to eq "000000010000000200000003" + expect(Util.bin_to_hex Abi.encode(["int64[]"], [[1, 2, 3]], true)).to eq "000000000000000100000000000000020000000000000003" + expect(Util.bin_to_hex Abi.encode(["int64"], [17], true)).to eq "0000000000000011" + expect(Util.bin_to_hex Abi.encode(["int128"], [17], true)).to eq "00000000000000000000000000000011" + # expect(Util.bin_to_hex Abi.encode(["bytes1"], [0x42], true)).to eq "42" + expect(Util.bin_to_hex Abi.encode(["string"], ["Hello, world!"], true)).to eq "00000000000000000000000000000011" + end + end end From 1e135090aa19c96c6ed2976eb6d85b77370dfe10 Mon Sep 17 00:00:00 2001 From: Afr Schoe <58883403+q9f@users.noreply.github.com> Date: Tue, 2 Jul 2024 19:13:06 +0200 Subject: [PATCH 2/8] add string and byte types --- Gemfile | 8 +- lib/eth/abi/encoder.rb | 6 +- spec/eth/abi/encoder_spec.rb | 203 +++++++++++++++++++++++++++++++++++ spec/eth/abi_spec.rb | 19 +--- 4 files changed, 212 insertions(+), 24 deletions(-) diff --git a/Gemfile b/Gemfile index fd412b1d..851287f1 100644 --- a/Gemfile +++ b/Gemfile @@ -6,10 +6,10 @@ group :test, :development do gem "bundler", "~> 2.2" gem "codecov", "~> 0.6" gem "pry", "~> 0.14" - gem "rake", "~> 13.0" - gem "rdoc", "~> 6.4" - gem "rspec", "~> 3.11" - gem "rufo", "~> 0.13" + gem "rake", "~> 13.2" + gem "rdoc", "~> 6.7" + gem "rspec", "~> 3.13" + gem "rufo", "~> 0.18" gem "simplecov", "~> 0.21" gem "yard", "~> 0.9" end diff --git a/lib/eth/abi/encoder.rb b/lib/eth/abi/encoder.rb index 8d34c89a..a8c10b3c 100644 --- a/lib/eth/abi/encoder.rb +++ b/lib/eth/abi/encoder.rb @@ -38,6 +38,7 @@ def type(type, arg, packed = false) # encodes strings and bytes size = type(Type.size_type, arg.size, packed) padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size) + return arg if packed "#{size}#{arg}#{padding}" elsif type.base_type == "tuple" && type.dimensions.size == 1 && type.dimensions[0] != 0 result = "" @@ -181,17 +182,18 @@ def bytes(arg, type, packed) raise EncodingError, "Expecting String: #{arg}" unless arg.instance_of? String arg = handle_hex_string arg, type + # no padding or size handling for packed encoding + return arg if packed + if type.sub_type.empty? size = Util.zpad_int arg.size padding = Constant::BYTE_ZERO * (Util.ceil32(arg.size) - arg.size) - pp size, arg, padding if packed # variable length string/bytes "#{size}#{arg}#{padding}" else raise ValueOutOfBounds, arg unless arg.size <= type.sub_type.to_i padding = Constant::BYTE_ZERO * (32 - arg.size) - pp arg, padding if packed # fixed length string/bytes "#{arg}#{padding}" diff --git a/spec/eth/abi/encoder_spec.rb b/spec/eth/abi/encoder_spec.rb index 5f7a3387..04375b0e 100644 --- a/spec/eth/abi/encoder_spec.rb +++ b/spec/eth/abi/encoder_spec.rb @@ -110,6 +110,209 @@ expect(Util.bin_to_hex Abi.encode(["int64[]"], [[1, 2, 3]], true)).to eq "000000000000000100000000000000020000000000000003" expect(Util.bin_to_hex Abi.encode(["int64"], [17], true)).to eq "0000000000000011" expect(Util.bin_to_hex Abi.encode(["int128"], [17], true)).to eq "00000000000000000000000000000011" + expect(Util.bin_to_hex Abi.encode(["bytes1"], ["0x42"], true)).to eq "42" + expect(Util.bin_to_hex Abi.encode(["bytes"], ["dave".b], true)).to eq "64617665" + expect(Util.bin_to_hex Abi.encode(["string"], ["dave"], true)).to eq "64617665" + expect(Util.bin_to_hex Abi.encode(["string"], ["Hello, World"], true)).to eq "48656c6c6f2c20576f726c64" + end + + context "wuminzhe's tests" do + # ref https://github.com/wuminzhe/abi_coder_rb/blob/701af2315cfc94a94872beb6c639ece400fca589/spec/packed_encoding_spec.rb + + it "bool" do + type = "bool" + value = true + data = "01" + + expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + end + + it "bytes" do + type = "bytes" + value = "dave".b + data = "64617665" + + expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + end + + it "types4" do + type = "bytes4" + value = "dave".b + data = "64617665" + + expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + end + + it "string" do + type = "string" + value = "dave" + data = "64617665" + + expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + end + + # it "address1" do + # type = "address" + # value = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" + # data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" + + # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # end + + # it "address2" do + # type = "address" + # value = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" + # data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" + + # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # end + + # it "address3" do + # type = "address" + # value = 0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826 + # data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" + + # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # end + + it "uint32" do + type = "uint32" + value = 17 + data = "00000011" + + expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + end + + it "int64" do + type = "int64" + value = 17 + data = "0000000000000011" + + expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + end + + # it "(uint64)" do + # type = "(uint64)" + # value = [17] + # data = "0000000000000011" + + # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # end + + # it "(int32,uint64)" do + # type = "(int32,uint64)" + # value = [17, 17] + # # data = "000000110000000000000011" + + # expect do + # encode(type, value, true) + # end.to raise_error("AbiCoderRb::Tuple with multi inner types is not supported in packed mode") + # end + + # it "int32,uint64" do + # types = %w[int32 uint64] + # values = [17, 17] + # data = "000000110000000000000011" + + # expect(encode(types, values, true)).to eq data + # end + + # it "uint16[]" do + # type = "uint16[]" + # value = [1, 2] + # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002" + + # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # end + + # it "bool[]" do + # type = "bool[]" + # value = [true, false] + # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000" + + # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # end + + # it "(uint16[])" do + # type = "(uint16[])" + # value = [[1, 2]] + # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002" + + # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # end + + # it "uint16[2]" do + # type = "uint16[2]" + # value = [1, 2] + # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002" + + # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # end + + # it "bytes[2]" do + # type = "bytes[2]" + # value = ["dave".b, "dave".b] + + # expect do + # encode(type, value, true) + # end.to raise_error("AbiCoderRb::FixedArray with dynamic inner type is not supported in packed mode") + # end + + # it "encodes packed types" do + # expect( + # encode("uint8[]", [1, 2, 3], true) + # ).to eq( + # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") + # ) + + # expect( + # encode("uint16[]", [1, 2, 3], true) + # ).to eq( + # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") + # ) + + # expect( + # encode("uint32", 17, true) + # ).to eq( + # hex("00000011") + # ) + + # expect( + # encode("uint64", 17, true) + # ).to eq( + # hex("0000000000000011") + # ) + + # expect( + # encode("bool[]", [true, false], true) + # ).to eq( + # hex("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000") + # ) + + # expect( + # encode("bool", true, true) + # ).to eq hex("01") + + # expect( + # encode("int32[]", [1, 2, 3], true) + # ).to eq( + # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") + # ) + + # expect( + # encode("int64[]", [1, 2, 3], true) + # ).to eq( + # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") + # ) + + # expect( + # encode("int64", 17, true) + # ).to eq hex("0000000000000011") + + # expect( + # encode("int128", 17, true) + # ).to eq hex("00000000000000000000000000000011") + # end end end end diff --git a/spec/eth/abi_spec.rb b/spec/eth/abi_spec.rb index cea5eb2c..3f685b16 100644 --- a/spec/eth/abi_spec.rb +++ b/spec/eth/abi_spec.rb @@ -492,7 +492,7 @@ def assert(data, types, args) describe "edge cases" do it "test negative number" do - types = ["int24"] + types = ["int24"] args = [-887220] data = Util.hex_to_bin "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2764c" expect(data).to eq Abi.encode(types, args) @@ -512,21 +512,4 @@ def assert(data, types, args) expect(Abi.decode(["int24"], "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff2764c")).to eq [-887220] end end - - describe "packed encoding" do - it "encodes packed types" do - expect(Util.bin_to_hex Abi.encode(["uint8[]"], [[1, 2, 3]], true)).to eq "010203" - expect(Util.bin_to_hex Abi.encode(["uint16[]"], [[1, 2, 3]], true)).to eq "000100020003" - expect(Util.bin_to_hex Abi.encode(["uint32"], [17], true)).to eq "00000011" - expect(Util.bin_to_hex Abi.encode(["uint64"], [17], true)).to eq "0000000000000011" - expect(Util.bin_to_hex Abi.encode(["bool[]"], [[true, false]], true)).to eq "0100" - expect(Util.bin_to_hex Abi.encode(["bool"], [true], true)).to eq "01" - expect(Util.bin_to_hex Abi.encode(["int32[]"], [[1, 2, 3]], true)).to eq "000000010000000200000003" - expect(Util.bin_to_hex Abi.encode(["int64[]"], [[1, 2, 3]], true)).to eq "000000000000000100000000000000020000000000000003" - expect(Util.bin_to_hex Abi.encode(["int64"], [17], true)).to eq "0000000000000011" - expect(Util.bin_to_hex Abi.encode(["int128"], [17], true)).to eq "00000000000000000000000000000011" - # expect(Util.bin_to_hex Abi.encode(["bytes1"], [0x42], true)).to eq "42" - expect(Util.bin_to_hex Abi.encode(["string"], ["Hello, world!"], true)).to eq "00000000000000000000000000000011" - end - end end From 84ecf6fe719a5943a41bac095fdf40a90d61ac0b Mon Sep 17 00:00:00 2001 From: Afr Schoe <58883403+q9f@users.noreply.github.com> Date: Tue, 2 Jul 2024 19:53:13 +0200 Subject: [PATCH 3/8] add packed decoding ;-) --- lib/eth/abi.rb | 27 ++++++++++++++++++++++++--- lib/eth/abi/encoder.rb | 2 +- spec/eth/abi_spec.rb | 18 ++++++++++++++++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/lib/eth/abi.rb b/lib/eth/abi.rb index d742170e..60af13d9 100644 --- a/lib/eth/abi.rb +++ b/lib/eth/abi.rb @@ -38,7 +38,7 @@ class ValueOutOfBounds < StandardError; end # # @param types [Array] types to be ABI-encoded. # @param args [Array] values to be ABI-encoded. - # @param packed [Boolean] use custom packed encoding. + # @param packed [Boolean] use custom packed encoding (default: false). # @return [String] the encoded ABI data. def encode(types, args, packed = false) @@ -59,7 +59,9 @@ def encode(types, args, packed = false) # encode types and arguments args.each_with_index do |arg, i| - if parsed_types[i].dynamic? + if packed + head += Abi::Encoder.type(parsed_types[i], arg, packed) + elsif parsed_types[i].dynamic? head += Abi::Encoder.type(Type.size_type, head_size + tail.size, packed) tail += Abi::Encoder.type(parsed_types[i], arg, packed) else @@ -75,6 +77,16 @@ def encode(types, args, packed = false) packed ? "#{tail}" : "#{head}#{tail}" end + # Encodes a custom, packed Application Binary Interface (packed ABI) data. + # It accepts multiple arguments and encodes according to the Solidity specification. + # + # @param types [Array] types to be ABI-encoded. + # @param args [Array] values to be ABI-encoded. + # @return [String] the packed encoded ABI data. + def encode_packed(types, args) + encode(types, args, true) + end + # Decodes Application Binary Interface (ABI) data. It accepts multiple # arguments and decodes using the head/tail mechanism. # @@ -82,7 +94,7 @@ def encode(types, args, packed = false) # @param data [String] ABI data to be decoded. # @param packed [Boolean] use custom packed decoding. # @return [Array] the decoded ABI data. - def decode(types, data, packed = false) + def decode(types, data) # accept hex abi but decode it first data = Util.hex_to_bin data if Util.hex? data @@ -132,6 +144,15 @@ def decode(types, data, packed = false) # return the decoded ABI types and data parsed_types.zip(outputs).map { |(type, out)| Abi::Decoder.type(type, out, packed) } end + + # Since the encoding is ambiguous, there is no decoding function. + # + # @param types [Array] the ABI to be decoded. + # @param data [String] ABI data to be decoded. + # @raise [DecodingError] if you try to decode packed ABI data. + def decode_packed(types, data) + raise DecodingError, "Since the encoding is ambiguous, there is no decoding function." + end end end diff --git a/lib/eth/abi/encoder.rb b/lib/eth/abi/encoder.rb index a8c10b3c..5f457bc0 100644 --- a/lib/eth/abi/encoder.rb +++ b/lib/eth/abi/encoder.rb @@ -148,7 +148,7 @@ def int(arg, type, packed) raise ValueOutOfBounds, arg unless i >= -2 ** (real_size - 1) and i < 2 ** (real_size - 1) if packed len = real_size / 8 - return Util.zpad_int(i % 2 ** 256, len) + return Util.zpad_int(i % 2 ** real_size, len) else return Util.zpad_int(i % 2 ** 256) end diff --git a/spec/eth/abi_spec.rb b/spec/eth/abi_spec.rb index 3f685b16..108024d4 100644 --- a/spec/eth/abi_spec.rb +++ b/spec/eth/abi_spec.rb @@ -311,6 +311,24 @@ end end + describe ".encode_packed .decode_packed" do + it "encodes the solidity docs example" do + # ref https://docs.soliditylang.org/en/v0.8.26/abi-spec.html#non-standard-packed-mode + + expect(Util.bin_to_hex Abi.encode_packed(["int16"], [-1])).to eq "ffff" + expect(Util.bin_to_hex Abi.encode_packed(["bytes1"], ["42".b])).to eq "42" + expect(Util.bin_to_hex Abi.encode_packed(["uint16"], [0x03])).to eq "0003" + expect(Util.bin_to_hex Abi.encode_packed(["string"], ["Hello, world!"])).to eq "48656c6c6f2c20776f726c6421" + expect(Util.bin_to_hex Abi.encode_packed(["int16", "bytes1", "uint16", "string"], [-1, "42", 0x03, "Hello, world!"])).to eq "ffff42000348656c6c6f2c20776f726c6421" + end + + it "won't decode packed abi" do + expect { + Abi.decode_packed(["int16", "bytes1", "uint16", "string"], "ffff42000348656c6c6f2c20776f726c6421") + }.to raise_error Abi::DecodingError, "Since the encoding is ambiguous, there is no decoding function." + end + end + describe "abicoder tests" do # https://github.com/rubycocos/blockchain/blob/ccef43a600e0832fb5e662bb0840656c974c0dc5/abicoder/test/test_spec.rb def assert(data, types, args) From 3f39495ee4fb6a6988758adb6a82344d91e44d86 Mon Sep 17 00:00:00 2001 From: Afr Schoe <58883403+q9f@users.noreply.github.com> Date: Tue, 2 Jul 2024 20:00:57 +0200 Subject: [PATCH 4/8] fix packed decoding --- lib/eth/abi.rb | 3 +-- lib/eth/abi/decoder.rb | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/eth/abi.rb b/lib/eth/abi.rb index 60af13d9..cf3d466c 100644 --- a/lib/eth/abi.rb +++ b/lib/eth/abi.rb @@ -92,7 +92,6 @@ def encode_packed(types, args) # # @param types [Array] the ABI to be decoded. # @param data [String] ABI data to be decoded. - # @param packed [Boolean] use custom packed decoding. # @return [Array] the decoded ABI data. def decode(types, data) @@ -142,7 +141,7 @@ def decode(types, data) end # return the decoded ABI types and data - parsed_types.zip(outputs).map { |(type, out)| Abi::Decoder.type(type, out, packed) } + parsed_types.zip(outputs).map { |(type, out)| Abi::Decoder.type(type, out) } end # Since the encoding is ambiguous, there is no decoding function. diff --git a/lib/eth/abi/decoder.rb b/lib/eth/abi/decoder.rb index b1c73002..01673831 100644 --- a/lib/eth/abi/decoder.rb +++ b/lib/eth/abi/decoder.rb @@ -28,10 +28,9 @@ module Decoder # # @param type [Eth::Abi::Type] type to be decoded. # @param arg [String] encoded type data string. - # @param packed [Boolean] use custom packed decoding. # @return [String] the decoded data for the type. # @raise [DecodingError] if decoding fails for type. - def type(type, arg, packed = false) + def type(type, arg) if %w(string bytes).include?(type.base_type) and type.sub_type.empty? # Case: decoding a string/bytes if type.dimensions.empty? @@ -96,10 +95,9 @@ def type(type, arg, packed = false) # # @param type [Eth::Abi::Type] type to be decoded. # @param data [String] encoded primitive type data string. - # @param packed [Boolean] use custom packed decoding. # @return [String] the decoded data for the type. # @raise [DecodingError] if decoding fails for type. - def primitive_type(type, data, packed = false) + def primitive_type(type, data) case type.base_type when "address" From e3f2b9cf575b7927750757ce742d40edb19d7105 Mon Sep 17 00:00:00 2001 From: Afr Schoe <58883403+q9f@users.noreply.github.com> Date: Tue, 2 Jul 2024 20:01:48 +0200 Subject: [PATCH 5/8] actually test the packed encoder --- spec/eth/abi/encoder_spec.rb | 94 ++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/spec/eth/abi/encoder_spec.rb b/spec/eth/abi/encoder_spec.rb index 04375b0e..1aea58c4 100644 --- a/spec/eth/abi/encoder_spec.rb +++ b/spec/eth/abi/encoder_spec.rb @@ -100,20 +100,20 @@ describe "packed encoding" do it "encodes packed types" do - expect(Util.bin_to_hex Abi.encode(["uint8[]"], [[1, 2, 3]], true)).to eq "010203" - expect(Util.bin_to_hex Abi.encode(["uint16[]"], [[1, 2, 3]], true)).to eq "000100020003" - expect(Util.bin_to_hex Abi.encode(["uint32"], [17], true)).to eq "00000011" - expect(Util.bin_to_hex Abi.encode(["uint64"], [17], true)).to eq "0000000000000011" - expect(Util.bin_to_hex Abi.encode(["bool[]"], [[true, false]], true)).to eq "0100" - expect(Util.bin_to_hex Abi.encode(["bool"], [true], true)).to eq "01" - expect(Util.bin_to_hex Abi.encode(["int32[]"], [[1, 2, 3]], true)).to eq "000000010000000200000003" - expect(Util.bin_to_hex Abi.encode(["int64[]"], [[1, 2, 3]], true)).to eq "000000000000000100000000000000020000000000000003" - expect(Util.bin_to_hex Abi.encode(["int64"], [17], true)).to eq "0000000000000011" - expect(Util.bin_to_hex Abi.encode(["int128"], [17], true)).to eq "00000000000000000000000000000011" - expect(Util.bin_to_hex Abi.encode(["bytes1"], ["0x42"], true)).to eq "42" - expect(Util.bin_to_hex Abi.encode(["bytes"], ["dave".b], true)).to eq "64617665" - expect(Util.bin_to_hex Abi.encode(["string"], ["dave"], true)).to eq "64617665" - expect(Util.bin_to_hex Abi.encode(["string"], ["Hello, World"], true)).to eq "48656c6c6f2c20576f726c64" + expect(Util.bin_to_hex Abi.encode_packed(["uint8[]"], [[1, 2, 3]])).to eq "010203" + expect(Util.bin_to_hex Abi.encode_packed(["uint16[]"], [[1, 2, 3]])).to eq "000100020003" + expect(Util.bin_to_hex Abi.encode_packed(["uint32"], [17])).to eq "00000011" + expect(Util.bin_to_hex Abi.encode_packed(["uint64"], [17])).to eq "0000000000000011" + expect(Util.bin_to_hex Abi.encode_packed(["bool[]"], [[true, false]])).to eq "0100" + expect(Util.bin_to_hex Abi.encode_packed(["bool"], [true])).to eq "01" + expect(Util.bin_to_hex Abi.encode_packed(["int32[]"], [[1, 2, 3]])).to eq "000000010000000200000003" + expect(Util.bin_to_hex Abi.encode_packed(["int64[]"], [[1, 2, 3]])).to eq "000000000000000100000000000000020000000000000003" + expect(Util.bin_to_hex Abi.encode_packed(["int64"], [17])).to eq "0000000000000011" + expect(Util.bin_to_hex Abi.encode_packed(["int128"], [17])).to eq "00000000000000000000000000000011" + expect(Util.bin_to_hex Abi.encode_packed(["bytes1"], ["0x42"])).to eq "42" + expect(Util.bin_to_hex Abi.encode_packed(["bytes"], ["dave".b])).to eq "64617665" + expect(Util.bin_to_hex Abi.encode_packed(["string"], ["dave"])).to eq "64617665" + expect(Util.bin_to_hex Abi.encode_packed(["string"], ["Hello, World"])).to eq "48656c6c6f2c20576f726c64" end context "wuminzhe's tests" do @@ -124,7 +124,7 @@ value = true data = "01" - expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data end it "bytes" do @@ -132,7 +132,7 @@ value = "dave".b data = "64617665" - expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data end it "types4" do @@ -140,7 +140,7 @@ value = "dave".b data = "64617665" - expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data end it "string" do @@ -148,7 +148,7 @@ value = "dave" data = "64617665" - expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data end # it "address1" do @@ -156,7 +156,7 @@ # value = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" # data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" - # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data # end # it "address2" do @@ -164,7 +164,7 @@ # value = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" # data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" - # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data # end # it "address3" do @@ -172,7 +172,7 @@ # value = 0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826 # data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" - # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data # end it "uint32" do @@ -180,7 +180,7 @@ value = 17 data = "00000011" - expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data end it "int64" do @@ -188,7 +188,7 @@ value = 17 data = "0000000000000011" - expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data end # it "(uint64)" do @@ -196,7 +196,7 @@ # value = [17] # data = "0000000000000011" - # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data # end # it "(int32,uint64)" do @@ -205,24 +205,24 @@ # # data = "000000110000000000000011" # expect do - # encode(type, value, true) + # encode_packed(type, value) # end.to raise_error("AbiCoderRb::Tuple with multi inner types is not supported in packed mode") # end - # it "int32,uint64" do - # types = %w[int32 uint64] - # values = [17, 17] - # data = "000000110000000000000011" + it "int32,uint64" do + types = %w[int32 uint64] + values = [17, 17] + data = "000000110000000000000011" - # expect(encode(types, values, true)).to eq data - # end + expect(Util.bin_to_hex Abi.encode_packed(types, values)).to eq data + end # it "uint16[]" do # type = "uint16[]" # value = [1, 2] # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002" - # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data # end # it "bool[]" do @@ -230,7 +230,7 @@ # value = [true, false] # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000" - # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data # end # it "(uint16[])" do @@ -238,7 +238,7 @@ # value = [[1, 2]] # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002" - # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data # end # it "uint16[2]" do @@ -246,7 +246,7 @@ # value = [1, 2] # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002" - # expect(Util.bin_to_hex Abi.encode([type], [value], true)).to eq data + # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data # end # it "bytes[2]" do @@ -254,63 +254,63 @@ # value = ["dave".b, "dave".b] # expect do - # encode(type, value, true) + # encode_packed(type, value) # end.to raise_error("AbiCoderRb::FixedArray with dynamic inner type is not supported in packed mode") # end - # it "encodes packed types" do + # it "encode_packeds packed types" do # expect( - # encode("uint8[]", [1, 2, 3], true) + # encode_packed("uint8[]", [1, 2, 3]) # ).to eq( # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") # ) # expect( - # encode("uint16[]", [1, 2, 3], true) + # encode_packed("uint16[]", [1, 2, 3]) # ).to eq( # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") # ) # expect( - # encode("uint32", 17, true) + # encode_packed("uint32", 17) # ).to eq( # hex("00000011") # ) # expect( - # encode("uint64", 17, true) + # encode_packed("uint64", 17) # ).to eq( # hex("0000000000000011") # ) # expect( - # encode("bool[]", [true, false], true) + # encode_packed("bool[]", [true, false]) # ).to eq( # hex("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000") # ) # expect( - # encode("bool", true, true) + # encode_packed("bool") # ).to eq hex("01") # expect( - # encode("int32[]", [1, 2, 3], true) + # encode_packed("int32[]", [1, 2, 3]) # ).to eq( # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") # ) # expect( - # encode("int64[]", [1, 2, 3], true) + # encode_packed("int64[]", [1, 2, 3]) # ).to eq( # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") # ) # expect( - # encode("int64", 17, true) + # encode_packed("int64", 17) # ).to eq hex("0000000000000011") # expect( - # encode("int128", 17, true) + # encode_packed("int128", 17) # ).to eq hex("00000000000000000000000000000011") # end end From c3cbc672557f7d2aa961ee00c47f387e98a10b85 Mon Sep 17 00:00:00 2001 From: Afr Schoe <58883403+q9f@users.noreply.github.com> Date: Tue, 2 Jul 2024 20:46:30 +0200 Subject: [PATCH 6/8] packed address --- lib/eth/abi.rb | 2 + lib/eth/abi/encoder.rb | 11 +++- spec/eth/abi/encoder_spec.rb | 114 +++++++++++++++++------------------ 3 files changed, 67 insertions(+), 60 deletions(-) diff --git a/lib/eth/abi.rb b/lib/eth/abi.rb index cf3d466c..45edf94f 100644 --- a/lib/eth/abi.rb +++ b/lib/eth/abi.rb @@ -41,6 +41,8 @@ class ValueOutOfBounds < StandardError; end # @param packed [Boolean] use custom packed encoding (default: false). # @return [String] the encoded ABI data. def encode(types, args, packed = false) + types = [types] unless types.instance_of? Array + args = [args] unless args.instance_of? Array # parse all types parsed_types = types.map { |t| Type === t ? t : Type.parse(t) } diff --git a/lib/eth/abi/encoder.rb b/lib/eth/abi/encoder.rb index 5f457bc0..a8eef2ba 100644 --- a/lib/eth/abi/encoder.rb +++ b/lib/eth/abi/encoder.rb @@ -115,8 +115,8 @@ def primitive_type(type, arg, packed = false) tuple arg, type when "hash" # TODO: Q9F hash arg, type - when "address" # TODO: Q9F - address arg + when "address" + address arg, packed else raise EncodingError, "Unhandled type: #{type.base_type} #{type.sub_type}" end @@ -272,26 +272,31 @@ def hash(arg, type) end # Properly encodes addresses. - def address(arg) + def address(arg, packed) if arg.is_a? Address # from checksummed address with 0x prefix + return arg.to_s[2..-1].downcase if packed Util.zpad_hex arg.to_s[2..-1] elsif arg.is_a? Integer # address from integer + return Util.int_to_big_endian arg if packed Util.zpad_int arg elsif arg.size == 20 # address from encoded address + return arg if packed Util.zpad arg, 32 elsif arg.size == 40 # address from hexadecimal address + return Util.hex_to_bin arg if packed Util.zpad_hex arg elsif arg.size == 42 and arg[0, 2] == "0x" # address from hexadecimal address with 0x prefix + return Util.hex_to_bin arg if packed Util.zpad_hex arg[2..-1] else raise EncodingError, "Could not parse address: #{arg}" diff --git a/spec/eth/abi/encoder_spec.rb b/spec/eth/abi/encoder_spec.rb index 1aea58c4..3aa345de 100644 --- a/spec/eth/abi/encoder_spec.rb +++ b/spec/eth/abi/encoder_spec.rb @@ -114,6 +114,10 @@ expect(Util.bin_to_hex Abi.encode_packed(["bytes"], ["dave".b])).to eq "64617665" expect(Util.bin_to_hex Abi.encode_packed(["string"], ["dave"])).to eq "64617665" expect(Util.bin_to_hex Abi.encode_packed(["string"], ["Hello, World"])).to eq "48656c6c6f2c20576f726c64" + expect(Abi.encode_packed(["address"], ["\xff" * 20])).to eq "\xff" * 20 + expect(Abi.encode_packed(["address"], ["ff" * 20])).to eq "\xff" * 20 + expect(Abi.encode_packed(["address"], ["0x" + "ff" * 20])).to eq "\xff" * 20 + expect(Abi.encode_packed(["address"], [Address.new("0x" + "ff" * 20)])).to eq "ff" * 20 end context "wuminzhe's tests" do @@ -124,7 +128,7 @@ value = true data = "01" - expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data end it "bytes" do @@ -132,7 +136,7 @@ value = "dave".b data = "64617665" - expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data end it "types4" do @@ -140,7 +144,7 @@ value = "dave".b data = "64617665" - expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data end it "string" do @@ -148,39 +152,39 @@ value = "dave" data = "64617665" - expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data end - # it "address1" do - # type = "address" - # value = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" - # data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" + it "address1" do + type = "address" + value = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" + data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" - # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data - # end + expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data + end - # it "address2" do - # type = "address" - # value = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" - # data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" + it "address2" do + type = "address" + value = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" + data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" - # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data - # end + expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data + end - # it "address3" do - # type = "address" - # value = 0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826 - # data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" + it "address3" do + type = "address" + value = 0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826 + data = "cd2a3d9f938e13cd947ec05abc7fe734df8dd826" - # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data - # end + expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data + end it "uint32" do type = "uint32" value = 17 data = "00000011" - expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data end it "int64" do @@ -188,7 +192,7 @@ value = 17 data = "0000000000000011" - expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data end # it "(uint64)" do @@ -196,7 +200,7 @@ # value = [17] # data = "0000000000000011" - # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + # expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data # end # it "(int32,uint64)" do @@ -205,7 +209,7 @@ # # data = "000000110000000000000011" # expect do - # encode_packed(type, value) + # Abi.encode_packed(type, value) # end.to raise_error("AbiCoderRb::Tuple with multi inner types is not supported in packed mode") # end @@ -222,7 +226,7 @@ # value = [1, 2] # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002" - # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + # expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data # end # it "bool[]" do @@ -230,7 +234,7 @@ # value = [true, false] # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000" - # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + # expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data # end # it "(uint16[])" do @@ -238,7 +242,7 @@ # value = [[1, 2]] # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002" - # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + # expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data # end # it "uint16[2]" do @@ -246,7 +250,7 @@ # value = [1, 2] # data = "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002" - # expect(Util.bin_to_hex Abi.encode_packed([type], [value])).to eq data + # expect(Util.bin_to_hex Abi.encode_packed(type, value)).to eq data # end # it "bytes[2]" do @@ -254,65 +258,61 @@ # value = ["dave".b, "dave".b] # expect do - # encode_packed(type, value) + # Abi.encode_packed(type, value) # end.to raise_error("AbiCoderRb::FixedArray with dynamic inner type is not supported in packed mode") # end - # it "encode_packeds packed types" do + it "encode_packeds packed types" do # expect( - # encode_packed("uint8[]", [1, 2, 3]) + # Abi.encode_packed("uint8[]", [1, 2, 3]) # ).to eq( # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") # ) # expect( - # encode_packed("uint16[]", [1, 2, 3]) + # Abi.encode_packed("uint16[]", [1, 2, 3]) # ).to eq( # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") # ) - # expect( - # encode_packed("uint32", 17) - # ).to eq( - # hex("00000011") - # ) + expect( + Abi.encode_packed("uint32", 17) + ).to eq Util.hex_to_bin "00000011" - # expect( - # encode_packed("uint64", 17) - # ).to eq( - # hex("0000000000000011") - # ) + expect( + Abi.encode_packed("uint64", 17) + ).to eq Util.hex_to_bin "0000000000000011" # expect( - # encode_packed("bool[]", [true, false]) + # Abi.encode_packed("bool[]", [true, false]) # ).to eq( # hex("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000") # ) - # expect( - # encode_packed("bool") - # ).to eq hex("01") + expect( + Abi.encode_packed("bool", true) + ).to eq Util.hex_to_bin "01" # expect( - # encode_packed("int32[]", [1, 2, 3]) + # Abi.encode_packed("int32[]", [1, 2, 3]) # ).to eq( # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") # ) # expect( - # encode_packed("int64[]", [1, 2, 3]) + # Abi.encode_packed("int64[]", [1, 2, 3]) # ).to eq( # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") # ) - # expect( - # encode_packed("int64", 17) - # ).to eq hex("0000000000000011") + expect( + Abi.encode_packed("int64", 17) + ).to eq Util.hex_to_bin "0000000000000011" - # expect( - # encode_packed("int128", 17) - # ).to eq hex("00000000000000000000000000000011") - # end + expect( + Abi.encode_packed("int128", 17) + ).to eq Util.hex_to_bin "00000000000000000000000000000011" + end end end end From e455af7ac89ef81c6313ab1016235e928653969c Mon Sep 17 00:00:00 2001 From: Afr Schoe <58883403+q9f@users.noreply.github.com> Date: Tue, 2 Jul 2024 21:02:21 +0200 Subject: [PATCH 7/8] add packed hashes --- lib/eth/abi/encoder.rb | 9 ++++++--- spec/eth/abi/encoder_spec.rb | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/eth/abi/encoder.rb b/lib/eth/abi/encoder.rb index a8eef2ba..e822024f 100644 --- a/lib/eth/abi/encoder.rb +++ b/lib/eth/abi/encoder.rb @@ -113,8 +113,8 @@ def primitive_type(type, arg, packed = false) bytes arg, type, packed when "tuple" # TODO: Q9F tuple arg, type - when "hash" # TODO: Q9F - hash arg, type + when "hash" + hash arg, type, packed when "address" address arg, packed else @@ -251,20 +251,23 @@ def struct_offsets(type, arg) end # Properly encodes hash-strings. - def hash(arg, type) + def hash(arg, type, packed) size = type.sub_type.to_i raise EncodingError, "Argument too long: #{arg}" unless size > 0 and size <= 32 if arg.is_a? Integer # hash from integer + return Util.int_to_big_endian arg if packed Util.zpad_int arg elsif arg.size == size # hash from encoded hash + return arg if packed Util.zpad arg, 32 elsif arg.size == size * 2 # hash from hexadecimal hash + return Util.hex_to_bin arg if packed Util.zpad_hex arg else raise EncodingError, "Could not parse hash: #{arg}" diff --git a/spec/eth/abi/encoder_spec.rb b/spec/eth/abi/encoder_spec.rb index 3aa345de..1188c59c 100644 --- a/spec/eth/abi/encoder_spec.rb +++ b/spec/eth/abi/encoder_spec.rb @@ -118,6 +118,8 @@ expect(Abi.encode_packed(["address"], ["ff" * 20])).to eq "\xff" * 20 expect(Abi.encode_packed(["address"], ["0x" + "ff" * 20])).to eq "\xff" * 20 expect(Abi.encode_packed(["address"], [Address.new("0x" + "ff" * 20)])).to eq "ff" * 20 + expect(Util.bin_to_hex Abi.encode_packed(["hash32"], ["8<\xAE\xB6pn\x00\xE2\fr\x05XH\x88\xBAW\xBFV\xEA\xFFMDe\xA8<\x9C{\e!GH\xA6"])).to eq "383caeb6706e00e20c7205584888ba57bf56eaff4d4465a83c9c7b1b214748a6" + expect(Util.bin_to_hex Abi.encode_packed(["hash20"], ["H\x88\xBAW\xBFV\xEA\xFFMDe\xA8<\x9C{\e!GH\xA6"])).to eq "4888ba57bf56eaff4d4465a83c9c7b1b214748a6" end context "wuminzhe's tests" do From 8b84fa5afc575867490dd76c61d483cb56f8842b Mon Sep 17 00:00:00 2001 From: Afr Schoe <58883403+q9f@users.noreply.github.com> Date: Tue, 2 Jul 2024 21:09:49 +0200 Subject: [PATCH 8/8] ru fo --- spec/eth/abi/encoder_spec.rb | 54 ++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/spec/eth/abi/encoder_spec.rb b/spec/eth/abi/encoder_spec.rb index 1188c59c..4f5ef2c1 100644 --- a/spec/eth/abi/encoder_spec.rb +++ b/spec/eth/abi/encoder_spec.rb @@ -265,17 +265,17 @@ # end it "encode_packeds packed types" do - # expect( - # Abi.encode_packed("uint8[]", [1, 2, 3]) - # ).to eq( - # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") - # ) - - # expect( - # Abi.encode_packed("uint16[]", [1, 2, 3]) - # ).to eq( - # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") - # ) + # expect( + # Abi.encode_packed("uint8[]", [1, 2, 3]) + # ).to eq( + # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") + # ) + + # expect( + # Abi.encode_packed("uint16[]", [1, 2, 3]) + # ).to eq( + # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") + # ) expect( Abi.encode_packed("uint32", 17) @@ -285,27 +285,27 @@ Abi.encode_packed("uint64", 17) ).to eq Util.hex_to_bin "0000000000000011" - # expect( - # Abi.encode_packed("bool[]", [true, false]) - # ).to eq( - # hex("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000") - # ) + # expect( + # Abi.encode_packed("bool[]", [true, false]) + # ).to eq( + # hex("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000") + # ) expect( Abi.encode_packed("bool", true) ).to eq Util.hex_to_bin "01" - # expect( - # Abi.encode_packed("int32[]", [1, 2, 3]) - # ).to eq( - # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") - # ) + # expect( + # Abi.encode_packed("int32[]", [1, 2, 3]) + # ).to eq( + # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") + # ) - # expect( - # Abi.encode_packed("int64[]", [1, 2, 3]) - # ).to eq( - # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") - # ) + # expect( + # Abi.encode_packed("int64[]", [1, 2, 3]) + # ).to eq( + # hex("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003") + # ) expect( Abi.encode_packed("int64", 17) @@ -313,7 +313,7 @@ expect( Abi.encode_packed("int128", 17) - ).to eq Util.hex_to_bin "00000000000000000000000000000011" + ).to eq Util.hex_to_bin "00000000000000000000000000000011" end end end