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

channel.gno

9.69 Kb · 325 lines
  1package core
  2
  3import (
  4	"gno.land/p/onbloc/ibc/union/types"
  5	core "gno.land/r/onbloc/ibc/union/core"
  6)
  7
  8// channel.gno: channel handshake and close logic. The admin gate stays on the
  9// proxy entrypoint; the Force* recovery variants remain proxy admin operations.
 10// Each handshake step dispatches the matching callback to the channel's app.
 11
 12// ChannelOpenInit starts a channel in the Init state and notifies its app. The
 13// created channel id is observable through the emitted event, not returned.
 14func (c *coreV1) ChannelOpenInit(_ int, rlm realm, msg types.MsgChannelOpenInit) {
 15	assertIsRlmCurrent(0, rlm)
 16
 17	relayer := rlm.Previous().Address()
 18	portId := types.Bytes(types.CloneBytes(msg.PortId))
 19	connection := c.ensureConnectionState(msg.ConnectionId)
 20	channelId := c.store.NextChannelId(0, rlm)
 21
 22	channel := core.NewChannel(
 23		types.ChannelStateInit,
 24		msg.ConnectionId,
 25		0,
 26		types.Bytes(types.CloneBytes(msg.CounterpartyPortId)),
 27		msg.Version,
 28	)
 29	c.store.CreateChannel(0, rlm, channelId, channel, portId)
 30
 31	a, err := c.getAppAtPort(portId)
 32	if err != nil {
 33		panic(err)
 34	}
 35
 36	a.OnChannelOpenInit(cross(rlm), msg.ConnectionId, channelId, msg.Version, relayer)
 37	core.EmitChannelOpenInit(0, rlm, portId, channelId, channel, connection)
 38}
 39
 40// ChannelOpenTry proves a counterparty Init and opens the local channel in
 41// TryOpen, notifying its app.
 42func (c *coreV1) ChannelOpenTry(_ int, rlm realm, msg types.MsgChannelOpenTry) {
 43	assertIsRlmCurrent(0, rlm)
 44
 45	c.channelOpenTry(0, rlm, msg, true)
 46}
 47
 48// ForceChannelOpenTry is the admin recovery variant of ChannelOpenTry that skips
 49// counterparty proof verification.
 50func (c *coreV1) ForceChannelOpenTry(_ int, rlm realm, msg types.MsgChannelOpenTry) {
 51	assertIsRlmCurrent(0, rlm)
 52
 53	c.channelOpenTry(0, rlm, msg, false)
 54}
 55
 56func (c *coreV1) channelOpenTry(_ int, rlm realm, msg types.MsgChannelOpenTry, verify bool) {
 57	relayer := rlm.Previous().Address()
 58	portId := types.Bytes(types.CloneBytes(msg.PortId))
 59	if msg.Channel.State != types.ChannelStateTryOpen {
 60		panic(makeError(core.ErrInvalidChannelState))
 61	}
 62
 63	if msg.Channel.CounterpartyChannelId == 0 {
 64		panic(makeError(core.ErrInvalidCounterpartyChannelId))
 65	}
 66
 67	connection := c.ensureConnectionState(msg.Channel.ConnectionId)
 68	channelId := c.store.NextChannelId(0, rlm)
 69
 70	if verify {
 71		expectedChannel := core.NewChannel(
 72			types.ChannelStateInit,
 73			connection.CounterpartyConnectionId,
 74			0,
 75			portId,
 76			msg.CounterpartyVersion,
 77		)
 78		key := types.ChannelPath(msg.Channel.CounterpartyChannelId)
 79		value := core.ChannelValue(expectedChannel)
 80		c.verifyMembership(connection.ClientId, types.NewHeight(msg.ProofHeight), msg.ProofInit, key[:], value[:])
 81	}
 82
 83	c.store.CreateChannel(0, rlm, channelId, msg.Channel, portId)
 84
 85	a, err := c.getAppAtPort(portId)
 86	if err != nil {
 87		panic(err)
 88	}
 89
 90	a.OnChannelOpenTry(cross(rlm), msg.Channel.ConnectionId, channelId, msg.Channel.Version, msg.CounterpartyVersion, relayer)
 91
 92	core.EmitChannelOpenTry(0, rlm, portId, channelId, msg.Channel, msg.CounterpartyVersion, connection)
 93}
 94
 95// ChannelOpenAck proves the counterparty TryOpen and moves the local Init
 96// channel to Open, notifying its app.
 97func (c *coreV1) ChannelOpenAck(_ int, rlm realm, msg types.MsgChannelOpenAck) {
 98	assertIsRlmCurrent(0, rlm)
 99
100	c.channelOpenAck(0, rlm, msg, true)
101}
102
103// ForceChannelOpenAck is the admin recovery variant of ChannelOpenAck that skips
104// counterparty proof verification.
105func (c *coreV1) ForceChannelOpenAck(_ int, rlm realm, msg types.MsgChannelOpenAck) {
106	assertIsRlmCurrent(0, rlm)
107
108	c.channelOpenAck(0, rlm, msg, false)
109}
110
111func (c *coreV1) channelOpenAck(_ int, rlm realm, msg types.MsgChannelOpenAck, verify bool) {
112	relayer := rlm.Previous().Address()
113
114	channel, err := c.getChannel(msg.ChannelId)
115	if err != nil {
116		panic(err)
117	}
118
119	if channel.State != types.ChannelStateInit {
120		panic(makeError(core.ErrInvalidChannelState))
121	}
122
123	connection := c.ensureConnectionState(channel.ConnectionId)
124	portId, _ := c.store.GetChannelOwner(msg.ChannelId)
125
126	if verify {
127		expectedChannel := core.NewChannel(
128			types.ChannelStateTryOpen,
129			connection.CounterpartyConnectionId,
130			msg.ChannelId,
131			portId,
132			msg.CounterpartyVersion,
133		)
134		key := types.ChannelPath(msg.CounterpartyChannelId)
135		value := core.ChannelValue(expectedChannel)
136		c.verifyMembership(connection.ClientId, types.NewHeight(msg.ProofHeight), msg.ProofTry, key[:], value[:])
137	}
138
139	updated := core.NewChannel(
140		types.ChannelStateOpen,
141		channel.ConnectionId,
142		msg.CounterpartyChannelId,
143		channel.CounterpartyPortId.Clone(),
144		msg.CounterpartyVersion,
145	)
146	c.store.SaveChannel(0, rlm, msg.ChannelId, updated)
147
148	a, err := c.getAppAtPort(portId)
149	if err != nil {
150		panic(err)
151	}
152
153	a.OnChannelOpenAck(cross(rlm), msg.ChannelId, msg.CounterpartyChannelId, msg.CounterpartyVersion, relayer)
154
155	core.EmitChannelOpenAck(0, rlm, portId, msg.ChannelId, updated, connection)
156}
157
158// ChannelOpenConfirm proves the counterparty Open and moves the local TryOpen
159// channel to Open, notifying its app.
160func (c *coreV1) ChannelOpenConfirm(_ int, rlm realm, msg types.MsgChannelOpenConfirm) {
161	assertIsRlmCurrent(0, rlm)
162
163	c.channelOpenConfirm(0, rlm, msg, true)
164}
165
166// ForceChannelOpenConfirm is the admin recovery variant of ChannelOpenConfirm
167// that skips counterparty proof verification.
168func (c *coreV1) ForceChannelOpenConfirm(_ int, rlm realm, msg types.MsgChannelOpenConfirm) {
169	assertIsRlmCurrent(0, rlm)
170
171	c.channelOpenConfirm(0, rlm, msg, false)
172}
173
174func (c *coreV1) channelOpenConfirm(_ int, rlm realm, msg types.MsgChannelOpenConfirm, verify bool) {
175	relayer := rlm.Previous().Address()
176
177	channel, err := c.getChannel(msg.ChannelId)
178	if err != nil {
179		panic(err)
180	}
181
182	if channel.State != types.ChannelStateTryOpen {
183		panic(makeError(core.ErrInvalidChannelState))
184	}
185
186	if channel.CounterpartyChannelId == 0 {
187		panic(makeError(core.ErrInvalidCounterpartyChannelId))
188	}
189
190	connection := c.ensureConnectionState(channel.ConnectionId)
191	portId, _ := c.store.GetChannelOwner(msg.ChannelId)
192
193	if verify {
194		expectedChannel := core.NewChannel(
195			types.ChannelStateOpen,
196			connection.CounterpartyConnectionId,
197			msg.ChannelId,
198			portId,
199			channel.Version,
200		)
201		key := types.ChannelPath(channel.CounterpartyChannelId)
202		value := core.ChannelValue(expectedChannel)
203		c.verifyMembership(connection.ClientId, types.NewHeight(msg.ProofHeight), msg.ProofAck, key[:], value[:])
204	}
205
206	updated := core.NewChannel(
207		types.ChannelStateOpen,
208		channel.ConnectionId,
209		channel.CounterpartyChannelId,
210		channel.CounterpartyPortId.Clone(),
211		channel.Version,
212	)
213	c.store.SaveChannel(0, rlm, msg.ChannelId, updated)
214
215	a, err := c.getAppAtPort(portId)
216	if err != nil {
217		panic(err)
218	}
219
220	a.OnChannelOpenConfirm(cross(rlm), msg.ChannelId, relayer)
221
222	core.EmitChannelOpenConfirm(0, rlm, portId, msg.ChannelId, updated, connection)
223}
224
225// ChannelCloseInit closes an Open channel locally and notifies its app.
226func (c *coreV1) ChannelCloseInit(_ int, rlm realm, msg types.MsgChannelCloseInit) {
227	assertIsRlmCurrent(0, rlm)
228
229	relayer := rlm.Previous().Address()
230
231	channel, err := c.getChannel(msg.ChannelId)
232	if err != nil {
233		panic(err)
234	}
235
236	if channel.State != types.ChannelStateOpen {
237		panic(makeError(core.ErrInvalidChannelState))
238	}
239
240	if channel.CounterpartyChannelId == 0 {
241		panic(makeError(core.ErrInvalidCounterpartyChannelId))
242	}
243
244	connection := c.ensureConnectionState(channel.ConnectionId)
245	portId, _ := c.store.GetChannelOwner(msg.ChannelId)
246
247	updated := c.closedChannel(channel)
248	c.store.SaveChannel(0, rlm, msg.ChannelId, updated)
249
250	a, ok := c.store.GetAppByPortId(portId)
251	if !ok {
252		panic("port " + string(portId) + " not found")
253	}
254	a.OnChannelCloseInit(cross(rlm), msg.ChannelId, relayer)
255	core.EmitChannelCloseInit(0, rlm, portId, msg.ChannelId, updated, connection)
256}
257
258// ChannelCloseConfirm proves the counterparty Closed and closes the local
259// channel, notifying its app.
260func (c *coreV1) ChannelCloseConfirm(_ int, rlm realm, msg types.MsgChannelCloseConfirm) {
261	assertIsRlmCurrent(0, rlm)
262
263	relayer := rlm.Previous().Address()
264
265	channel, err := c.getChannel(msg.ChannelId)
266	if err != nil {
267		panic(err)
268	}
269
270	if channel.State != types.ChannelStateOpen {
271		panic(makeError(core.ErrInvalidChannelState))
272	}
273
274	if channel.CounterpartyChannelId == 0 {
275		panic(makeError(core.ErrInvalidCounterpartyChannelId))
276	}
277
278	connection := c.ensureConnectionState(channel.ConnectionId)
279	portId, _ := c.store.GetChannelOwner(msg.ChannelId)
280
281	expectedChannel := core.NewChannel(
282		types.ChannelStateClosed,
283		connection.CounterpartyConnectionId,
284		msg.ChannelId,
285		portId,
286		channel.Version,
287	)
288	key := types.ChannelPath(channel.CounterpartyChannelId)
289	value := core.ChannelValue(expectedChannel)
290	c.verifyMembership(connection.ClientId, types.NewHeight(msg.ProofHeight), msg.ProofInit, key[:], value[:])
291
292	updated := c.closedChannel(channel)
293	c.store.SaveChannel(0, rlm, msg.ChannelId, updated)
294
295	a, ok := c.store.GetAppByPortId(portId)
296	if !ok {
297		panic("port " + string(portId) + " not found")
298	}
299	a.OnChannelCloseConfirm(cross(rlm), msg.ChannelId, relayer)
300	core.EmitChannelCloseConfirm(0, rlm, portId, msg.ChannelId, updated, connection)
301}
302
303// closedChannel returns a Closed copy of channel without mutating the store-owned
304// value.
305func (c *coreV1) closedChannel(channel types.Channel) types.Channel {
306	return core.NewChannel(
307		types.ChannelStateClosed,
308		channel.ConnectionId,
309		channel.CounterpartyChannelId,
310		channel.CounterpartyPortId.Clone(),
311		channel.Version,
312	)
313}
314
315// getChannel returns the stored channel for channelId, or an error when it is
316// missing. It centralizes the lookup shared by the handshake and packet paths;
317// the caller (entrypoint) decides whether a miss reverts.
318func (c *coreV1) getChannel(channelId types.ChannelId) (types.Channel, error) {
319	channel, ok := c.store.GetChannel(channelId)
320	if !ok {
321		return types.Channel{}, makeError(core.ErrChannelNotFound, channelId.String())
322	}
323
324	return channel, nil
325}