Skip to content

Commit b9b2cc3

Browse files
committed
Updates for Prague.
1 parent 32805bb commit b9b2cc3

15 files changed

+1274
-29
lines changed

spec/authorizationlistentry.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright © 2025 Attestant Limited.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package spec
15+
16+
import (
17+
"bytes"
18+
"encoding/hex"
19+
"encoding/json"
20+
"fmt"
21+
"math/big"
22+
"strconv"
23+
24+
"github.com/attestantio/go-execution-client/types"
25+
"github.com/attestantio/go-execution-client/util"
26+
"github.com/pkg/errors"
27+
)
28+
29+
// AuthorizationListEntry contains a single entry in an authorization list.
30+
type AuthorizationListEntry struct {
31+
Address types.Address
32+
ChainID *big.Int
33+
Nonce uint64
34+
R *big.Int
35+
S *big.Int
36+
YParity *big.Int
37+
}
38+
39+
// authorizationListEntryJSON is the spec representation of the struct.
40+
type authorizationListEntryJSON struct {
41+
Address string `json:"address"`
42+
ChainID string `json:"chainId"`
43+
Nonce string `json:"nonce"`
44+
R string `json:"r"`
45+
S string `json:"s"`
46+
YParity string `json:"yParity,omitempty"`
47+
}
48+
49+
// MarshalJSON implements json.Marshaler.
50+
func (a *AuthorizationListEntry) MarshalJSON() ([]byte, error) {
51+
return json.Marshal(&authorizationListEntryJSON{
52+
Address: util.MarshalByteArray(a.Address[:]),
53+
ChainID: util.MarshalBigInt(a.ChainID),
54+
Nonce: util.MarshalUint64(a.Nonce),
55+
R: util.MarshalBigInt(a.R),
56+
S: util.MarshalBigInt(a.S),
57+
YParity: util.MarshalUint64(a.YParity.Uint64()),
58+
})
59+
}
60+
61+
// UnmarshalJSON implements json.Unmarshaler.
62+
func (a *AuthorizationListEntry) UnmarshalJSON(input []byte) error {
63+
var data authorizationListEntryJSON
64+
if err := json.Unmarshal(input, &data); err != nil {
65+
return errors.Wrap(err, "invalid JSON")
66+
}
67+
68+
return a.unpack(&data)
69+
}
70+
71+
func (a *AuthorizationListEntry) unpack(data *authorizationListEntryJSON) error {
72+
var success bool
73+
var err error
74+
75+
if data.Address == "" {
76+
return errors.New("address missing")
77+
}
78+
address, err := hex.DecodeString(util.PreUnmarshalHexString(data.Address))
79+
if err != nil {
80+
return errors.Wrap(err, "address invalid")
81+
}
82+
copy(a.Address[:], address)
83+
84+
if data.ChainID == "" {
85+
return errors.New("chain id missing")
86+
}
87+
a.ChainID, success = new(big.Int).SetString(util.PreUnmarshalHexString(data.ChainID), 16)
88+
if !success {
89+
return errors.New("chain id invalid")
90+
}
91+
92+
if data.Nonce == "" {
93+
return errors.New("nonce missing")
94+
}
95+
a.Nonce, err = strconv.ParseUint(util.PreUnmarshalHexString(data.Nonce), 16, 64)
96+
if err != nil {
97+
return errors.Wrap(err, "nonce invalid")
98+
}
99+
100+
if data.R == "" {
101+
return errors.New("r missing")
102+
}
103+
a.R, success = new(big.Int).SetString(util.PreUnmarshalHexString(data.R), 16)
104+
if !success {
105+
return errors.New("r invalid")
106+
}
107+
108+
if data.S == "" {
109+
return errors.New("s missing")
110+
}
111+
a.S, success = new(big.Int).SetString(util.PreUnmarshalHexString(data.S), 16)
112+
if !success {
113+
return errors.New("s invalid")
114+
}
115+
116+
if data.YParity == "" {
117+
return errors.New("yParity missing")
118+
}
119+
a.YParity, success = new(big.Int).SetString(util.PreUnmarshalHexString(data.YParity), 16)
120+
if !success {
121+
return errors.New("yParity invalid")
122+
}
123+
124+
return nil
125+
}
126+
127+
// String returns a string version of the structure.
128+
func (a *AuthorizationListEntry) String() string {
129+
data, err := json.Marshal(a)
130+
if err != nil {
131+
return fmt.Sprintf("ERR: %v", err)
132+
}
133+
134+
return string(bytes.TrimSuffix(data, []byte("\n")))
135+
}

