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

dispatch.gno

8.29 Kb · 283 lines
  1package ucs03_zkgm
  2
  3import (
  4	"bytes"
  5
  6	types "gno.land/p/onbloc/ibc/union/types"
  7
  8	"gno.land/p/nt/ufmt/v0"
  9	z "gno.land/p/onbloc/ibc/union/zkgm"
 10	u256 "gno.land/p/onbloc/math/uint256"
 11	core "gno.land/r/onbloc/ibc/union/core"
 12)
 13
 14// verifyInternal routes an instruction to its opcode-specific verifier (the send-side validation entry point).
 15// reference: https://github.com/unionlabs/union/blob/d91c5e94354e15801bd5f82dc658eae3b79f2dad/cosmwasm/app/ucs03-zkgm/src/contract.rs#L2742-L2796
 16func (v *ucs03ZkgmV1) verifyInternal(_ int, rlm realm, funds *funds, channelId types.ChannelId, path *u256.Uint, instruction z.Instruction) error {
 17	switch instruction.Opcode {
 18	case z.OP_TOKEN_ORDER:
 19		order, err := decodeOrderInstruction(instruction)
 20		if err != nil {
 21			return err
 22		}
 23
 24		return v.verifyTokenOrderV2(0, rlm, funds, channelId, path, order)
 25	case z.OP_BATCH:
 26		batch, err := decodeBatchInstruction(instruction)
 27		if err != nil {
 28			return err
 29		}
 30
 31		return v.verifyBatch(0, rlm, funds, channelId, path, batch)
 32	case z.OP_FORWARD:
 33		forward, err := decodeForwardInstruction(instruction)
 34		if err != nil {
 35			return err
 36		}
 37
 38		return v.verifyForward(0, rlm, funds, channelId, forward)
 39	case z.OP_CALL:
 40		call, err := decodeCallInstruction(instruction)
 41		if err != nil {
 42			return err
 43		}
 44
 45		return v.verifyCall(0, rlm, call)
 46	default:
 47		return makeError(errUnsupportedOpcode)
 48	}
 49}
 50
 51// execFatal is a sentinel panic value.
 52//
 53// Unlike ordinary panics, values of this type are re-panicked by the recover
 54// handler so the VM can observe the failure after transaction rollback.
 55type execFatal struct{ msg string }
 56
 57func (e execFatal) Error() string { return e.msg }
 58
 59// dispatchExecutePacket routes a received instruction to its opcode handler, turning non-fatal errors into a failure ack.
 60func (v *ucs03ZkgmV1) dispatchExecutePacket(
 61	_ int, rlm realm,
 62	packet types.Packet,
 63	relayer address,
 64	relayerMsg []byte,
 65	salt [32]byte,
 66	path *u256.Uint,
 67	instruction z.Instruction,
 68	intent bool,
 69) (result types.RecvPacketResult, err error) {
 70	defer func() {
 71		if r := recover(); r != nil {
 72			if _, ok := r.(execFatal); ok {
 73				panic(r)
 74			}
 75
 76			result = failureResult(universalErrorAck())
 77			err = nil
 78		}
 79	}()
 80
 81	switch instruction.Opcode {
 82	case z.OP_TOKEN_ORDER:
 83		order, err := decodeOrderInstruction(instruction)
 84		if err != nil {
 85			return core.NewRecvPacketResult(types.PacketStatusUnknown, nil), err
 86		}
 87
 88		return v.executeTokenOrderV2(0, rlm, packet, relayer, relayerMsg, salt, path, order, intent)
 89	case z.OP_BATCH:
 90		batch, err := decodeBatchInstruction(instruction)
 91		if err != nil {
 92			return core.NewRecvPacketResult(types.PacketStatusUnknown, nil), err
 93		}
 94
 95		ack, err := v.executeBatch(0, rlm, packet, relayer, relayerMsg, salt, path, batch, intent)
 96		if err != nil {
 97			return core.NewRecvPacketResult(types.PacketStatusUnknown, nil), err
 98		}
 99
100		return core.NewRecvPacketResult(types.PacketStatusSuccess, ack), nil
101	case z.OP_CALL:
102		call, err := decodeCallInstruction(instruction)
103		if err != nil {
104			return core.NewRecvPacketResult(types.PacketStatusUnknown, nil), err
105		}
106
107		return v.executeCall(0, rlm, packet, relayer, relayerMsg, path, call, intent)
108	case z.OP_FORWARD:
109		forward, err := decodeForwardInstruction(instruction)
110		if err != nil {
111			return core.NewRecvPacketResult(types.PacketStatusUnknown, nil), err
112		}
113
114		return v.executeForward(0, rlm, packet, salt, path, forward, intent)
115	default:
116		return core.NewRecvPacketResult(types.PacketStatusUnknown, nil), makeError(errUnsupportedOpcode)
117	}
118}
119
120// acknowledgeInternal routes an acknowledgement to its opcode handler.
121func (v *ucs03ZkgmV1) acknowledgeInternal(_ int, rlm realm, packet types.Packet, relayer address, salt [32]byte, path *u256.Uint, instruction z.Instruction, successful bool, ack []byte) error {
122	switch instruction.Opcode {
123	case z.OP_TOKEN_ORDER:
124		order, err := decodeOrderInstruction(instruction)
125		if err != nil {
126			return err
127		}
128
129		var orderAck z.TokenOrderAck
130
131		hasOrderAck := false
132
133		if successful {
134			decoded, err := z.DecodeTokenOrderAck(ack)
135			if err != nil {
136				return err
137			}
138
139			orderAck = decoded
140			hasOrderAck = true
141		}
142
143		return v.acknowledgeTokenOrderV2(0, rlm, packet, relayer, salt, path, order, orderAck, hasOrderAck)
144	case z.OP_BATCH:
145		batch, err := decodeBatchInstruction(instruction)
146		if err != nil {
147			return err
148		}
149
150		return v.acknowledgeBatch(0, rlm, packet, relayer, salt, path, batch, successful, ack)
151	case z.OP_CALL:
152		call, err := decodeCallInstruction(instruction)
153		if err != nil {
154			return err
155		}
156
157		return v.acknowledgeCall(0, rlm, packet, path, call)
158	default:
159		return makeError(errUnsupportedOpcode)
160	}
161}
162
163// timeoutInternal routes a timeout to its opcode handler.
164func (v *ucs03ZkgmV1) timeoutInternal(_ int, rlm realm, packet types.Packet, salt [32]byte, path *u256.Uint, instruction z.Instruction) error {
165	switch instruction.Opcode {
166	case z.OP_TOKEN_ORDER:
167		order, err := decodeOrderInstruction(instruction)
168		if err != nil {
169			return err
170		}
171
172		return v.refundV2(0, rlm, path, packet.SourceChannelId, order)
173	case z.OP_BATCH:
174		batch, err := decodeBatchInstruction(instruction)
175		if err != nil {
176			return err
177		}
178
179		return v.timeoutBatch(0, rlm, packet, salt, path, batch)
180	case z.OP_CALL:
181		call, err := decodeCallInstruction(instruction)
182		if err != nil {
183			return err
184		}
185
186		return v.timeoutCall(0, rlm, packet, path, call)
187	default:
188		return makeError(errUnsupportedOpcode)
189	}
190}
191
192// decodeOrderInstruction decodes a v2 token order, rejecting unsupported instruction versions.
193func decodeOrderInstruction(instruction z.Instruction) (z.TokenOrderV2, error) {
194	if instruction.Version != z.INSTR_VERSION_2 {
195		return z.TokenOrderV2{}, makeError(errUnsupportedTokenOrderVersion)
196	}
197
198	return z.DecodeTokenOrderV2(instruction.Operand)
199}
200
201// decodeCallInstruction decodes a call, rejecting unsupported instruction versions.
202func decodeCallInstruction(instruction z.Instruction) (z.Call, error) {
203	if instruction.Version != z.INSTR_VERSION_0 {
204		return z.Call{}, makeError(errUnsupportedCallVersion)
205	}
206
207	return z.DecodeCall(instruction.Operand)
208}
209
210// decodeBatchInstruction decodes a batch, rejecting unsupported instruction versions.
211func decodeBatchInstruction(instruction z.Instruction) (z.Batch, error) {
212	if instruction.Version != z.INSTR_VERSION_0 {
213		return z.Batch{}, makeError(errUnsupportedBatchVersion)
214	}
215
216	return z.DecodeBatch(instruction.Operand)
217}
218
219// decodeForwardInstruction decodes a forward, rejecting unsupported instruction versions.
220func decodeForwardInstruction(instruction z.Instruction) (z.Forward, error) {
221	if instruction.Version != z.INSTR_VERSION_0 {
222		return z.Forward{}, makeError(errUnsupportedForwardVersion)
223	}
224
225	return z.DecodeForward(instruction.Operand)
226}
227
228// mustEncodeUniversalErrorAck returns the ABI-encoded failure acknowledgement
229// `z.Ack{Tag: TAG_ACK_FAILURE, InnerAck: empty}` that zkgm-internal recv failures
230// emit. The empty inner ack matches union's `Ack{tag: TAG_ACK_FAILURE, inner_ack:
231// Default::default()}`; the encoded Ack itself is non-empty, satisfying core's
232// non-empty acknowledgement requirement.
233func mustEncodeUniversalErrorAck() []byte {
234	bz, err := z.EncodeAck(z.Ack{
235		Tag:      u256.NewUint(0),
236		InnerAck: []byte{},
237	})
238	if err != nil {
239		panic(err)
240	}
241
242	return bz
243}
244
245// failureResult wraps an ack as a failed recv result.
246func failureResult(ack []byte) types.RecvPacketResult {
247	return core.NewRecvPacketResult(types.PacketStatusFailure, ack)
248}
249
250// universalErrorAck returns the cached sentinel error acknowledgement.
251func universalErrorAck() []byte {
252	return mustEncodeUniversalErrorAck()
253}
254
255// tokenOrderDestinationChannel rejects the zero-channel sentinel so the
256// derived destChan is safe to feed into UpdateChannelPath / PopChannelFromPath
257// without colliding with the "no channel" slot. This mirrors the TokenOrder
258// path helper's channel-zero rejection.
259func tokenOrderDestinationChannel(channelId types.ChannelId) (uint32, error) {
260	raw := uint32(channelId)
261	if raw == 0 {
262		return 0, makeError(errInvalidTokenOrderDestChannel)
263	}
264
265	return raw, nil
266}
267
268// isOnlyMakerAck reports whether ack is the only-maker sentinel.
269func isOnlyMakerAck(ack []byte) bool {
270	return bytes.Equal(ack, z.ACK_ERR_ONLY_MAKER)
271}
272
273// extractPanicMessage renders a recovered panic value as a string.
274func extractPanicMessage(r any) string {
275	switch v := r.(type) {
276	case string:
277		return v
278	case error:
279		return v.Error()
280	default:
281		return ufmt.Sprintf("%v", v)
282	}
283}