Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

client.gno

7.07 Kb · 213 lines
  1package core
  2
  3import (
  4	"gno.land/p/onbloc/ibc/union/lightclient"
  5	"gno.land/p/onbloc/ibc/union/types"
  6	core "gno.land/r/onbloc/ibc/union/core"
  7)
  8
  9// RegisterClient stores a light client implementation for a client type.
 10// CreateClient calls it to produce a fresh instance from the submitted state
 11// bytes. Callers that pass nil are rejected as unauthorized.
 12// Union reference:
 13// https://github.com/unionlabs/union/blob/1bb07590230e7c4d071f32ad7185be021a1a1789/cosmwasm/core/src/contract.rs#L849-L865
 14func (c *coreV1) RegisterClient(_ int, rlm realm, clientType types.ClientType, clientImpl lightclient.ClientImpl) {
 15	assertIsRlmCurrent(0, rlm)
 16
 17	if clientImpl == nil {
 18		panic("caller not authorized to register client: unauthorized")
 19	}
 20	c.store.RegisterClient(0, rlm, clientType, clientImpl)
 21	core.EmitRegisterClient(0, rlm, clientType)
 22}
 23
 24// HasClient reports whether a light client is registered for a client type.
 25func (c *coreV1) HasClient(clientType types.ClientType) bool {
 26	return c.store.HasRegisteredClient(clientType)
 27}
 28
 29// CreateClient assigns a client id for a registered light client type, stores
 30// the submitted state bytes, then lets the light client verify creation against
 31// that freshly stored state.
 32func (c *coreV1) CreateClient(_ int, rlm realm, msg types.MsgCreateClient) {
 33	assertIsRlmCurrent(0, rlm)
 34
 35	creator := rlm.Previous().Address()
 36	relayer := resolveRelayer(creator, msg.Relayer)
 37
 38	clientImpl, ok := c.store.ClientRegistry(msg.ClientType)
 39	if !ok {
 40		panic("client type not found")
 41	}
 42
 43	lc, err := clientImpl(msg.ClientStateBytes, msg.ConsensusStateBytes)
 44	if err != nil {
 45		panic("create client: " + err.Error())
 46	}
 47
 48	clientId := c.store.AddClient(0, rlm, msg.ClientType, creator, lc)
 49	c.initClient(0, rlm, clientId, msg.ClientStateBytes, msg.ConsensusStateBytes, relayer)
 50
 51	core.EmitCreateClient(0, rlm, clientId, msg.ClientType, lc.GetCounterpartyChainID())
 52}
 53
 54// initClient mirrors the submitted client/consensus state for a stored client,
 55// runs the light client's creation verification, then persists any state
 56// overwrite and storage writes the verification returns. It returns the height
 57// the consensus state was stored at.
 58func (c *coreV1) initClient(_ int, rlm realm, clientId types.ClientId, clientStateBytes, consensusStateBytes []byte, relayer address) types.Height {
 59	lc, ok := c.store.GetLightClient(clientId)
 60	if !ok {
 61		panic("client " + clientId.String() + " not found")
 62	}
 63
 64	c.store.SaveClientState(0, rlm, clientId, clientStateBytes)
 65
 66	height := types.NewHeight(lc.GetLatestHeight())
 67	c.store.SaveConsensusState(0, rlm, clientId, height, consensusStateBytes)
 68
 69	// union threads info.sender as the caller; gno derives the authenticated
 70	// caller from the realm (internal calls preserve the entry frame).
 71	result, err := lc.VerifyCreation(rlm.Previous().Address(), relayer)
 72	if err != nil {
 73		panic("create client: " + err.Error())
 74	}
 75
 76	if len(result.ClientStateBytes) != 0 {
 77		c.store.SaveClientState(0, rlm, clientId, result.ClientStateBytes)
 78	}
 79
 80	for k, v := range result.StorageWrites {
 81		c.store.SaveClientStore(0, rlm, clientId, k, v)
 82	}
 83
 84	return height
 85}
 86
 87// UpdateClient verifies a client message against the light client object, which
 88// updates its own state, and mirrors the resulting state bytes for queries and
 89// counterparty proofs.
 90func (c *coreV1) UpdateClient(_ int, rlm realm, msg types.MsgUpdateClient) {
 91	assertIsRlmCurrent(0, rlm)
 92
 93	lc, err := c.getActiveClient(msg.ClientId)
 94	if err != nil {
 95		panic(err)
 96	}
 97
 98	caller := rlm.Previous().Address()
 99	relayer := resolveRelayer(caller, msg.Relayer)
100
101	update, err := lc.VerifyHeader(caller, msg.ClientMessage, relayer)
102	if err != nil {
103		panic("update client: " + err.Error())
104	}
105
106	height := types.NewHeight(update.Height)
107
108	if len(update.ClientStateBytes) != 0 {
109		c.store.SaveClientState(0, rlm, msg.ClientId, update.ClientStateBytes)
110	}
111
112	// Union stores consensus_state_bytes unconditionally because the field is not
113	// optional in the update response.
114	c.store.SaveConsensusState(0, rlm, msg.ClientId, height, update.ConsensusStateBytes)
115
116	for k, v := range update.StorageWrites {
117		c.store.SaveClientStore(0, rlm, msg.ClientId, k, v)
118	}
119
120	core.EmitUpdateClient(0, rlm, msg.ClientId, height)
121}
122
123func (c *coreV1) ForceUpdateClient(_ int, rlm realm, msg types.MsgForceUpdateClient) {
124	assertIsRlmCurrent(0, rlm)
125
126	clientImpl, ok := c.store.GetClientImpl(msg.ClientId)
127	if !ok {
128		panic(makeError(core.ErrClientNotFound, msg.ClientId.String()))
129	}
130
131	next, err := clientImpl(msg.ClientStateBytes, msg.ConsensusStateBytes)
132	if err != nil {
133		panic("force update client: " + err.Error())
134	}
135
136	c.store.SetLightClient(0, rlm, msg.ClientId, next)
137
138	caller := rlm.Previous().Address()
139	height := c.initClient(0, rlm, msg.ClientId, msg.ClientStateBytes, msg.ConsensusStateBytes, caller)
140
141	core.EmitForceUpdateClient(0, rlm, msg.ClientId, height)
142}
143
144// Misbehaviour verifies a misbehaviour message against an active light client and
145// freezes it. Mirroring union (contract.rs L1048-L1093), the host commits the
146// frozen client-state bytes the light client returns back to the client-state
147// mirror / ClientStatePath commitment before emitting the event.
148// reference: https://github.com/unionlabs/union/blob/d91c5e94354e15801bd5f82dc658eae3b79f2dad/cosmwasm/core/src/contract.rs#L1048-L1093
149func (c *coreV1) Misbehaviour(_ int, rlm realm, msg types.MsgMisbehaviour) {
150	assertIsRlmCurrent(0, rlm)
151
152	lc, err := c.getActiveClient(msg.ClientId)
153	if err != nil {
154		panic(err)
155	}
156
157	caller := rlm.Previous().Address()
158	relayer := resolveRelayer(caller, msg.Relayer)
159
160	clientStateBytes, err := lc.Misbehaviour(caller, msg.ClientMessage, relayer)
161	if err != nil {
162		panic("misbehaviour: " + err.Error())
163	}
164
165	if len(clientStateBytes) != 0 {
166		c.store.SaveClientState(0, rlm, msg.ClientId, clientStateBytes)
167	}
168
169	core.EmitMisbehaviour(0, rlm, msg.ClientId)
170}
171
172// verifyMembership checks a membership proof against a registered, active light
173// client. Shared by the connection/channel handshakes.
174func (c *coreV1) verifyMembership(clientId types.ClientId, height types.Height, proof, key, value []byte) {
175	lc, err := c.getActiveClient(clientId)
176	if err != nil {
177		panic(err)
178	}
179
180	err = lc.VerifyMembership(height.RevisionHeight, key, proof, value)
181	if err != nil {
182		panic(err)
183	}
184}
185
186// getClient returns the light client registered under clientId, or an error
187// when no client is registered for it. The caller (entrypoint) decides whether
188// a miss reverts.
189func (c *coreV1) getClient(clientId types.ClientId) (lightclient.Interface, error) {
190	lc, ok := c.store.GetLightClient(clientId)
191	if !ok {
192		return nil, makeError(core.ErrClientNotFound, clientId.String())
193	}
194
195	return lc, nil
196}
197
198// getActiveClient returns the light client registered under clientId, or an
199// error when it is missing or not in the Active status. It centralizes the
200// "registered and active" precondition shared by the update, misbehaviour, and
201// proof-verification paths.
202func (c *coreV1) getActiveClient(clientId types.ClientId) (lightclient.Interface, error) {
203	lc, err := c.getClient(clientId)
204	if err != nil {
205		return nil, err
206	}
207
208	if lc.Status() != lightclient.Active {
209		return nil, makeError(core.ErrClientNotActive)
210	}
211
212	return lc, nil
213}