diff --git a/common/hexutil/json.go b/common/hexutil/json.go
index 50db20811..e0ac98f52 100644
--- a/common/hexutil/json.go
+++ b/common/hexutil/json.go
@@ -23,6 +23,8 @@ import (
"math/big"
"reflect"
"strconv"
+
+ "github.com/holiman/uint256"
)
var (
@@ -30,6 +32,7 @@ var (
bigT = reflect.TypeOf((*Big)(nil))
uintT = reflect.TypeOf(Uint(0))
uint64T = reflect.TypeOf(Uint64(0))
+ u256T = reflect.TypeOf((*uint256.Int)(nil))
)
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
@@ -225,6 +228,48 @@ func (b *Big) UnmarshalGraphQL(input interface{}) error {
return err
}
+// U256 marshals/unmarshals as a JSON string with 0x prefix.
+// The zero value marshals as "0x0".
+type U256 uint256.Int
+
+// MarshalText implements encoding.TextMarshaler
+func (b U256) MarshalText() ([]byte, error) {
+ u256 := (*uint256.Int)(&b)
+ return []byte(u256.Hex()), nil
+}
+
+// UnmarshalJSON implements json.Unmarshaler.
+func (b *U256) UnmarshalJSON(input []byte) error {
+ // The uint256.Int.UnmarshalJSON method accepts "dec", "0xhex"; we must be
+ // more strict, hence we check string and invoke SetFromHex directly.
+ if !isString(input) {
+ return errNonString(u256T)
+ }
+ // The hex decoder needs to accept empty string ("") as '0', which uint256.Int
+ // would reject.
+ if len(input) == 2 {
+ (*uint256.Int)(b).Clear()
+ return nil
+ }
+ err := (*uint256.Int)(b).SetFromHex(string(input[1 : len(input)-1]))
+ if err != nil {
+ return &json.UnmarshalTypeError{Value: err.Error(), Type: u256T}
+ }
+ return nil
+}
+
+// UnmarshalText implements encoding.TextUnmarshaler
+func (b *U256) UnmarshalText(input []byte) error {
+ // The uint256.Int.UnmarshalText method accepts "dec", "0xhex"; we must be
+ // more strict, hence we check string and invoke SetFromHex directly.
+ return (*uint256.Int)(b).SetFromHex(string(input))
+}
+
+// String returns the hex encoding of b.
+func (b *U256) String() string {
+ return (*uint256.Int)(b).Hex()
+}
+
// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
// The zero value marshals as "0x0".
type Uint64 uint64
diff --git a/core/types/gen_authorization.go b/core/types/gen_authorization.go
new file mode 100644
index 000000000..f34c07fe5
--- /dev/null
+++ b/core/types/gen_authorization.go
@@ -0,0 +1,75 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package types
+
+import (
+ "encoding/json"
+ "errors"
+
+ "github.com/holiman/uint256"
+ "github.com/morph-l2/go-ethereum/common"
+ "github.com/morph-l2/go-ethereum/common/hexutil"
+)
+
+var _ = (*authorizationMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (s SetCodeAuthorization) MarshalJSON() ([]byte, error) {
+ type SetCodeAuthorization struct {
+ 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"`
+ R hexutil.U256 `json:"r" gencodec:"required"`
+ S hexutil.U256 `json:"s" gencodec:"required"`
+ }
+ var enc SetCodeAuthorization
+ enc.ChainID = hexutil.U256(s.ChainID)
+ enc.Address = s.Address
+ enc.Nonce = hexutil.Uint64(s.Nonce)
+ enc.V = hexutil.Uint64(s.V)
+ enc.R = hexutil.U256(s.R)
+ enc.S = hexutil.U256(s.S)
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (s *SetCodeAuthorization) UnmarshalJSON(input []byte) error {
+ type SetCodeAuthorization struct {
+ 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"`
+ R *hexutil.U256 `json:"r" gencodec:"required"`
+ S *hexutil.U256 `json:"s" gencodec:"required"`
+ }
+ var dec SetCodeAuthorization
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.ChainID == nil {
+ return errors.New("missing required field 'chainId' for SetCodeAuthorization")
+ }
+ s.ChainID = uint256.Int(*dec.ChainID)
+ if dec.Address == nil {
+ return errors.New("missing required field 'address' for SetCodeAuthorization")
+ }
+ s.Address = *dec.Address
+ if dec.Nonce == nil {
+ return errors.New("missing required field 'nonce' for SetCodeAuthorization")
+ }
+ s.Nonce = uint64(*dec.Nonce)
+ if dec.V == nil {
+ return errors.New("missing required field 'yParity' for SetCodeAuthorization")
+ }
+ s.V = uint8(*dec.V)
+ if dec.R == nil {
+ return errors.New("missing required field 'r' for SetCodeAuthorization")
+ }
+ s.R = uint256.Int(*dec.R)
+ if dec.S == nil {
+ return errors.New("missing required field 's' for SetCodeAuthorization")
+ }
+ s.S = uint256.Int(*dec.S)
+ return nil
+}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index 5c9c9ef6e..6dcb1cee8 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -51,6 +51,7 @@ const (
AccessListTxType = 0x01
DynamicFeeTxType = 0x02
BlobTxType = 0x03
+ SetCodeTxType = 0x04
L1MessageTxType = 0x7E
)
@@ -204,6 +205,8 @@ func (tx *Transaction) decodeTyped(b []byte) (TxData, error) {
inner = new(BlobTx)
case L1MessageTxType:
inner = new(L1MessageTx)
+ case SetCodeTxType:
+ inner = new(SetCodeTx)
default:
return nil, ErrTxTypeNotSupported
}
@@ -466,6 +469,38 @@ func (tx *Transaction) WithoutBlobTxSidecar() *Transaction {
return cpy
}
+// SetCodeAuthorizations returns the authorizations list of the transaction.
+func (tx *Transaction) SetCodeAuthorizations() []SetCodeAuthorization {
+ setcodetx, ok := tx.inner.(*SetCodeTx)
+ if !ok {
+ return nil
+ }
+ return setcodetx.AuthList
+}
+
+// SetCodeAuthorities returns a list of unique authorities from the
+// authorization list.
+func (tx *Transaction) SetCodeAuthorities() []common.Address {
+ setcodetx, ok := tx.inner.(*SetCodeTx)
+ if !ok {
+ return nil
+ }
+ var (
+ marks = make(map[common.Address]bool)
+ auths = make([]common.Address, 0, len(setcodetx.AuthList))
+ )
+ for _, auth := range setcodetx.AuthList {
+ if addr, err := auth.Authority(); err == nil {
+ if marks[addr] {
+ continue
+ }
+ marks[addr] = true
+ auths = append(auths, addr)
+ }
+ }
+ return auths
+}
+
// Hash returns the transaction hash.
func (tx *Transaction) Hash() common.Hash {
if hash := tx.hash.Load(); hash != nil {
diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go
index 1d95cfce6..4a0d68692 100644
--- a/core/types/transaction_marshalling.go
+++ b/core/types/transaction_marshalling.go
@@ -32,22 +32,23 @@ import (
type txJSON struct {
Type hexutil.Uint64 `json:"type"`
- ChainID *hexutil.Big `json:"chainId,omitempty"`
- Nonce *hexutil.Uint64 `json:"nonce"`
- To *common.Address `json:"to"`
- Gas *hexutil.Uint64 `json:"gas"`
- GasPrice *hexutil.Big `json:"gasPrice"`
- MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
- MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
- MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"`
- Value *hexutil.Big `json:"value"`
- Input *hexutil.Bytes `json:"input"`
- AccessList *AccessList `json:"accessList,omitempty"`
- BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
- V *hexutil.Big `json:"v"`
- R *hexutil.Big `json:"r"`
- S *hexutil.Big `json:"s"`
- YParity *hexutil.Uint64 `json:"yParity,omitempty"`
+ ChainID *hexutil.Big `json:"chainId,omitempty"`
+ Nonce *hexutil.Uint64 `json:"nonce"`
+ To *common.Address `json:"to"`
+ Gas *hexutil.Uint64 `json:"gas"`
+ GasPrice *hexutil.Big `json:"gasPrice"`
+ MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"`
+ MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"`
+ MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"`
+ Value *hexutil.Big `json:"value"`
+ Input *hexutil.Bytes `json:"input"`
+ AccessList *AccessList `json:"accessList,omitempty"`
+ BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
+ AuthorizationList []SetCodeAuthorization `json:"authorizationList,omitempty"`
+ V *hexutil.Big `json:"v"`
+ R *hexutil.Big `json:"r"`
+ S *hexutil.Big `json:"s"`
+ YParity *hexutil.Uint64 `json:"yParity,omitempty"`
// Blob transaction sidecar encoding:
Blobs []kzg4844.Blob `json:"blobs,omitempty"`
@@ -166,6 +167,23 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
enc.Commitments = itx.Sidecar.Commitments
enc.Proofs = itx.Sidecar.Proofs
}
+
+ case *SetCodeTx:
+ enc.ChainID = (*hexutil.Big)(itx.ChainID.ToBig())
+ enc.Nonce = (*hexutil.Uint64)(&itx.Nonce)
+ enc.To = tx.To()
+ enc.Gas = (*hexutil.Uint64)(&itx.Gas)
+ enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap.ToBig())
+ enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap.ToBig())
+ enc.Value = (*hexutil.Big)(itx.Value.ToBig())
+ enc.Input = (*hexutil.Bytes)(&itx.Data)
+ enc.AccessList = &itx.AccessList
+ enc.AuthorizationList = itx.AuthList
+ enc.V = (*hexutil.Big)(itx.V.ToBig())
+ enc.R = (*hexutil.Big)(itx.R.ToBig())
+ enc.S = (*hexutil.Big)(itx.S.ToBig())
+ yparity := itx.V.Uint64()
+ enc.YParity = (*hexutil.Uint64)(&yparity)
}
return json.Marshal(&enc)
}
@@ -449,6 +467,84 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
}
itx.Sender = *dec.Sender
+ case SetCodeTxType:
+ var itx SetCodeTx
+ inner = &itx
+ if dec.ChainID == nil {
+ return errors.New("missing required field 'chainId' in transaction")
+ }
+ 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")
+ }
+ itx.Nonce = uint64(*dec.Nonce)
+ if dec.To == nil {
+ return errors.New("missing required field 'to' in transaction")
+ }
+ itx.To = *dec.To
+ if dec.Gas == nil {
+ return errors.New("missing required field 'gas' for txdata")
+ }
+ itx.Gas = uint64(*dec.Gas)
+ if dec.MaxPriorityFeePerGas == nil {
+ return errors.New("missing required field 'maxPriorityFeePerGas' for txdata")
+ }
+ itx.GasTipCap = uint256.MustFromBig((*big.Int)(dec.MaxPriorityFeePerGas))
+ if dec.MaxFeePerGas == nil {
+ return errors.New("missing required field 'maxFeePerGas' for txdata")
+ }
+ itx.GasFeeCap = uint256.MustFromBig((*big.Int)(dec.MaxFeePerGas))
+ if dec.Value == nil {
+ return errors.New("missing required field 'value' in transaction")
+ }
+ itx.Value = uint256.MustFromBig((*big.Int)(dec.Value))
+ if dec.Input == nil {
+ return errors.New("missing required field 'input' in transaction")
+ }
+ itx.Data = *dec.Input
+ if dec.AccessList != nil {
+ itx.AccessList = *dec.AccessList
+ }
+ if dec.AuthorizationList == nil {
+ return errors.New("missing required field 'authorizationList' in transaction")
+ }
+ itx.AuthList = dec.AuthorizationList
+
+ // signature R
+ if dec.R == nil {
+ return errors.New("missing required field 'r' in transaction")
+ }
+ itx.R, overflow = uint256.FromBig((*big.Int)(dec.R))
+ if overflow {
+ return errors.New("'r' value overflows uint256")
+ }
+ // signature S
+ if dec.S == nil {
+ return errors.New("missing required field 's' in transaction")
+ }
+ itx.S, overflow = uint256.FromBig((*big.Int)(dec.S))
+ if overflow {
+ return errors.New("'s' value overflows uint256")
+ }
+ // signature V
+ vbig, err := dec.yParityValue()
+ if err != nil {
+ return err
+ }
+ itx.V, overflow = uint256.FromBig(vbig)
+ if overflow {
+ return errors.New("'v' value overflows uint256")
+ }
+ if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 {
+ if err := sanityCheckSignature(vbig, itx.R.ToBig(), itx.S.ToBig(), false); err != nil {
+ return err
+ }
+ }
+
default:
return ErrTxTypeNotSupported
}
diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go
new file mode 100644
index 000000000..e6e990850
--- /dev/null
+++ b/core/types/tx_setcode.go
@@ -0,0 +1,242 @@
+// Copyright 2024 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "bytes"
+ "crypto/ecdsa"
+ "errors"
+ "math/big"
+
+ "github.com/holiman/uint256"
+ "github.com/morph-l2/go-ethereum/common"
+ "github.com/morph-l2/go-ethereum/common/hexutil"
+ "github.com/morph-l2/go-ethereum/crypto"
+ "github.com/morph-l2/go-ethereum/rlp"
+)
+
+// DelegationPrefix is used by code to denote the account is delegating to
+// another account.
+var DelegationPrefix = []byte{0xef, 0x01, 0x00}
+
+// ParseDelegation tries to parse the address from a delegation slice.
+func ParseDelegation(b []byte) (common.Address, bool) {
+ if len(b) != 23 || !bytes.HasPrefix(b, DelegationPrefix) {
+ return common.Address{}, false
+ }
+ return common.BytesToAddress(b[len(DelegationPrefix):]), true
+}
+
+// AddressToDelegation adds the delegation prefix to the specified address.
+func AddressToDelegation(addr common.Address) []byte {
+ return append(DelegationPrefix, addr.Bytes()...)
+}
+
+// SetCodeTx implements the EIP-7702 transaction type which temporarily installs
+// the code at the signer's address.
+type SetCodeTx struct {
+ ChainID *uint256.Int
+ Nonce uint64
+ GasTipCap *uint256.Int // a.k.a. maxPriorityFeePerGas
+ GasFeeCap *uint256.Int // a.k.a. maxFeePerGas
+ Gas uint64
+ To common.Address
+ Value *uint256.Int
+ Data []byte
+ AccessList AccessList
+ AuthList []SetCodeAuthorization
+
+ // Signature values
+ 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 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"`
+ R uint256.Int `json:"r" gencodec:"required"`
+ S uint256.Int `json:"s" gencodec:"required"`
+}
+
+// field type overrides for gencodec
+type authorizationMarshaling struct {
+ ChainID hexutil.U256
+ Nonce hexutil.Uint64
+ V hexutil.Uint64
+ R hexutil.U256
+ S hexutil.U256
+}
+
+// SignSetCode creates a signed the SetCode authorization.
+func SignSetCode(prv *ecdsa.PrivateKey, auth SetCodeAuthorization) (SetCodeAuthorization, error) {
+ sighash := auth.sigHash()
+ sig, err := crypto.Sign(sighash[:], prv)
+ if err != nil {
+ return SetCodeAuthorization{}, err
+ }
+ r, s, _ := decodeSignature(sig)
+ return SetCodeAuthorization{
+ ChainID: auth.ChainID,
+ Address: auth.Address,
+ Nonce: auth.Nonce,
+ V: sig[64],
+ R: *uint256.MustFromBig(r),
+ S: *uint256.MustFromBig(s),
+ }, nil
+}
+
+func (a *SetCodeAuthorization) sigHash() common.Hash {
+ return prefixedRlpHash(0x05, []any{
+ a.ChainID,
+ a.Address,
+ a.Nonce,
+ })
+}
+
+// Authority recovers the the authorizing account of an authorization.
+func (a *SetCodeAuthorization) Authority() (common.Address, error) {
+ sighash := a.sigHash()
+ if !crypto.ValidateSignatureValues(a.V, a.R.ToBig(), a.S.ToBig(), true) {
+ return common.Address{}, ErrInvalidSig
+ }
+ // encode the signature in uncompressed format
+ var sig [crypto.SignatureLength]byte
+ a.R.WriteToSlice(sig[:32])
+ a.S.WriteToSlice(sig[32:64])
+ sig[64] = a.V
+ // recover the public key from the signature
+ pub, err := crypto.Ecrecover(sighash[:], sig[:])
+ if err != nil {
+ return common.Address{}, err
+ }
+ if len(pub) == 0 || pub[0] != 4 {
+ return common.Address{}, errors.New("invalid public key")
+ }
+ var addr common.Address
+ copy(addr[:], crypto.Keccak256(pub[1:])[12:])
+ return addr, nil
+}
+
+// copy creates a deep copy of the transaction data and initializes all fields.
+func (tx *SetCodeTx) copy() TxData {
+ cpy := &SetCodeTx{
+ Nonce: tx.Nonce,
+ To: tx.To,
+ Data: common.CopyBytes(tx.Data),
+ Gas: tx.Gas,
+ // These are copied below.
+ AccessList: make(AccessList, len(tx.AccessList)),
+ AuthList: make([]SetCodeAuthorization, len(tx.AuthList)),
+ Value: new(uint256.Int),
+ ChainID: new(uint256.Int),
+ GasTipCap: new(uint256.Int),
+ GasFeeCap: new(uint256.Int),
+ V: new(uint256.Int),
+ R: new(uint256.Int),
+ S: new(uint256.Int),
+ }
+ copy(cpy.AccessList, tx.AccessList)
+ copy(cpy.AuthList, tx.AuthList)
+ if tx.Value != nil {
+ cpy.Value.Set(tx.Value)
+ }
+ if tx.ChainID != nil {
+ cpy.ChainID.Set(tx.ChainID)
+ }
+ if tx.GasTipCap != nil {
+ cpy.GasTipCap.Set(tx.GasTipCap)
+ }
+ if tx.GasFeeCap != nil {
+ cpy.GasFeeCap.Set(tx.GasFeeCap)
+ }
+ if tx.V != nil {
+ cpy.V.Set(tx.V)
+ }
+ if tx.R != nil {
+ cpy.R.Set(tx.R)
+ }
+ if tx.S != nil {
+ cpy.S.Set(tx.S)
+ }
+ return cpy
+}
+
+// accessors for innerTx.
+func (tx *SetCodeTx) txType() byte { return SetCodeTxType }
+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 }
+func (tx *SetCodeTx) gasFeeCap() *big.Int { return tx.GasFeeCap.ToBig() }
+func (tx *SetCodeTx) gasTipCap() *big.Int { return tx.GasTipCap.ToBig() }
+func (tx *SetCodeTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() }
+func (tx *SetCodeTx) value() *big.Int { return tx.Value.ToBig() }
+func (tx *SetCodeTx) nonce() uint64 { return tx.Nonce }
+func (tx *SetCodeTx) to() *common.Address { tmp := tx.To; return &tmp }
+
+func (tx *SetCodeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int {
+ if baseFee == nil {
+ return dst.Set(tx.GasFeeCap.ToBig())
+ }
+ tip := dst.Sub(tx.GasFeeCap.ToBig(), baseFee)
+ if tip.Cmp(tx.GasTipCap.ToBig()) > 0 {
+ tip.Set(tx.GasTipCap.ToBig())
+ }
+ return tip.Add(tip, baseFee)
+}
+
+func (tx *SetCodeTx) rawSignatureValues() (v, r, s *big.Int) {
+ return tx.V.ToBig(), tx.R.ToBig(), tx.S.ToBig()
+}
+
+func (tx *SetCodeTx) setSignatureValues(chainID, v, r, s *big.Int) {
+ tx.ChainID = uint256.MustFromBig(chainID)
+ tx.V.SetFromBig(v)
+ tx.R.SetFromBig(r)
+ tx.S.SetFromBig(s)
+}
+
+func (tx *SetCodeTx) encode(b *bytes.Buffer) error {
+ return rlp.Encode(b, tx)
+}
+
+func (tx *SetCodeTx) decode(input []byte) error {
+ return rlp.DecodeBytes(input, tx)
+}
+
+func (tx *SetCodeTx) sigHash(chainID *big.Int) common.Hash {
+ return prefixedRlpHash(
+ SetCodeTxType,
+ []any{
+ chainID,
+ tx.Nonce,
+ tx.GasTipCap,
+ tx.GasFeeCap,
+ tx.Gas,
+ tx.To,
+ tx.Value,
+ tx.Data,
+ tx.AccessList,
+ tx.AuthList,
+ })
+}
diff --git a/core/types/tx_setcode_test.go b/core/types/tx_setcode_test.go
new file mode 100644
index 000000000..06c1c82d7
--- /dev/null
+++ b/core/types/tx_setcode_test.go
@@ -0,0 +1,70 @@
+// Copyright 2024 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see .
+
+package types
+
+import (
+ "testing"
+
+ "github.com/morph-l2/go-ethereum/common"
+)
+
+// TestParseDelegation tests a few possible delegation designator values and
+// ensures they are parsed correctly.
+func TestParseDelegation(t *testing.T) {
+ addr := common.Address{0x42}
+ for _, tt := range []struct {
+ val []byte
+ want *common.Address
+ }{
+ { // simple correct delegation
+ val: append(DelegationPrefix, addr.Bytes()...),
+ want: &addr,
+ },
+ { // wrong address size
+ val: append(DelegationPrefix, addr.Bytes()[0:19]...),
+ },
+ { // short address
+ val: append(DelegationPrefix, 0x42),
+ },
+ { // long address
+ val: append(append(DelegationPrefix, addr.Bytes()...), 0x42),
+ },
+ { // wrong prefix size
+ val: append(DelegationPrefix[:2], addr.Bytes()...),
+ },
+ { // wrong prefix
+ val: append([]byte{0xef, 0x01, 0x01}, addr.Bytes()...),
+ },
+ { // wrong prefix
+ val: append([]byte{0xef, 0x00, 0x00}, addr.Bytes()...),
+ },
+ { // no prefix
+ val: addr.Bytes(),
+ },
+ { // no address
+ val: DelegationPrefix,
+ },
+ } {
+ got, ok := ParseDelegation(tt.val)
+ if ok && tt.want == nil {
+ t.Fatalf("expected fail, got %s", got.Hex())
+ }
+ if !ok && tt.want != nil {
+ t.Fatalf("failed to parse, want %s", tt.want.Hex())
+ }
+ }
+}
diff --git a/params/version.go b/params/version.go
index b161be4fb..a602cb1b1 100644
--- a/params/version.go
+++ b/params/version.go
@@ -24,7 +24,7 @@ import (
const (
VersionMajor = 2 // Major version component of the current release
VersionMinor = 0 // Minor version component of the current release
- VersionPatch = 3 // Patch version component of the current release
+ VersionPatch = 5 // Patch version component of the current release
VersionMeta = "mainnet" // Version metadata to append to the version string
)