From 04a336aee8a911504b2bacc3fb755d726829b235 Mon Sep 17 00:00:00 2001
From: Felix Lange <fjl@twurst.com>
Date: Tue, 14 Jan 2025 14:42:18 +0100
Subject: [PATCH] core/types: change SetCodeTx.ChainID to uint256 (#30982)

We still need to decide how to handle non-specfic `chainId` in the JSON
encoding of authorizations. With `chainId` being a uint64, the previous
implementation just used value zero. However, it might actually be more
correct to use the value `null` for this case.
---
 core/blockchain_test.go               |  5 ++---
 core/state_transition.go              |  4 ++--
 core/types/gen_authorization.go       |  8 ++++----
 core/types/transaction_marshalling.go | 16 +++++++++++-----
 core/types/transaction_signing.go     |  2 +-
 core/types/tx_blob.go                 |  6 +++---
 core/types/tx_dynamic_fee.go          |  6 +++---
 core/types/tx_setcode.go              | 16 ++++++++--------
 internal/ethapi/transaction_args.go   |  2 +-
 tests/gen_stauthorization.go          | 11 ++++++-----
 tests/state_test_util.go              |  6 +++---
 11 files changed, 44 insertions(+), 38 deletions(-)

diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index f2a9b953a1..7805a7c6e8 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -4265,12 +4265,11 @@ func TestEIP7702(t *testing.T) {
 	// 2. addr1:0xaaaa calls into addr2:0xbbbb
 	// 3. addr2:0xbbbb  writes to storage
 	auth1, _ := types.SignSetCode(key1, types.SetCodeAuthorization{
-		ChainID: gspec.Config.ChainID.Uint64(),
+		ChainID: *uint256.MustFromBig(gspec.Config.ChainID),
 		Address: aa,
 		Nonce:   1,
 	})
 	auth2, _ := types.SignSetCode(key2, types.SetCodeAuthorization{
-		ChainID: 0,
 		Address: bb,
 		Nonce:   0,
 	})
@@ -4278,7 +4277,7 @@ func TestEIP7702(t *testing.T) {
 	_, blocks, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, b *BlockGen) {
 		b.SetCoinbase(aa)
 		txdata := &types.SetCodeTx{
-			ChainID:   gspec.Config.ChainID.Uint64(),
+			ChainID:   uint256.MustFromBig(gspec.Config.ChainID),
 			Nonce:     0,
 			To:        addr1,
 			Gas:       500000,
diff --git a/core/state_transition.go b/core/state_transition.go
index 009b679b27..b6203e6aae 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -529,8 +529,8 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
 
 // validateAuthorization validates an EIP-7702 authorization against the state.
 func (st *stateTransition) validateAuthorization(auth *types.SetCodeAuthorization) (authority common.Address, err error) {
-	// Verify chain ID is 0 or equal to current chain ID.
-	if auth.ChainID != 0 && st.evm.ChainConfig().ChainID.Uint64() != auth.ChainID {
+	// Verify chain ID is null or equal to current chain ID.
+	if !auth.ChainID.IsZero() && auth.ChainID.CmpBig(st.evm.ChainConfig().ChainID) != 0 {
 		return authority, ErrAuthorizationWrongChainID
 	}
 	// Limit nonce to 2^64-1 per EIP-2681.
diff --git a/core/types/gen_authorization.go b/core/types/gen_authorization.go
index be5467c50d..57069cbb1f 100644
--- a/core/types/gen_authorization.go
+++ b/core/types/gen_authorization.go
@@ -16,7 +16,7 @@ var _ = (*authorizationMarshaling)(nil)
 // MarshalJSON marshals as JSON.
 func (s SetCodeAuthorization) MarshalJSON() ([]byte, error) {
 	type SetCodeAuthorization struct {
-		ChainID hexutil.Uint64 `json:"chainId" gencodec:"required"`
+		ChainID hexutil.U256   `json:"chainId" gencodec:"required"`
 		Address common.Address `json:"address" gencodec:"required"`
 		Nonce   hexutil.Uint64 `json:"nonce" gencodec:"required"`
 		V       hexutil.Uint64 `json:"yParity" gencodec:"required"`
@@ -24,7 +24,7 @@ func (s SetCodeAuthorization) MarshalJSON() ([]byte, error) {
 		S       hexutil.U256   `json:"s" gencodec:"required"`
 	}
 	var enc SetCodeAuthorization
-	enc.ChainID = hexutil.Uint64(s.ChainID)
+	enc.ChainID = hexutil.U256(s.ChainID)
 	enc.Address = s.Address
 	enc.Nonce = hexutil.Uint64(s.Nonce)
 	enc.V = hexutil.Uint64(s.V)
@@ -36,7 +36,7 @@ func (s SetCodeAuthorization) MarshalJSON() ([]byte, error) {
 // UnmarshalJSON unmarshals from JSON.
 func (s *SetCodeAuthorization) UnmarshalJSON(input []byte) error {
 	type SetCodeAuthorization struct {
-		ChainID *hexutil.Uint64 `json:"chainId" gencodec:"required"`
+		ChainID *hexutil.U256   `json:"chainId" gencodec:"required"`
 		Address *common.Address `json:"address" gencodec:"required"`
 		Nonce   *hexutil.Uint64 `json:"nonce" gencodec:"required"`
 		V       *hexutil.Uint64 `json:"yParity" gencodec:"required"`
@@ -50,7 +50,7 @@ func (s *SetCodeAuthorization) UnmarshalJSON(input []byte) error {
 	if dec.ChainID == nil {
 		return errors.New("missing required field 'chainId' for SetCodeAuthorization")
 	}
-	s.ChainID = uint64(*dec.ChainID)
+	s.ChainID = uint256.Int(*dec.ChainID)
 	if dec.Address == nil {
 		return errors.New("missing required field 'address' for SetCodeAuthorization")
 	}
diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go
index 993d633c6f..1bbb97a3ec 100644
--- a/core/types/transaction_marshalling.go
+++ b/core/types/transaction_marshalling.go
@@ -155,7 +155,7 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
 			enc.Proofs = itx.Sidecar.Proofs
 		}
 	case *SetCodeTx:
-		enc.ChainID = (*hexutil.Big)(new(big.Int).SetUint64(itx.ChainID))
+		enc.ChainID = (*hexutil.Big)(itx.ChainID.ToBig())
 		enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
 		enc.To = tx.To()
 		enc.Gas = (*hexutil.Uint64)(&itx.Gas)
@@ -353,7 +353,11 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
 		if dec.ChainID == nil {
 			return errors.New("missing required field 'chainId' in transaction")
 		}
-		itx.ChainID = uint256.MustFromBig((*big.Int)(dec.ChainID))
+		var overflow bool
+		itx.ChainID, overflow = uint256.FromBig(dec.ChainID.ToInt())
+		if overflow {
+			return errors.New("'chainId' value overflows uint256")
+		}
 		if dec.Nonce == nil {
 			return errors.New("missing required field 'nonce' in transaction")
 		}
@@ -395,7 +399,6 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
 		itx.BlobHashes = dec.BlobVersionedHashes
 
 		// signature R
-		var overflow bool
 		if dec.R == nil {
 			return errors.New("missing required field 'r' in transaction")
 		}
@@ -432,7 +435,11 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
 		if dec.ChainID == nil {
 			return errors.New("missing required field 'chainId' in transaction")
 		}
-		itx.ChainID = dec.ChainID.ToInt().Uint64()
+		var overflow bool
+		itx.ChainID, overflow = uint256.FromBig(dec.ChainID.ToInt())
+		if overflow {
+			return errors.New("'chainId' value overflows uint256")
+		}
 		if dec.Nonce == nil {
 			return errors.New("missing required field 'nonce' in transaction")
 		}
@@ -470,7 +477,6 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
 		itx.AuthList = dec.AuthorizationList
 
 		// signature R
-		var overflow bool
 		if dec.R == nil {
 			return errors.New("missing required field 'r' in transaction")
 		}
diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go
index d72643b4a8..4d70f37bd3 100644
--- a/core/types/transaction_signing.go
+++ b/core/types/transaction_signing.go
@@ -219,7 +219,7 @@ func (s pragueSigner) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big
 	}
 	// Check that chain ID of tx matches the signer. We also accept ID zero here,
 	// because it indicates that the chain ID was not specified in the tx.
-	if txdata.ChainID != 0 && new(big.Int).SetUint64(txdata.ChainID).Cmp(s.chainId) != 0 {
+	if txdata.ChainID != nil && txdata.ChainID.CmpBig(s.chainId) != 0 {
 		return nil, nil, nil, fmt.Errorf("%w: have %d want %d", ErrInvalidChainId, txdata.ChainID, s.chainId)
 	}
 	R, S, _ = decodeSignature(sig)
diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go
index ce1f287caa..88251ab957 100644
--- a/core/types/tx_blob.go
+++ b/core/types/tx_blob.go
@@ -47,9 +47,9 @@ type BlobTx struct {
 	Sidecar *BlobTxSidecar `rlp:"-"`
 
 	// Signature values
-	V *uint256.Int `json:"v" gencodec:"required"`
-	R *uint256.Int `json:"r" gencodec:"required"`
-	S *uint256.Int `json:"s" gencodec:"required"`
+	V *uint256.Int
+	R *uint256.Int
+	S *uint256.Int
 }
 
 // BlobTxSidecar contains the blobs of a blob transaction.
diff --git a/core/types/tx_dynamic_fee.go b/core/types/tx_dynamic_fee.go
index 8b5b514fde..981755cf70 100644
--- a/core/types/tx_dynamic_fee.go
+++ b/core/types/tx_dynamic_fee.go
@@ -37,9 +37,9 @@ type DynamicFeeTx struct {
 	AccessList AccessList
 
 	// Signature values
-	V *big.Int `json:"v" gencodec:"required"`
-	R *big.Int `json:"r" gencodec:"required"`
-	S *big.Int `json:"s" gencodec:"required"`
+	V *big.Int
+	R *big.Int
+	S *big.Int
 }
 
 // copy creates a deep copy of the transaction data and initializes all fields.
diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go
index f14ae3bc9d..0fb5362c26 100644
--- a/core/types/tx_setcode.go
+++ b/core/types/tx_setcode.go
@@ -49,7 +49,7 @@ func AddressToDelegation(addr common.Address) []byte {
 // SetCodeTx implements the EIP-7702 transaction type which temporarily installs
 // the code at the signer's address.
 type SetCodeTx struct {
-	ChainID    uint64
+	ChainID    *uint256.Int
 	Nonce      uint64
 	GasTipCap  *uint256.Int // a.k.a. maxPriorityFeePerGas
 	GasFeeCap  *uint256.Int // a.k.a. maxFeePerGas
@@ -61,16 +61,16 @@ type SetCodeTx struct {
 	AuthList   []SetCodeAuthorization
 
 	// Signature values
-	V *uint256.Int `json:"v" gencodec:"required"`
-	R *uint256.Int `json:"r" gencodec:"required"`
-	S *uint256.Int `json:"s" gencodec:"required"`
+	V *uint256.Int
+	R *uint256.Int
+	S *uint256.Int
 }
 
 //go:generate go run github.com/fjl/gencodec -type SetCodeAuthorization -field-override authorizationMarshaling -out gen_authorization.go
 
 // SetCodeAuthorization is an authorization from an account to deploy code at its address.
 type SetCodeAuthorization struct {
-	ChainID uint64         `json:"chainId" gencodec:"required"`
+	ChainID uint256.Int    `json:"chainId" gencodec:"required"`
 	Address common.Address `json:"address" gencodec:"required"`
 	Nonce   uint64         `json:"nonce" gencodec:"required"`
 	V       uint8          `json:"yParity" gencodec:"required"`
@@ -80,7 +80,7 @@ type SetCodeAuthorization struct {
 
 // field type overrides for gencodec
 type authorizationMarshaling struct {
-	ChainID hexutil.Uint64
+	ChainID hexutil.U256
 	Nonce   hexutil.Uint64
 	V       hexutil.Uint64
 	R       hexutil.U256
@@ -180,7 +180,7 @@ func (tx *SetCodeTx) copy() TxData {
 
 // accessors for innerTx.
 func (tx *SetCodeTx) txType() byte           { return SetCodeTxType }
-func (tx *SetCodeTx) chainID() *big.Int      { return big.NewInt(int64(tx.ChainID)) }
+func (tx *SetCodeTx) chainID() *big.Int      { return tx.ChainID.ToBig() }
 func (tx *SetCodeTx) accessList() AccessList { return tx.AccessList }
 func (tx *SetCodeTx) data() []byte           { return tx.Data }
 func (tx *SetCodeTx) gas() uint64            { return tx.Gas }
@@ -207,7 +207,7 @@ func (tx *SetCodeTx) rawSignatureValues() (v, r, s *big.Int) {
 }
 
 func (tx *SetCodeTx) setSignatureValues(chainID, v, r, s *big.Int) {
-	tx.ChainID = chainID.Uint64()
+	tx.ChainID = uint256.MustFromBig(chainID)
 	tx.V.SetFromBig(v)
 	tx.R.SetFromBig(r)
 	tx.S.SetFromBig(s)
diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go
index a39a6666f4..175ac13a0f 100644
--- a/internal/ethapi/transaction_args.go
+++ b/internal/ethapi/transaction_args.go
@@ -503,7 +503,7 @@ func (args *TransactionArgs) ToTransaction(defaultType int) *types.Transaction {
 		}
 		data = &types.SetCodeTx{
 			To:         *args.To,
-			ChainID:    args.ChainID.ToInt().Uint64(),
+			ChainID:    uint256.MustFromBig(args.ChainID.ToInt()),
 			Nonce:      uint64(*args.Nonce),
 			Gas:        uint64(*args.Gas),
 			GasFeeCap:  uint256.MustFromBig((*big.Int)(args.MaxFeePerGas)),
diff --git a/tests/gen_stauthorization.go b/tests/gen_stauthorization.go
index fbafd6fdea..4f2c50bd9f 100644
--- a/tests/gen_stauthorization.go
+++ b/tests/gen_stauthorization.go
@@ -16,7 +16,7 @@ var _ = (*stAuthorizationMarshaling)(nil)
 // MarshalJSON marshals as JSON.
 func (s stAuthorization) MarshalJSON() ([]byte, error) {
 	type stAuthorization struct {
-		ChainID math.HexOrDecimal64
+		ChainID *math.HexOrDecimal256 `json:"chainId" gencodec:"required"`
 		Address common.Address        `json:"address" gencodec:"required"`
 		Nonce   math.HexOrDecimal64   `json:"nonce" gencodec:"required"`
 		V       math.HexOrDecimal64   `json:"v" gencodec:"required"`
@@ -24,7 +24,7 @@ func (s stAuthorization) MarshalJSON() ([]byte, error) {
 		S       *math.HexOrDecimal256 `json:"s" gencodec:"required"`
 	}
 	var enc stAuthorization
-	enc.ChainID = math.HexOrDecimal64(s.ChainID)
+	enc.ChainID = (*math.HexOrDecimal256)(s.ChainID)
 	enc.Address = s.Address
 	enc.Nonce = math.HexOrDecimal64(s.Nonce)
 	enc.V = math.HexOrDecimal64(s.V)
@@ -36,7 +36,7 @@ func (s stAuthorization) MarshalJSON() ([]byte, error) {
 // UnmarshalJSON unmarshals from JSON.
 func (s *stAuthorization) UnmarshalJSON(input []byte) error {
 	type stAuthorization struct {
-		ChainID *math.HexOrDecimal64
+		ChainID *math.HexOrDecimal256 `json:"chainId" gencodec:"required"`
 		Address *common.Address       `json:"address" gencodec:"required"`
 		Nonce   *math.HexOrDecimal64  `json:"nonce" gencodec:"required"`
 		V       *math.HexOrDecimal64  `json:"v" gencodec:"required"`
@@ -47,9 +47,10 @@ func (s *stAuthorization) UnmarshalJSON(input []byte) error {
 	if err := json.Unmarshal(input, &dec); err != nil {
 		return err
 	}
-	if dec.ChainID != nil {
-		s.ChainID = uint64(*dec.ChainID)
+	if dec.ChainID == nil {
+		return errors.New("missing required field 'chainId' for stAuthorization")
 	}
+	s.ChainID = (*big.Int)(dec.ChainID)
 	if dec.Address == nil {
 		return errors.New("missing required field 'address' for stAuthorization")
 	}
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index 6e66bbaa72..740ebd4afd 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -140,7 +140,7 @@ type stTransactionMarshaling struct {
 
 // Authorization is an authorization from an account to deploy code at it's address.
 type stAuthorization struct {
-	ChainID uint64
+	ChainID *big.Int       `json:"chainId" gencodec:"required"`
 	Address common.Address `json:"address" gencodec:"required"`
 	Nonce   uint64         `json:"nonce" gencodec:"required"`
 	V       uint8          `json:"v" gencodec:"required"`
@@ -150,7 +150,7 @@ type stAuthorization struct {
 
 // field type overrides for gencodec
 type stAuthorizationMarshaling struct {
-	ChainID math.HexOrDecimal64
+	ChainID *math.HexOrDecimal256
 	Nonce   math.HexOrDecimal64
 	V       math.HexOrDecimal64
 	R       *math.HexOrDecimal256
@@ -446,7 +446,7 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess
 		authList = make([]types.SetCodeAuthorization, len(tx.AuthorizationList))
 		for i, auth := range tx.AuthorizationList {
 			authList[i] = types.SetCodeAuthorization{
-				ChainID: auth.ChainID,
+				ChainID: *uint256.MustFromBig(auth.ChainID),
 				Address: auth.Address,
 				Nonce:   auth.Nonce,
 				V:       auth.V,