Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}