Skip to content
This repository was archived by the owner on May 26, 2025. It is now read-only.

Commit e5347ef

Browse files
authored
feat: filter channels by status, return channels event on auth, use unified method to send updates (#22)
1 parent 4d96943 commit e5347ef

5 files changed

Lines changed: 218 additions & 83 deletions

File tree

channel.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,22 @@ func GetChannelByID(tx *gorm.DB, channelID string) (*Channel, error) {
7676
return &channel, nil
7777
}
7878

79-
// getChannelsForParticipant finds all channels for a participant
80-
func getChannelsForParticipant(tx *gorm.DB, participant string) ([]Channel, error) {
79+
// getChannelsByParticipant finds all channels for a participant
80+
func getChannelsByParticipant(tx *gorm.DB, participant string, status string) ([]Channel, error) {
8181
var channels []Channel
82-
if err := tx.Where("participant = ?",
83-
participant).Order("created_at DESC").Find(&channels).Error; err != nil {
82+
q := tx.Where("participant = ?", participant)
83+
if status != "" {
84+
q = q.Where("status = ?", status)
85+
}
86+
87+
if err := q.Order("created_at DESC").Find(&channels).Error; err != nil {
8488
return nil, fmt.Errorf("error finding channels for participant %s: %w", participant, err)
8589
}
90+
8691
return channels, nil
8792
}
8893

89-
// CheckExistingChannels checks if there is an existing open channel on the same network between participant A and B
94+
// CheckExistingChannels checks if there is an existing open channel on the same network between participant and broker
9095
func CheckExistingChannels(tx *gorm.DB, participantA, token string, chainID uint32) (*Channel, error) {
9196
var channel Channel
9297
err := tx.Where("participant = ? AND token = ? AND chain_id = ? AND status = ?", participantA, token, chainID, ChannelStatusOpen).

docs/API.md

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -591,15 +591,54 @@ Balance updates are sent as unsolicited server messages with the "bu" method:
591591

592592
The balance update provides the latest balances for all assets in the participant's unified ledger, allowing clients to maintain an up-to-date view of available funds without explicitly requesting them.
593593

594+
### Open Channels
595+
596+
The server automatically sends all open channels as a batch update to clients after successful authentication.
597+
598+
```json
599+
{
600+
"res": [1234567890123, "channels", [[
601+
{
602+
"channel_id": "0xfedcba9876543210...",
603+
"participant": "0x1234567890abcdef...",
604+
"status": "open",
605+
"token": "0xeeee567890abcdef...",
606+
"amount": "100000",
607+
"chain_id": 137,
608+
"adjudicator": "0xAdjudicatorContractAddress...",
609+
"challenge": 86400,
610+
"nonce": 1,
611+
"version": 2,
612+
"created_at": "2023-05-01T12:00:00Z",
613+
"updated_at": "2023-05-01T12:30:00Z"
614+
},
615+
{
616+
"channel_id": "0xabcdef1234567890...",
617+
"participant": "0x1234567890abcdef...",
618+
"status": "open",
619+
"token": "0xeeee567890abcdef...",
620+
"amount": "50000",
621+
"chain_id": 42220,
622+
"adjudicator": "0xAdjudicatorContractAddress...",
623+
"challenge": 86400,
624+
"nonce": 1,
625+
"version": 3,
626+
"created_at": "2023-04-15T10:00:00Z",
627+
"updated_at": "2023-04-20T14:30:00Z"
628+
}
629+
]], 1619123456789],
630+
"sig": ["0xabcd1234..."]
631+
}
632+
```
633+
594634
### Channel Updates
595635

596-
The server automatically sends channel updates to clients in these scenarios:
597-
1. After successful authentication (for all existing channels)
598-
2. When a channel is created
599-
3. When a channel's status changes (open, joined, closed)
600-
4. When a channel is resized
636+
For channel updates, the server sends them in these scenarios:
637+
1. When a channel is created
638+
2. When a channel's status changes (open, joined, closed)
639+
3. When a channel is resized
601640

602-
Channel updates are sent as unsolicited server messages with the "cu" method:
641+
Individual channel updates are sent as unsolicited server messages with the "cu" method:
603642

604643
```json
605644
{

handlers.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,13 +872,15 @@ func HandleCloseChannel(rpc *RPCMessage, db *gorm.DB, signer *Signer) (*RPCMessa
872872
// TODO: add filters, pagination, etc.
873873
func HandleGetChannels(rpc *RPCMessage, db *gorm.DB) (*RPCMessage, error) {
874874
var participant string
875+
var status string
875876

876877
if len(rpc.Req.Params) > 0 {
877878
paramsJSON, err := json.Marshal(rpc.Req.Params[0])
878879
if err == nil {
879880
var params map[string]string
880881
if err := json.Unmarshal(paramsJSON, &params); err == nil {
881882
participant = params["participant"]
883+
status = params["status"]
882884
}
883885
}
884886
}
@@ -887,7 +889,7 @@ func HandleGetChannels(rpc *RPCMessage, db *gorm.DB) (*RPCMessage, error) {
887889
return nil, errors.New("missing participant parameter")
888890
}
889891

890-
channels, err := getChannelsForParticipant(db, participant)
892+
channels, err := getChannelsByParticipant(db, participant, status)
891893
if err != nil {
892894
return nil, fmt.Errorf("failed to get channels: %w", err)
893895
}

handlers_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,108 @@ func TestHandleGetChannels(t *testing.T) {
605605
assert.NotEmpty(t, ch.UpdatedAt, "UpdatedAt should not be empty")
606606
}
607607

608+
// Test with status filter for "open" channels
609+
openStatusParams := map[string]string{
610+
"participant": participantAddr,
611+
"status": string(ChannelStatusOpen),
612+
}
613+
openStatusParamsJSON, err := json.Marshal(openStatusParams)
614+
require.NoError(t, err)
615+
616+
openStatusRequest := &RPCMessage{
617+
Req: &RPCData{
618+
RequestID: 456,
619+
Method: "get_channels",
620+
Params: []any{json.RawMessage(openStatusParamsJSON)},
621+
Timestamp: uint64(time.Now().Unix()),
622+
},
623+
}
624+
625+
reqBytes, err = json.Marshal(openStatusRequest.Req)
626+
require.NoError(t, err)
627+
signed, err = signer.Sign(reqBytes)
628+
require.NoError(t, err)
629+
openStatusRequest.Sig = []string{hexutil.Encode(signed)}
630+
631+
openStatusResponse, err := HandleGetChannels(openStatusRequest, db)
632+
require.NoError(t, err)
633+
require.NotNil(t, openStatusResponse)
634+
635+
// Extract and verify filtered channels
636+
openChannels, ok := openStatusResponse.Res.Params[0].([]ChannelResponse)
637+
require.True(t, ok, "Response parameter should be a slice of ChannelResponse")
638+
assert.Len(t, openChannels, 1, "Should return only 1 open channel")
639+
assert.Equal(t, "0xChannel1", openChannels[0].ChannelID, "Should return the open channel")
640+
assert.Equal(t, ChannelStatusOpen, openChannels[0].Status, "Status should be open")
641+
642+
// Test with status filter for "closed" channels
643+
closedStatusParams := map[string]string{
644+
"participant": participantAddr,
645+
"status": string(ChannelStatusClosed),
646+
}
647+
closedStatusParamsJSON, err := json.Marshal(closedStatusParams)
648+
require.NoError(t, err)
649+
650+
closedStatusRequest := &RPCMessage{
651+
Req: &RPCData{
652+
RequestID: 457,
653+
Method: "get_channels",
654+
Params: []any{json.RawMessage(closedStatusParamsJSON)},
655+
Timestamp: uint64(time.Now().Unix()),
656+
},
657+
}
658+
659+
reqBytes, err = json.Marshal(closedStatusRequest.Req)
660+
require.NoError(t, err)
661+
signed, err = signer.Sign(reqBytes)
662+
require.NoError(t, err)
663+
closedStatusRequest.Sig = []string{hexutil.Encode(signed)}
664+
665+
closedStatusResponse, err := HandleGetChannels(closedStatusRequest, db)
666+
require.NoError(t, err)
667+
require.NotNil(t, closedStatusResponse)
668+
669+
// Extract and verify filtered channels
670+
closedChannels, ok := closedStatusResponse.Res.Params[0].([]ChannelResponse)
671+
require.True(t, ok, "Response parameter should be a slice of ChannelResponse")
672+
assert.Len(t, closedChannels, 1, "Should return only 1 closed channel")
673+
assert.Equal(t, "0xChannel2", closedChannels[0].ChannelID, "Should return the closed channel")
674+
assert.Equal(t, ChannelStatusClosed, closedChannels[0].Status, "Status should be closed")
675+
676+
// Test with status filter for "joining" channels
677+
joiningStatusParams := map[string]string{
678+
"participant": participantAddr,
679+
"status": string(ChannelStatusJoining),
680+
}
681+
joiningStatusParamsJSON, err := json.Marshal(joiningStatusParams)
682+
require.NoError(t, err)
683+
684+
joiningStatusRequest := &RPCMessage{
685+
Req: &RPCData{
686+
RequestID: 458,
687+
Method: "get_channels",
688+
Params: []any{json.RawMessage(joiningStatusParamsJSON)},
689+
Timestamp: uint64(time.Now().Unix()),
690+
},
691+
}
692+
693+
reqBytes, err = json.Marshal(joiningStatusRequest.Req)
694+
require.NoError(t, err)
695+
signed, err = signer.Sign(reqBytes)
696+
require.NoError(t, err)
697+
joiningStatusRequest.Sig = []string{hexutil.Encode(signed)}
698+
699+
joiningStatusResponse, err := HandleGetChannels(joiningStatusRequest, db)
700+
require.NoError(t, err)
701+
require.NotNil(t, joiningStatusResponse)
702+
703+
// Extract and verify filtered channels
704+
joiningChannels, ok := joiningStatusResponse.Res.Params[0].([]ChannelResponse)
705+
require.True(t, ok, "Response parameter should be a slice of ChannelResponse")
706+
assert.Len(t, joiningChannels, 1, "Should return only 1 joining channel")
707+
assert.Equal(t, "0xChannel3", joiningChannels[0].ChannelID, "Should return the joining channel")
708+
assert.Equal(t, ChannelStatusJoining, joiningChannels[0].Status, "Status should be joining")
709+
608710
// Test with missing participant parameter
609711
missingParamReq := &RPCMessage{
610712
Req: &RPCData{

0 commit comments

Comments
 (0)