Skip to content
Merged
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
33 changes: 33 additions & 0 deletions core/types/morph_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,39 @@ func (tx *MorphTx) EncodeRLP(w io.Writer) error {
return err
}

// DecodeRLP implements rlp.Decoder so that direct rlp.Decode calls use the
// version-aware decode logic instead of reflection-based struct decoding.
// Without this, the field order mismatch between MorphTx (which has Version
// before FeeTokenID) and the v0 wire format (which lacks Version) causes
// decode failures.
func (tx *MorphTx) DecodeRLP(s *rlp.Stream) error {
kind, _, err := s.Kind()
if err != nil {
return err
}
if kind == rlp.List {
// V0 format: data is a single RLP list
raw, err := s.Raw()
if err != nil {
return err
}
return decodeV0MorphTxRLP(tx, raw)
}
// V1+ format: version byte followed by RLP list
versionByte, err := s.Uint8()
if err != nil {
return err
}
if versionByte != MorphTxVersion1 {
return errors.New("unsupported morph tx version: " + strconv.Itoa(int(versionByte)))
}
Comment on lines +211 to +218
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Keep DecodeRLP aligned with decode() for the 0x00 V0 form.

Line 296 still routes a leading 0x00 to the V0 decoder, but Lines 216-218 reject the same input as an unsupported version. Right now rlp.DecodeBytes and decode() do not accept the same byte streams.

Also applies to: 292-305

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/types/morph_tx.go` around lines 211 - 218, DecodeRLP currently rejects a
leading 0x00 version byte while decode() treats 0x00 as the V0 form; change the
version handling in DecodeRLP so that when reading versionByte via s.Uint8() a
value of 0x00 is routed to the existing V0 decoder (same behavior as decode()),
allow MorphTxVersion1 to continue to be handled as the V1 path, and only return
an unsupported-version error for other bytes. Update the same logic block
referenced around the other occurrence (lines ~292-305) so both DecodeRLP and
decode() accept identical byte streams; refer to the symbols versionByte,
MorphTxVersion1, DecodeRLP and decode() to locate and update the checks.

raw, err := s.Raw()
if err != nil {
return err
}
return decodeV1MorphTxRLP(tx, raw)
Comment on lines +209 to +223
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reset version-specific fields before decoding into a reused receiver.

DecodeRLP now fills the caller-supplied MorphTx, but decodeV0MorphTxRLP never restores Version/Reference/Memo, and decodeV1MorphTxRLP only assigns Reference/Memo when the new payload is non-empty. Decode V1(with memo) followed by V0 or V1 minimal into the same instance and the stale V1 state survives, which then drives the wrong downstream behavior.

Suggested fix
func decodeV1MorphTxRLP(tx *MorphTx, blob []byte) error {
	var v1 v1MorphTxRLP
	if err := rlp.DecodeBytes(blob, &v1); err != nil {
		return err
	}

+	tx.Reference = nil
+	tx.Memo = nil
 	tx.ChainID = v1.ChainID
 	tx.Nonce = v1.Nonce
 	tx.GasTipCap = v1.GasTipCap
 	tx.GasFeeCap = v1.GasFeeCap
 	tx.Gas = v1.Gas
 	tx.To = v1.To
 	tx.Value = v1.Value
 	tx.Data = v1.Data
 	tx.AccessList = v1.AccessList
 	tx.Version = MorphTxVersion1
 	tx.FeeTokenID = v1.FeeTokenID
 	tx.FeeLimit = v1.FeeLimit
 	// Convert []byte to *common.Reference
 	if len(v1.Reference) != 0 && len(v1.Reference) != common.ReferenceLength {
 		return errors.New("invalid reference length: expected 0 or " + strconv.Itoa(common.ReferenceLength) + ", got " + strconv.Itoa(len(v1.Reference)))
 	}
 	if len(v1.Reference) == common.ReferenceLength {
 		ref := common.BytesToReference(v1.Reference)
 		tx.Reference = &ref
 	}
 	// Convert []byte to *[]byte and validate memo length
 	if len(v1.Memo) > common.MaxMemoLength {
 		return errors.New("memo exceeds maximum length of " + strconv.Itoa(common.MaxMemoLength) + " bytes, got " + strconv.Itoa(len(v1.Memo)))
 	}
 	if len(v1.Memo) > 0 {
 		tx.Memo = &v1.Memo
 	}
 	tx.V = v1.V
 	tx.R = v1.R
 	tx.S = v1.S

 	return nil
}

func decodeV0MorphTxRLP(tx *MorphTx, blob []byte) error {
	var v0 v0MorphTxRLP
	if err := rlp.DecodeBytes(blob, &v0); err != nil {
		return err
	}

	if v0.FeeTokenID == 0 {
		return errors.New("invalid fee token id, expected non-zero")
	}

+	tx.Version = MorphTxVersion0
+	tx.Reference = nil
+	tx.Memo = nil
 	tx.ChainID = v0.ChainID
 	tx.Nonce = v0.Nonce
 	tx.GasTipCap = v0.GasTipCap
 	tx.GasFeeCap = v0.GasFeeCap
 	tx.Gas = v0.Gas
 	tx.To = v0.To
 	tx.Value = v0.Value
 	tx.Data = v0.Data
 	tx.AccessList = v0.AccessList
 	tx.FeeTokenID = v0.FeeTokenID
 	tx.FeeLimit = v0.FeeLimit
 	tx.V = v0.V
 	tx.R = v0.R
 	tx.S = v0.S

 	return nil
}

Also applies to: 314-340, 348-373

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/types/morph_tx.go` around lines 209 - 223, Before decoding into a
potentially reused MorphTx, clear version-specific state so stale V1 data
doesn't survive: in DecodeRLP (the function that calls
decodeV0MorphTxRLP/decodeV1MorphTxRLP) set tx.Version = 0 and tx.Reference = nil
and tx.Memo = nil before branching; when you detect a V1 versionByte, set
tx.Version = MorphTxVersion1 after validation and before calling
decodeV1MorphTxRLP. Apply the same reset pattern to the other decoding
entry-points referenced (the blocks that call
decodeV0MorphTxRLP/decodeV1MorphTxRLP at the other locations noted) so
decodeV0MorphTxRLP and decodeV1MorphTxRLP never accidentally inherit prior
Reference/Memo values.

}

func (tx *MorphTx) encode(b *bytes.Buffer) error {
switch tx.Version {
case MorphTxVersion0:
Expand Down
Loading
Loading