@@ -19,108 +19,124 @@ package params
1919import (
2020 "encoding/json"
2121 "fmt"
22-
23- "github.com/ava-labs/libevm/libevm/pseudo"
2422)
2523
2624var _ interface {
2725 json.Marshaler
2826 json.Unmarshaler
2927} = (* ChainConfig )(nil )
3028
31- // chainConfigWithoutMethods avoids infinite recurion into
29+ // chainConfigWithoutMethods avoids infinite recursion into
3230// [ChainConfig.UnmarshalJSON].
3331type chainConfigWithoutMethods ChainConfig
3432
35- // chainConfigWithExportedExtra supports JSON (un)marshalling of a [ChainConfig]
36- // while exposing the `extra` field as the "extra" JSON key.
37- type chainConfigWithExportedExtra struct {
38- * chainConfigWithoutMethods // embedded to achieve regular JSON unmarshalling
39- Extra * pseudo.Type `json:"extra"` // `c.extra` is otherwise unexported
33+ // UnmarshalJSON implements the [json.Unmarshaler] interface. If extra payloads
34+ // were registered, UnmarshalJSON decodes data as described by [Extras] and
35+ // [RegisterExtras] otherwise it unmarshals directly into c as if ChainConfig
36+ // didn't implement json.Unmarshaler.
37+ func (c * ChainConfig ) UnmarshalJSON (data []byte ) (err error ) {
38+ if ! registeredExtras .Registered () {
39+ return json .Unmarshal (data , (* chainConfigWithoutMethods )(c ))
40+ }
41+ ec := registeredExtras .Get ()
42+ c .extra = ec .newChainConfig ()
43+ return UnmarshalChainConfigJSON (data , c , c .extra , ec .reuseJSONRoot )
4044}
4145
42- // UnmarshalJSON implements the [json.Unmarshaler] interface.
43- func (c * ChainConfig ) UnmarshalJSON (data []byte ) error {
44- switch reg := registeredExtras ; {
45- case reg .Registered () && ! reg .Get ().reuseJSONRoot :
46- return c .unmarshalJSONWithExtra (data )
46+ // UnmarshalChainConfigJSON is equivalent to [ChainConfig.UnmarshalJSON]
47+ // had [Extras] with `C` been registered, but without the need to call
48+ // [RegisterExtras]. The `extra` argument MUST NOT be nil.
49+ func UnmarshalChainConfigJSON [C any ](data []byte , config * ChainConfig , extra * C , reuseJSONRoot bool ) (err error ) {
50+ if extra == nil {
51+ return fmt .Errorf ("%T argument is nil; use %T.UnmarshalJSON() directly" , extra , config )
52+ }
4753
48- case reg .Registered () && reg .Get ().reuseJSONRoot : // although the latter is redundant, it's clearer
49- c .extra = reg .Get ().newChainConfig ()
50- if err := json .Unmarshal (data , c .extra ); err != nil {
51- c .extra = nil
52- return err
54+ if reuseJSONRoot {
55+ if err := json .Unmarshal (data , (* chainConfigWithoutMethods )(config )); err != nil {
56+ return fmt .Errorf ("decoding JSON into %T: %s" , config , err )
5357 }
54- fallthrough // Important! We've only unmarshalled the extra field.
55- default : // reg == nil
56- return json .Unmarshal (data , (* chainConfigWithoutMethods )(c ))
58+ if err := json .Unmarshal (data , extra ); err != nil {
59+ return fmt .Errorf ("decoding JSON into %T: %s" , extra , err )
60+ }
61+ return nil
5762 }
58- }
5963
60- // unmarshalJSONWithExtra unmarshals JSON under the assumption that the
61- // registered [Extras] payload is in the JSON "extra" key. All other
62- // unmarshalling is performed as if no [Extras] were registered.
63- func (c * ChainConfig ) unmarshalJSONWithExtra (data []byte ) error {
64- cc := & chainConfigWithExportedExtra {
65- chainConfigWithoutMethods : (* chainConfigWithoutMethods )(c ),
66- Extra : registeredExtras .Get ().newChainConfig (),
64+ combined := struct {
65+ * chainConfigWithoutMethods
66+ Extra * C `json:"extra"`
67+ }{
68+ (* chainConfigWithoutMethods )(config ),
69+ extra ,
6770 }
68- if err := json .Unmarshal (data , cc ); err != nil {
69- return err
71+ if err := json .Unmarshal (data , & combined ); err != nil {
72+ return fmt . Errorf ( `decoding JSON into combination of %T and %T (as "extra" key): %s` , config , extra , err )
7073 }
71- c .extra = cc .Extra
7274 return nil
7375}
7476
7577// MarshalJSON implements the [json.Marshaler] interface.
78+ // If extra payloads were registered, MarshalJSON encodes JSON as
79+ // described by [Extras] and [RegisterExtras] otherwise it marshals
80+ // `c` as if ChainConfig didn't implement json.Marshaler.
7681func (c * ChainConfig ) MarshalJSON () ([]byte , error ) {
77- switch reg := registeredExtras ; {
78- case ! reg .Registered ():
82+ if ! registeredExtras .Registered () {
7983 return json .Marshal ((* chainConfigWithoutMethods )(c ))
84+ }
85+ ec := registeredExtras .Get ()
86+ return MarshalChainConfigJSON (* c , c .extra , ec .reuseJSONRoot )
87+ }
8088
81- case ! reg .Get ().reuseJSONRoot :
82- return c .marshalJSONWithExtra ()
83-
84- default : // reg.reuseJSONRoot == true
85- // The inverse of reusing the JSON root is merging two JSON buffers,
86- // which isn't supported by the native package. So we use
87- // map[string]json.RawMessage intermediates.
88- geth , err := toJSONRawMessages ((* chainConfigWithoutMethods )(c ))
89- if err != nil {
90- return nil , err
89+ // MarshalChainConfigJSON is equivalent to [ChainConfig.MarshalJSON]
90+ // had [Extras] with `C` been registered, but without the need to
91+ // call [RegisterExtras].
92+ func MarshalChainConfigJSON [C any ](config ChainConfig , extra C , reuseJSONRoot bool ) (data []byte , err error ) {
93+ if ! reuseJSONRoot {
94+ jsonExtra := struct {
95+ ChainConfig
96+ Extra C `json:"extra,omitempty"`
97+ }{
98+ config ,
99+ extra ,
91100 }
92- extra , err := toJSONRawMessages ( c . extra )
101+ data , err = json . Marshal ( jsonExtra )
93102 if err != nil {
94- return nil , err
103+ return nil , fmt . Errorf ( `encoding combination of %T and %T (as "extra" key) to JSON: %s` , config , extra , err )
95104 }
105+ return data , nil
106+ }
96107
97- for k , v := range extra {
98- if _ , ok := geth [k ]; ok {
99- return nil , fmt .Errorf ("duplicate JSON key %q in both %T and registered extra" , k , c )
100- }
101- geth [k ] = v
102- }
103- return json .Marshal (geth )
108+ // The inverse of reusing the JSON root is merging two JSON buffers,
109+ // which isn't supported by the native package. So we use
110+ // map[string]json.RawMessage intermediates.
111+ // Note we cannot encode a combined struct directly because of the extra
112+ // type generic nature which cannot be embedded in such a combined struct.
113+ configJSONRaw , err := toJSONRawMessages ((chainConfigWithoutMethods )(config ))
114+ if err != nil {
115+ return nil , fmt .Errorf ("converting config to JSON raw messages: %s" , err )
116+ }
117+ extraJSONRaw , err := toJSONRawMessages (extra )
118+ if err != nil {
119+ return nil , fmt .Errorf ("converting extra config to JSON raw messages: %s" , err )
104120 }
105- }
106121
107- // marshalJSONWithExtra is the inverse of unmarshalJSONWithExtra().
108- func (c * ChainConfig ) marshalJSONWithExtra () ([]byte , error ) {
109- cc := & chainConfigWithExportedExtra {
110- chainConfigWithoutMethods : (* chainConfigWithoutMethods )(c ),
111- Extra : c .extra ,
122+ for k , v := range extraJSONRaw {
123+ _ , ok := configJSONRaw [k ]
124+ if ok {
125+ return nil , fmt .Errorf ("duplicate JSON key %q in ChainConfig and extra %T" , k , extra )
126+ }
127+ configJSONRaw [k ] = v
112128 }
113- return json .Marshal (cc )
129+ return json .Marshal (configJSONRaw )
114130}
115131
116132func toJSONRawMessages (v any ) (map [string ]json.RawMessage , error ) {
117133 buf , err := json .Marshal (v )
118134 if err != nil {
119- return nil , err
135+ return nil , fmt . Errorf ( "encoding %T: %s" , v , err )
120136 }
121137 msgs := make (map [string ]json.RawMessage )
122138 if err := json .Unmarshal (buf , & msgs ); err != nil {
123- return nil , err
139+ return nil , fmt . Errorf ( "decoding JSON encoding of %T into %T: %s" , v , msgs , err )
124140 }
125141 return msgs , nil
126142}
0 commit comments