Search Apps Documentation Source Content File Folder Download Copy Actions Download

client.gno

6.76 Kb · 201 lines
  1package core
  2
  3import (
  4	"chain"
  5	"chain/runtime/unsafe"
  6
  7	"gno.land/p/aib/ibc/host"
  8	"gno.land/p/aib/ibc/lightclient"
  9	"gno.land/p/aib/ibc/types"
 10	"gno.land/p/nt/ufmt/v0"
 11)
 12
 13// CreateClient generates a new client identifier and invokes the associated
 14// light client module in order to initialize the client.
 15func CreateClient(cur realm, clientState lightclient.ClientState, consensusState lightclient.ConsensusState) string {
 16	if clientState.ClientType() != consensusState.ClientType() {
 17		panic("client type for client state and consensus state do not match")
 18	}
 19	if err := types.ValidateClientType(clientState.ClientType()); err != nil {
 20		panic(ufmt.Sprintf(
 21			"client type does not meet naming constraints: %v", err,
 22		))
 23	}
 24
 25	if err := clientState.ValidateBasic(); err != nil {
 26		panic(err)
 27	}
 28	if err := consensusState.ValidateBasic(); err != nil {
 29		panic(err)
 30	}
 31	relayer := ensureAuthorizedRelayer()
 32	c := store.addClient(clientState.ClientType(), relayer)
 33	err := c.lightClient.Initialize(clientState, consensusState)
 34	if err != nil {
 35		panic(err)
 36	}
 37	if status := c.lightClient.Status(); status != lightclient.Active {
 38		panic(ufmt.Sprintf("cannot create client (%s) with status %s", c.id, status))
 39	}
 40	// Emit create client event
 41	chain.Emit(types.EventTypeCreateClient,
 42		types.AttributeKeyClientID, c.id,
 43		types.AttributeKeyClientType, c.typ,
 44		types.AttributeKeyConsensusHeights, c.lightClient.LatestHeight().String(),
 45	)
 46	return c.id
 47}
 48
 49// ClientIDs returns the list of known client identifiers, sorted lexically by
 50// id (the underlying b+tree order). Intended for read-only callers (UIs,
 51// other realms building txlinks).
 52func ClientIDs() []string {
 53	ids := make([]string, 0, store.clientByID.Size())
 54	store.clientByID.IterateByOffset(0, store.clientByID.Size(), func(k string, _ any) bool {
 55		ids = append(ids, k)
 56		return false
 57	})
 58	return ids
 59}
 60
 61// RegisterCounterparty will register the IBC v2 counterparty info for the
 62// given clientID. It must be called by the same relayer that called
 63// CreateClient.
 64func RegisterCounterparty(cur realm, clientID string, counterpartyMerklePrefix [][]byte, counterpartyClientID string) {
 65	if !types.IsValidClientID(counterpartyClientID) {
 66		panic("invalid counterparty client id")
 67	}
 68	c := store.getClient(clientID)
 69	if c == nil {
 70		panic(ufmt.Sprintf("client %s not found", clientID))
 71	}
 72	caller := unsafe.OriginCaller()
 73	if c.creator != caller {
 74		panic(ufmt.Sprintf("expected same signer as CreateClient submitter %s, got %s", c.creator, caller))
 75	}
 76	if c.counterpartyClientID != "" {
 77		panic("cannot register counterparty once it is already set")
 78	}
 79	c.counterpartyClientID = counterpartyClientID
 80	c.counterpartyMerklePrefix = counterpartyMerklePrefix
 81}
 82
 83// UpdateClient will update the given IBC v2 light client with a new header.
 84// Can also be used to submit a misbehavior (clientMessage can be a header or
 85// a misbehavior, maybe split into 2 functions would make more sense here).
 86func UpdateClient(cur realm, clientID string, clientMessage lightclient.ClientMessage) {
 87	if err := clientMessage.ValidateBasic(); err != nil {
 88		panic(err)
 89	}
 90	c := store.getClient(clientID)
 91	if c == nil {
 92		panic(ufmt.Sprintf("client %s not found", clientID))
 93	}
 94	ensureAuthorizedRelayer()
 95	if c.typ != clientMessage.ClientType() {
 96		panic("client type for client state and client message do not match")
 97	}
 98	if status := c.lightClient.Status(); status != lightclient.Active {
 99		panic(ufmt.Sprintf("cannot update client (%s) with status %s", c.id, status))
100	}
101
102	err := c.lightClient.VerifyClientMessage(clientMessage)
103	if err != nil {
104		panic(err)
105	}
106
107	foundMisbehavior := c.lightClient.CheckForMisbehaviour(clientMessage)
108	if foundMisbehavior {
109		c.lightClient.UpdateStateOnMisbehaviour(clientMessage)
110
111		chain.Emit(types.EventTypeSubmitMisbehaviour,
112			types.AttributeKeyClientID, c.id,
113			types.AttributeKeyClientType, c.typ,
114		)
115		return
116	}
117
118	consensusHeights := c.lightClient.UpdateState(clientMessage)
119
120	// Emit update client event
121	var consensusHeightsStr string
122	for i, h := range consensusHeights {
123		consensusHeightsStr += h.String()
124		if i < len(consensusHeights)-1 {
125			consensusHeightsStr += ", "
126		}
127	}
128	chain.Emit(types.EventTypeUpdateClient,
129		types.AttributeKeyClientID, c.id,
130		types.AttributeKeyClientType, c.typ,
131		types.AttributeKeyConsensusHeights, consensusHeightsStr,
132	)
133}
134
135// UpgradeClient upgrades the client to a new client and consensus state,
136// verified by proofs that the counterparty chain committed to those states
137// at its UpgradePath under the current client's latest consensus root.
138func UpgradeClient(cur realm, clientID string, clientState, consensusState, proofUpgradeClient, proofUpgradeConsensusState any) {
139	if err := host.ClientIdentifierValidator(clientID); err != nil {
140		panic(err)
141	}
142	ensureAuthorizedRelayer()
143	c := store.getClient(clientID)
144	if c == nil {
145		panic(ufmt.Sprintf("client %s not found", clientID))
146	}
147	if status := c.lightClient.Status(); status != lightclient.Active {
148		panic(ufmt.Sprintf("cannot upgrade client (%s) with status %s", c.id, status))
149	}
150	if err := c.lightClient.VerifyUpgradeAndUpdateState(clientState, consensusState, proofUpgradeClient, proofUpgradeConsensusState); err != nil {
151		panic(err)
152	}
153	chain.Emit(types.EventTypeUpgradeClient,
154		types.AttributeKeyClientID, c.id,
155		types.AttributeKeyClientType, c.typ,
156		types.AttributeKeyConsensusHeight, c.lightClient.LatestHeight().String(),
157	)
158}
159
160// RecoverClient recovers a frozen or expired subject client using a healthy
161// substitute client that tracks the same counterparty chain.
162func RecoverClient(cur realm, subjectClientID, substituteClientID string) {
163	ensureAdminCaller()
164	if subjectClientID == substituteClientID {
165		panic("subject and substitute client IDs must differ")
166	}
167	subject := store.getClient(subjectClientID)
168	if subject == nil {
169		panic(ufmt.Sprintf("subject client %s not found", subjectClientID))
170	}
171	substitute := store.getClient(substituteClientID)
172	if substitute == nil {
173		panic(ufmt.Sprintf("substitute client %s not found", substituteClientID))
174	}
175	if subject.typ != substitute.typ {
176		panic(ufmt.Sprintf(
177			"subject client type %s does not match substitute client type %s",
178			subject.typ, substitute.typ,
179		))
180	}
181	if status := subject.lightClient.Status(); status != lightclient.Frozen && status != lightclient.Expired {
182		panic(ufmt.Sprintf(
183			"cannot recover subject client %s with status %s",
184			subject.id, status,
185		))
186	}
187	if status := substitute.lightClient.Status(); status != lightclient.Active {
188		panic(ufmt.Sprintf(
189			"substitute client %s must be Active, got %s",
190			substitute.id, status,
191		))
192	}
193	if err := subject.lightClient.RecoverClient(substitute.lightClient); err != nil {
194		panic(err)
195	}
196	chain.Emit(types.EventTypeRecoverClient,
197		types.AttributeKeySubjectClientID, subject.id,
198		types.AttributeKeySubstituteClientID, substitute.id,
199		types.AttributeKeyClientType, subject.typ,
200	)
201}