/
store.go
193 lines (165 loc) · 7.11 KB
/
store.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
package types
import (
"encoding/binary"
"strings"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
clienttypes "github.com/teleport-network/teleport/x/xibc/core/client/types"
"github.com/teleport-network/teleport/x/xibc/core/host"
"github.com/teleport-network/teleport/x/xibc/exported"
)
// KeyProcessedTime is appended to consensus state key to store the processed time
var (
KeyIterateConsensusStatePrefix = "iterateConsensusStates"
KeyProcessedTime = []byte("/processedTime")
)
// GetConsensusState retrieves the consensus state from the client prefixed
// store. An error is returned if the consensus state does not exist.
func GetConsensusState(store sdk.KVStore, cdc codec.BinaryCodec, height exported.Height) (*ConsensusState, error) {
bz := store.Get(host.ConsensusStateKey(height))
if bz == nil {
return nil, sdkerrors.Wrapf(
clienttypes.ErrConsensusStateNotFound,
"consensus state does not exist for height %s",
height,
)
}
consensusStateI, err := clienttypes.UnmarshalConsensusState(cdc, bz)
if err != nil {
return nil, sdkerrors.Wrapf(clienttypes.ErrInvalidConsensus, "unmarshal error: %v", err)
}
consensusState, ok := consensusStateI.(*ConsensusState)
if !ok {
return nil, sdkerrors.Wrapf(
clienttypes.ErrInvalidConsensus,
"invalid consensus type %T, expected %T",
consensusState, &ConsensusState{},
)
}
return consensusState, nil
}
// IterateProcessedTime iterates through the prefix store and applies the callback.
// If the cb returns true, then iterator will close and stop.
func IterateProcessedTime(store sdk.KVStore, cb func(key, val []byte) bool) {
iterator := sdk.KVStorePrefixIterator(store, []byte(host.KeyConsensusStatePrefix))
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
key := iterator.Key()
keySplit := strings.Split(string(key), "/")
// processed time key in prefix store has format: "consensusState/<height>/processedTime"
if len(keySplit) != 3 || keySplit[2] != "processedTime" {
// ignore all consensus state keys
continue
}
if cb(iterator.Key(), iterator.Value()) {
break
}
}
}
// GetHeightFromIterationKey takes an iteration key and returns the height that it references
func GetHeightFromIterationKey(iterKey []byte) exported.Height {
bigEndianBytes := iterKey[len([]byte(KeyIterateConsensusStatePrefix)):]
revisionBytes := bigEndianBytes[0:8]
heightBytes := bigEndianBytes[8:]
revision := binary.BigEndian.Uint64(revisionBytes)
height := binary.BigEndian.Uint64(heightBytes)
return clienttypes.NewHeight(revision, height)
}
// IterateConsensusStateAscending iterates through the consensus states in ascending order. It calls the provided
// callback on each height, until stop=true is returned.
func IterateConsensusStateAscending(clientStore sdk.KVStore,
cb func(height exported.Height) (stop bool)) {
iterator := sdk.KVStorePrefixIterator(clientStore, []byte(KeyIterateConsensusStatePrefix))
defer iterator.Close()
for ; iterator.Valid(); iterator.Next() {
iterKey := iterator.Key()
height := GetHeightFromIterationKey(iterKey)
if cb(height) {
return
}
}
}
// ProcessedTime Store code
// ProcessedTimeKey returns the key under which the processed time will be stored in the client store.
func ProcessedTimeKey(height exported.Height) []byte {
return append(host.ConsensusStateKey(height), KeyProcessedTime...)
}
// SetProcessedTime stores the time at which a header was processed and the corresponding consensus state was created.
// This is useful when validating whether a packet has reached the specified delay period in the tendermint client's
// verification functions
func SetProcessedTime(clientStore sdk.KVStore, height exported.Height, timeNs uint64) {
key := ProcessedTimeKey(height)
val := sdk.Uint64ToBigEndian(timeNs)
clientStore.Set(key, val)
}
// GetProcessedTime gets the time (in nanoseconds) at which this chain received and processed a tendermint header.
// This is used to validate that a received packet has passed the delay period.
func GetProcessedTime(clientStore sdk.KVStore, height exported.Height) (uint64, bool) {
key := ProcessedTimeKey(height)
bz := clientStore.Get(key)
if bz == nil {
return 0, false
}
return sdk.BigEndianToUint64(bz), true
}
// IterationKey returns the key under which the consensus state key will be stored.
// The iteration key is a BigEndian representation of the consensus state key to support efficient iteration.
func IterationKey(height exported.Height) []byte {
heightBytes := bigEndianHeightBytes(height)
return append([]byte(KeyIterateConsensusStatePrefix), heightBytes...)
}
// SetIterationKey stores the consensus state key under a key that is more efficient for ordered iteration
func SetIterationKey(clientStore sdk.KVStore, height exported.Height) {
key := IterationKey(height)
val := host.ConsensusStateKey(height)
clientStore.Set(key, val)
}
// GetIterationKey returns the consensus state key stored under the efficient iteration key.
// NOTE: This function is currently only used for testing purposes
func GetIterationKey(clientStore sdk.KVStore, height exported.Height) []byte {
key := IterationKey(height)
return clientStore.Get(key)
}
// setConsensusMetadata sets context time as processed time and set context height as processed height
// as this is internal tendermint light client logic.
// client state and consensus state will be set by client keeper
// set iteration key to provide ability for efficient ordered iteration of consensus states.
func setConsensusMetadata(ctx sdk.Context, clientStore sdk.KVStore, height exported.Height) {
setConsensusMetadataWithValues(clientStore, height, clienttypes.GetSelfHeight(ctx), uint64(ctx.BlockTime().UnixNano()))
}
// deleteConsensusMetadata deletes the metadata stored for a particular consensus state.
func deleteConsensusMetadata(clientStore sdk.KVStore, height exported.Height) {
deleteProcessedTime(clientStore, height)
deleteIterationKey(clientStore, height)
}
// setConsensusMetadataWithValues sets the consensus metadata with the provided values
func setConsensusMetadataWithValues(
clientStore sdk.KVStore, height,
processedHeight exported.Height,
processedTime uint64,
) {
SetProcessedTime(clientStore, height, processedTime)
SetIterationKey(clientStore, height)
}
// deleteConsensusState deletes the consensus state at the given height
func deleteConsensusState(clientStore sdk.KVStore, height exported.Height) {
key := host.ConsensusStateKey(height)
clientStore.Delete(key)
}
// deleteProcessedTime deletes the processedTime for a given height
func deleteProcessedTime(clientStore sdk.KVStore, height exported.Height) {
key := ProcessedTimeKey(height)
clientStore.Delete(key)
}
// deleteIterationKey deletes the iteration key for a given height
func deleteIterationKey(clientStore sdk.KVStore, height exported.Height) {
key := IterationKey(height)
clientStore.Delete(key)
}
func bigEndianHeightBytes(height exported.Height) []byte {
heightBytes := make([]byte, 16)
binary.BigEndian.PutUint64(heightBytes, height.GetRevisionNumber())
binary.BigEndian.PutUint64(heightBytes[8:], height.GetRevisionHeight())
return heightBytes
}