forked from babylonlabs-io/babylon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcanonical_chain_indexer.go
73 lines (65 loc) · 2.73 KB
/
canonical_chain_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
package keeper
import (
"context"
"fmt"
"github.com/cosmos/cosmos-sdk/runtime"
sdkerrors "cosmossdk.io/errors"
"cosmossdk.io/store/prefix"
"github.com/babylonlabs-io/babylon/x/zoneconcierge/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// FindClosestHeader finds the IndexedHeader that is closest to (but not after) the given height
func (k Keeper) FindClosestHeader(ctx context.Context, chainID string, height uint64) (*types.IndexedHeader, error) {
chainInfo, err := k.GetChainInfo(ctx, chainID)
if err != nil {
return nil, fmt.Errorf("failed to get chain info for chain with ID %s: %w", chainID, err)
}
// if the given height is no lower than the latest header, return the latest header directly
if chainInfo.LatestHeader.Height <= height {
return chainInfo.LatestHeader, nil
}
// the requested height is lower than the latest header, trace back until finding a timestamped header
store := k.canonicalChainStore(ctx, chainID)
heightBytes := sdk.Uint64ToBigEndian(height)
iter := store.ReverseIterator(nil, heightBytes)
defer iter.Close()
// if there is no key within range [0, height], return error
if !iter.Valid() {
return nil, fmt.Errorf("chain with ID %s does not have a timestamped header before height %d", chainID, height)
}
// find the header in bytes, decode and return
headerBytes := iter.Value()
var header types.IndexedHeader
k.cdc.MustUnmarshal(headerBytes, &header)
return &header, nil
}
func (k Keeper) GetHeader(ctx context.Context, chainID string, height uint64) (*types.IndexedHeader, error) {
store := k.canonicalChainStore(ctx, chainID)
heightBytes := sdk.Uint64ToBigEndian(height)
if !store.Has(heightBytes) {
return nil, types.ErrHeaderNotFound
}
headerBytes := store.Get(heightBytes)
var header types.IndexedHeader
k.cdc.MustUnmarshal(headerBytes, &header)
return &header, nil
}
func (k Keeper) insertHeader(ctx context.Context, chainID string, header *types.IndexedHeader) error {
if header == nil {
return sdkerrors.Wrapf(types.ErrInvalidHeader, "header is nil")
}
// NOTE: we can accept header without ancestor since IBC connection can be established at any height
store := k.canonicalChainStore(ctx, chainID)
store.Set(sdk.Uint64ToBigEndian(header.Height), k.cdc.MustMarshal(header))
return nil
}
// canonicalChainStore stores the canonical chain of a CZ, formed as a list of IndexedHeader
// prefix: CanonicalChainKey || chainID
// key: height
// value: IndexedHeader
func (k Keeper) canonicalChainStore(ctx context.Context, chainID string) prefix.Store {
storeAdapter := runtime.KVStoreAdapter(k.storeService.OpenKVStore(ctx))
canonicalChainStore := prefix.NewStore(storeAdapter, types.CanonicalChainKey)
chainIDBytes := []byte(chainID)
return prefix.NewStore(canonicalChainStore, chainIDBytes)
}