Skip to content

Commit

Permalink
Updates for Prague.
Browse files Browse the repository at this point in the history
  • Loading branch information
mcdee committed Feb 7, 2025
1 parent 32805bb commit b9b2cc3
Show file tree
Hide file tree
Showing 15 changed files with 1,274 additions and 29 deletions.
135 changes: 135 additions & 0 deletions spec/authorizationlistentry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright © 2025 Attestant Limited.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package spec

import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"strconv"

"github.com/attestantio/go-execution-client/types"
"github.com/attestantio/go-execution-client/util"
"github.com/pkg/errors"
)

// AuthorizationListEntry contains a single entry in an authorization list.
type AuthorizationListEntry struct {
Address types.Address
ChainID *big.Int
Nonce uint64
R *big.Int
S *big.Int
YParity *big.Int
}

// authorizationListEntryJSON is the spec representation of the struct.
type authorizationListEntryJSON struct {
Address string `json:"address"`
ChainID string `json:"chainId"`
Nonce string `json:"nonce"`
R string `json:"r"`
S string `json:"s"`
YParity string `json:"yParity,omitempty"`
}

// MarshalJSON implements json.Marshaler.
func (a *AuthorizationListEntry) MarshalJSON() ([]byte, error) {
return json.Marshal(&authorizationListEntryJSON{
Address: util.MarshalByteArray(a.Address[:]),
ChainID: util.MarshalBigInt(a.ChainID),
Nonce: util.MarshalUint64(a.Nonce),
R: util.MarshalBigInt(a.R),
S: util.MarshalBigInt(a.S),
YParity: util.MarshalUint64(a.YParity.Uint64()),
})
}

// UnmarshalJSON implements json.Unmarshaler.
func (a *AuthorizationListEntry) UnmarshalJSON(input []byte) error {
var data authorizationListEntryJSON
if err := json.Unmarshal(input, &data); err != nil {
return errors.Wrap(err, "invalid JSON")
}

return a.unpack(&data)
}

func (a *AuthorizationListEntry) unpack(data *authorizationListEntryJSON) error {
var success bool
var err error

if data.Address == "" {
return errors.New("address missing")
}
address, err := hex.DecodeString(util.PreUnmarshalHexString(data.Address))
if err != nil {
return errors.Wrap(err, "address invalid")
}
copy(a.Address[:], address)

if data.ChainID == "" {
return errors.New("chain id missing")
}
a.ChainID, success = new(big.Int).SetString(util.PreUnmarshalHexString(data.ChainID), 16)
if !success {
return errors.New("chain id invalid")
}

if data.Nonce == "" {
return errors.New("nonce missing")
}
a.Nonce, err = strconv.ParseUint(util.PreUnmarshalHexString(data.Nonce), 16, 64)
if err != nil {
return errors.Wrap(err, "nonce invalid")
}

if data.R == "" {
return errors.New("r missing")
}
a.R, success = new(big.Int).SetString(util.PreUnmarshalHexString(data.R), 16)
if !success {
return errors.New("r invalid")
}

if data.S == "" {
return errors.New("s missing")
}
a.S, success = new(big.Int).SetString(util.PreUnmarshalHexString(data.S), 16)
if !success {
return errors.New("s invalid")
}

if data.YParity == "" {
return errors.New("yParity missing")
}
a.YParity, success = new(big.Int).SetString(util.PreUnmarshalHexString(data.YParity), 16)
if !success {
return errors.New("yParity invalid")
}

return nil
}

// String returns a string version of the structure.
func (a *AuthorizationListEntry) String() string {
data, err := json.Marshal(a)
if err != nil {
return fmt.Sprintf("ERR: %v", err)
}

return string(bytes.TrimSuffix(data, []byte("\n")))
}
150 changes: 150 additions & 0 deletions spec/authorizationlistentry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright © 2025 Attestant Limited.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package spec_test

import (
"encoding/json"
"testing"

"github.com/attestantio/go-execution-client/spec"
"github.com/stretchr/testify/require"
)

func TestAuthorizationListEntryJSON(t *testing.T) {
tests := []struct {
name string
input []byte
err string
}{
{
name: "Empty",
err: "unexpected end of JSON input",
},
{
name: "JSONBad",
input: []byte("[]"),
err: "invalid JSON: json: cannot unmarshal array into Go value of type spec.authorizationListEntryJSON",
},
{
name: "ChainIDMissing",
input: []byte(`{"address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "chain id missing",
},
{
name: "ChainIDWrongType",
input: []byte(`{"chainId":true,"address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.chainId of type string",
},
{
name: "ChainIDInvalid",
input: []byte(`{"chainId":"true","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "chain id invalid",
},
{
name: "AddressMissing",
input: []byte(`{"chainId":"0x1a5887710","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "address missing",
},
{
name: "AddressWrongType",
input: []byte(`{"chainId":"0x1a5887710","address":true,"nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.address of type string",
},
{
name: "AddressInvalid",
input: []byte(`{"chainId":"0x1a5887710","address":"true","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "address invalid: encoding/hex: invalid byte: U+0074 't'",
},
{
name: "NonceMissing",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "nonce missing",
},
{
name: "NonceWrongType",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":true,"yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.nonce of type string",
},
{
name: "NonceInvalid",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"true","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "nonce invalid: strconv.ParseUint: parsing \"true\": invalid syntax",
},
{
name: "YParityMissing",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "yParity missing",
},
{
name: "YParityWrongType",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":true,"r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.yParity of type string",
},
{
name: "YParityInvalid",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"true","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "yParity invalid",
},
{
name: "RMissing",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "r missing",
},
{
name: "RWrongType",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":true,"s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.r of type string",
},
{
name: "RInvalid",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"true","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
err: "r invalid",
},
{
name: "SMissing",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd"}`),
err: "s missing",
},
{
name: "SWrongType",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":true}`),
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.s of type string",
},
{
name: "SInvalid",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"true"}`),
err: "s invalid",
},
{
name: "Good",
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var res spec.AuthorizationListEntry
err := json.Unmarshal(test.input, &res)
if test.err != "" {
require.EqualError(t, err, test.err)
} else {
require.NoError(t, err)
rt, err := json.Marshal(&res)
require.NoError(t, err)
require.JSONEq(t, string(test.input), string(rt))
require.JSONEq(t, string(test.input), res.String())
}
})
}
}
Loading

0 comments on commit b9b2cc3

Please sign in to comment.