impl.gno
7.67 Kb · 217 lines
1package ucs03_zkgm
2
3import (
4 "chain/runtime/unsafe"
5
6 types "gno.land/p/onbloc/ibc/union/types"
7
8 z "gno.land/p/onbloc/ibc/union/zkgm"
9 u256 "gno.land/p/onbloc/math/uint256"
10 zkgm "gno.land/r/onbloc/ibc/union/apps/ucs03_zkgm"
11 "gno.land/r/onbloc/ibc/union/core"
12)
13
14// ucs03ZkgmV1 is the v1 implementation of the zkgm app. It holds the injected
15// proxy state (zkgm.IStore) and contains all the protocol logic; the proxy keeps
16// only the stable identity, the escrow funds, and the swap point. State writes go
17// through the store's borrow-rule setters: v.store.SetXXX(0, rlm, ...).
18type ucs03ZkgmV1 struct {
19 store zkgm.IStore
20}
21
22var _ zkgm.IApp = &ucs03ZkgmV1{}
23
24// NewUCS03ZkgmV1 constructs a fresh v1 impl bound to the injected store.
25func NewUCS03ZkgmV1(store zkgm.IStore) zkgm.IApp {
26 return &ucs03ZkgmV1{store: store}
27}
28
29// OnChannelOpenInit accepts a channel only if it speaks the zkgm protocol version.
30func (v *ucs03ZkgmV1) OnChannelOpenInit(_ int, rlm realm, connectionId types.ConnectionId, channelId types.ChannelId, version string, relayer address) {
31 assertIsRlmCurrent(0, rlm)
32
33 enforceVersion(version)
34}
35
36// OnChannelOpenTry enforces the protocol version on both sides of the handshake.
37func (v *ucs03ZkgmV1) OnChannelOpenTry(_ int, rlm realm, connectionId types.ConnectionId, channelId types.ChannelId, version string, counterpartyVersion string, relayer address) {
38 assertIsRlmCurrent(0, rlm)
39
40 enforceVersion(version)
41 enforceVersion(counterpartyVersion)
42}
43
44// OnChannelOpenAck enforces the counterparty's negotiated protocol version.
45func (v *ucs03ZkgmV1) OnChannelOpenAck(_ int, rlm realm, channelId types.ChannelId, counterpartyChannelId types.ChannelId, counterpartyVersion string, relayer address) {
46 assertIsRlmCurrent(0, rlm)
47
48 enforceVersion(counterpartyVersion)
49}
50
51// OnChannelOpenConfirm completes the handshake; zkgm keeps no per-channel state.
52func (v *ucs03ZkgmV1) OnChannelOpenConfirm(_ int, rlm realm, channelId types.ChannelId, relayer address) {
53 assertIsRlmCurrent(0, rlm)
54}
55
56// Channel closing is not supported by zkgm (reference returns an error).
57func (v *ucs03ZkgmV1) OnChannelCloseInit(_ int, rlm realm, channelId types.ChannelId, relayer address) {
58 assertIsRlmCurrent(0, rlm)
59
60 panic("channel closing is not allowed")
61}
62
63func (v *ucs03ZkgmV1) OnChannelCloseConfirm(_ int, rlm realm, channelId types.ChannelId, relayer address) {
64 assertIsRlmCurrent(0, rlm)
65
66 panic("channel closing is not allowed")
67}
68
69// OnRecvPacket decodes the packet and dispatches it; decode/exec failures return a failure ack rather than aborting.
70func (v *ucs03ZkgmV1) OnRecvPacket(_ int, rlm realm, packet types.Packet, relayer address, relayerMsg []byte) types.RecvPacketResult {
71 assertIsRlmCurrent(0, rlm)
72
73 zp, err := z.DecodeZkgmPacket(types.CloneBytes(packet.Data))
74 if err != nil {
75 zkgm.EmitRecvError(0, rlm, packet.SourceChannelId, packet.DestinationChannelId, err.Error())
76 return failureResult(universalErrorAck())
77 }
78
79 res, err := v.dispatchExecutePacket(0, rlm, packet, relayer, relayerMsg, zp.Salt, zp.Path, zp.Instruction, false)
80 if err != nil {
81 zkgm.EmitRecvError(0, rlm, packet.SourceChannelId, packet.DestinationChannelId, err.Error())
82 return failureResult(universalErrorAck())
83 }
84
85 // An only-maker outcome (a maker-only fill that could not be satisfied, or
86 // an only-maker child anywhere in a batch) must revert the whole recv tx —
87 // no ack is written and any already-applied batch-child mutations are rolled
88 // back. This mirrors union's reply handler returning Err(OnlyMaker) for both
89 // the single-instruction and batch cases (contract.rs L2530, L2545), and the
90 // symmetric guard already in OnIntentRecvPacket. Core does not recover recv
91 // panics, so this reverts atomically.
92 if isOnlyMakerAck(res.Acknowledgement) {
93 panic(errIsOnlyMakerAck)
94 }
95
96 return res
97}
98
99// OnIntentRecvPacket executes the market-maker fast path. Only-maker outcomes
100// abort the intent tx so proven recv can proceed instead. This is also the
101// explicit stop point for market-maker fills that require unsupported native
102// maker funding.
103func (v *ucs03ZkgmV1) OnIntentRecvPacket(_ int, rlm realm, packet types.Packet, marketMaker address, marketMakerMsg []byte) types.RecvPacketResult {
104 assertIsRlmCurrent(0, rlm)
105
106 zp, err := z.DecodeZkgmPacket(types.CloneBytes(packet.Data))
107 if err != nil {
108 zkgm.EmitRecvError(0, rlm, packet.SourceChannelId, packet.DestinationChannelId, err.Error())
109 return failureResult(universalErrorAck())
110 }
111
112 res, err := v.dispatchExecutePacket(0, rlm, packet, marketMaker, marketMakerMsg, zp.Salt, zp.Path, zp.Instruction, true)
113 if err != nil {
114 zkgm.EmitRecvError(0, rlm, packet.SourceChannelId, packet.DestinationChannelId, err.Error())
115 return failureResult(universalErrorAck())
116 }
117
118 if isOnlyMakerAck(res.Acknowledgement) {
119 // Intent recv must not silently succeed without a maker fill. Panicking
120 // preserves the proven recv fallback path and makes unsupported native
121 // market-maker funding fail explicitly until funds plumbing is added.
122 panic("only maker")
123 }
124
125 return res
126}
127
128// OnAcknowledgementPacket routes the ack to its instruction, or to the forwarded parent packet.
129func (v *ucs03ZkgmV1) OnAcknowledgementPacket(_ int, rlm realm, packet types.Packet, acknowledgement []byte, relayer address) {
130 assertIsRlmCurrent(0, rlm)
131
132 zp, err := z.DecodeZkgmPacket(types.CloneBytes(packet.Data))
133 if err != nil {
134 panic(err.Error())
135 }
136
137 if v.handleForwardChild(0, rlm, packet, zp, acknowledgement) {
138 return
139 }
140
141 outerAck, err := z.DecodeAck(acknowledgement)
142 if err != nil {
143 panic(err.Error())
144 }
145
146 successful := outerAck.Tag != nil && outerAck.Tag.Eq(tagAckSuccess())
147 if err := v.acknowledgeInternal(0, rlm, packet, relayer, zp.Salt, zp.Path, zp.Instruction, successful, outerAck.InnerAck); err != nil {
148 panic(err.Error())
149 }
150}
151
152// OnTimeoutPacket refunds the timed-out instruction, or settles the forwarded parent packet.
153func (v *ucs03ZkgmV1) OnTimeoutPacket(_ int, rlm realm, packet types.Packet, relayer address) {
154 assertIsRlmCurrent(0, rlm)
155
156 zp, err := z.DecodeZkgmPacket(types.CloneBytes(packet.Data))
157 if err != nil {
158 panic(err.Error())
159 }
160
161 if v.handleForwardChild(0, rlm, packet, zp, universalErrorAck()) {
162 return
163 }
164
165 if err := v.timeoutInternal(0, rlm, packet, zp.Salt, zp.Path, zp.Instruction); err != nil {
166 panic(err.Error())
167 }
168}
169
170// Send verifies the instruction against the attached funds, namespaces the salt, and emits the packet through core.
171func (v *ucs03ZkgmV1) Send(_ int, rlm realm, channelId types.ChannelId, timeoutTimestamp types.Timestamp, salt [32]byte, instruction z.Instruction) types.Packet {
172 assertIsRlmCurrent(0, rlm)
173
174 sender := rlm.Previous().Address().String()
175
176 funds := newFunds(parseCoins(""))
177
178 // SAFE: native coins attached with -send live on the chain-root envelope,
179 // not on rlm.Previous(). Guard direct user calls before reading
180 // unsafe.OriginSend(), then seed the proxy escrow budget from it.
181 if rlm.Previous().IsUserCall() {
182 sentCoins := unsafe.OriginSend()
183 funds = newFunds(sentCoins)
184 }
185
186 err := v.verifyInternal(0, rlm, funds, channelId, u256.Zero(), instruction)
187 if err != nil {
188 panic(err)
189 }
190
191 err = funds.assertEmpty()
192 if err != nil {
193 panic(err)
194 }
195
196 // Only user-initiated sends apply DeriveSenderSalt. Forwarded packets are
197 // built in forward.gno and use DeriveForwardSalt instead.
198 hashedSalt := z.DeriveSenderSalt([]byte(sender), salt)
199
200 data, err := z.EncodeZkgmPacket(z.ZkgmPacket{
201 Salt: hashedSalt,
202 Path: u256.Zero(),
203 Instruction: instruction,
204 })
205 if err != nil {
206 panic(err)
207 }
208
209 return core.SendPacket(cross(rlm), channelId, timeoutTimestamp, data)
210}
211
212// enforceVersion rejects channels that do not speak the zkgm protocol version.
213func enforceVersion(version string) {
214 if version != zkgm.Version {
215 panic("unsupported channel version")
216 }
217}