@@ -19,108 +19,124 @@ package params
19
19
import (
20
20
"encoding/json"
21
21
"fmt"
22
-
23
- "github.com/ava-labs/libevm/libevm/pseudo"
24
22
)
25
23
26
24
var _ interface {
27
25
json.Marshaler
28
26
json.Unmarshaler
29
27
} = (* ChainConfig )(nil )
30
28
31
- // chainConfigWithoutMethods avoids infinite recurion into
29
+ // chainConfigWithoutMethods avoids infinite recursion into
32
30
// [ChainConfig.UnmarshalJSON].
33
31
type chainConfigWithoutMethods ChainConfig
34
32
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 )
40
44
}
41
45
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
+ }
47
53
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 )
53
57
}
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
57
62
}
58
- }
59
63
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 ,
67
70
}
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 )
70
73
}
71
- c .extra = cc .Extra
72
74
return nil
73
75
}
74
76
75
77
// 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.
76
81
func (c * ChainConfig ) MarshalJSON () ([]byte , error ) {
77
- switch reg := registeredExtras ; {
78
- case ! reg .Registered ():
82
+ if ! registeredExtras .Registered () {
79
83
return json .Marshal ((* chainConfigWithoutMethods )(c ))
84
+ }
85
+ ec := registeredExtras .Get ()
86
+ return MarshalChainConfigJSON (* c , c .extra , ec .reuseJSONRoot )
87
+ }
80
88
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 ,
91
100
}
92
- extra , err := toJSONRawMessages ( c . extra )
101
+ data , err = json . Marshal ( jsonExtra )
93
102
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 )
95
104
}
105
+ return data , nil
106
+ }
96
107
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 )
104
120
}
105
- }
106
121
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
112
128
}
113
- return json .Marshal (cc )
129
+ return json .Marshal (configJSONRaw )
114
130
}
115
131
116
132
func toJSONRawMessages (v any ) (map [string ]json.RawMessage , error ) {
117
133
buf , err := json .Marshal (v )
118
134
if err != nil {
119
- return nil , err
135
+ return nil , fmt . Errorf ( "encoding %T: %s" , v , err )
120
136
}
121
137
msgs := make (map [string ]json.RawMessage )
122
138
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 )
124
140
}
125
141
return msgs , nil
126
142
}
0 commit comments