spec/authorizationlistentry_test.go

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Copyright © 2025 Attestant Limited.
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package spec_test
15+
16+
import (
17+
"encoding/json"
18+
"testing"
19+
20+
"github.com/attestantio/go-execution-client/spec"
21+
"github.com/stretchr/testify/require"
22+
)
23+
24+
func TestAuthorizationListEntryJSON(t *testing.T) {
25+
tests := []struct {
26+
name string
27+
input []byte
28+
err string
29+
}{
30+
{
31+
name: "Empty",
32+
err: "unexpected end of JSON input",
33+
},
34+
{
35+
name: "JSONBad",
36+
input: []byte("[]"),
37+
err: "invalid JSON: json: cannot unmarshal array into Go value of type spec.authorizationListEntryJSON",
38+
},
39+
{
40+
name: "ChainIDMissing",
41+
input: []byte(`{"address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
42+
err: "chain id missing",
43+
},
44+
{
45+
name: "ChainIDWrongType",
46+
input: []byte(`{"chainId":true,"address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
47+
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.chainId of type string",
48+
},
49+
{
50+
name: "ChainIDInvalid",
51+
input: []byte(`{"chainId":"true","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
52+
err: "chain id invalid",
53+
},
54+
{
55+
name: "AddressMissing",
56+
input: []byte(`{"chainId":"0x1a5887710","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
57+
err: "address missing",
58+
},
59+
{
60+
name: "AddressWrongType",
61+
input: []byte(`{"chainId":"0x1a5887710","address":true,"nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
62+
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.address of type string",
63+
},
64+
{
65+
name: "AddressInvalid",
66+
input: []byte(`{"chainId":"0x1a5887710","address":"true","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
67+
err: "address invalid: encoding/hex: invalid byte: U+0074 't'",
68+
},
69+
{
70+
name: "NonceMissing",
71+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
72+
err: "nonce missing",
73+
},
74+
{
75+
name: "NonceWrongType",
76+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":true,"yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
77+
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.nonce of type string",
78+
},
79+
{
80+
name: "NonceInvalid",
81+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"true","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
82+
err: "nonce invalid: strconv.ParseUint: parsing \"true\": invalid syntax",
83+
},
84+
{
85+
name: "YParityMissing",
86+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
87+
err: "yParity missing",
88+
},
89+
{
90+
name: "YParityWrongType",
91+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":true,"r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
92+
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.yParity of type string",
93+
},
94+
{
95+
name: "YParityInvalid",
96+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"true","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
97+
err: "yParity invalid",
98+
},
99+
{
100+
name: "RMissing",
101+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
102+
err: "r missing",
103+
},
104+
{
105+
name: "RWrongType",
106+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":true,"s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
107+
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.r of type string",
108+
},
109+
{
110+
name: "RInvalid",
111+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"true","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
112+
err: "r invalid",
113+
},
114+
{
115+
name: "SMissing",
116+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd"}`),
117+
err: "s missing",
118+
},
119+
{
120+
name: "SWrongType",
121+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":true}`),
122+
err: "invalid JSON: json: cannot unmarshal bool into Go struct field authorizationListEntryJSON.s of type string",
123+
},
124+
{
125+
name: "SInvalid",
126+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"true"}`),
127+
err: "s invalid",
128+
},
129+
{
130+
name: "Good",
131+
input: []byte(`{"chainId":"0x1a5887710","address":"0xfc278435313cc88059bf72b92952763e7fc511fb","nonce":"0x0","yParity":"0x0","r":"0xb46862488912b8b80cf1b962dac36ca81f58f7fb32b5365729e96fb73e3048cd","s":"0x6783eadde8364bb04c9c758c0c4cb6481de01330d7a7114ff8a00227a65133c7"}`),
132+
},
133+
}
134+
135+
for _, test := range tests {
136+
t.Run(test.name, func(t *testing.T) {
137+
var res spec.AuthorizationListEntry
138+
err := json.Unmarshal(test.input, &res)
139+
if test.err != "" {
140+
require.EqualError(t, err, test.err)
141+
} else {
142+
require.NoError(t, err)
143+
rt, err := json.Marshal(&res)
144+
require.NoError(t, err)
145+
require.JSONEq(t, string(test.input), string(rt))
146+
require.JSONEq(t, string(test.input), res.String())
147+
}
148+
})
149+
}
150+
}

0 commit comments

Comments
 (0)