Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eth/abi: use packed encoding for eip 712 #313

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion lib/eth/abi/type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ def validate_base_type(base_type, sub_type)
# booleans cannot have any suffix
raise ParseError, "Bool cannot have suffix" unless sub_type.empty?
else

# we cannot parse arbitrary types such as 'decimal' or 'hex'
raise ParseError, "Unknown base type"
end
Expand Down
10 changes: 6 additions & 4 deletions lib/eth/eip712.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def type_dependencies(primary_type, types, result = [])

# recursively look for further nested dependencies
types[primary_type.to_sym].each do |t|
dependency = type_dependencies t[:type], types, result
_ = type_dependencies t[:type], types, result
end
return result
end
Expand Down Expand Up @@ -98,12 +98,14 @@ def hash_type(primary_type, types)
end

# Recursively ABI-encodes all data and types according to EIP-712.
# Defaults to packed solidity encoding for complex data, e.g., arrays.
#
# @param primary_type [String] the primary type which we want to encode.
# @param data [Array] the data in the data structure we want to encode.
# @param types [Array] all existing types in the data structure.
# @param packed [Bool] set true to return packed encoding (default: `false`).
# @return [String] an ABI-encoded representation of the data and the types.
def encode_data(primary_type, data, types)
def encode_data(primary_type, data, types, packed = false)

# first data field is the type hash
encoded_types = ["bytes32"]
Expand All @@ -113,7 +115,7 @@ def encode_data(primary_type, data, types)
types[primary_type.to_sym].each do |field|
value = data[field[:name].to_sym]
type = field[:type]
raise NotImplementedError, "Arrays currently unimplemented for EIP-712." if type.end_with? "]"
packed = true if type.end_with? "]"
if type == "string"
encoded_types.push "bytes32"
encoded_values.push Util.keccak256 value
Expand All @@ -132,7 +134,7 @@ def encode_data(primary_type, data, types)
end

# all data is abi-encoded
return Abi.encode encoded_types, encoded_values
return Abi.encode encoded_types, encoded_values, packed
end

# Recursively ABI-encodes and hashes all data and types.
Expand Down
24 changes: 24 additions & 0 deletions spec/eth/abi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,30 @@
end
end

describe ".solidity_packed" do
it "can encode packed abi" do
# https://ethereum.stackexchange.com/questions/72199/testing-sha256abi-encodepacked-argument
types_0 = ["bytes1", "bytes2"]
values_0 = ["a", "bc"]
packed_0 = Abi.solidity_packed(types_0, values_0)
expect(packed_0).to eq "abc"
types_1 = ["bytes2", "bytes1"]
values_1 = ["ab", "c"]
packed_1 = Abi.solidity_packed(types_1, values_1)
expect(packed_1).to eq "abc"
hash = Util.hex_to_bin "4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"
expect(Util.keccak256 packed_1).to eq hash
expect(packed_1).to eq packed_0
expect(Util.keccak256 packed_1).to eq Util.keccak256 packed_0

types = ["string"]
values = ["Hello World!"]
packed = Abi.solidity_packed(types, values)
hash = Util.keccak256 packed
expect(hash).to eq Util.hex_to_bin "0x3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0"
end
end

describe "abicoder tests" do
# https://github.com/rubycocos/blockchain/blob/ccef43a600e0832fb5e662bb0840656c974c0dc5/abicoder/test/test_spec.rb
def assert(data, types, args)
Expand Down
85 changes: 85 additions & 0 deletions spec/eth/eip712_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,5 +169,90 @@
sig_string = "8255c17ce6be5fb6ee3430784a52a5163c63fc87e2dcae32251d9c49ba849fad7067454b0d7e694698c02e552fd7af283dcaadc754d58ecba978856de8742e361b"
expect(key.sign_typed_data data_string).to eq sig_string
end

it "can abi-encode complex nested data" do
# ref https://github.com/q9f/eth.rb/issues/127#issuecomment-1447441576
key = Key.new(priv: "0x8e589ba6280400cfa426229684f7c2ac9ebf132f7ad658a82ed57553a0a9dee8")
data = {
:types => {
:OrderComponents => [
{ :name => "offerer", :type => "address" },
{ :name => "zone", :type => "address" },
{ :name => "offer", :type => "OfferItem[]" },
{ :name => "consideration", :type => "ConsiderationItem[]" },
{ :name => "orderType", :type => "uint8" },
{ :name => "startTime", :type => "uint256" },
{ :name => "endTime", :type => "uint256" },
{ :name => "zoneHash", :type => "bytes32" },
{ :name => "salt", :type => "uint256" },
{ :name => "conduitKey", :type => "bytes32" },
{ :name => "counter", :type => "uint256" },
],
:OfferItem => [
{ :name => "itemType", :type => "uint8" },
{ :name => "token", :type => "address" },
{ :name => "identifierOrCriteria", :type => "uint256" },
{ :name => "startAmount", :type => "uint256" },
{ :name => "endAmount", :type => "uint256" },
],
:ConsiderationItem => [
{ :name => "itemType", :type => "uint8" },
{ :name => "token", :type => "address" },
{ :name => "identifierOrCriteria", :type => "uint256" },
{ :name => "startAmount", :type => "uint256" },
{ :name => "endAmount", :type => "uint256" },
{ :name => "recipient", :type => "address" },
],
:EIP712Domain => [
{ :name => "name", :type => "string" },
{ :name => "version", :type => "string" },
{ :name => "chainId", :type => "uint256" },
{ :name => "verifyingContract", :type => "address" },
],
},
:domain => {
:name => "Seaport",
:version => "1.1",
:chainId => 1,
:verifyingContract => "0x00000000006c3852cbef3e08e8df289169ede581",
},
:primaryType => "OrderComponents",
:message => {
:offerer => "0x0000000000000000000000000000000000000001",
:zone => "0x0000000000000000000000000000000000000000",
:zoneHash => "0x0000000000000000000000000000000000000000000000000000000000000000",
:offer => [
{
:itemType => 2,
:token => "0x0000000000000000000000000000000000000002",
:identifierOrCriteria => 2,
:startAmount => 1,
:endAmount => 1,
},
],
:consideration => [
{
:itemType => 0,
:identifierOrCriteria => 0,
:startAmount => 9750000000000000000,
:endAmount => 9750000000000000000,
:recipient => "0x0000000000000000000000000000000000000003",
},
{ :itemType => 0,
:identifierOrCriteria => 0,
:startAmount => 250000000000000000,
:endAmount => 250000000000000000,
:recipient => "0x0000000000000000000000000000000000000004" },
],
:salt => 12686911856931635052326433555881236148,
:conduitKey => "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000",
:nonce => 0,
},
}

sig_bytes = "ddefbbe703f59949a87ece451321924bb9297100dda63e1f39559b72db3ec9e83dae2056c25b52ddb8bd53ab536e84d2e4f70d98219ed14e46b021a59aefb4eb1c"
pending("https://github.com/q9f/eth.rb/issues/127")
expect(key.sign_typed_data data).to eq sig_bytes
end
end
end
Loading