store.gno
6.44 Kb · 229 lines
1package core
2
3import (
4 "chain/params"
5
6 "gno.land/p/aib/ibc/app"
7 "gno.land/p/aib/ibc/host"
8 "gno.land/p/aib/ibc/lightclient"
9 "gno.land/p/aib/ibc/lightclient/tendermint"
10 "gno.land/p/aib/ibc/types"
11 "gno.land/p/nt/bptree/v0"
12 "gno.land/p/nt/seqid/v0"
13 "gno.land/p/nt/ufmt/v0"
14)
15
16var store *Store
17
18func init() {
19 store = newStore()
20}
21
22type Store struct {
23 clientSeq seqid.ID
24 clientByID *bptree.BPTree // id:client
25
26 routes map[string]ibcApp
27}
28
29func newStore() *Store {
30 return &Store{
31 clientByID: bptree.NewBPTree32(),
32 routes: make(map[string]ibcApp),
33 }
34}
35
36type client struct {
37 id string
38 typ string
39 creator address
40 counterpartyClientID string
41 counterpartyMerklePrefix [][]byte
42
43 lightClient lightclient.Interface
44
45 sendSeq seqid.ID
46 packetCommitmentsBySeq *bptree.BPTree // sequence:commitment
47 packetReceiptsBySeq *bptree.BPTree // sequence:receipt
48 packetAcknowledgementsBySeq *bptree.BPTree // sequence:ack
49 pendingAsyncAcksBySeq *bptree.BPTree // sequence:pendingAsyncAck
50}
51
52type ibcApp struct {
53 app.IBCApp
54 pkgPath string
55 address address
56}
57
58type pendingAsyncAck struct {
59 packet types.Packet
60 appPkgPath string
61}
62
63func (c *client) getPacketCommitment(sequence uint64) []byte {
64 v, found := c.packetCommitmentsBySeq.Get(seqid.ID(sequence).Binary())
65 if !found {
66 return nil
67 }
68 return v.([]byte)
69}
70
71// setPacketCommitment stores the packet commitment in the client data and in
72// the params.
73func (c *client) setPacketCommitment(sequence uint64, packet types.Packet) {
74 commitment := types.CommitPacket(packet)
75
76 // store in client data
77 c.packetCommitmentsBySeq.Set(seqid.ID(sequence).Binary(), commitment)
78
79 // store in params, so it is provable by the counterparty
80 // The key will be: (abci path=.store/main/key)
81 // "/pv/vm:gno.land/r/aib/ibc/core:" + clientID + "1" + sequence
82 key := host.PacketCommitmentKey(c.id, sequence)
83 setChainParam(key, commitment)
84}
85
86func (c *client) deletePacketCommitment(sequence uint64) {
87 c.packetCommitmentsBySeq.Remove(seqid.ID(sequence).Binary())
88
89 key := host.PacketCommitmentKey(c.id, sequence)
90 setChainParam(key, nil)
91}
92
93// hasPacketReceipt returns true if the packet receipt exists, otherwise false.
94func (c *client) hasPacketReceipt(sequence uint64) bool {
95 return c.packetReceiptsBySeq.Has(seqid.ID(sequence).Binary())
96}
97
98// setPacketReceipt writes the packet receipt under the receipt path
99// This is a public path that is standardized by the IBC V2 specification.
100func (c *client) setPacketReceipt(sequence uint64) {
101 value := []byte{byte(2)} // constant value
102
103 // store in client data
104 c.packetReceiptsBySeq.Set(seqid.ID(sequence).Binary(), value)
105
106 // store in params, so it is provable by the counterparty
107 // The key will be: (abci path=.store/main/key)
108 // "/pv/vm:gno.land/r/aib/ibc/core:" + clientID + "2" + sequence
109 key := host.PacketReceiptKey(c.id, sequence)
110 setChainParam(key, value)
111}
112
113func (c *client) deletePacketReceipt(sequence uint64) {
114 c.packetReceiptsBySeq.Remove(seqid.ID(sequence).Binary())
115
116 key := host.PacketReceiptKey(c.id, sequence)
117 setChainParam(key, nil)
118}
119
120// hasPacketAcknowledgement checks if the packet ack hash is already on the
121// store.
122func (c *client) hasPacketAcknowledgement(sequence uint64) bool {
123 return c.packetAcknowledgementsBySeq.Has(seqid.ID(sequence).Binary())
124}
125
126// GetPacketAcknowledgement fetches the packet acknowledgement from the store.
127func (c *client) getPacketAcknowledgement(sequence uint64) []byte {
128 v, found := c.packetAcknowledgementsBySeq.Get(seqid.ID(sequence).Binary())
129 if !found {
130 return nil
131 }
132 return v.([]byte)
133}
134
135func (c *client) savePendingAsyncAck(packet types.Packet, appPkgPath string) {
136 sequence := packet.Sequence
137 if c.hasPendingAsyncAck(sequence) {
138 panic(ufmt.Sprintf("pending async ack already exists for sequence %d", sequence))
139 }
140 if c.hasPacketAcknowledgement(sequence) {
141 panic(ufmt.Sprintf("acknowledgement already written for sequence %d", sequence))
142 }
143 c.pendingAsyncAcksBySeq.Set(seqid.ID(sequence).Binary(), &pendingAsyncAck{
144 packet: packet,
145 appPkgPath: appPkgPath,
146 })
147}
148
149func (c *client) getPendingAsyncAck(sequence uint64) (*pendingAsyncAck, bool) {
150 v, found := c.pendingAsyncAcksBySeq.Get(seqid.ID(sequence).Binary())
151 if !found {
152 return nil, false
153 }
154 return v.(*pendingAsyncAck), true
155}
156
157func (c *client) deletePendingAsyncAck(sequence uint64) {
158 c.pendingAsyncAcksBySeq.Remove(seqid.ID(sequence).Binary())
159}
160
161func (c *client) hasPendingAsyncAck(sequence uint64) bool {
162 return c.pendingAsyncAcksBySeq.Has(seqid.ID(sequence).Binary())
163}
164
165// SetPacketAcknowledgement writes the acknowledgement hash under the
166// acknowledgement path
167// This is a public path that is standardized by the IBC V2 specification.
168func (c *client) setPacketAcknowledgement(sequence uint64, ackHash []byte) {
169 // store in client data
170 c.packetAcknowledgementsBySeq.Set(seqid.ID(sequence).Binary(), ackHash)
171
172 // store in params, so it is provable by the counterparty
173 // The key will be: (abci path=.store/main/key)
174 // "/pv/vm:gno.land/r/aib/ibc/core:" + clientID + "3" + sequence
175 key := host.PacketAcknowledgementKey(c.id, sequence)
176 setChainParam(key, ackHash)
177}
178
179func (c *client) deletePacketAcknowledgement(sequence uint64) {
180 c.packetAcknowledgementsBySeq.Remove(seqid.ID(sequence).Binary())
181
182 key := host.PacketAcknowledgementKey(c.id, sequence)
183 setChainParam(key, nil)
184}
185
186func setChainParam(key, value []byte) {
187 // TODO find a way to assert this write in tests (difficult since params does
188 // not expose getters)
189 params.SetBytes(string(key), value)
190}
191
192func (cs *Store) addClient(typ string, creator address) *client {
193 id := ufmt.Sprintf("%s-%d", typ, uint64(cs.clientSeq.Next()))
194 c := &client{
195 id: id,
196 typ: typ,
197 creator: creator,
198
199 packetCommitmentsBySeq: bptree.NewBPTree32(),
200 packetReceiptsBySeq: bptree.NewBPTree32(),
201 packetAcknowledgementsBySeq: bptree.NewBPTree32(),
202 pendingAsyncAcksBySeq: bptree.NewBPTree32(),
203 }
204 switch typ {
205 case lightclient.Tendermint:
206 c.lightClient = tendermint.NewTMLightClient()
207 default:
208 panic("unhandled light client type " + typ)
209 }
210 cs.clientByID.Set(id, c)
211 return c
212}
213
214func (cs *Store) getClient(id string) *client {
215 x, found := cs.clientByID.Get(id)
216 if !found {
217 return nil
218 }
219 return x.(*client)
220}
221
222// route returns an IBCApp for the given portID.
223func (cs *Store) route(portID string) ibcApp {
224 app, ok := cs.routes[portID]
225 if !ok {
226 panic(ufmt.Sprintf("no registered app for port %s", portID))
227 }
228 return app
229}