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

fix/EIP712_wrapper_v1_for_master #277

Merged
merged 1 commit into from
Jan 24, 2025
Merged

Conversation

aarmoa
Copy link
Collaborator

@aarmoa aarmoa commented Jan 23, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced EIP-712 transaction type support with improved codec and marshaling capabilities
    • Added new interface for advanced message encoding and decoding
  • Improvements

    • Refined error handling for transaction message processing
    • Updated transaction wrapping functions to support more flexible message types
    • Improved type conversion and codec management
  • Testing

    • Added comprehensive test suite for EIP-712 transaction wrapper functionality
    • Introduced new test cases to validate transaction encoding and decoding processes

@aarmoa aarmoa requested a review from maxim-inj January 23, 2025 16:20
Copy link

coderabbitai bot commented Jan 23, 2025

Warning

There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.

🔧 golangci-lint (1.62.2)

level=warning msg="[lintersdb] The name "gas" is deprecated. The linter has been renamed to: gosec."
level=warning msg="[lintersdb] The linter named "megacheck" is deprecated. It has been split into: gosimple, staticcheck, unused."

Walkthrough

The pull request introduces significant changes to the EIP-712 transaction handling in the Cosmos SDK. The modifications primarily focus on enhancing message marshaling, codec handling, and type conversion for EIP-712 transactions. A new interface AminoProtoCodecMarshaler is introduced, replacing the previous codec approach. The changes include updates to message processing, error handling, and the addition of a comprehensive test suite for the EIP-712 wrapper functionality.

Changes

File Changes
eip712_cosmos.go - Package changed from ante to sdk
- Added AminoProtoCodecMarshaler interface
- Updated function signatures for WrapTxToEIP712, WrapTxToEIP712V2, and EIP712Wrapper
- Modified message marshaling logic
eip712_cosmos_test.go - Added new test file in sdk package
- Implemented TestEIP712Wrapper function
- Added helper types and functions for testing EIP-712 wrapper
typeddata/typed_data.go - Renamed ComputeTypedDataHash to ComputeTypedDataAndHash
- Added SetCodec, GetEIP712TypedDataForMsg, and ExtractMsgTypes functions
- Introduced LegacyAminoCodec and ProtoCodec variables

Sequence Diagram

sequenceDiagram
    participant Client
    participant EIP712Wrapper
    participant Codec
    participant MessageMarshaler
    
    Client->>EIP712Wrapper: Prepare Transaction
    EIP712Wrapper->>Codec: Use AminoProtoCodecMarshaler
    Codec->>MessageMarshaler: Marshal Messages to JSON
    MessageMarshaler-->>Codec: Return Marshaled Messages
    EIP712Wrapper->>EIP712Wrapper: Process Transaction Data
    EIP712Wrapper-->>Client: Return Wrapped Transaction
Loading

Poem

🐰 Hop, hop, through codec's maze,
EIP-712 dances with grace,
Messages wrapped in JSON's embrace,
A rabbit's code, a digital race,
Cosmos transactions now set the pace! 🚀

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (7)
eip712_cosmos.go (1)

73-97: Simplify Message Marshaling Logic

The current implementation separately marshals each message into Amino JSON and then reconstructs the msgs field. Consider simplifying this by directly marshaling the entire msgs slice, if possible, to improve efficiency and reduce complexity.

Apply this diff to streamline the message marshaling:

 msgsData := make(map[string]interface{})
-bzMsgs, err := json.Marshal(map[string][]json.RawMessage{
-	"msgs": msgsJsons,
-})
+bzMsgs, err := cdc.MarshalAminoJSON(struct {
+	Msgs []cosmtypes.Msg `json:"msgs"`
+}{
+	Msgs: msgs,
+})
 if err != nil {
 	return typeddata.TypedData{}, errors.Wrap(err, "marshal msgs JSON")
 }
-if err := json.Unmarshal(bzMsgs, &msgsData); err != nil {
-	err = errors.Wrap(err, "failed to unmarshal msgs from proto-compatible amino JSON")
-	return typeddata.TypedData{}, err
-}
 
-txData["msgs"] = msgsData["msgs"]
+if err := json.Unmarshal(bzMsgs, &txData); err != nil {
+	err = errors.Wrap(err, "failed to unmarshal msgs into txData")
+	return typeddata.TypedData{}, err
+}
typeddata/typed_data.go (3)

863-875: Clarify Chain ID Mapping Logic

The chain ID mapping replaces certain chain IDs with hardcoded values (e.g., 777 and 888 mapped to 11155111, and others to 1). This could lead to confusion or errors if new chain IDs are introduced. Consider making this mapping configurable or documenting the rationale behind these choices.


969-983: Avoid Using Named Return Values for Error Recovery

Using named return values in conjunction with deferred functions for error handling (as in doRecover) can be prone to errors and make the code harder to maintain. Consider refactoring to pass the error explicitly or use other error handling mechanisms.


1283-1313: Simplify Chain ID Parsing Logic

The ParseCosmosChainID function uses a complex regular expression to parse the chain ID. Simplify the regex or consider alternative parsing methods to improve readability and maintainability.

