Skip to content

Commit

Permalink
EIP-7702 authorization: limit chain_id to uint64 and y_parity to uint8 (
Browse files Browse the repository at this point in the history
  • Loading branch information
yperbasis authored Nov 7, 2024
1 parent 5bc53cc commit f475cc4
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 99 deletions.
6 changes: 5 additions & 1 deletion cmd/evm/internal/t8ntool/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,11 @@ func getTransaction(txJson jsonrpc.RPCTransaction) (types.Transaction, error) {

auths := make([]types.Authorization, 0)
for _, auth := range *txJson.Authorizations {
auths = append(auths, auth.ToAuthorization())
a, err := auth.ToAuthorization()
if err != nil {
return nil, err
}
auths = append(auths, a)
}

return &types.SetCodeTransaction{
Expand Down
2 changes: 1 addition & 1 deletion core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ func (st *StateTransition) TransitionDb(refunds bool, gasBailout bool) (*evmtype
data.Reset()

// 1. chainId check
if auth.ChainID != nil && !auth.ChainID.IsZero() && auth.ChainID.CmpBig(st.evm.ChainRules().ChainID) != 0 {
if auth.ChainID != 0 && (!rules.ChainID.IsUint64() || rules.ChainID.Uint64() != auth.ChainID) {
log.Debug("invalid chainID, skipping", "chainId", auth.ChainID, "auth index", i)
continue
}
Expand Down
60 changes: 27 additions & 33 deletions core/types/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,36 @@ import (
"github.com/holiman/uint256"

libcommon "github.com/ledgerwatch/erigon-lib/common"
libcrypto "github.com/ledgerwatch/erigon-lib/crypto"

"github.com/ledgerwatch/erigon-lib/common/length"
libcrypto "github.com/ledgerwatch/erigon-lib/crypto"
rlp2 "github.com/ledgerwatch/erigon-lib/rlp"
"github.com/ledgerwatch/erigon/common/u256"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rlp"
)

type Authorization struct {
ChainID *uint256.Int `json:"chainId"`
Address libcommon.Address `json:"address"`
Nonce uint64 `json:"nonce"`
V uint256.Int `json:"v"`
R uint256.Int `json:"r"`
S uint256.Int `json:"s"`
ChainID uint64
Address libcommon.Address
Nonce uint64
YParity uint8
R uint256.Int
S uint256.Int
}

func (ath *Authorization) copy() *Authorization {
return &Authorization{
ChainID: ath.ChainID,
Address: ath.Address,
Nonce: ath.Nonce,
V: *ath.V.Clone(),
YParity: ath.YParity,
R: *ath.R.Clone(),
S: *ath.S.Clone(),
}
}

func (ath *Authorization) RecoverSigner(data *bytes.Buffer, b []byte) (*libcommon.Address, error) {
authLen := 1 + rlp.Uint256LenExcludingHead(ath.ChainID)
authLen := rlp2.U64Len(ath.ChainID)
authLen += (1 + length.Addr)
authLen += rlp2.U64Len(ath.Nonce)

Expand All @@ -49,7 +47,7 @@ func (ath *Authorization) RecoverSigner(data *bytes.Buffer, b []byte) (*libcommo
}

// chainId, address, nonce
if err := ath.ChainID.EncodeRLP(data); err != nil {
if err := rlp.EncodeInt(ath.ChainID, data, b); err != nil {
return nil, err
}

Expand All @@ -72,19 +70,12 @@ func (ath *Authorization) RecoverSigner(data *bytes.Buffer, b []byte) (*libcommo
copy(sig[64-len(s):64], s)

if ath.Nonce == 1<<64-1 {
return nil, errors.New("Failed assertion: auth.nonce < 2**64 - 1")
}
if _, overflow := ath.ChainID.Uint64WithOverflow(); overflow {
return nil, errors.New("Failed assertion: auth.chain_id < 2**64")
}
if ath.V.GtUint64(1 << 8) {
return nil, errors.New("Failed assertion: auth.y_parity < 2**8")
return nil, errors.New("failed assertion: auth.nonce < 2**64 - 1")
}

if ath.V.Eq(u256.Num0) || ath.V.Eq(u256.Num1) {
sig[64] = byte(ath.V.Uint64())
if ath.YParity == 0 || ath.YParity == 1 {
sig[64] = ath.YParity
} else {
return nil, fmt.Errorf("invalid v value: %d", ath.V.Uint64())
return nil, fmt.Errorf("invalid y parity value: %d", ath.YParity)
}

if !libcrypto.TransactionSignatureIsValid(sig[64], &ath.R, &ath.S, false /* allowPreEip2s */) {
Expand All @@ -105,11 +96,11 @@ func (ath *Authorization) RecoverSigner(data *bytes.Buffer, b []byte) (*libcommo
}

func authorizationSize(auth Authorization) (authLen int) {
authLen = 1 + rlp.Uint256LenExcludingHead(auth.ChainID)
authLen = rlp2.U64Len(auth.ChainID)
authLen += rlp2.U64Len(auth.Nonce)
authLen += (1 + length.Addr)

authLen += (1 + rlp.Uint256LenExcludingHead(&auth.V)) + (1 + rlp.Uint256LenExcludingHead(&auth.R)) + (1 + rlp.Uint256LenExcludingHead(&auth.S))
authLen += rlp2.U64Len(uint64(auth.YParity)) + (1 + rlp.Uint256LenExcludingHead(&auth.R)) + (1 + rlp.Uint256LenExcludingHead(&auth.S))

return
}
Expand All @@ -134,10 +125,9 @@ func decodeAuthorizations(auths *[]Authorization, s *rlp.Stream) error {
auth := Authorization{}

// chainId
if b, err = s.Uint256Bytes(); err != nil {
if auth.ChainID, err = s.Uint(); err != nil {
return err
}
auth.ChainID = new(uint256.Int).SetBytes(b)

// address
if b, err = s.Bytes(); err != nil {
Expand All @@ -154,11 +144,15 @@ func decodeAuthorizations(auths *[]Authorization, s *rlp.Stream) error {
return err
}

// v
if b, err = s.Uint256Bytes(); err != nil {
// yParity
var yParity uint64
if yParity, err = s.Uint(); err != nil {
return err
}
auth.V.SetBytes(b)
if yParity >= 1<<8 {
return fmt.Errorf("authorizations: y parity it too big: %d", yParity)
}
auth.YParity = uint8(yParity)

// r
if b, err = s.Uint256Bytes(); err != nil {
Expand Down Expand Up @@ -197,7 +191,7 @@ func encodeAuthorizations(authorizations []Authorization, w io.Writer, b []byte)
}

// 1. encode ChainId
if err := auth.ChainID.EncodeRLP(w); err != nil {
if err := rlp.EncodeInt(auth.ChainID, w, b); err != nil {
return err
}
// 2. encode Address
Expand All @@ -208,8 +202,8 @@ func encodeAuthorizations(authorizations []Authorization, w io.Writer, b []byte)
if err := rlp.EncodeInt(auth.Nonce, w, b); err != nil {
return err
}
// 4. encode V, R, S
if err := auth.V.EncodeRLP(w); err != nil {
// 4. encode YParity, R, S
if err := rlp.EncodeInt(uint64(auth.YParity), w, b); err != nil {
return err
}
if err := auth.R.EncodeRLP(w); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions core/types/encdec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,10 @@ func (tr *TRand) RandAuthorizations(size int) []Authorization {
auths := make([]Authorization, size)
for i := 0; i < size; i++ {
auths[i] = Authorization{
ChainID: uint256.NewInt(*tr.RandUint64()),
ChainID: *tr.RandUint64(),
Address: tr.RandAddress(),
Nonce: *tr.RandUint64(),
V: *uint256.NewInt(*tr.RandUint64()),
YParity: uint8(*tr.RandUint64()),
R: *uint256.NewInt(*tr.RandUint64()),
S: *uint256.NewInt(*tr.RandUint64()),
}
Expand Down
50 changes: 31 additions & 19 deletions core/types/transaction_marshalling.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,39 +51,47 @@ type txJSON struct {
}

type JsonAuthorization struct {
ChainID *hexutil.Big `json:"chainId"`
ChainID hexutil.Uint64 `json:"chainId"`
Address libcommon.Address `json:"address"`
Nonce uint64 `json:"nonce"`
V hexutil.Big `json:"v"`
Nonce hexutil.Uint64 `json:"nonce"`
V hexutil.Uint64 `json:"v"`
R hexutil.Big `json:"r"`
S hexutil.Big `json:"s"`
}

func (a JsonAuthorization) FromAuthorization(authorization Authorization) JsonAuthorization {
chainId := hexutil.Big(*authorization.ChainID.ToBig())
a.ChainID = &chainId
a.ChainID = (hexutil.Uint64)(authorization.ChainID)
a.Address = authorization.Address
a.Nonce = authorization.Nonce
a.Nonce = (hexutil.Uint64)(authorization.Nonce)

a.V = hexutil.Big(*authorization.V.ToBig())
a.V = (hexutil.Uint64)(authorization.YParity)
a.R = hexutil.Big(*authorization.R.ToBig())
a.S = hexutil.Big(*authorization.S.ToBig())
return a
}

func (a JsonAuthorization) ToAuthorization() Authorization {
v, _ := uint256.FromBig((*big.Int)(&a.V))
r, _ := uint256.FromBig((*big.Int)(&a.R))
s, _ := uint256.FromBig((*big.Int)(&a.S))
chainId, _ := uint256.FromBig((*big.Int)(a.ChainID))
return Authorization{
ChainID: chainId,
func (a JsonAuthorization) ToAuthorization() (Authorization, error) {
auth := Authorization{
ChainID: a.ChainID.Uint64(),
Address: a.Address,
Nonce: a.Nonce,
V: *v,
R: *r,
S: *s,
Nonce: a.Nonce.Uint64(),
}
yParity := a.V.Uint64()
if yParity >= 1<<8 {
return auth, errors.New("y parity in authorization does not fit in 8 bits")
}
auth.YParity = uint8(yParity)
r, overflow := uint256.FromBig((*big.Int)(&a.R))
if overflow {
return auth, errors.New("r in authorization does not fit in 256 bits")
}
auth.R = *r
s, overflow := uint256.FromBig((*big.Int)(&a.S))
if overflow {
return auth, errors.New("s in authorization does not fit in 256 bits")
}
auth.S = *s
return auth, nil
}

func (tx *LegacyTx) MarshalJSON() ([]byte, error) {
Expand Down Expand Up @@ -471,7 +479,11 @@ func (tx *SetCodeTransaction) UnmarshalJSON(input []byte) error {
}
tx.Authorizations = make([]Authorization, len(*dec.Authorizations))
for i, auth := range *dec.Authorizations {
tx.Authorizations[i] = auth.ToAuthorization()
var err error
tx.Authorizations[i], err = auth.ToAuthorization()
if err != nil {
return err
}
}
return nil
}
Expand Down
9 changes: 8 additions & 1 deletion erigon-lib/types/txn.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,9 @@ func parseSignature(payload []byte, pos int, legacy bool, cfgChainId *uint256.In
}
}
} else {
if !sig.V.LtUint64(1 << 8) {
return 0, 0, fmt.Errorf("v is too big: %s", &sig.V)
}
yParity = byte(sig.V.Uint64())
}

Expand Down Expand Up @@ -512,6 +515,10 @@ func (ctx *TxParseContext) parseTransactionBody(payload []byte, pos, p0 int, slo
if err != nil {
return 0, fmt.Errorf("%w: authorization chainId: %s", ErrParseTxn, err) //nolint
}
if !sig.ChainID.IsUint64() {
// https://github.com/ethereum/EIPs/pull/8929
return 0, fmt.Errorf("%w: authorization chainId is too big: %s", ErrParseTxn, &sig.ChainID)
}
p2, err = rlp.StringOfLen(payload, p2, 20) // address
if err != nil {
return 0, fmt.Errorf("%w: authorization address: %s", ErrParseTxn, err) //nolint
Expand Down Expand Up @@ -585,7 +592,7 @@ func (ctx *TxParseContext) parseTransactionBody(payload []byte, pos, p0 int, slo
}
} else {
if ctx.Signature.V.GtUint64(1) {
return 0, fmt.Errorf("%w: v is loo large: %s", ErrParseTxn, &ctx.Signature.V)
return 0, fmt.Errorf("%w: v is too big: %s", ErrParseTxn, &ctx.Signature.V)
}
}

Expand Down
10 changes: 1 addition & 9 deletions erigon-lib/types/txn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,13 +405,5 @@ func TestSetCodeTxParsingWithLargeAuthorizationValues(t *testing.T) {
assert.Equal(t, SetCodeTxType, txType)

_, err = ctx.ParseTransaction(bodyRlx, 0, &tx, nil, false /* hasEnvelope */, false, nil)
require.NoError(t, err)
assert.Equal(t, SetCodeTxType, tx.Type)
require.Equal(t, 1, len(tx.Authorizations))

maxUint256 := new(uint256.Int).SetAllOne()
assert.True(t, tx.Authorizations[0].ChainID.Eq(maxUint256))
assert.True(t, tx.Authorizations[0].V.Eq(maxUint256))
assert.True(t, tx.Authorizations[0].R.Eq(maxUint256))
assert.True(t, tx.Authorizations[0].S.Eq(maxUint256))
assert.ErrorContains(t, err, "chainId is too big")
}
49 changes: 16 additions & 33 deletions tests/state_test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,38 +86,18 @@ type stPostState struct {
}

type stTransaction struct {
GasPrice *math.HexOrDecimal256 `json:"gasPrice"`
MaxFeePerGas *math.HexOrDecimal256 `json:"maxFeePerGas"`
MaxPriorityFeePerGas *math.HexOrDecimal256 `json:"maxPriorityFeePerGas"`
Nonce math.HexOrDecimal64 `json:"nonce"`
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
PrivateKey hexutility.Bytes `json:"secretKey"`
To string `json:"to"`
Data []string `json:"data"`
Value []string `json:"value"`
AccessLists []*types2.AccessList `json:"accessLists,omitempty"`
BlobGasFeeCap *math.HexOrDecimal256 `json:"maxFeePerBlobGas,omitempty"`
Authorizations []stAuthorization `json:"authorizationList,omitempty"`
}

type stAuthorization struct {
ChainID *math.HexOrDecimal256 `json:"chainId"`
Address string `json:"address"`
Nonce *math.HexOrDecimal256 `json:"nonce"`
V *math.HexOrDecimal256 `json:"v"`
R *math.HexOrDecimal256 `json:"r"`
S *math.HexOrDecimal256 `json:"s"`
}

func (a *stAuthorization) ToAuthorization() types.Authorization {
return types.Authorization{
ChainID: uint256.MustFromBig((*big.Int)(a.ChainID)),
Address: libcommon.HexToAddress(a.Address),
Nonce: ((*big.Int)(a.Nonce)).Uint64(),
V: *uint256.MustFromBig((*big.Int)(a.V)),
R: *uint256.MustFromBig((*big.Int)(a.R)),
S: *uint256.MustFromBig((*big.Int)(a.S)),
}
GasPrice *math.HexOrDecimal256 `json:"gasPrice"`
MaxFeePerGas *math.HexOrDecimal256 `json:"maxFeePerGas"`
MaxPriorityFeePerGas *math.HexOrDecimal256 `json:"maxPriorityFeePerGas"`
Nonce math.HexOrDecimal64 `json:"nonce"`
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
PrivateKey hexutility.Bytes `json:"secretKey"`
To string `json:"to"`
Data []string `json:"data"`
Value []string `json:"value"`
AccessLists []*types2.AccessList `json:"accessLists,omitempty"`
BlobGasFeeCap *math.HexOrDecimal256 `json:"maxFeePerBlobGas,omitempty"`
Authorizations []types.JsonAuthorization `json:"authorizationList,omitempty"`
}

//go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
Expand Down Expand Up @@ -506,7 +486,10 @@ func toMessage(tx stTransaction, ps stPostState, baseFee *big.Int) (core.Message
if len(tx.Authorizations) > 0 {
authorizations := make([]types.Authorization, len(tx.Authorizations))
for i, auth := range tx.Authorizations {
authorizations[i] = auth.ToAuthorization()
authorizations[i], err = auth.ToAuthorization()
if err != nil {
return nil, err
}
}
msg.SetAuthorizations(authorizations)
}
Expand Down

0 comments on commit f475cc4

Please sign in to comment.