-
Notifications
You must be signed in to change notification settings - Fork 573
/
genesis.go
250 lines (205 loc) · 7.94 KB
/
genesis.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package types
import (
"fmt"
"sort"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
host "github.com/cosmos/ibc-go/v4/modules/core/24-host"
"github.com/cosmos/ibc-go/v4/modules/core/exported"
)
var (
_ codectypes.UnpackInterfacesMessage = IdentifiedClientState{}
_ codectypes.UnpackInterfacesMessage = ClientsConsensusStates{}
_ codectypes.UnpackInterfacesMessage = ClientConsensusStates{}
_ codectypes.UnpackInterfacesMessage = GenesisState{}
)
var (
_ sort.Interface = ClientsConsensusStates{}
_ exported.GenesisMetadata = GenesisMetadata{}
)
// ClientsConsensusStates defines a slice of ClientConsensusStates that supports the sort interface
type ClientsConsensusStates []ClientConsensusStates
// Len implements sort.Interface
func (ccs ClientsConsensusStates) Len() int { return len(ccs) }
// Less implements sort.Interface
func (ccs ClientsConsensusStates) Less(i, j int) bool { return ccs[i].ClientId < ccs[j].ClientId }
// Swap implements sort.Interface
func (ccs ClientsConsensusStates) Swap(i, j int) { ccs[i], ccs[j] = ccs[j], ccs[i] }
// Sort is a helper function to sort the set of ClientsConsensusStates in place
func (ccs ClientsConsensusStates) Sort() ClientsConsensusStates {
sort.Sort(ccs)
return ccs
}
// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
func (ccs ClientsConsensusStates) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
for _, clientConsensus := range ccs {
if err := clientConsensus.UnpackInterfaces(unpacker); err != nil {
return err
}
}
return nil
}
// NewClientConsensusStates creates a new ClientConsensusStates instance.
func NewClientConsensusStates(clientID string, consensusStates []ConsensusStateWithHeight) ClientConsensusStates {
return ClientConsensusStates{
ClientId: clientID,
ConsensusStates: consensusStates,
}
}
// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
func (ccs ClientConsensusStates) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
for _, consStateWithHeight := range ccs.ConsensusStates {
if err := consStateWithHeight.UnpackInterfaces(unpacker); err != nil {
return err
}
}
return nil
}
// NewGenesisState creates a GenesisState instance.
func NewGenesisState(
clients []IdentifiedClientState, clientsConsensus ClientsConsensusStates, clientsMetadata []IdentifiedGenesisMetadata,
params Params, createLocalhost bool, nextClientSequence uint64,
) GenesisState {
return GenesisState{
Clients: clients,
ClientsConsensus: clientsConsensus,
ClientsMetadata: clientsMetadata,
Params: params,
CreateLocalhost: createLocalhost,
NextClientSequence: nextClientSequence,
}
}
// DefaultGenesisState returns the ibc client submodule's default genesis state.
func DefaultGenesisState() GenesisState {
return GenesisState{
Clients: []IdentifiedClientState{},
ClientsConsensus: ClientsConsensusStates{},
Params: DefaultParams(),
CreateLocalhost: false,
NextClientSequence: 0,
}
}
// UnpackInterfaces implements UnpackInterfacesMessage.UnpackInterfaces
func (gs GenesisState) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
for _, client := range gs.Clients {
if err := client.UnpackInterfaces(unpacker); err != nil {
return err
}
}
return gs.ClientsConsensus.UnpackInterfaces(unpacker)
}
// Validate performs basic genesis state validation returning an error upon any
// failure.
func (gs GenesisState) Validate() error {
// keep track of the max sequence to ensure it is less than
// the next sequence used in creating client identifers.
var maxSequence uint64 = 0
if err := gs.Params.Validate(); err != nil {
return err
}
validClients := make(map[string]string)
for i, client := range gs.Clients {
if err := host.ClientIdentifierValidator(client.ClientId); err != nil {
return fmt.Errorf("invalid client consensus state identifier %s index %d: %w", client.ClientId, i, err)
}
clientState, ok := client.ClientState.GetCachedValue().(exported.ClientState)
if !ok {
return fmt.Errorf("invalid client state with ID %s", client.ClientId)
}
if !gs.Params.IsAllowedClient(clientState.ClientType()) {
return fmt.Errorf("client type %s not allowed by genesis params", clientState.ClientType())
}
if err := clientState.Validate(); err != nil {
return fmt.Errorf("invalid client %v index %d: %w", client, i, err)
}
clientType, sequence, err := ParseClientIdentifier(client.ClientId)
if err != nil {
return err
}
if clientType != clientState.ClientType() {
return fmt.Errorf("client state type %s does not equal client type in client identifier %s", clientState.ClientType(), clientType)
}
if err := ValidateClientType(clientType); err != nil {
return err
}
if sequence > maxSequence {
maxSequence = sequence
}
// add client id to validClients map
validClients[client.ClientId] = clientState.ClientType()
}
for _, cc := range gs.ClientsConsensus {
// check that consensus state is for a client in the genesis clients list
clientType, ok := validClients[cc.ClientId]
if !ok {
return fmt.Errorf("consensus state in genesis has a client id %s that does not map to a genesis client", cc.ClientId)
}
for i, consensusState := range cc.ConsensusStates {
if consensusState.Height.IsZero() {
return fmt.Errorf("consensus state height cannot be zero")
}
cs, ok := consensusState.ConsensusState.GetCachedValue().(exported.ConsensusState)
if !ok {
return fmt.Errorf("invalid consensus state with client ID %s at height %s", cc.ClientId, consensusState.Height)
}
if err := cs.ValidateBasic(); err != nil {
return fmt.Errorf("invalid client consensus state %v clientID %s index %d: %w", cs, cc.ClientId, i, err)
}
// ensure consensus state type matches client state type
if clientType != cs.ClientType() {
return fmt.Errorf("consensus state client type %s does not equal client state client type %s", cs.ClientType(), clientType)
}
}
}
for _, clientMetadata := range gs.ClientsMetadata {
// check that metadata is for a client in the genesis clients list
_, ok := validClients[clientMetadata.ClientId]
if !ok {
return fmt.Errorf("metadata in genesis has a client id %s that does not map to a genesis client", clientMetadata.ClientId)
}
for i, gm := range clientMetadata.ClientMetadata {
if err := gm.Validate(); err != nil {
return fmt.Errorf("invalid client metadata %v clientID %s index %d: %w", gm, clientMetadata.ClientId, i, err)
}
}
}
if gs.CreateLocalhost && !gs.Params.IsAllowedClient(exported.Localhost) {
return fmt.Errorf("localhost client is not registered on the allowlist")
}
if maxSequence != 0 && maxSequence >= gs.NextClientSequence {
return fmt.Errorf("next client identifier sequence %d must be greater than the maximum sequence used in the provided client identifiers %d", gs.NextClientSequence, maxSequence)
}
return nil
}
// NewGenesisMetadata is a constructor for GenesisMetadata
func NewGenesisMetadata(key, val []byte) GenesisMetadata {
return GenesisMetadata{
Key: key,
Value: val,
}
}
// GetKey returns the key of metadata. Implements exported.GenesisMetadata interface.
func (gm GenesisMetadata) GetKey() []byte {
return gm.Key
}
// GetValue returns the value of metadata. Implements exported.GenesisMetadata interface.
func (gm GenesisMetadata) GetValue() []byte {
return gm.Value
}
// Validate ensures key and value of metadata are not empty
func (gm GenesisMetadata) Validate() error {
if len(gm.Key) == 0 {
return fmt.Errorf("genesis metadata key cannot be empty")
}
if len(gm.Value) == 0 {
return fmt.Errorf("genesis metadata value cannot be empty")
}
return nil
}
// NewIdentifiedGenesisMetadata takes in a client ID and list of genesis metadata for that client
// and constructs a new IdentifiedGenesisMetadata.
func NewIdentifiedGenesisMetadata(clientID string, gms []GenesisMetadata) IdentifiedGenesisMetadata {
return IdentifiedGenesisMetadata{
ClientId: clientID,
ClientMetadata: gms,
}
}