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

store.gno

21.74 Kb · 693 lines
  1package core
  2
  3import (
  4	"chain/params"
  5	"encoding/hex"
  6
  7	"gno.land/p/nt/bptree/v0"
  8	"gno.land/p/nt/seqid/v0"
  9	"gno.land/p/onbloc/ibc/union/app"
 10	"gno.land/p/onbloc/ibc/union/lightclient"
 11	"gno.land/p/onbloc/ibc/union/types"
 12)
 13
 14var (
 15	store        *Store
 16	proxyPkgPath string
 17)
 18
 19func init(cur realm) {
 20	store = newStore()
 21	proxyPkgPath = cur.PkgPath()
 22}
 23
 24// Store is the proxy-owned persistent state. It is a flat data layer: clients,
 25// connections, channels, and commitments are keyed directly by their globally
 26// allocated id or storage path, with no cross-entity indirection. All protocol
 27// logic — handshake state machines, the acknowledge/timeout flow, proof
 28// verification — lives in the impl (core/v1). The Store only stores and
 29// retrieves, mirroring commitment values into chain params so counterparties
 30// can prove them.
 31type Store struct {
 32	clientSeq      seqid.ID
 33	clientRegistry map[string]lightclient.ClientImpl // ClientRegistry: type → factory
 34	clientTypeByID map[uint32]types.ClientType       // ClientTypes: clientId → type
 35	clientImplByID map[uint32]lightclient.ClientImpl // ClientImpls: clientId → factory
 36	clientByID     map[uint32]*client                // clientId → instantiated state
 37
 38	connectionSeq seqid.ID
 39	connections   *bptree.BPTree // connectionKey(ConnectionId) → types.Connection
 40
 41	channelSeq seqid.ID
 42	channels   *bptree.BPTree // channelKey(ChannelId) → *channelEntry
 43
 44	// commitments holds every packet/batch commitment keyed by its global storage
 45	// path. The path derivations (packet commitment, packet receipt, batch packets,
 46	// batch receipts) are namespaced so they never collide, so a single flat map
 47	// replaces the former per-client batch trees. Membership proofs keep their own
 48	// maps because they are read back by a different key shape.
 49	commitments         map[types.H256]types.H256
 50	membershipProofs    map[types.H256]types.H256
 51	nonMembershipProofs map[types.H256]types.H256
 52
 53	ports map[string]app.IApp
 54}
 55
 56type client struct {
 57	id                types.ClientId
 58	creator           address
 59	lightClient       lightclient.Interface
 60	clientState       []byte
 61	consensusByHeight *bptree.BPTree // string(height.Bytes()) → []byte
 62	// clientStore holds the light client's own key/value storage writes
 63	// (lightclient result StorageWrites). It stays per-client because the keys
 64	// are opaque to the host; the flat commitment map only covers host-derived
 65	// commitment paths.
 66	clientStore map[string][]byte
 67}
 68
 69type channelEntry struct {
 70	channel types.Channel
 71	portId  types.Bytes
 72}
 73
 74func newStore() *Store {
 75	return &Store{
 76		clientRegistry:      make(map[string]lightclient.ClientImpl),
 77		clientTypeByID:      make(map[uint32]types.ClientType),
 78		clientImplByID:      make(map[uint32]lightclient.ClientImpl),
 79		clientByID:          make(map[uint32]*client),
 80		connections:         bptree.NewBPTree32(),
 81		channels:            bptree.NewBPTree32(),
 82		commitments:         make(map[types.H256]types.H256),
 83		membershipProofs:    make(map[types.H256]types.H256),
 84		nonMembershipProofs: make(map[types.H256]types.H256),
 85		ports:               make(map[string]app.IApp),
 86	}
 87}
 88
 89// The Store satisfies the IStore interface the impl is injected with. Each
 90// adapter routes to the flat data primitives below. Setters take the borrow-rule
 91// (_ int, rlm realm, ...) form; the realm token is threaded by the caller and
 92// unused here because the underlying writes already run in the proxy's ownership
 93// context.
 94var _ IStore = (*Store)(nil)
 95
 96// RegisterClient stores a factory function for typ in the client registry.
 97func (s *Store) RegisterClient(_ int, rlm realm, typ types.ClientType, impl lightclient.ClientImpl) {
 98	assertIsRlmCurrent(0, rlm)
 99
100	s.registerClient(typ, impl)
101}
102
103// ClientRegistry returns the registered factory for typ.
104func (s *Store) ClientRegistry(typ types.ClientType) (lightclient.ClientImpl, bool) {
105	return s.clientRegistryEntry(typ)
106}
107
108// HasRegisteredClient reports whether a factory is registered for typ.
109func (s *Store) HasRegisteredClient(typ types.ClientType) bool {
110	_, found := s.clientRegistryEntry(typ)
111	return found
112}
113
114// AddClient allocates a new client id and stores the light client object,
115// returning the assigned id.
116func (s *Store) AddClient(_ int, rlm realm, typ types.ClientType, creator address, lc lightclient.Interface) types.ClientId {
117	assertIsRlmCurrent(0, rlm)
118
119	return s.addClient(s.nextClientId(), typ, creator, lc).id
120}
121
122// GetClientImpl returns the factory stored for clientId.
123func (s *Store) GetClientImpl(clientId types.ClientId) (lightclient.ClientImpl, bool) {
124	impl, found := s.clientImplByID[uint32(clientId)]
125	return impl, found
126}
127
128// SaveClientStore records a light client storage write (result StorageWrites),
129// returning false when the client is unknown.
130func (s *Store) SaveClientStore(_ int, rlm realm, clientId types.ClientId, key string, value []byte) bool {
131	assertIsRlmCurrent(0, rlm)
132
133	c := s.getClient(clientId)
134	if c == nil {
135		return false
136	}
137
138	c.saveClientStore(key, value)
139
140	return true
141}
142
143// ClientExists reports whether a client is registered under clientId.
144func (s *Store) ClientExists(clientId types.ClientId) bool {
145	_, found := s.clientTypeByID[uint32(clientId)]
146	return found
147}
148
149// ClientType returns the registered client type for clientId.
150func (s *Store) ClientType(clientId types.ClientId) (types.ClientType, bool) {
151	typ, found := s.clientTypeByID[uint32(clientId)]
152	return typ, found
153}
154
155// GetLightClient returns the stored light client object for clientId.
156func (s *Store) GetLightClient(clientId types.ClientId) (lightclient.Interface, bool) {
157	return s.lightClientOf(clientId)
158}
159
160// SetLightClient replaces a client's light client object (force-update recovery),
161// returning false when the client is unknown.
162func (s *Store) SetLightClient(_ int, rlm realm, clientId types.ClientId, lc lightclient.Interface) bool {
163	assertIsRlmCurrent(0, rlm)
164
165	c := s.getClient(clientId)
166	if c == nil {
167		return false
168	}
169
170	c.lightClient = lc
171
172	return true
173}
174
175// SaveClientState mirrors the client state bytes for queries and counterparty
176// proofs, returning false when the client is unknown.
177func (s *Store) SaveClientState(_ int, rlm realm, clientId types.ClientId, clientStateBytes []byte) bool {
178	assertIsRlmCurrent(0, rlm)
179
180	c := s.getClient(clientId)
181	if c == nil {
182		return false
183	}
184
185	c.saveClientState(clientStateBytes)
186
187	return true
188}
189
190// SaveConsensusState records the consensus state at height, returning false when
191// the client is unknown.
192func (s *Store) SaveConsensusState(_ int, rlm realm, clientId types.ClientId, height types.Height, consensusStateBytes []byte) bool {
193	assertIsRlmCurrent(0, rlm)
194
195	c := s.getClient(clientId)
196	if c == nil {
197		return false
198	}
199
200	c.saveConsensusState(height, consensusStateBytes)
201
202	return true
203}
204
205// NextConnectionId allocates the next connection id from the sequence.
206func (s *Store) NextConnectionId(_ int, rlm realm) types.ConnectionId {
207	assertIsRlmCurrent(0, rlm)
208
209	return s.nextConnectionId()
210}
211
212// SaveConnection persists a connection and mirrors its commitment.
213func (s *Store) SaveConnection(_ int, rlm realm, connectionId types.ConnectionId, connection types.Connection) {
214	assertIsRlmCurrent(0, rlm)
215
216	s.saveConnection(connectionId, connection)
217}
218
219// GetConnection returns the stored connection for connectionId.
220func (s *Store) GetConnection(connectionId types.ConnectionId) (types.Connection, bool) {
221	return s.getConnection(connectionId)
222}
223
224// NextChannelId allocates the next channel id from the sequence.
225func (s *Store) NextChannelId(_ int, rlm realm) types.ChannelId {
226	assertIsRlmCurrent(0, rlm)
227
228	return s.nextChannelId()
229}
230
231// CreateChannel persists a new channel owned by portId and mirrors its commitment.
232func (s *Store) CreateChannel(_ int, rlm realm, channelId types.ChannelId, channel types.Channel, portId types.Bytes) {
233	assertIsRlmCurrent(0, rlm)
234
235	s.createChannel(channelId, channel, portId)
236}
237
238// SaveChannel updates a stored channel and mirrors its commitment.
239func (s *Store) SaveChannel(_ int, rlm realm, channelId types.ChannelId, channel types.Channel) {
240	assertIsRlmCurrent(0, rlm)
241
242	s.saveChannel(channelId, channel)
243}
244
245// GetChannels returns all stored channel records.
246// The bptree callback returns false to keep iterating (returning true stops early),
247// and the empty range bounds walk every entry.
248func (s *Store) GetChannels() ([]types.Channel, error) {
249	channels := make([]types.Channel, 0)
250
251	s.channels.Iterate("", "", func(key string, value any) bool {
252		entry, ok := value.(*channelEntry)
253		if !ok {
254			return false
255		}
256
257		channels = append(channels, entry.channel)
258
259		return false
260	})
261
262	return channels, nil
263}
264
265// GetChannel returns the stored channel for channelId.
266func (s *Store) GetChannel(channelId types.ChannelId) (types.Channel, bool) {
267	entry, ok := s.getChannelEntry(channelId)
268	if !ok {
269		return types.Channel{}, false
270	}
271
272	return entry.channel, true
273}
274
275// GetChannelOwner returns the port id that owns channelId.
276func (s *Store) GetChannelOwner(channelId types.ChannelId) (types.Bytes, bool) {
277	entry, ok := s.getChannelEntry(channelId)
278	if !ok {
279		return nil, false
280	}
281
282	return entry.portId, true
283}
284
285// GetAppByPortId returns the IBC app registered at portId.
286func (s *Store) GetAppByPortId(portId types.Bytes) (app.IApp, bool) {
287	a, ok := s.ports[portKey(portId)]
288
289	return a, ok
290}
291
292// HasApp reports whether an app is registered at portId.
293func (s *Store) HasApp(portId types.Bytes) bool {
294	_, ok := s.ports[portKey(portId)]
295
296	return ok
297}
298
299// RegisterAppAtPort installs an app at portId, panicking if one is already
300// registered there.
301func (s *Store) RegisterAppAtPort(_ int, rlm realm, portId types.Bytes, a app.IApp) {
302	assertIsRlmCurrent(0, rlm)
303
304	key := portKey(portId)
305	if _, ok := s.ports[key]; ok {
306		panic("port " + string(portId) + " already registered")
307	}
308
309	s.ports[key] = a
310}
311
312// HasPacketCommitment reports whether the source-side packet commitment exists.
313func (s *Store) HasPacketCommitment(packet types.Packet) bool {
314	_, found := s.getCommitment(packetCommitmentPath(packet))
315	return found
316}
317
318// CommitPacket records the source-side commitment for a sent packet.
319func (s *Store) CommitPacket(_ int, rlm realm, packet types.Packet) {
320	assertIsRlmCurrent(0, rlm)
321
322	s.setCommitment(packetCommitmentPath(packet), types.COMMITMENT_MAGIC)
323}
324
325// SetPacketAcknowledged flips a source-side commitment to the acknowledged
326// marker. It is a pure write: the impl owns the state machine (verifying the
327// commitment exists and is not already acknowledged) before calling this. The
328// flip stays in memory only — the chain-param commitment keeps recording that
329// the packet was sent.
330func (s *Store) SetPacketAcknowledged(_ int, rlm realm, packet types.Packet) {
331	assertIsRlmCurrent(0, rlm)
332
333	s.setCommitmentMem(packetCommitmentPath(packet), types.COMMITMENT_MAGIC_ACK)
334}
335
336// HasPacketReceipt reports whether a destination-side receipt exists.
337func (s *Store) HasPacketReceipt(packet types.Packet) bool {
338	_, found := s.getCommitment(packetAcknowledgementPath(packet))
339	return found
340}
341
342// SetPacketReceipt records a destination-side receipt (replay protection).
343func (s *Store) SetPacketReceipt(_ int, rlm realm, packet types.Packet) {
344	assertIsRlmCurrent(0, rlm)
345
346	s.setCommitment(packetAcknowledgementPath(packet), types.COMMITMENT_MAGIC)
347}
348
349// CommitAcknowledgement records the acknowledgement for a received packet.
350func (s *Store) CommitAcknowledgement(_ int, rlm realm, packet types.Packet, ack []byte) {
351	assertIsRlmCurrent(0, rlm)
352
353	s.setCommitment(packetAcknowledgementPath(packet), types.CommitAcks([][]byte{ack}))
354}
355
356// AcknowledgementReceipt returns the receipt stored at a packet's ack path.
357func (s *Store) AcknowledgementReceipt(channelId types.ChannelId, packet types.Packet) (types.H256, bool) {
358	return s.getCommitment(packetAcknowledgementPath(packet))
359}
360
361// PacketCommitment returns the commitment stored at a packet's commitment path.
362func (s *Store) PacketCommitment(channelId types.ChannelId, packet types.Packet) (types.H256, bool) {
363	return s.getCommitment(packetCommitmentPath(packet))
364}
365
366// SaveBatchPackets records a batch commitment under batchHash.
367func (s *Store) SaveBatchPackets(_ int, rlm realm, channelId types.ChannelId, batchHash, value types.H256) {
368	assertIsRlmCurrent(0, rlm)
369
370	s.setCommitment(types.BatchPacketsPath(batchHash), value)
371}
372
373// SaveBatchReceipts records a batch receipt under batchHash.
374func (s *Store) SaveBatchReceipts(_ int, rlm realm, channelId types.ChannelId, batchHash, value types.H256) {
375	assertIsRlmCurrent(0, rlm)
376
377	s.setCommitment(types.BatchReceiptsPath(batchHash), value)
378}
379
380// SaveMembershipProof records a verified membership commitment.
381func (s *Store) SaveMembershipProof(_ int, rlm realm, clientId types.ClientId, proofHeight uint64, path []byte, value types.H256) {
382	assertIsRlmCurrent(0, rlm)
383
384	s.saveMembershipProof(clientId, proofHeight, path, value)
385}
386
387// SaveNonMembershipProof records a verified non-membership commitment.
388func (s *Store) SaveNonMembershipProof(_ int, rlm realm, clientId types.ClientId, proofHeight uint64, path []byte, value types.H256) {
389	assertIsRlmCurrent(0, rlm)
390
391	s.saveNonMembershipProof(clientId, proofHeight, path, value)
392}
393
394// ClientStateBytes returns the mirrored client state bytes for a client.
395func (s *Store) ClientStateBytes(clientId types.ClientId) ([]byte, bool) {
396	c := s.getClient(clientId)
397	if c == nil || len(c.clientState) == 0 {
398		return nil, false
399	}
400
401	return types.CloneBytes(c.clientState), true
402}
403
404// ConsensusStateBytes returns the consensus state bytes at a height.
405func (s *Store) ConsensusStateBytes(clientId types.ClientId, height types.Height) ([]byte, bool) {
406	c := s.getClient(clientId)
407	if c == nil {
408		return nil, false
409	}
410
411	return c.getConsensusState(height)
412}
413
414// BatchPacketsAt returns the batch packet commitment for a batch hash. The
415// commitment is keyed by the global batch path, so no channel id is required.
416func (s *Store) BatchPacketsAt(batchHash types.H256) (types.H256, bool) {
417	return s.getCommitment(types.BatchPacketsPath(batchHash))
418}
419
420// BatchReceiptsAt returns the batch receipt commitment for a batch hash. The
421// commitment is keyed by the global batch path, so no channel id is required.
422func (s *Store) BatchReceiptsAt(batchHash types.H256) (types.H256, bool) {
423	return s.getCommitment(types.BatchReceiptsPath(batchHash))
424}
425
426// MembershipProofAt returns the committed membership proof value for the
427// client/height/path key.
428func (s *Store) MembershipProofAt(clientId types.ClientId, proofHeight uint64, path []byte) (types.H256, bool) {
429	return s.getMembershipProof(clientId, proofHeight, path)
430}
431
432// HasNonMembershipProof reports whether a non-membership proof was committed
433// for the client/height/path key.
434func (s *Store) HasNonMembershipProof(clientId types.ClientId, proofHeight uint64, path []byte) bool {
435	return s.hasNonMembershipProof(clientId, proofHeight, path)
436}
437
438// --- Key helpers ---
439
440func connectionKey(id types.ConnectionId) string {
441	return seqid.ID(id).Binary()
442}
443
444func channelKey(id types.ChannelId) string {
445	return seqid.ID(id).Binary()
446}
447
448func setChainParam(key string, value []byte) {
449	// NOTE: find a way to assert this write in tests (difficult since params does
450	// not expose getters)
451	params.SetBytes(key, value)
452}
453
454// h256ChainParamKey encodes Union commitment paths for chain params. Unlike
455// classic IBC string paths, Union paths are H256 storage keys and are mirrored
456// into params as lowercase hex strings.
457func h256ChainParamKey(key types.H256) string {
458	return hex.EncodeToString(key[:])
459}
460
461// --- Commitment primitives (path → value) ---
462
463// setCommitment stores a commitment value and mirrors it into chain params so
464// counterparties can prove it.
465func (s *Store) setCommitment(path types.H256, value types.H256) {
466	s.commitments[path] = value
467	setChainParam(h256ChainParamKey(path), value[:])
468}
469
470// setCommitmentMem stores a commitment value in memory only (no chain-param
471// mirror). Used for the acknowledged flip, which is local bookkeeping.
472func (s *Store) setCommitmentMem(path types.H256, value types.H256) {
473	s.commitments[path] = value
474}
475
476// getCommitment reads the commitment value stored at path.
477func (s *Store) getCommitment(path types.H256) (types.H256, bool) {
478	v, found := s.commitments[path]
479	if !found {
480		var zero types.H256
481		return zero, false
482	}
483
484	return v, true
485}
486
487// --- Client state ---
488
489func (c *client) saveClientState(clientStateBytes []byte) {
490	c.clientState = types.CloneBytes(clientStateBytes)
491	setChainParam(h256ChainParamKey(types.ClientStatePath(c.id)), h256Bytes(keccak(clientStateBytes)))
492}
493
494func (c *client) saveConsensusState(height types.Height, consensusStateBytes []byte) {
495	c.consensusByHeight.Set(string(height.Bytes()), types.CloneBytes(consensusStateBytes))
496	setChainParam(h256ChainParamKey(types.ConsensusStatePath(c.id, height)), h256Bytes(keccak(consensusStateBytes)))
497}
498
499func (c *client) getConsensusState(height types.Height) ([]byte, bool) {
500	v := c.consensusByHeight.Get(string(height.Bytes()))
501	if v == nil {
502		return nil, false
503	}
504
505	return types.CloneBytes(v.([]byte)), true
506}
507
508// --- Client store (light client storage writes) ---
509
510func (c *client) saveClientStore(key string, value []byte) {
511	c.clientStore[key] = types.CloneBytes(value)
512}
513
514func (c *client) getClientStore(key string) ([]byte, bool) {
515	v, found := c.clientStore[key]
516	if !found {
517		return nil, false
518	}
519
520	return types.CloneBytes(v), true
521}
522
523// --- Committed proofs (global paths) ---
524
525func (s *Store) saveMembershipProof(clientId types.ClientId, proofHeight uint64, path []byte, value types.H256) {
526	key := types.MembershipProofPath(clientId, proofHeight, path)
527	s.membershipProofs[key] = value
528	setChainParam(h256ChainParamKey(key), value[:])
529}
530
531func (s *Store) getMembershipProof(clientId types.ClientId, proofHeight uint64, path []byte) (types.H256, bool) {
532	v, found := s.membershipProofs[types.MembershipProofPath(clientId, proofHeight, path)]
533	if !found {
534		var zero types.H256
535		return zero, false
536	}
537
538	return v, true
539}
540
541func (s *Store) saveNonMembershipProof(clientId types.ClientId, proofHeight uint64, path []byte, value types.H256) {
542	key := types.NonMembershipProofPath(clientId, proofHeight, path)
543	s.nonMembershipProofs[key] = value
544	setChainParam(h256ChainParamKey(key), value[:])
545}
546
547func (s *Store) hasNonMembershipProof(clientId types.ClientId, proofHeight uint64, path []byte) bool {
548	v, found := s.nonMembershipProofs[types.NonMembershipProofPath(clientId, proofHeight, path)]
549	return found && v == types.NON_MEMBERSHIP_COMMITMENT_VALUE
550}
551
552// --- ID sequences ---
553
554func (s *Store) nextConnectionId() types.ConnectionId {
555	return types.ConnectionId(uint32(s.connectionSeq.Next()))
556}
557
558func (s *Store) nextChannelId() types.ChannelId {
559	return types.ChannelId(uint32(s.channelSeq.Next()))
560}
561
562func (s *Store) nextClientId() types.ClientId {
563	return types.ClientId(uint32(s.clientSeq.Next()))
564}
565
566// --- Client ---
567
568func (s *Store) addClient(clientId types.ClientId, typ types.ClientType, creator address, lc lightclient.Interface) *client {
569	id := uint32(clientId)
570	clientImpl, _ := s.clientRegistryEntry(typ)
571	c := &client{
572		id:                clientId,
573		creator:           creator,
574		lightClient:       lc,
575		consensusByHeight: bptree.NewBPTree32(),
576		clientStore:       make(map[string][]byte),
577	}
578	s.clientTypeByID[id] = typ
579	s.clientImplByID[id] = clientImpl
580	s.clientByID[id] = c
581
582	return c
583}
584
585func (s *Store) registerClient(typ types.ClientType, impl lightclient.ClientImpl) {
586	key := typ.String()
587	if _, ok := s.clientRegistry[key]; ok {
588		panic("client type already exists")
589	}
590	s.clientRegistry[key] = impl
591}
592
593func (s *Store) clientRegistryEntry(typ types.ClientType) (lightclient.ClientImpl, bool) {
594	impl, found := s.clientRegistry[string(typ)]
595	return impl, found
596}
597
598func (s *Store) getClient(clientId types.ClientId) *client {
599	c, found := s.clientByID[uint32(clientId)]
600	if !found {
601		return nil
602	}
603
604	return c
605}
606
607func (s *Store) lightClientOf(clientId types.ClientId) (lightclient.Interface, bool) {
608	c := s.getClient(clientId)
609	if c == nil {
610		return nil, false
611	}
612
613	return c.lightClient, true
614}
615
616// --- App routing ---
617
618func portKey(portId types.Bytes) string {
619	return string(portId)
620}
621
622func (s *Store) getAppByPortId(portId types.Bytes) app.IApp {
623	a, ok := s.ports[portKey(portId)]
624	if !ok {
625		panic(makeError(ErrPortNotFound, string(portId)))
626	}
627
628	return a
629}
630
631// --- Connection ---
632
633func (s *Store) saveConnection(connectionId types.ConnectionId, connection types.Connection) {
634	s.connections.Set(connectionKey(connectionId), connection)
635	setChainParam(h256ChainParamKey(types.ConnectionPath(connectionId)), h256Bytes(connectionValue(connection)))
636}
637
638func (s *Store) getConnection(connectionId types.ConnectionId) (types.Connection, bool) {
639	v := s.connections.Get(connectionKey(connectionId))
640	if v == nil {
641		return types.Connection{}, false
642	}
643
644	return v.(types.Connection), true
645}
646
647// --- Channel ---
648
649func (s *Store) createChannel(channelId types.ChannelId, channel types.Channel, portId types.Bytes) {
650	entry := &channelEntry{
651		channel: cloneChannel(channel),
652		portId:  portId.Clone(),
653	}
654	s.channels.Set(channelKey(channelId), entry)
655	setChainParam(h256ChainParamKey(types.ChannelPath(channelId)), h256Bytes(channelValue(channel)))
656}
657
658func (s *Store) saveChannel(channelId types.ChannelId, channel types.Channel) {
659	entry, found := s.getChannelEntry(channelId)
660	if !found {
661		panic(makeError(ErrChannelNotFound, channelId.String()))
662	}
663
664	entry.channel = cloneChannel(channel)
665	setChainParam(h256ChainParamKey(types.ChannelPath(channelId)), h256Bytes(channelValue(channel)))
666}
667
668func (s *Store) getChannelEntry(channelId types.ChannelId) (*channelEntry, bool) {
669	v := s.channels.Get(channelKey(channelId))
670	if v == nil {
671		return nil, false
672	}
673
674	return v.(*channelEntry), true
675}
676
677func (s *Store) getChannel(channelId types.ChannelId) types.Channel {
678	entry, found := s.getChannelEntry(channelId)
679	if !found {
680		panic(makeError(ErrChannelNotFound, channelId.String()))
681	}
682
683	return entry.channel
684}
685
686func (s *Store) getChannelOwner(channelId types.ChannelId) types.Bytes {
687	entry, found := s.getChannelEntry(channelId)
688	if !found {
689		panic(makeError(ErrChannelNotFound, channelId.String()))
690	}
691
692	return entry.portId
693}