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

batch.gno

4.50 Kb · 131 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)
  9
 10// verifyBatch verifies each allowed child instruction against the shared funds budget.
 11// reference: https://github.com/unionlabs/union/blob/d91c5e94354e15801bd5f82dc658eae3b79f2dad/cosmwasm/app/ucs03-zkgm/src/contract.rs#L2941-L2967
 12func (v *ucs03ZkgmV1) verifyBatch(_ int, rlm realm, funds *funds, channelId types.ChannelId, path *u256.Uint, batch z.Batch) error {
 13	for _, instruction := range batch.Instructions {
 14		if !isAllowedBatchInstruction(instruction.Opcode) {
 15			return makeError(errInvalidBatchInstruction)
 16		}
 17
 18		if err := v.verifyInternal(0, rlm, funds, channelId, path, instruction); err != nil {
 19			return err
 20		}
 21	}
 22
 23	return nil
 24}
 25
 26// executeBatch runs each child in order, aborting the whole batch on an only-maker child.
 27// reference: https://github.com/unionlabs/union/blob/d91c5e94354e15801bd5f82dc658eae3b79f2dad/cosmwasm/app/ucs03-zkgm/src/contract.rs#L1686-L1726
 28func (v *ucs03ZkgmV1) executeBatch(_ int, rlm realm, packet types.Packet, relayer address, relayerMsg []byte, salt [32]byte, path *u256.Uint, batch z.Batch, intent bool) ([]byte, error) {
 29	acks := make([][]byte, len(batch.Instructions))
 30
 31	// NOTE: batch.Instructions remains uncapped until the Union reference defines
 32	// a maximum. Each child runs dispatchExecutePacket, including ABI decode and
 33	// keccak work, so execution cost scales with child count.
 34	for i, instruction := range batch.Instructions {
 35		if !isAllowedBatchInstruction(instruction.Opcode) {
 36			panic(execFatal{makeError(errInvalidBatchInstruction).Error()})
 37		}
 38
 39		childSalt := z.DeriveBatchSalt(u256.NewUint(uint64(i)), salt)
 40
 41		childRes, err := v.dispatchExecutePacket(0, rlm, packet, relayer, relayerMsg, childSalt, path, instruction, intent)
 42		if err != nil {
 43			panic(execFatal{err.Error()})
 44		}
 45
 46		childAck := childRes.Acknowledgement
 47		if len(childAck) == 0 {
 48			// empty ack is unrecoverable once earlier children have committed.
 49			panic(execFatal{"batch child returned empty ack"})
 50		}
 51		// A single only-maker child aborts the entire batch: return the sentinel up
 52		// to the recv boundary (OnRecvPacket / OnIntentRecvPacket), which panics on
 53		// it and reverts the whole recv tx — so already-processed children's
 54		// mutations do NOT stand. Mirrors union's reply handler returning
 55		// Err(OnlyMaker) on an only-maker batch ack (contract.rs L2528-2530).
 56		if isOnlyMakerAck(childAck) {
 57			return types.CloneBytes(z.ACK_ERR_ONLY_MAKER), nil
 58		}
 59
 60		acks[i] = childAck
 61	}
 62
 63	batchAckBytes, err := z.EncodeBatchAck(z.BatchAck{Acknowledgements: acks})
 64	if err != nil {
 65		panic(execFatal{err.Error()})
 66	}
 67
 68	ack, err := z.EncodeAck(z.Ack{Tag: tagAckSuccess(), InnerAck: batchAckBytes})
 69	if err != nil {
 70		panic(execFatal{err.Error()})
 71	}
 72
 73	return ack, nil
 74}
 75
 76// acknowledgeBatch fans the outer ack out to each child instruction.
 77func (v *ucs03ZkgmV1) acknowledgeBatch(_ int, rlm realm, packet types.Packet, relayer address, salt [32]byte, path *u256.Uint, batch z.Batch, successful bool, ack []byte) error {
 78	var batchAck z.BatchAck
 79
 80	if successful {
 81		decoded, err := z.DecodeBatchAck(ack)
 82		if err != nil {
 83			return err
 84		}
 85
 86		batchAck = decoded
 87		if len(batchAck.Acknowledgements) != len(batch.Instructions) {
 88			return makeError(errBatchAckCountMismatch)
 89		}
 90	}
 91
 92	for i, instruction := range batch.Instructions {
 93		var childAck []byte
 94
 95		childSuccessful := successful
 96
 97		if successful {
 98			decodedChildAck, err := z.DecodeAck(batchAck.Acknowledgements[i])
 99			if err != nil {
100				return err
101			}
102
103			childSuccessful = decodedChildAck.Tag != nil && decodedChildAck.Tag.Eq(tagAckSuccess())
104			childAck = decodedChildAck.InnerAck
105		}
106
107		childSalt := z.DeriveBatchSalt(u256.NewUint(uint64(i)), salt)
108		if err := v.acknowledgeInternal(0, rlm, packet, relayer, childSalt, path, instruction, childSuccessful, childAck); err != nil {
109			return err
110		}
111	}
112
113	return nil
114}
115
116// timeoutBatch fans the timeout out to each child instruction.
117func (v *ucs03ZkgmV1) timeoutBatch(_ int, rlm realm, packet types.Packet, salt [32]byte, path *u256.Uint, batch z.Batch) error {
118	for i, instruction := range batch.Instructions {
119		childSalt := z.DeriveBatchSalt(u256.NewUint(uint64(i)), salt)
120		if err := v.timeoutInternal(0, rlm, packet, childSalt, path, instruction); err != nil {
121			return err
122		}
123	}
124
125	return nil
126}
127
128// isAllowedBatchInstruction reports whether opcode may appear in a batch.
129func isAllowedBatchInstruction(opcode uint8) bool {
130	return opcode == z.OP_CALL || opcode == z.OP_TOKEN_ORDER
131}