eip712_cosmos_test.go (3)

46-56: Redundant Wrapper Function in Test

The wrapper function defined in the test simply calls WrapTxToEIP712. Since it doesn't add any additional logic, consider calling WrapTxToEIP712 directly to simplify the test code.

Apply this diff to remove the redundant wrapper:

-// Create wrapper function
-wrapper := func(
-	cdc *codec.ProtoCodec,
-	chainID uint64,
-	signerData *authsigning.SignerData,
-	timeoutHeight uint64,
-	memo string,
-	feeInfo legacytx.StdFee,
-	msgs []cosmtypes.Msg,
-	feeDelegation *FeeDelegationOptions,
-) (typeddata.TypedData, error) {
-	return WrapTxToEIP712(cdc, chainID, signerData, timeoutHeight, memo, feeInfo, msgs, feeDelegation)
-}
-
 // Test wrapper execution
-typedData, err := wrapper(
+typedData, err := WrapTxToEIP712(
 	protoCodec,
 	uint64(testReq.ChainID),
 	&authsigning.SignerData{
 		ChainID:       "injective-777",

283-302: Export prepareEip712Payload and Related Types

The types prepareEip712Payload, cosmosTxFee, cosmosCoin, and uint64AsString are defined in the test file but may be useful in other tests or packages. Consider exporting these types and moving them to a shared location if they are generally applicable.


331-340: Add Error Handling for JSON Unmarshal

In the UnmarshalJSON method for uint64AsString, consider handling the case where the incoming JSON value is a number and not a string. This will make the code more robust when dealing with different JSON formats.

Apply this diff to handle numeric values:

func (u *uint64AsString) UnmarshalJSON(data []byte) error {
-	var s string
-	if err := json.Unmarshal(data, &s); err != nil {
-		return err
-	}
-	v, err := strconv.ParseUint(s, 10, 64)
+	var v uint64
+	if err := json.Unmarshal(data, &v); err != nil {
+		var s string
+		if err := json.Unmarshal(data, &s); err != nil {
+			return err
+		}
+		var err error
+		v, err = strconv.ParseUint(s, 10, 64)
+		if err != nil {
+			return err
+		}
+	}
 	*u = uint64AsString(v)
 	return nil
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87a8944 and 02be6cb.

📒 Files selected for processing (3)
  • eip712_cosmos.go (7 hunks)
  • eip712_cosmos_test.go (1 hunks)
  • typeddata/typed_data.go (3 hunks)
🔇 Additional comments (6)
eip712_cosmos.go (5)

1-1: Package Change Confirmation

The change from package ante to package sdk is acceptable if it aligns with the new module structure and promotes better organization.


27-33: Introduction of AminoProtoCodecMarshaler Interface

The new AminoProtoCodecMarshaler interface extends codec.Codec with a MarshalAminoJSON method. This addition enhances flexibility by allowing the codec to marshal messages into Amino JSON format, which is necessary for EIP-712 transaction processing.


Line range hint 49-64: Verify Passing Empty Messages to StdSignBytes

In line 64, the msgs parameter passed to legacytx.StdSignBytes is replaced with an empty slice []cosmtypes.Msg{}. Please verify that passing an empty message slice does not adversely affect the computation of sign bytes or any downstream processes that rely on the messages. It's essential to ensure that all necessary message data is correctly included in the transaction.


Line range hint 111-118: Ensure feePayer Field Alignment in EIP-712 Types

When adding the feePayer to the fee structure, make sure that the Fee type in the msgTypes map reflects this change. The current implementation updates msgTypes["Fee"], but if other parts of the code rely on the original Fee structure, this might cause inconsistencies.


Line range hint 525-581: Consistency in WrapTxToEIP712V2 Function

The WrapTxToEIP712V2 function mirrors changes made in WrapTxToEIP712. Ensure that both functions maintain consistent behavior, especially concerning fee delegation and message handling. Verify that the domain information and the typed data structure conform to the expected EIP-712 standards.

typeddata/typed_data.go (1)

859-861: Avoid Setting timeout_height to Zero Implicitly

Setting timeout_height to "0" when it is nil might lead to unintended behavior. Ensure that this default value aligns with the expected transaction semantics and consider whether a different default or an explicit error would be more appropriate.

Comment on lines +810 to +818

var (
LegacyAminoCodec *codec.LegacyAmino
ProtoCodec *codec.ProtoCodec
)

func SetCodec(amino *codec.LegacyAmino, proto *codec.ProtoCodec) {
LegacyAminoCodec, ProtoCodec = amino, proto
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Thread Safety Consideration for Global Codec Variables

The global variables LegacyAminoCodec and ProtoCodec are set using SetCodec, which might lead to concurrency issues in a multi-threaded environment. Consider avoiding global state or using synchronization mechanisms to prevent race conditions.

Comment on lines +826 to +845
// parse txData to StdSignDoc in order to parse the msg for ExtractMsgTypes
var signDoc legacytx.StdSignDoc
if err := LegacyAminoCodec.UnmarshalJSON(signDocBytes, &signDoc); err != nil {
return TypedData{}, err
}

var msg cosmtypes.Msg
if err := LegacyAminoCodec.UnmarshalJSON(signDoc.Msgs[0], &msg); err != nil {
return TypedData{}, errors.Wrap(err, "failed to unmarshal msg")
}

// For some reason this type cast does not work during UnmarshalJSON above
// If the underlying msg does implement the UnpackInterfacesMessage interface (MsgGrant, MsgExec...),
// we explicitly call the method here to ensure potential Any fields within the message are correctly parsed
if unpacker, ok := msg.(codectypes.UnpackInterfacesMessage); ok {
if err := unpacker.UnpackInterfaces(codectypes.AminoJSONUnpacker{Cdc: LegacyAminoCodec.Amino}); err != nil {
return TypedData{}, errors.Wrap(err, "failed to unpack msg")
}
}

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle Multiple Messages in GetEIP712TypedDataForMsg

The current implementation only unmarshals and processes the first message in signDoc.Msgs. If multiple messages are included, the additional messages are ignored. Update the function to handle all messages appropriately.

Comment on lines +950 to +967
func walkFields(cdc codec.ProtoCodecMarshaler, typeMap Types, rootType string, in interface{}) (err error) {
defer doRecover(&err)

t := reflect.TypeOf(in)
v := reflect.ValueOf(in)

for {
if t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface {
t = t.Elem()
v = v.Elem()
continue
}
break
}

err = traverseFields(cdc, typeMap, rootType, typeDefPrefix, t, v)
return
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Potential Panic in walkFields Function

The walkFields function depends on doRecover to handle panics. Relying on panic and recover for control flow is generally discouraged. Refactor the code to handle errors without panicking to improve reliability and readability.

Comment on lines +121 to +134
// ComputeTypedDataAndHash computes the typed data and its keccak hash for signing
func ComputeTypedDataAndHash(typedData TypedData) (hash, data []byte, err error) {
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
if err != nil {
err = errors.Wrap(err, "failed to pack and hash typedData EIP712Domain")
return nil, err
return nil, nil, errors.Wrap(err, "failed to pack and hash typedData EIP712Domain")
}

typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
if err != nil {
err = errors.Wrap(err, "failed to pack and hash typedData primary type")
return nil, err
return nil, nil, errors.Wrap(err, "failed to pack and hash typedData EIP712Domain")
}

rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
return crypto.Keccak256(rawData), nil
return crypto.Keccak256(rawData), rawData, nil
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct Error Message in ComputeTypedDataAndHash

The error message on line 130 refers to "failed to pack and hash typedData EIP712Domain" when it should indicate an issue with the typedData.PrimaryType. This might cause confusion during debugging.

Apply this diff to correct the error message:

-		return nil, nil, errors.Wrap(err, "failed to pack and hash typedData EIP712Domain")
+		return nil, nil, errors.Wrapf(err, "failed to pack and hash typedData %s", typedData.PrimaryType)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// ComputeTypedDataAndHash computes the typed data and its keccak hash for signing
func ComputeTypedDataAndHash(typedData TypedData) (hash, data []byte, err error) {
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
if err != nil {
err = errors.Wrap(err, "failed to pack and hash typedData EIP712Domain")
return nil, err
return nil, nil, errors.Wrap(err, "failed to pack and hash typedData EIP712Domain")
}
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
if err != nil {
err = errors.Wrap(err, "failed to pack and hash typedData primary type")
return nil, err
return nil, nil, errors.Wrap(err, "failed to pack and hash typedData EIP712Domain")
}
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
return crypto.Keccak256(rawData), nil
return crypto.Keccak256(rawData), rawData, nil
// ComputeTypedDataAndHash computes the typed data and its keccak hash for signing
func ComputeTypedDataAndHash(typedData TypedData) (hash, data []byte, err error) {
domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
if err != nil {
return nil, nil, errors.Wrap(err, "failed to pack and hash typedData EIP712Domain")
}
typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to pack and hash typedData %s", typedData.PrimaryType)
}
rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
return crypto.Keccak256(rawData), rawData, nil

Comment on lines +25 to +27
chainCtx, _ := chainclient.NewClientContext("", "", nil)
protoCodec := codec.NewProtoCodec(chainCtx.InterfaceRegistry)

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Initialize Codec Correctly for Testing

The test initializes a new ProtoCodec without registering all necessary interfaces, which might lead to incomplete or incorrect marshaling during the test. Ensure that all relevant message types are registered in the codec used for testing.

Comment on lines +34 to +35
legacytx.RegressionTestingAminoCodec = codec.NewLegacyAmino()

Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid Using Global Variables in Tests

The assignment to legacytx.RegressionTestingAminoCodec affects global state and could interfere with other tests. Use a locally scoped codec within the test to prevent side effects.

@aarmoa aarmoa merged commit e9ea79e into master Jan 24, 2025
4 checks passed
@aarmoa aarmoa deleted the fix/EIP712_wrapper_v1_for_master branch January 24, 2025 01:53
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.

1 participant