-
Notifications
You must be signed in to change notification settings - Fork 691
/
Copy pathlight_client_module.go
181 lines (149 loc) · 7.12 KB
/
light_client_module.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
package localhost
import (
"bytes"
corestore "cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
clienttypes "github.com/cosmos/ibc-go/v10/modules/core/02-client/types"
commitmenttypes "github.com/cosmos/ibc-go/v10/modules/core/23-commitment/types"
commitmenttypesv2 "github.com/cosmos/ibc-go/v10/modules/core/23-commitment/types/v2"
host "github.com/cosmos/ibc-go/v10/modules/core/24-host"
ibcerrors "github.com/cosmos/ibc-go/v10/modules/core/errors"
"github.com/cosmos/ibc-go/v10/modules/core/exported"
)
const (
// ModuleName defines the 09-localhost light client module name
ModuleName = "09-localhost"
)
// SentinelProof defines the 09-localhost sentinel proof.
// Submission of nil or empty proofs is disallowed in core IBC messaging.
// This serves as a placeholder value for relayers to leverage as the proof field in various message types.
// Localhost client state verification will fail if the sentintel proof value is not provided.
var SentinelProof = []byte{0x01}
var _ exported.LightClientModule = (*LightClientModule)(nil)
// LightClientModule implements the core IBC api.LightClientModule interface.
type LightClientModule struct {
cdc codec.BinaryCodec
storeService corestore.KVStoreService
}
// NewLightClientModule creates and returns a new 09-localhost LightClientModule.
func NewLightClientModule(cdc codec.BinaryCodec, storeService corestore.KVStoreService) *LightClientModule {
return &LightClientModule{
cdc: cdc,
storeService: storeService,
}
}
// Initialize returns an error because it is stateless.
func (LightClientModule) Initialize(_ sdk.Context, _ string, _, _ []byte) error {
return errorsmod.Wrap(clienttypes.ErrClientExists, "localhost is stateless and cannot be initialized")
}
// VerifyClientMessage is unsupported by the 09-localhost client type and returns an error.
func (LightClientModule) VerifyClientMessage(_ sdk.Context, _ string, _ exported.ClientMessage) error {
return errorsmod.Wrap(clienttypes.ErrUpdateClientFailed, "client message verification is unsupported by the localhost client")
}
// CheckForMisbehaviour is unsupported by the 09-localhost client type and performs a no-op, returning false.
func (LightClientModule) CheckForMisbehaviour(_ sdk.Context, _ string, _ exported.ClientMessage) bool {
return false
}
// UpdateStateOnMisbehaviour is unsupported by the 09-localhost client type and performs a no-op.
func (LightClientModule) UpdateStateOnMisbehaviour(_ sdk.Context, _ string, _ exported.ClientMessage) {
// no-op
}
// UpdateState performs a no-op and returns the context height in the updated heights return value.
func (LightClientModule) UpdateState(ctx sdk.Context, _ string, _ exported.ClientMessage) []exported.Height {
return []exported.Height{clienttypes.GetSelfHeight(ctx)}
}
// VerifyMembership is a generic proof verification method which verifies the existence of a given key and value within the IBC store.
// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24).
// The caller must provide the full IBC store.
func (l LightClientModule) VerifyMembership(
ctx sdk.Context,
clientID string,
height exported.Height,
delayTimePeriod uint64,
delayBlockPeriod uint64,
proof []byte,
path exported.Path,
value []byte,
) error {
ibcStore := l.storeService.OpenKVStore(ctx)
// ensure the proof provided is the expected sentinel localhost client proof
if !bytes.Equal(proof, SentinelProof) {
return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "expected %s, got %s", string(SentinelProof), string(proof))
}
merklePath, ok := path.(commitmenttypesv2.MerklePath)
if !ok {
return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypesv2.MerklePath{}, path)
}
if len(merklePath.GetKeyPath()) != 2 {
return errorsmod.Wrapf(host.ErrInvalidPath, "path must be of length 2: %s", merklePath.GetKeyPath())
}
// The commitment prefix (eg: "ibc") is omitted when operating on the core IBC store
bz, err := ibcStore.Get(merklePath.KeyPath[1])
if err != nil {
panic(err)
}
if bz == nil {
return errorsmod.Wrapf(clienttypes.ErrFailedMembershipVerification, "value not found for path %s", path)
}
if !bytes.Equal(bz, value) {
return errorsmod.Wrapf(clienttypes.ErrFailedMembershipVerification, "value provided does not equal value stored at path: %s", path)
}
return nil
}
// VerifyNonMembership is a generic proof verification method which verifies the absence of a given CommitmentPath within the IBC store.
// The caller is expected to construct the full CommitmentPath from a CommitmentPrefix and a standardized path (as defined in ICS 24).
// The caller must provide the full IBC store.
func (l LightClientModule) VerifyNonMembership(
ctx sdk.Context,
clientID string,
height exported.Height,
delayTimePeriod uint64,
delayBlockPeriod uint64,
proof []byte,
path exported.Path,
) error {
ibcStore := l.storeService.OpenKVStore(ctx)
// ensure the proof provided is the expected sentinel localhost client proof
if !bytes.Equal(proof, SentinelProof) {
return errorsmod.Wrapf(commitmenttypes.ErrInvalidProof, "expected %s, got %s", string(SentinelProof), string(proof))
}
merklePath, ok := path.(commitmenttypesv2.MerklePath)
if !ok {
return errorsmod.Wrapf(ibcerrors.ErrInvalidType, "expected %T, got %T", commitmenttypesv2.MerklePath{}, path)
}
if len(merklePath.GetKeyPath()) != 2 {
return errorsmod.Wrapf(host.ErrInvalidPath, "path must be of length 2: %s", merklePath.GetKeyPath())
}
// The commitment prefix (eg: "ibc") is omitted when operating on the core IBC store
has, err := ibcStore.Has(merklePath.KeyPath[1])
if err != nil {
return errorsmod.Wrapf(err, "error checking for value for path %s", path)
}
if has {
return errorsmod.Wrapf(clienttypes.ErrFailedNonMembershipVerification, "value found for path %s", path)
}
return nil
}
// Status always returns Active. The 09-localhost status cannot be changed.
func (LightClientModule) Status(_ sdk.Context, _ string) exported.Status {
return exported.Active
}
// LatestHeight returns the context height.
func (LightClientModule) LatestHeight(ctx sdk.Context, _ string) exported.Height {
return clienttypes.GetSelfHeight(ctx)
}
// TimestampAtHeight returns the current block time retrieved from the application context. The localhost client does not store consensus states and thus
// cannot provide a timestamp for the provided height.
func (LightClientModule) TimestampAtHeight(ctx sdk.Context, _ string, _ exported.Height) (uint64, error) {
return uint64(ctx.BlockTime().UnixNano()), nil
}
// RecoverClient returns an error. The localhost cannot be modified by proposals.
func (LightClientModule) RecoverClient(_ sdk.Context, _, _ string) error {
return errorsmod.Wrap(clienttypes.ErrUpdateClientFailed, "cannot update localhost client with a proposal")
}
// VerifyUpgradeAndUpdateState returns an error since localhost cannot be upgraded.
func (LightClientModule) VerifyUpgradeAndUpdateState(_ sdk.Context, _ string, _, _, _, _ []byte) error {
return errorsmod.Wrap(clienttypes.ErrInvalidUpgradeClient, "cannot upgrade localhost client")
}