forked from babylonlabs-io/babylon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchain_info_indexer.go
141 lines (123 loc) · 5.37 KB
/
chain_info_indexer.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
package keeper
import (
"context"
"fmt"
"github.com/cosmos/cosmos-sdk/runtime"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/store/prefix"
"github.com/babylonlabs-io/babylon/x/zoneconcierge/types"
)
func (k Keeper) setChainInfo(ctx context.Context, chainInfo *types.ChainInfo) {
store := k.chainInfoStore(ctx)
store.Set([]byte(chainInfo.ChainId), k.cdc.MustMarshal(chainInfo))
}
func (k Keeper) InitChainInfo(ctx context.Context, chainID string) (*types.ChainInfo, error) {
if len(chainID) == 0 {
return nil, fmt.Errorf("chainID is empty")
}
// ensure chain info has not been initialised yet
if k.HasChainInfo(ctx, chainID) {
return nil, errorsmod.Wrapf(types.ErrInvalidChainInfo, "chain info has already initialized")
}
chainInfo := &types.ChainInfo{
ChainId: chainID,
LatestHeader: nil,
LatestForks: &types.Forks{
Headers: []*types.IndexedHeader{},
},
TimestampedHeadersCount: 0,
}
k.setChainInfo(ctx, chainInfo)
return chainInfo, nil
}
// HasChainInfo returns whether the chain info exists for a given ID
// Since IBC does not provide API that allows to initialise chain info right before creating an IBC connection,
// we can only check its existence every time, and return an empty one if it's not initialised yet.
func (k Keeper) HasChainInfo(ctx context.Context, chainID string) bool {
store := k.chainInfoStore(ctx)
return store.Has([]byte(chainID))
}
// GetChainInfo returns the ChainInfo struct for a chain with a given ID
// Since IBC does not provide API that allows to initialise chain info right before creating an IBC connection,
// we can only check its existence every time, and return an empty one if it's not initialised yet.
func (k Keeper) GetChainInfo(ctx context.Context, chainID string) (*types.ChainInfo, error) {
if !k.HasChainInfo(ctx, chainID) {
return nil, types.ErrChainInfoNotFound
}
store := k.chainInfoStore(ctx)
chainInfoBytes := store.Get([]byte(chainID))
var chainInfo types.ChainInfo
k.cdc.MustUnmarshal(chainInfoBytes, &chainInfo)
return &chainInfo, nil
}
// updateLatestHeader updates the chainInfo w.r.t. the given header, including
// - replace the old latest header with the given one
// - increment the number of timestamped headers
// Note that this function is triggered only upon receiving headers from the relayer,
// and only a subset of headers in CZ are relayed. Thus TimestampedHeadersCount is not
// equal to the total number of headers in CZ.
func (k Keeper) updateLatestHeader(ctx context.Context, chainID string, header *types.IndexedHeader) error {
if header == nil {
return errorsmod.Wrapf(types.ErrInvalidHeader, "header is nil")
}
chainInfo, err := k.GetChainInfo(ctx, chainID)
if err != nil {
// chain info has not been initialised yet
return fmt.Errorf("failed to get chain info of %s: %w", chainID, err)
}
chainInfo.LatestHeader = header // replace the old latest header with the given one
chainInfo.TimestampedHeadersCount++ // increment the number of timestamped headers
k.setChainInfo(ctx, chainInfo)
return nil
}
// tryToUpdateLatestForkHeader tries to update the chainInfo w.r.t. the given fork header
// - If no fork exists, add this fork header as the latest one
// - If there is a fork header at the same height, add this fork to the set of latest fork headers
// - If this fork header is newer than the previous one, replace the old fork headers with this fork header
// - If this fork header is older than the current latest fork, ignore
func (k Keeper) tryToUpdateLatestForkHeader(ctx context.Context, chainID string, header *types.IndexedHeader) error {
if header == nil {
return errorsmod.Wrapf(types.ErrInvalidHeader, "header is nil")
}
chainInfo, err := k.GetChainInfo(ctx, chainID)
if err != nil {
return errorsmod.Wrapf(types.ErrChainInfoNotFound, "cannot insert fork header when chain info is not initialized")
}
if len(chainInfo.LatestForks.Headers) == 0 {
// no fork at the moment, add this fork header as the latest one
chainInfo.LatestForks.Headers = append(chainInfo.LatestForks.Headers, header)
} else if chainInfo.LatestForks.Headers[0].Height == header.Height {
// there exists fork headers at the same height, add this fork header to the set of latest fork headers
chainInfo.LatestForks.Headers = append(chainInfo.LatestForks.Headers, header)
} else if chainInfo.LatestForks.Headers[0].Height < header.Height {
// this fork header is newer than the previous one, replace the old fork headers with this fork header
chainInfo.LatestForks = &types.Forks{
Headers: []*types.IndexedHeader{header},
}
} else {
// this fork header is older than the current latest fork, don't record this fork header in chain info
return nil
}
k.setChainInfo(ctx, chainInfo)
return nil
}
// GetAllChainIDs gets all chain IDs that integrate Babylon
func (k Keeper) GetAllChainIDs(ctx context.Context) []string {
chainIDs := []string{}
iter := k.chainInfoStore(ctx).Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
chainIDBytes := iter.Key()
chainID := string(chainIDBytes)
chainIDs = append(chainIDs, chainID)
}
return chainIDs
}
// msgChainInfoStore stores the information of canonical chains and forks for CZs
// prefix: ChainInfoKey
// key: chainID
// value: ChainInfo
func (k Keeper) chainInfoStore(ctx context.Context) prefix.Store {
storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
return prefix.NewStore(storeAdapter, types.ChainInfoKey)
}