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

signer/core/apitypes: support more input types for eip-712 encoding #26074

Merged
merged 5 commits into from Nov 4, 2022
Merged

signer/core/apitypes: support more input types for eip-712 encoding #26074

merged 5 commits into from Nov 4, 2022

Conversation

ghost
Copy link

@ghost ghost commented Oct 31, 2022

  1. Previously, geth would not accept byte arrays (i.e. [32]byte) even for arguments where bytes32 is specified. This makes it terribly inconvenient to work with fixed size byte arrays.
  2. Additionally, the Typed Data hasher wouldn't accept anything except strings in place of addresses, which doesn't make much sense. It's an 20 byte array after all. Not to mention the inefficiency in using hexadecimal strings in place of a byte array. I've adjusted it to accept both byte slices and byte arrays.
  3. It also would not accept big.Ints in certain places, despite it being internally converted to a big.Int internally.

This PR solves both problems.

@ghost ghost requested a review from holiman as a code owner October 31, 2022 10:56
ligi
ligi previously requested changes Nov 1, 2022
Copy link
Member

@ligi ligi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use reflection and add tests

@ghost ghost requested review from holiman and ligi and removed request for holiman and ligi November 1, 2022 14:55
@ligi ligi removed the status:triage label Nov 1, 2022
signer/core/apitypes/types.go Outdated Show resolved Hide resolved
@holiman
Copy link
Contributor

holiman commented Nov 2, 2022

the Typed Data hasher wouldn't accept anything except strings in place of addresses, which doesn't make much sense. It's an 20 byte array after all. Not to mention the inefficiency in using hexadecimal strings in place of a byte array.

EIP 712 spec

This EIP aims to improve the usability of off-chain message signing for use on-chain. We are seeing growing adoption of off-chain message signing as it saves gas and reduces the number of transactions on the blockchain. Currently signed messages are an opaque hex string displayed to the user with little context about the items that make up the message.

The EIP basically outlines how a human-readable text-based structure is encoded to a canonical form, and hashed/signed. The inputs are typically json inputs, and address would therefore be a hex-encoded string.

What is the usecase where some different type would be passed into EncodePrimitiveValue?

@holiman
Copy link
Contributor

holiman commented Nov 2, 2022

Also, please add a testcase for the change to parsing integers. e.g

--- a/signer/core/apitypes/signed_data_internal_test.go
+++ b/signer/core/apitypes/signed_data_internal_test.go
@@ -128,6 +128,7 @@ func TestParseInteger(t *testing.T) {
                {"int32", "-123", big.NewInt(-123)},
                {"uint32", "0xff", big.NewInt(0xff)},
                {"int8", "0xffff", nil},
+               {"int32", big.NewInt(-124), big.NewInt(-124)},
        } {
                res, err := parseInteger(tt.t, tt.v)
                if tt.exp == nil && res == nil {

@ghost
Copy link
Author

ghost commented Nov 2, 2022

the Typed Data hasher wouldn't accept anything except strings in place of addresses, which doesn't make much sense. It's an 20 byte array after all. Not to mention the inefficiency in using hexadecimal strings in place of a byte array.

EIP 712 spec

This EIP aims to improve the usability of off-chain message signing for use on-chain. We are seeing growing adoption of off-chain message signing as it saves gas and reduces the number of transactions on the blockchain. Currently signed messages are an opaque hex string displayed to the user with little context about the items that make up the message.

The EIP basically outlines how a human-readable text-based structure is encoded to a canonical form, and hashed/signed. The inputs are typically json inputs, and address would therefore be a hex-encoded string.

What is the usecase where some different type would be passed into EncodePrimitiveValue?

Our use case is making contract transactions that require the same data structure to be used as an input to the typed data hasher and encoded using the ABI at multiple times. Despite the types being the same, it doesn't make sense that some APIs expect different types for the same underlying thing. These data structures are internally generated and not provided from a human.

EIP712 may have been intended for human-readable inputs, but that doesn't imply one shouldn't use programmatically.

Comment on lines 503 to 517
stringValue, isString := encValue.(string)
if isString {
if !common.IsHexAddress(stringValue) {
return nil, dataMismatchError(encType, encValue)
}
retval := make([]byte, 32)
copy(retval[12:], common.HexToAddress(stringValue).Bytes())
return retval, nil
}
byteSlice, isByteSlice := encValue.([]byte)
if isByteSlice {
if len(byteSlice) == 20 {
retval := make([]byte, 32)
copy(retval[12:], byteSlice)
return retval, nil
} else {
return nil, errors.New("if a byte slice is passed in, in place of an address, it must be either 20 or 32 bytes long")
}
}
twentyByteArr, isTwentyByteArr := encValue.([20]byte)
if isTwentyByteArr {
retval := make([]byte, 32)
copy(retval[12:], twentyByteArr[:])
return retval, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
stringValue, isString := encValue.(string)
if isString {
if !common.IsHexAddress(stringValue) {
return nil, dataMismatchError(encType, encValue)
}
retval := make([]byte, 32)
copy(retval[12:], common.HexToAddress(stringValue).Bytes())
return retval, nil
}
byteSlice, isByteSlice := encValue.([]byte)
if isByteSlice {
if len(byteSlice) == 20 {
retval := make([]byte, 32)
copy(retval[12:], byteSlice)
return retval, nil
} else {
return nil, errors.New("if a byte slice is passed in, in place of an address, it must be either 20 or 32 bytes long")
}
}
twentyByteArr, isTwentyByteArr := encValue.([20]byte)
if isTwentyByteArr {
retval := make([]byte, 32)
copy(retval[12:], twentyByteArr[:])
return retval, nil
retval := make([]byte, 32)
switch val := encValue.(type) {
case string:
if common.IsHexAddress(val) {
copy(retval[12:], common.HexToAddress(val).Bytes())
return retval, nil
}
case []byte:
if len(val) == 20 {
copy(retval[12:], val)
return retval, nil
}
case [20]byte:
copy(retval[12:], val[:])
return retval, nil
}

IMO a bit clearer, and definitely shorter

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we need tests for these type-conversions, (and preferrably the fail-cases too)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests for this, and adopted your change.

@holiman holiman changed the title Fix ABI and TypedData handling signer/core/apitypes: support more input types for eip-712 encoding Nov 4, 2022
Copy link
Contributor

@holiman holiman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@holiman holiman added this to the 1.11.0 milestone Nov 4, 2022
@holiman holiman merged commit 6d55908 into ethereum:master Nov 4, 2022
shekhirin pushed a commit to shekhirin/go-ethereum that referenced this pull request Jun 6, 2023
…thereum#26074)

* apitypes: synchronize handling of types

* signer/core/apitypes: improve array check

* apitypes: add a test for big.Int -> int32

* signer/core/apitypes: Add a test for parsing addresses from [20]byte, []byte and string

* signer/core/apitypes: add some testcases

Co-authored-by: Felix Lange <fjl@twurst.com>
Co-authored-by: Martin Holst Swende <martin@swende.se>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants