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

call.gno

5.96 Kb · 192 lines
  1package ucs03_zkgm
  2
  3import (
  4	types "gno.land/p/onbloc/ibc/union/types"
  5
  6	z "gno.land/p/onbloc/ibc/union/zkgm"
  7	u256 "gno.land/p/onbloc/math/uint256"
  8	core "gno.land/r/onbloc/ibc/union/core"
  9)
 10
 11// verifyCall rejects eureka mode and requires the call sender to match the dispatch sender.
 12// reference: https://github.com/unionlabs/union/blob/d91c5e94354e15801bd5f82dc658eae3b79f2dad/cosmwasm/app/ucs03-zkgm/src/contract.rs#L2997-L3019
 13func (v *ucs03ZkgmV1) verifyCall(_ int, rlm realm, call z.Call) error {
 14	if call.Eureka {
 15		return makeError(errEurekaUnsupported)
 16	}
 17
 18	if string(call.Sender) != rlm.Previous().Address().String() {
 19		return makeError(errInvalidCallSender)
 20	}
 21
 22	return nil
 23}
 24
 25// executeCall dispatches an OP_CALL to the registered receiver's OnZkgm callback; failures and panics fail closed.
 26// reference: https://github.com/unionlabs/union/blob/d91c5e94354e15801bd5f82dc658eae3b79f2dad/cosmwasm/app/ucs03-zkgm/src/contract.rs#L1500-L1683
 27func (v *ucs03ZkgmV1) executeCall(_ int, rlm realm, packet types.Packet, relayer address, relayerMsg []byte, path *u256.Uint, call z.Call, intent bool) (types.RecvPacketResult, error) {
 28	// TODO: when eureka is supported, fail it closed on intent (return
 29	// ACK_ERR_ONLY_MAKER) like every other opcode instead of a failure ack.
 30	if call.Eureka {
 31		return callErrAck(0, rlm, makeError(errEurekaUnsupported)), nil
 32	}
 33
 34	sender := types.CloneBytes(call.Sender)
 35	calldata := types.CloneBytes(call.ContractCalldata)
 36	receiverPath := string(call.ContractAddress)
 37	receiver, ok := v.store.GetReceiver(receiverPath)
 38
 39	if !ok || receiver == nil {
 40		return callErrAck(0, rlm, makeError(errReceiverNotRegistered, receiverPath)), nil
 41	}
 42
 43	relayerStr := string(relayer)
 44	proxyAccount := z.PredictCallProxyAccount(path, packet.DestinationChannelId, sender)
 45
 46	// Intent settlement dispatches OnIntentZkgm to the receiver (union execute_call
 47	// intent arm), and is maker-only: any receiver failure reverts to ACK_ERR_ONLY_MAKER
 48	// so the proven recv can settle instead.
 49	if intent {
 50		env := z.IntentCallEnv{
 51			Caller:             relayerStr,
 52			ProxyAccount:       proxyAccount,
 53			Path:               path,
 54			SourceChannel:      packet.SourceChannelId.String(),
 55			DestinationChannel: packet.DestinationChannelId.String(),
 56			Sender:             sender,
 57			Calldata:           calldata,
 58			MarketMaker:        []byte(relayerStr),
 59			MarketMakerMsg:     types.CloneBytes(relayerMsg),
 60		}
 61		if err, panicked := safeCallOnIntentZkgm(0, rlm, receiver, env); panicked || err != nil {
 62			return core.NewRecvPacketResult(types.PacketStatusSuccess, types.CloneBytes(z.ACK_ERR_ONLY_MAKER)), nil
 63		}
 64
 65		return callSuccessAck()
 66	}
 67
 68	env := z.CallEnv{
 69		Caller:             relayerStr,
 70		ProxyAccount:       proxyAccount,
 71		Path:               path,
 72		SourceChannel:      packet.SourceChannelId.String(),
 73		DestinationChannel: packet.DestinationChannelId.String(),
 74		Sender:             sender,
 75		Calldata:           calldata,
 76		Relayer:            []byte(relayerStr),
 77		RelayerMsg:         types.CloneBytes(relayerMsg),
 78	}
 79
 80	err, panicked := safeCallOnZkgm(0, rlm, receiver, env)
 81	if panicked {
 82		return core.NewRecvPacketResult(types.PacketStatusSuccess, types.CloneBytes(z.ACK_ERR_ONLY_MAKER)), nil
 83	}
 84
 85	if err != nil {
 86		return callErrAck(0, rlm, err), nil
 87	}
 88
 89	return callSuccessAck()
 90}
 91
 92// callSuccessAck builds the empty success acknowledgement a completed call writes.
 93func callSuccessAck() (types.RecvPacketResult, error) {
 94	ack, err := z.EncodeAck(z.Ack{Tag: tagAckSuccess(), InnerAck: []byte{}})
 95	if err != nil {
 96		return core.NewRecvPacketResult(types.PacketStatusUnknown, nil), err
 97	}
 98
 99	return core.NewRecvPacketResult(types.PacketStatusSuccess, ack), nil
100}
101
102// acknowledgeCall is a no-op for standard calls; eureka mode is unsupported.
103func (v *ucs03ZkgmV1) acknowledgeCall(_ int, rlm realm, packet types.Packet, path *u256.Uint, call z.Call) error {
104	if call.Eureka {
105		return makeError(errEurekaUnsupported)
106	}
107
108	return nil
109}
110
111// timeoutCall is a no-op for standard calls; eureka mode is unsupported.
112func (v *ucs03ZkgmV1) timeoutCall(_ int, rlm realm, packet types.Packet, path *u256.Uint, call z.Call) error {
113	if call.Eureka {
114		return makeError(errEurekaUnsupported)
115	}
116
117	return nil
118}
119
120// safeCallOnZkgm runs the receiver and reports a failure as panicked=true.
121// revive is preferred since it also catches cross-realm aborts. The recover
122// fallback, used only when revive is unsupported, catches ordinary panics.
123func safeCallOnZkgm(_ int, rlm realm, receiver z.Zkgmable, env z.CallEnv) (err error, panicked bool) {
124	if canRevive() {
125		p := revive(func() {
126			err = receiver.OnZkgm(cross(rlm), env)
127		})
128
129		return err, p != nil
130	}
131
132	defer func() {
133		if recover() != nil {
134			err = nil
135			panicked = true
136		}
137	}()
138
139	err = receiver.OnZkgm(cross(rlm), env)
140
141	return err, false
142}
143
144// safeCallOnIntentZkgm runs the receiver's intent callback and reports a failure
145// as panicked=true, mirroring safeCallOnZkgm for the OnIntentZkgm path.
146func safeCallOnIntentZkgm(_ int, rlm realm, receiver z.Zkgmable, env z.IntentCallEnv) (err error, panicked bool) {
147	if canRevive() {
148		p := revive(func() {
149			err = receiver.OnIntentZkgm(cross(rlm), env)
150		})
151
152		return err, p != nil
153	}
154
155	defer func() {
156		if recover() != nil {
157			err = nil
158			panicked = true
159		}
160	}()
161
162	err = receiver.OnIntentZkgm(cross(rlm), env)
163
164	return err, false
165}
166
167// canRevive reports whether revive works in the current runtime. revive panics
168// rather than returning when the interpreter lacks support for it.
169func canRevive() (ok bool) {
170	ok = true
171
172	defer func() {
173		if recover() != nil {
174			ok = false
175		}
176	}()
177	revive(func() {})
178
179	return ok
180}
181
182// callErrAck encodes err as a failure acknowledgement for the call.
183func callErrAck(_ int, rlm realm, err error) types.RecvPacketResult {
184	ack, encErr := z.EncodeAck(z.Ack{Tag: tagAckFailure(), InnerAck: []byte(err.Error())})
185	if encErr != nil {
186		// EncodeAck on a 2-field schema is essentially infallible. A failure
187		// would emit a non-Tag-prefixed ack the counterparty can't decode.
188		panic(encErr)
189	}
190
191	return failureResult(ack)
192}