-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(eip-712): Add EIP-712 package. (#13)
- Loading branch information
1 parent
cfa87f2
commit 51d63cd
Showing
18 changed files
with
4,678 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ jobs: | |
if: (github.actor != 'dependabot[bot]') | ||
steps: | ||
- name: Permission issue fix | ||
run: git config --global --add safe.directory /__w/evmos/os | ||
run: git config --global --add safe.directory /__w/os/os | ||
- uses: actions/checkout@v4 | ||
- name: Get Diff | ||
uses: technote-space/[email protected] | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// Copyright Tharsis Labs Ltd.(Evmos) | ||
// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) | ||
package eip712 | ||
|
||
import ( | ||
"github.com/ethereum/go-ethereum/common/math" | ||
"github.com/ethereum/go-ethereum/signer/core/apitypes" | ||
) | ||
|
||
// createEIP712Domain creates the typed data domain for the given chainID. | ||
func createEIP712Domain(chainID uint64) apitypes.TypedDataDomain { | ||
domain := apitypes.TypedDataDomain{ | ||
Name: "Cosmos Web3", | ||
Version: "1.0.0", | ||
ChainId: math.NewHexOrDecimal256(int64(chainID)), // #nosec G701 | ||
VerifyingContract: "cosmos", | ||
Salt: "0", | ||
} | ||
|
||
return domain | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Copyright Tharsis Labs Ltd.(Evmos) | ||
// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) | ||
package eip712 | ||
|
||
import ( | ||
"github.com/ethereum/go-ethereum/signer/core/apitypes" | ||
) | ||
|
||
// WrapTxToTypedData wraps an Amino-encoded Cosmos Tx JSON SignDoc | ||
// bytestream into an EIP712-compatible TypedData request. | ||
func WrapTxToTypedData( | ||
chainID uint64, | ||
data []byte, | ||
) (apitypes.TypedData, error) { | ||
messagePayload, err := createEIP712MessagePayload(data) | ||
message := messagePayload.message | ||
if err != nil { | ||
return apitypes.TypedData{}, err | ||
} | ||
|
||
types, err := createEIP712Types(messagePayload) | ||
if err != nil { | ||
return apitypes.TypedData{}, err | ||
} | ||
|
||
domain := createEIP712Domain(chainID) | ||
|
||
typedData := apitypes.TypedData{ | ||
Types: types, | ||
PrimaryType: txField, | ||
Domain: domain, | ||
Message: message, | ||
} | ||
|
||
return typedData, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
package eip712_test | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
rand "github.com/cometbft/cometbft/libs/rand" | ||
"github.com/evmos/os/ethereum/eip712" | ||
"github.com/tidwall/gjson" | ||
"github.com/tidwall/sjson" | ||
) | ||
|
||
type EIP712FuzzTestParams struct { | ||
numTestObjects int | ||
maxNumFieldsPerObject int | ||
minStringLength int | ||
maxStringLength int | ||
randomFloatRange float64 | ||
maxArrayLength int | ||
maxObjectDepth int | ||
} | ||
|
||
const ( | ||
numPrimitiveJSONTypes = 3 | ||
numJSONTypes = 5 | ||
asciiRangeStart = 65 | ||
asciiRangeEnd = 127 | ||
fuzzTestName = "Flatten" | ||
) | ||
|
||
const ( | ||
jsonBoolType = iota | ||
jsonStringType = iota | ||
jsonFloatType = iota | ||
jsonArrayType = iota | ||
jsonObjectType = iota | ||
) | ||
|
||
var params = EIP712FuzzTestParams{ | ||
numTestObjects: 16, | ||
maxNumFieldsPerObject: 16, | ||
minStringLength: 16, | ||
maxStringLength: 48, | ||
randomFloatRange: 120000000, | ||
maxArrayLength: 8, | ||
maxObjectDepth: 4, | ||
} | ||
|
||
// TestRandomPayloadFlattening generates many random payloads with different JSON values to ensure | ||
// that Flattening works across all inputs. | ||
// Note that this is a fuzz test, although it doesn't use Go's Fuzz testing suite, since there are | ||
// variable input sizes, types, and fields. While it may be possible to translate a single input into | ||
// a JSON object, it would require difficult parsing, and ultimately approximates our randomized unit | ||
// tests as they are. | ||
func (suite *EIP712TestSuite) TestRandomPayloadFlattening() { | ||
// Re-seed rand generator | ||
rand.Seed(rand.Int64()) | ||
|
||
for i := 0; i < params.numTestObjects; i++ { | ||
suite.Run(fmt.Sprintf("%v%d", fuzzTestName, i), func() { | ||
payload := suite.generateRandomPayload(i) | ||
|
||
flattened, numMessages, err := eip712.FlattenPayloadMessages(payload) | ||
|
||
suite.Require().NoError(err) | ||
suite.Require().Equal(numMessages, i) | ||
|
||
suite.verifyPayloadAgainstFlattened(payload, flattened) | ||
}) | ||
} | ||
} | ||
|
||
// generateRandomPayload creates a random payload of the desired format, with random sub-objects. | ||
func (suite *EIP712TestSuite) generateRandomPayload(numMessages int) gjson.Result { | ||
payload := suite.createRandomJSONObject().Raw | ||
msgs := make([]gjson.Result, numMessages) | ||
|
||
for i := 0; i < numMessages; i++ { | ||
msgs[i] = suite.createRandomJSONObject() | ||
} | ||
|
||
payload, err := sjson.Set(payload, msgsFieldName, msgs) | ||
suite.Require().NoError(err) | ||
|
||
return gjson.Parse(payload) | ||
} | ||
|
||
// createRandomJSONObject creates a JSON object with random fields. | ||
func (suite *EIP712TestSuite) createRandomJSONObject() gjson.Result { | ||
var err error | ||
payloadRaw := "" | ||
|
||
numFields := suite.createRandomIntInRange(0, params.maxNumFieldsPerObject) | ||
for i := 0; i < numFields; i++ { | ||
key := suite.createRandomString() | ||
|
||
randField := suite.createRandomJSONField(i, 0) | ||
payloadRaw, err = sjson.Set(payloadRaw, key, randField) | ||
suite.Require().NoError(err) | ||
} | ||
|
||
return gjson.Parse(payloadRaw) | ||
} | ||
|
||
// createRandomJSONField creates a random field with a random JSON type, with the possibility of | ||
// nested fields up to depth objects. | ||
func (suite *EIP712TestSuite) createRandomJSONField(t int, depth int) interface{} { | ||
switch t % numJSONTypes { | ||
case jsonBoolType: | ||
return suite.createRandomBoolean() | ||
case jsonStringType: | ||
return suite.createRandomString() | ||
case jsonFloatType: | ||
return suite.createRandomFloat() | ||
case jsonArrayType: | ||
return suite.createRandomJSONNestedArray(depth) | ||
case jsonObjectType: | ||
return suite.createRandomJSONNestedObject(depth) | ||
default: | ||
return nil | ||
} | ||
} | ||
|
||
// createRandomJSONNestedArray creates an array of random nested JSON fields. | ||
func (suite *EIP712TestSuite) createRandomJSONNestedArray(depth int) []interface{} { | ||
arr := make([]interface{}, rand.Intn(params.maxArrayLength)) | ||
for i := range arr { | ||
arr[i] = suite.createRandomJSONNestedField(depth) | ||
} | ||
|
||
return arr | ||
} | ||
|
||
// createRandomJSONNestedObject creates a key-value set of objects with random nested JSON fields. | ||
func (suite *EIP712TestSuite) createRandomJSONNestedObject(depth int) interface{} { | ||
numFields := rand.Intn(params.maxNumFieldsPerObject) | ||
obj := make(map[string]interface{}) | ||
|
||
for i := 0; i < numFields; i++ { | ||
subField := suite.createRandomJSONNestedField(depth) | ||
|
||
obj[suite.createRandomString()] = subField | ||
} | ||
|
||
return obj | ||
} | ||
|
||
// createRandomJSONNestedField serves as a helper for createRandomJSONField and returns a random | ||
// subfield to populate an array or object type. | ||
func (suite *EIP712TestSuite) createRandomJSONNestedField(depth int) interface{} { | ||
var newFieldType int | ||
|
||
if depth == params.maxObjectDepth { | ||
newFieldType = rand.Intn(numPrimitiveJSONTypes) | ||
} else { | ||
newFieldType = rand.Intn(numJSONTypes) | ||
} | ||
|
||
return suite.createRandomJSONField(newFieldType, depth+1) | ||
} | ||
|
||
func (suite *EIP712TestSuite) createRandomBoolean() bool { | ||
return rand.Intn(2) == 0 | ||
} | ||
|
||
func (suite *EIP712TestSuite) createRandomFloat() float64 { | ||
return (rand.Float64() - 0.5) * params.randomFloatRange | ||
} | ||
|
||
func (suite *EIP712TestSuite) createRandomString() string { | ||
bzLen := suite.createRandomIntInRange(params.minStringLength, params.maxStringLength) | ||
bz := make([]byte, bzLen) | ||
|
||
for i := 0; i < bzLen; i++ { | ||
bz[i] = byte(suite.createRandomIntInRange(asciiRangeStart, asciiRangeEnd)) | ||
} | ||
|
||
str := string(bz) | ||
|
||
// Remove control characters, since they will make JSON invalid | ||
str = strings.ReplaceAll(str, "{", "") | ||
str = strings.ReplaceAll(str, "}", "") | ||
str = strings.ReplaceAll(str, "]", "") | ||
str = strings.ReplaceAll(str, "[", "") | ||
|
||
return str | ||
} | ||
|
||
// createRandomIntInRange provides a random integer between [min, max) | ||
func (suite *EIP712TestSuite) createRandomIntInRange(min int, max int) int { | ||
return rand.Intn(max-min) + min | ||
} |
Oops, something went wrong.