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

forward.gno

5.89 Kb · 157 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	zkgm "gno.land/r/onbloc/ibc/union/apps/ucs03_zkgm"
  9	core "gno.land/r/onbloc/ibc/union/core"
 10)
 11
 12// verifyForward checks the forwarded instruction is allowed and the timeout is set, then verifies the inner instruction.
 13// reference: https://github.com/unionlabs/union/blob/d91c5e94354e15801bd5f82dc658eae3b79f2dad/cosmwasm/app/ucs03-zkgm/src/contract.rs#L2969-L2995
 14func (v *ucs03ZkgmV1) verifyForward(_ int, rlm realm, funds *funds, channelId types.ChannelId, forward z.Forward) error {
 15	if !isAllowedForwardInstruction(forward.Instruction.Opcode) {
 16		return makeError(errInvalidForwardInstruction)
 17	}
 18
 19	if forward.TimeoutTimestamp == 0 {
 20		return makeError(errForwardZeroTimeout)
 21	}
 22
 23	return v.verifyInternal(0, rlm, funds, channelId, forward.Path, forward.Instruction)
 24}
 25
 26// executeForward builds and dispatches the next-hop packet, recording the parent for async ack settlement.
 27// reference: https://github.com/unionlabs/union/blob/d91c5e94354e15801bd5f82dc658eae3b79f2dad/cosmwasm/app/ucs03-zkgm/src/contract.rs#L1392-L1497
 28func (v *ucs03ZkgmV1) executeForward(_ int, rlm realm, packet types.Packet, salt [32]byte, path *u256.Uint, forward z.Forward, intent bool) (types.RecvPacketResult, error) {
 29	// Recv bypasses verifyInternal (see imp.gno OnRecvPacket flow),
 30	// receive-side opcode check is the only enforcement point.
 31	// Do not remove it as redundant with verifyForward.
 32	if !isAllowedForwardInstruction(forward.Instruction.Opcode) {
 33		return core.NewRecvPacketResult(types.PacketStatusUnknown, nil), makeError(errInvalidForwardInstruction)
 34	}
 35
 36	if intent {
 37		return core.NewRecvPacketResult(types.PacketStatusSuccess, types.CloneBytes(z.ACK_ERR_ONLY_MAKER)), nil
 38	}
 39
 40	childPacket, err := buildForwardChild(packet, path, salt, forward)
 41	if err != nil {
 42		return core.NewRecvPacketResult(types.PacketStatusUnknown, nil), err
 43	}
 44
 45	// Forward children build ZkgmPacket directly. Their salt is derived with
 46	// DeriveForwardSalt, not DeriveSenderSalt. Route the child through core so
 47	// it receives a normal packet commitment, then track its parent so child
 48	// resolution can write the deferred parent ack.
 49	childPacket = core.SendPacket(cross(rlm), childPacket.SourceChannelId, childPacket.TimeoutTimestamp, childPacket.Data)
 50	childHash := types.MustCommit(types.CommitPacket(childPacket))
 51	v.store.SetInFlightPacket(0, rlm, childHash.String(), packet)
 52	zkgm.EmitForwardInFlightSet(0, rlm, childHash, types.MustCommit(types.CommitPacket(packet)))
 53
 54	return core.NewRecvPacketResult(types.PacketStatusAsync, nil), nil
 55}
 56
 57// handleForwardChild detects a forwarded child packet resolving on ack/timeout
 58// and writes the captured parent's deferred acknowledgement through core.
 59func (v *ucs03ZkgmV1) handleForwardChild(_ int, rlm realm, packet types.Packet, zp z.ZkgmPacket, ack []byte) bool {
 60	if !z.IsForwardedPacket(zp.Salt) {
 61		return false
 62	}
 63
 64	key := forwardInFlightKey(packet)
 65
 66	parent, ok := v.store.GetInFlightPacket(key)
 67	if !ok {
 68		zkgm.EmitForwardInFlightPopped(0, rlm, types.MustCommit(types.CommitPacket(packet)), false)
 69		return false
 70	}
 71
 72	v.store.RemoveInFlightPacket(0, rlm, key)
 73	zkgm.EmitForwardInFlightPopped(0, rlm, types.MustCommit(types.CommitPacket(packet)), true)
 74	core.WriteAcknowledgement(cross(rlm), types.NewMsgWriteAcknowledgement(parent, ack))
 75
 76	return true
 77}
 78
 79// forwardInFlightKey derives the in-flight key from the child packet's commitment hash.
 80func forwardInFlightKey(packet types.Packet) string {
 81	return types.MustCommit(types.CommitPacket(packet)).String()
 82}
 83
 84// buildForwardChild builds the next-hop packet, advancing the channel path and namespacing the salt.
 85func buildForwardChild(packet types.Packet, path *u256.Uint, parentSalt [32]byte, forward z.Forward) (types.Packet, error) {
 86	if forward.TimeoutTimestamp == 0 {
 87		return core.NewPacket(0, 0, nil, 0), makeError(errForwardZeroTimeout)
 88	}
 89
 90	tailPath, prevDestChannel := z.DequeueChannelFromPath(forward.Path)
 91	if prevDestChannel == 0 {
 92		return core.NewPacket(0, 0, nil, 0), makeError(errForwardMissingPrevDest)
 93	}
 94
 95	continuationPath, nextSourceChannel := z.DequeueChannelFromPath(tailPath)
 96	if nextSourceChannel == 0 {
 97		return core.NewPacket(0, 0, nil, 0), makeError(errForwardMissingNextSource)
 98	}
 99
100	if packet.DestinationChannelId != types.ChannelId(prevDestChannel) {
101		return core.NewPacket(0, 0, nil, 0), makeError(errForwardPrevDestMismatch)
102	}
103
104	nextInstruction := forward.Instruction
105
106	if !continuationPath.IsZero() {
107		nextForward := z.Forward{
108			Path:             continuationPath,
109			TimeoutHeight:    0,
110			TimeoutTimestamp: forward.TimeoutTimestamp,
111			Instruction:      forward.Instruction,
112		}
113
114		forwardBytes, err := z.EncodeForward(nextForward)
115		if err != nil {
116			return core.NewPacket(0, 0, nil, 0), err
117		}
118
119		nextInstruction = z.Instruction{
120			Version: z.INSTR_VERSION_0,
121			Opcode:  z.OP_FORWARD,
122			Operand: forwardBytes,
123		}
124	}
125
126	intermediate, err := z.UpdateChannelPath(path, prevDestChannel)
127	if err != nil {
128		return core.NewPacket(0, 0, nil, 0), err
129	}
130
131	nextPath, err := z.UpdateChannelPath(intermediate, nextSourceChannel)
132	if err != nil {
133		return core.NewPacket(0, 0, nil, 0), err
134	}
135
136	childBytes, err := z.EncodeZkgmPacket(z.ZkgmPacket{
137		Salt:        z.DeriveForwardSalt(parentSalt),
138		Path:        nextPath,
139		Instruction: nextInstruction,
140	})
141	if err != nil {
142		return core.NewPacket(0, 0, nil, 0), err
143	}
144
145	sourceChannelId := types.ChannelId(nextSourceChannel)
146	sourceChannel, err := core.GetChannel(sourceChannelId)
147	if err != nil {
148		return core.NewPacket(0, 0, nil, 0), err
149	}
150
151	return core.NewPacket(sourceChannelId, sourceChannel.CounterpartyChannelId, childBytes, types.Timestamp(forward.TimeoutTimestamp)), nil
152}
153
154// isAllowedForwardInstruction reports whether opcode may be forwarded.
155func isAllowedForwardInstruction(opcode uint8) bool {
156	return opcode == z.OP_CALL || opcode == z.OP_TOKEN_ORDER || opcode == z.OP_BATCH
157}