z1h_on_send_packet_voucher_bypass_filetest.gno
5.88 Kb · 143 lines
1// PKGPATH: gno.land/r/aib/main
2package main
3
4// Regression test: a voucher send must be rejected when it does not originate
5// from transfer.Transfer. OnSendPacket is reachable directly through the public
6// core.SendPacket router, so the direct-user-call guard in transfer.Transfer is
7// not sufficient on its own — OnSendPacket must also require that voucher (and
8// GRC20) sends were initiated through transfer.Transfer, mirroring the existing
9// native-branch pendingNativeEscrow check.
10//
11// Here core.SendPacket is invoked directly with a voucher payload whose Sender
12// equals OriginCaller; the immediate caller is a realm (this main), not a user.
13// Until the OnSendPacket-level guard lands this test FAILS because the send
14// succeeds and burns the owner's voucher.
15
16import (
17 "encoding/hex"
18 "time"
19
20 tmtesting "gno.land/p/aib/ibc/lightclient/tendermint/testing"
21 "gno.land/p/aib/ibc/types"
22
23 "gno.land/p/aib/ics23"
24 "gno.land/r/aib/ibc/apps/transfer"
25 "gno.land/r/aib/ibc/core"
26)
27
28func main(cur realm) {
29 var (
30 chainID = "chain-id-2"
31 trustedHeight = types.NewHeight(2, 2)
32 clientState = tmtesting.NewClientState(chainID, trustedHeight)
33 apphash, _ = hex.DecodeString("0eadf50c7992d8e1366b8ea63ab7d1346de4762c3d449764ef7dc404ba48381f")
34 trustedValset = tmtesting.GenValset()
35 consensusState = tmtesting.GenConsensusState(time.Now(), apphash, trustedValset.Hash())
36 counterpartyID = "07-tendermint-42"
37 )
38 clientID := core.CreateClient(cross(cur), clientState, consensusState)
39 core.RegisterCounterparty(cross(cur), clientID, [][]byte{[]byte("iavlStoreKey"), []byte("prefix2")}, counterpartyID)
40
41 // The voucher owner is the signing EOA (OriginCaller). RecvPacket mints 100
42 // vouchers to it so the balance is non-zero.
43 owner := cur.Previous().Address()
44 recvPacket(cur, clientID, counterpartyID, trustedHeight, owner)
45
46 // Build a voucher send packet and submit it straight to the core router,
47 // bypassing transfer.Transfer and its guard. The guard must instead reject
48 // this at the OnSendPacket layer.
49 data := transfer.NewFungibleTokenPacketData("transfer/"+clientID+"/uphoton", "100", owner.String(), "atone1user", "")
50 msg := types.MsgSendPacket{
51 SourceClient: clientID,
52 Payloads: []types.Payload{{
53 SourcePort: transfer.PortID,
54 DestinationPort: transfer.PortID,
55 Encoding: transfer.EncodingProtobuf,
56 Value: data.ProtoMarshal(),
57 Version: transfer.V1,
58 }},
59 TimeoutTimestamp: uint64(time.Now().Add(time.Hour).Unix()),
60 }
61 core.SendPacket(cross(cur), msg)
62}
63
64func recvPacket(cur realm, clientID, counterpartyID string, trustedHeight types.Height, receiver address) {
65 payload := transfer.NewFungibleTokenPacketData("uphoton", "100", "atone1user", receiver.String(), "")
66 payloadBz := payload.ProtoMarshal()
67
68 specs := ics23.IavlSpec()
69 // NOTE proof generated by:
70 // 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":"transfer","destination_port":"transfer","encoding":"application/x-protobuf", "value":"Cgd1cGhvdG9uEgMxMDAaCmF0b25lMXVzZXIiKGcxd3ltdTQ3ZHJocjBrdXEyMDk4bTc5Mmx5dGd0ajJueXg3N3lyc20=","version":"ics20-1"}]}'
71 proof := []ics23.CommitmentProof{
72
73 // iavl proof
74 ics23.CommitmentProof_Exist{
75 Exist: &ics23.ExistenceProof{
76 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"),
77 Value: []byte("\x12\x6c\xcc\xff\x00\xd6\xb9\x73\x09\xb3\x8b\x1b\xec\x0f\xe2\xef\xc9\x54\xe5\xac\x1d\x90\x47\x36\xd0\x32\xbc\xc0\xb1\x66\xc4\x8f"),
78 Leaf: &ics23.LeafOp{
79 Hash: specs.LeafSpec.Hash,
80 PrehashKey: specs.LeafSpec.PrehashKey,
81 PrehashValue: specs.LeafSpec.PrehashValue,
82 Length: specs.LeafSpec.Length,
83 Prefix: []byte("\x00\x02\x02"),
84 },
85 Path: []*ics23.InnerOp{
86 {
87 Hash: specs.InnerSpec.Hash,
88 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"),
89 Suffix: []byte(""),
90 },
91 {
92 Hash: specs.InnerSpec.Hash,
93 Prefix: []byte("\x04\x08\x02\x20"),
94 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"),
95 },
96 {
97 Hash: specs.InnerSpec.Hash,
98 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"),
99 Suffix: []byte(""),
100 },
101 },
102 },
103 },
104
105 // rootmulti proof
106 ics23.CommitmentProof_Exist{
107 Exist: &ics23.ExistenceProof{
108 Key: []byte("\x69\x61\x76\x6c\x53\x74\x6f\x72\x65\x4b\x65\x79"),
109 Value: []byte("\x4f\xa3\xdf\x13\x8a\x86\x32\xc8\x3f\xd8\x84\x92\x00\xe0\xf1\xd8\x8a\x05\x36\x85\x32\x15\x1b\x45\x67\xf6\x86\xec\xf1\xfb\x9f\x45"),
110 Leaf: &ics23.LeafOp{
111 Hash: specs.LeafSpec.Hash,
112 PrehashKey: specs.LeafSpec.PrehashKey,
113 PrehashValue: specs.LeafSpec.PrehashValue,
114 Length: specs.LeafSpec.Length,
115 Prefix: []byte("\x00"),
116 },
117 Path: []*ics23.InnerOp{},
118 },
119 },
120 }
121 recvMsg := types.MsgRecvPacket{
122 Packet: types.Packet{
123 Sequence: 1,
124 SourceClient: counterpartyID,
125 DestinationClient: clientID,
126 TimeoutTimestamp: uint64(time.Now().Add(time.Hour).Unix()),
127 Payloads: []types.Payload{{
128 SourcePort: transfer.PortID,
129 DestinationPort: transfer.PortID,
130 Encoding: transfer.EncodingProtobuf,
131 Value: payloadBz,
132 Version: transfer.V1,
133 }},
134 },
135 ProofCommitment: proof,
136 ProofHeight: trustedHeight,
137 }
138
139 core.RecvPacket(cross(cur), recvMsg)
140}
141
142// Error:
143// send packet failed for payload #0 app "transfer": voucher packet must be initiated through transfer.Transfer