Search Apps Documentation Source Content File Folder Download Copy Actions Download

z10a_async_ack_filetest.gno

12.45 Kb · 335 lines
  1// PKGPATH: gno.land/r/aib/main
  2package main
  3
  4
  5import (
  6	"encoding/hex"
  7	"time"
  8
  9	tmtesting "gno.land/p/aib/ibc/lightclient/tendermint/testing"
 10	"gno.land/p/aib/ibc/types"
 11	"gno.land/p/aib/ics23"
 12	appstesting "gno.land/r/aib/ibc/apps/testing"
 13	"gno.land/r/aib/ibc/core"
 14)
 15
 16// forwardingApp wraps the base mock and writes the deferred parent ack from
 17// inside OnAcknowledgementPacket. Defined here (rather than in apps/testing)
 18// so that apps/testing does not need to import core, which would create an
 19// import cycle with this directory's filetests during gnodev deploy. Pending
 20// state is held in package-level vars because the receiver is read-only when
 21// invoked across realms via the IBCApp interface.
 22type forwardingApp struct {
 23	*appstesting.App
 24}
 25
 26var (
 27	pendingClientID  string
 28	pendingSequence  uint64
 29	asyncDeferredAck = types.Acknowledgement{
 30		AppAcknowledgements: [][]byte{{0x02}},
 31	}
 32)
 33
 34func (f *forwardingApp) OnRecvPacket(
 35	cur realm,
 36	sourceClient string,
 37	destinationClient string,
 38	sequence uint64,
 39	payload types.Payload,
 40) types.RecvPacketResult {
 41	res := f.App.OnRecvPacket(cross(cur), sourceClient, destinationClient, sequence, payload)
 42	if res.Status == types.PacketStatus_Async {
 43		pendingClientID = destinationClient
 44		pendingSequence = sequence
 45	}
 46	return res
 47}
 48
 49func (f *forwardingApp) OnAcknowledgementPacket(
 50	cur realm,
 51	sourceClient string,
 52	destinationClient string,
 53	sequence uint64,
 54	acknowledgement []byte,
 55	payload types.Payload,
 56) error {
 57	err := f.App.OnAcknowledgementPacket(cross(cur), sourceClient, destinationClient, sequence, acknowledgement, payload)
 58	if pendingClientID != "" {
 59		clientID, seq := pendingClientID, pendingSequence
 60		pendingClientID, pendingSequence = "", 0
 61		core.WriteAcknowledgement(cross(cur), clientID, seq, asyncDeferredAck)
 62	}
 63	return err
 64}
 65
 66// Async ack via OnAcknowledgementPacket. Mirrors the PFM/ZKGM forwarded-ack
 67// flow on the middle chain of an A -> B -> C forward, where this realm acts
 68// as chain B:
 69//
 70//   - parent packet arrives from chain A, app returns Async (deferred)
 71//   - child packet is sent to chain C
 72//   - chain C acks the child; that ack triggers the mock's
 73//     OnAcknowledgementPacket, which calls core.WriteAcknowledgement to
 74//     finally ack the parent back to chain A
 75//
 76// On us (chain B) two clients are needed, one per remote chain:
 77//
 78//   - clientToA: local id 07-tendermint-1, chain A's id for us is 07-tendermint-42
 79//   - clientToC: local id 07-tendermint-2, chain C's id for us is 07-tendermint-99
 80func main(cur realm) {
 81	var (
 82		trustedHeight       = types.NewHeight(2, 2)
 83		trustedValset       = tmtesting.GenValset()
 84		chainAClientIDForUs = "07-tendermint-42"
 85		chainCClientIDForUs = "07-tendermint-99"
 86	)
 87
 88	// clientToA: tracks chain A. apphash commits to the parent packet
 89	// commitment at prefix2/07-tendermint-42/packet/seq=1 on chain A.
 90	apphashA, _ := hex.DecodeString("8da82bb625ee946f4b7aa03a82010cb115f9ce3879c7caad8d92e71eb821996e")
 91	clientToA := core.CreateClient(cross(cur),
 92		tmtesting.NewClientState("chainA-2", trustedHeight),
 93		tmtesting.GenConsensusState(time.Now(), apphashA, trustedValset.Hash()),
 94	)
 95	core.RegisterCounterparty(cross(cur), clientToA, [][]byte{[]byte("iavlStoreKey"), []byte("prefix2")}, chainAClientIDForUs)
 96
 97	// clientToC: tracks chain C. apphash commits to the child packet ack
 98	// commitment at prefix3/07-tendermint-99/ack/seq=1 on chain C with app
 99	// ack "done".
100	apphashC, _ := hex.DecodeString("f1c8be180f0c12cfd37970aa4bfbda7ef6d9fe419ac81ee4e02588c8a1a862c6")
101	clientToC := core.CreateClient(cross(cur),
102		tmtesting.NewClientState("chainC-2", trustedHeight),
103		tmtesting.GenConsensusState(time.Now(), apphashC, trustedValset.Hash()),
104	)
105	core.RegisterCounterparty(cross(cur), clientToC, [][]byte{[]byte("iavlStoreKey"), []byte("prefix3")}, chainCClientIDForUs)
106
107	// Register the forwarding mock from this realm, so the pkgPath stored
108	// in core is gno.land/r/aib/main — the same realm that later calls
109	// core.WriteAcknowledgement from inside OnAcknowledgementPacket.
110	var (
111		base      = appstesting.NewApp(cross(cur))
112		app       = &forwardingApp{App: base}
113		appPortID = "appID"
114	)
115	app.SetOnRecvPacketReturn(types.RecvPacketResult{Status: types.PacketStatus_Async})
116	core.RegisterApp(cross(cur), appPortID, app)
117
118	// Send the child packet to chain C. Its eventual ack will trigger the
119	// mock's OnAcknowledgementPacket and the deferred parent ack write.
120	sendPacket := types.MsgSendPacket{
121		SourceClient:     clientToC,
122		TimeoutTimestamp: uint64(time.Now().Add(time.Hour).Unix()),
123		Payloads: []types.Payload{{
124			SourcePort:      appPortID,
125			DestinationPort: appPortID,
126			Encoding:        "application/json",
127			Value:           []byte("{}"),
128			Version:         "v1",
129		}},
130	}
131	childSeq := core.SendPacket(cross(cur), sendPacket)
132
133	// Receive the parent packet from chain A. The mock returns Async, so
134	// core stores a pending entry and does not yet write the ack.
135	specs := ics23.IavlSpec()
136	// NOTE proof generated by:
137	// go run -C ./cmd/gen-proof . 'prefix2' '07-tendermint-42' 'packet' '{"sequence":1,"source_client":"07-tendermint-42","destination_client":"07-tendermint-1","timeout_timestamp":1234571490,"payloads":[{"source_port":"appID","destination_port":"appID","encoding":"application/json","value":"e30=","version":"v1"}]}'
138	parentRecvProof := []ics23.CommitmentProof{
139		// iavl proof
140		ics23.CommitmentProof_Exist{
141			Exist: &ics23.ExistenceProof{
142				Key:   []byte("\x70\x72\x65\x66\x69\x78\x32\x30\x37\x2d\x74\x65\x6e\x64\x65\x72\x6d\x69\x6e\x74\x2d\x34\x32\x01\x00\x00\x00\x00\x00\x00\x00\x01"),
143				Value: []byte("\x23\x99\xf6\x84\x16\xb7\xd0\x09\x3b\xe4\x9e\x5f\x8e\xe1\xbe\x1c\x8e\x07\xa0\x93\xc5\x67\x09\x03\x46\xce\x36\xc9\x7d\x11\x71\x71"),
144				Leaf: &ics23.LeafOp{
145					Hash:         specs.LeafSpec.Hash,
146					PrehashKey:   specs.LeafSpec.PrehashKey,
147					PrehashValue: specs.LeafSpec.PrehashValue,
148					Length:       specs.LeafSpec.Length,
149					Prefix:       []byte("\x00\x02\x02"),
150				},
151				Path: []*ics23.InnerOp{
152					{
153						Hash:   specs.InnerSpec.Hash,
154						Prefix: []byte("\x02\x04\x02\x20\x35\xf8\xea\x80\x53\x90\xe0\x84\x85\x4f\x39\x9b\x42\xcc\xde\xae\xa3\x3a\x1d\xed\xc1\x15\x63\x8a\xc4\x8d\x06\x00\x63\x7d\xba\x1f\x20"),
155						Suffix: []byte(""),
156					},
157					{
158						Hash:   specs.InnerSpec.Hash,
159						Prefix: []byte("\x04\x08\x02\x20"),
160						Suffix: []byte("\x20\x79\x8e\x2c\xaa\x96\xfd\xfb\xa3\x76\xdd\xeb\x47\x99\x99\x54\xd2\xf4\x7e\x65\x16\x22\x64\xb0\x53\x6a\xb5\xdf\xf7\xfc\x0a\x2e\x07"),
161					},
162					{
163						Hash:   specs.InnerSpec.Hash,
164						Prefix: []byte("\x06\x0c\x02\x20\x9a\xf3\x7d\xd5\x95\xa0\x19\x08\x03\xb5\xe0\x5a\xae\xf4\x2a\xe3\xfa\xd4\x99\xe4\xfb\xe3\x7f\x7c\xd3\x1c\xad\xff\x22\xa9\xee\x74\x20"),
165						Suffix: []byte(""),
166					},
167				},
168			},
169		},
170		// rootmulti proof
171		ics23.CommitmentProof_Exist{
172			Exist: &ics23.ExistenceProof{
173				Key:   []byte("\x69\x61\x76\x6c\x53\x74\x6f\x72\x65\x4b\x65\x79"),
174				Value: []byte("\xeb\xfd\x02\x8a\x5d\xe1\xcf\x93\xde\x5d\x0b\xa8\xcf\x6e\x0d\x5e\x29\xf8\x80\x1a\x08\x0b\x07\x20\x3c\xf1\xca\xf2\xf4\xdd\xd3\x10"),
175				Leaf: &ics23.LeafOp{
176					Hash:         specs.LeafSpec.Hash,
177					PrehashKey:   specs.LeafSpec.PrehashKey,
178					PrehashValue: specs.LeafSpec.PrehashValue,
179					Length:       specs.LeafSpec.Length,
180					Prefix:       []byte("\x00"),
181				},
182				Path: []*ics23.InnerOp{},
183			},
184		},
185	}
186	recvPacket := types.MsgRecvPacket{
187		Packet: types.Packet{
188			Sequence:          1,
189			SourceClient:      chainAClientIDForUs,
190			DestinationClient: clientToA,
191			TimeoutTimestamp:  uint64(time.Now().Add(time.Hour).Unix()),
192			Payloads: []types.Payload{{
193				SourcePort:      appPortID,
194				DestinationPort: appPortID,
195				Encoding:        "application/json",
196				Value:           []byte("{}"),
197				Version:         "v1",
198			}},
199		},
200		ProofCommitment: parentRecvProof,
201		ProofHeight:     trustedHeight,
202	}
203	recvRes := core.RecvPacket(cross(cur), recvPacket)
204
205	println("recv res:", recvRes)
206	println("\n----------- parent ack BEFORE child is acked (deferred, should not exist)")
207	println(core.Render("clients/" + clientToA + "/packet_acknowledgements/1"))
208
209	// Acknowledge the child packet on chain C's client. Inside, core invokes
210	// the mock's OnAcknowledgementPacket which itself calls
211	// core.WriteAcknowledgement for the pending parent on chain A's client.
212	// NOTE proof generated by:
213	// go run -C ./cmd/gen-proof . 'prefix3' '07-tendermint-99' 'acknowledgement' 'done'
214	childAckProof := []ics23.CommitmentProof{
215		// iavl proof
216		ics23.CommitmentProof_Exist{
217			Exist: &ics23.ExistenceProof{
218				Key:   []byte("\x70\x72\x65\x66\x69\x78\x33\x30\x37\x2d\x74\x65\x6e\x64\x65\x72\x6d\x69\x6e\x74\x2d\x39\x39\x03\x00\x00\x00\x00\x00\x00\x00\x01"),
219				Value: []byte("\x1d\xb9\x33\x99\xa4\x7b\x20\x69\x9a\xa3\xbc\xd6\xa5\x96\x72\xe2\x65\x4c\xb7\xb2\x70\x4f\xba\xc6\x02\x29\xc2\xba\x66\xea\xfb\x9c"),
220				Leaf: &ics23.LeafOp{
221					Hash:         specs.LeafSpec.Hash,
222					PrehashKey:   specs.LeafSpec.PrehashKey,
223					PrehashValue: specs.LeafSpec.PrehashValue,
224					Length:       specs.LeafSpec.Length,
225					Prefix:       []byte("\x00\x02\x02"),
226				},
227				Path: []*ics23.InnerOp{
228					{
229						Hash:   specs.InnerSpec.Hash,
230						Prefix: []byte("\x02\x04\x02\x20\x35\xf8\xea\x80\x53\x90\xe0\x84\x85\x4f\x39\x9b\x42\xcc\xde\xae\xa3\x3a\x1d\xed\xc1\x15\x63\x8a\xc4\x8d\x06\x00\x63\x7d\xba\x1f\x20"),
231						Suffix: []byte(""),
232					},
233					{
234						Hash:   specs.InnerSpec.Hash,
235						Prefix: []byte("\x04\x08\x02\x20"),
236						Suffix: []byte("\x20\x79\x8e\x2c\xaa\x96\xfd\xfb\xa3\x76\xdd\xeb\x47\x99\x99\x54\xd2\xf4\x7e\x65\x16\x22\x64\xb0\x53\x6a\xb5\xdf\xf7\xfc\x0a\x2e\x07"),
237					},
238					{
239						Hash:   specs.InnerSpec.Hash,
240						Prefix: []byte("\x06\x0c\x02\x20\x9a\xf3\x7d\xd5\x95\xa0\x19\x08\x03\xb5\xe0\x5a\xae\xf4\x2a\xe3\xfa\xd4\x99\xe4\xfb\xe3\x7f\x7c\xd3\x1c\xad\xff\x22\xa9\xee\x74\x20"),
241						Suffix: []byte(""),
242					},
243				},
244			},
245		},
246		// rootmulti proof
247		ics23.CommitmentProof_Exist{
248			Exist: &ics23.ExistenceProof{
249				Key:   []byte("\x69\x61\x76\x6c\x53\x74\x6f\x72\x65\x4b\x65\x79"),
250				Value: []byte("\x1b\xe4\x34\xdf\x98\x81\x4e\xc7\xab\x47\x19\xeb\x3e\x81\x46\x78\xef\x3c\x4f\x16\x6c\xff\x5d\x41\x4e\xb0\xfc\x24\xc1\xfb\x0f\x61"),
251				Leaf: &ics23.LeafOp{
252					Hash:         specs.LeafSpec.Hash,
253					PrehashKey:   specs.LeafSpec.PrehashKey,
254					PrehashValue: specs.LeafSpec.PrehashValue,
255					Length:       specs.LeafSpec.Length,
256					Prefix:       []byte("\x00"),
257				},
258				Path: []*ics23.InnerOp{},
259			},
260		},
261	}
262	ackPacket := types.MsgAcknowledgement{
263		Packet: types.Packet{
264			Sequence:          childSeq,
265			SourceClient:      clientToC,
266			DestinationClient: chainCClientIDForUs,
267			TimeoutTimestamp:  sendPacket.TimeoutTimestamp,
268			Payloads:          sendPacket.Payloads,
269		},
270		Acknowledgement: types.Acknowledgement{
271			AppAcknowledgements: [][]byte{[]byte("done")},
272		},
273		ProofAcked:  childAckProof,
274		ProofHeight: trustedHeight,
275	}
276	ackRes := core.Acknowledgement(cross(cur), ackPacket)
277
278	println("\nack res:", ackRes)
279	println("\n----------- parent ack AFTER child is acked (written by mock.OnAck)")
280	println(core.Render("clients/" + clientToA + "/packet_acknowledgements/1"))
281	println("\n----------- child commitment AFTER ack (cleared)")
282	println(core.Render("clients/" + clientToC + "/packet_commitments/1"))
283	println("\n----------- app report")
284	println(app.Report())
285}
286
287// Output:
288// recv res: (2 gno.land/p/aib/ibc/types.ResponseResultType)
289//
290// ----------- parent ack BEFORE child is acked (deferred, should not exist)
291// {"error":"sequence 1 not found"}
292//
293// ack res: (2 gno.land/p/aib/ibc/types.ResponseResultType)
294//
295// ----------- parent ack AFTER child is acked (written by mock.OnAck)
296// {"sequence":"1","data":"5HN8pSo727oJNsvAPYWESzo9UNpM5u0nIHMoEOg7WuA="}
297//
298// ----------- child commitment AFTER ack (cleared)
299// {"error":"sequence 1 not found"}
300//
301// ----------- app report
302// OnSendPacket (1)
303// - sourceClient: 07-tendermint-2
304// - destinationClient: 07-tendermint-99
305// - sequence: 1
306// - payload:
307//   - sourcePort: appID
308//   - destinationPort: appID
309//   - version: v1
310//   - encoding: application/json
311//   - value: {}
312//
313// OnRecvPacket (1)
314// - sourceClient: 07-tendermint-42
315// - destinationClient: 07-tendermint-1
316// - sequence: 1
317// - payload:
318//   - sourcePort: appID
319//   - destinationPort: appID
320//   - version: v1
321//   - encoding: application/json
322//   - value: {}
323//
324// OnTimeoutPacket (0)
325// OnAcknowledgementPacket (1)
326// - sourceClient: 07-tendermint-2
327// - destinationClient: 07-tendermint-99
328// - sequence: 1
329// - payload:
330//   - sourcePort: appID
331//   - destinationPort: appID
332//   - version: v1
333//   - encoding: application/json
334//   - value: {}
335// - ack: done