Skip to content

Commit 7eb6328

Browse files
authored
Merge pull request #762 from The-K-R-O-K/illia-malachyn/747-subscribe-account-statutes-endpoint
Add subscribe account statuses endpoint
2 parents 8b37d47 + f394c03 commit 7eb6328

5 files changed

Lines changed: 486 additions & 0 deletions

File tree

access/grpc/client.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,3 +413,26 @@ func convertSubscribeOptions(opts ...access.SubscribeOption) *SubscribeConfig {
413413
}
414414
return subsConf
415415
}
416+
417+
func (c *Client) SubscribeAccountStatusesFromStartHeight(
418+
ctx context.Context,
419+
startBlockHeight uint64,
420+
filter flow.AccountStatusFilter,
421+
) (<-chan flow.AccountStatus, <-chan error, error) {
422+
return c.grpc.SubscribeAccountStatusesFromStartHeight(ctx, startBlockHeight, filter)
423+
}
424+
425+
func (c *Client) SubscribeAccountStatusesFromStartBlockID(
426+
ctx context.Context,
427+
startBlockID flow.Identifier,
428+
filter flow.AccountStatusFilter,
429+
) (<-chan flow.AccountStatus, <-chan error, error) {
430+
return c.grpc.SubscribeAccountStatusesFromStartBlockID(ctx, startBlockID, filter)
431+
}
432+
433+
func (c *Client) SubscribeAccountStatusesFromLatestBlock(
434+
ctx context.Context,
435+
filter flow.AccountStatusFilter,
436+
) (<-chan flow.AccountStatus, <-chan error, error) {
437+
return c.grpc.SubscribeAccountStatusesFromLatestBlock(ctx, filter)
438+
}

access/grpc/convert/convert.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"fmt"
2424
"time"
2525

26+
"github.com/onflow/flow/protobuf/go/flow/executiondata"
2627
"google.golang.org/protobuf/types/known/timestamppb"
2728

2829
"github.com/onflow/cadence"
@@ -76,6 +77,43 @@ func MessageToAccount(m *entities.Account) (flow.Account, error) {
7677
}, nil
7778
}
7879

80+
func MessageToAccountStatus(m *executiondata.SubscribeAccountStatusesResponse) (flow.AccountStatus, error) {
81+
if m == nil {
82+
return flow.AccountStatus{}, ErrEmptyMessage
83+
}
84+
85+
results, err := MessageToAccountStatusResults(m.GetResults())
86+
if err != nil {
87+
return flow.AccountStatus{}, fmt.Errorf("error converting results: %w", err)
88+
}
89+
90+
return flow.AccountStatus{
91+
BlockID: MessageToIdentifier(m.GetBlockId()),
92+
BlockHeight: m.GetBlockHeight(),
93+
MessageIndex: m.GetMessageIndex(),
94+
Results: results,
95+
}, nil
96+
}
97+
98+
func MessageToAccountStatusResults(m []*executiondata.SubscribeAccountStatusesResponse_Result) ([]*flow.AccountStatusResult, error) {
99+
results := make([]*flow.AccountStatusResult, len(m))
100+
var emptyOptions []jsoncdc.Option
101+
102+
for i, r := range m {
103+
events, err := MessagesToEvents(r.GetEvents(), emptyOptions)
104+
if err != nil {
105+
return nil, fmt.Errorf("error converting events: %w", err)
106+
}
107+
108+
results[i] = &flow.AccountStatusResult{
109+
Address: flow.BytesToAddress(r.GetAddress()),
110+
Events: events,
111+
}
112+
}
113+
114+
return results, nil
115+
}
116+
79117
func AccountKeyToMessage(a *flow.AccountKey) *entities.AccountKey {
80118
return &entities.AccountKey{
81119
Index: uint32(a.Index),

access/grpc/grpc.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,148 @@ func receiveBlockHeadersFromClient[Client interface {
15171517
}
15181518
}
15191519

1520+
func (c *BaseClient) SubscribeAccountStatusesFromStartHeight(
1521+
ctx context.Context,
1522+
startHeight uint64,
1523+
filter flow.AccountStatusFilter,
1524+
opts ...grpc.CallOption,
1525+
) (<-chan flow.AccountStatus, <-chan error, error) {
1526+
request := &executiondata.SubscribeAccountStatusesFromStartHeightRequest{
1527+
StartBlockHeight: startHeight,
1528+
EventEncodingVersion: c.eventEncoding,
1529+
}
1530+
request.Filter = &executiondata.StatusFilter{
1531+
EventType: filter.EventTypes,
1532+
Address: filter.Addresses,
1533+
}
1534+
1535+
subscribeClient, err := c.executionDataClient.SubscribeAccountStatusesFromStartHeight(ctx, request, opts...)
1536+
if err != nil {
1537+
return nil, nil, newRPCError(err)
1538+
}
1539+
1540+
accountStatutesChan := make(chan flow.AccountStatus)
1541+
errChan := make(chan error)
1542+
1543+
go func() {
1544+
defer close(accountStatutesChan)
1545+
defer close(errChan)
1546+
receiveAccountStatusesFromStream(ctx, subscribeClient, accountStatutesChan, errChan)
1547+
}()
1548+
1549+
return accountStatutesChan, errChan, nil
1550+
}
1551+
1552+
func (c *BaseClient) SubscribeAccountStatusesFromStartBlockID(
1553+
ctx context.Context,
1554+
startBlockID flow.Identifier,
1555+
filter flow.AccountStatusFilter,
1556+
opts ...grpc.CallOption,
1557+
) (<-chan flow.AccountStatus, <-chan error, error) {
1558+
request := &executiondata.SubscribeAccountStatusesFromStartBlockIDRequest{
1559+
StartBlockId: startBlockID.Bytes(),
1560+
EventEncodingVersion: c.eventEncoding,
1561+
}
1562+
request.Filter = &executiondata.StatusFilter{
1563+
EventType: filter.EventTypes,
1564+
Address: filter.Addresses,
1565+
}
1566+
1567+
subscribeClient, err := c.executionDataClient.SubscribeAccountStatusesFromStartBlockID(ctx, request, opts...)
1568+
if err != nil {
1569+
return nil, nil, newRPCError(err)
1570+
}
1571+
1572+
accountStatutesChan := make(chan flow.AccountStatus)
1573+
errChan := make(chan error)
1574+
1575+
go func() {
1576+
defer close(accountStatutesChan)
1577+
defer close(errChan)
1578+
receiveAccountStatusesFromStream(ctx, subscribeClient, accountStatutesChan, errChan)
1579+
}()
1580+
1581+
return accountStatutesChan, errChan, nil
1582+
}
1583+
1584+
func (c *BaseClient) SubscribeAccountStatusesFromLatestBlock(
1585+
ctx context.Context,
1586+
filter flow.AccountStatusFilter,
1587+
opts ...grpc.CallOption,
1588+
) (<-chan flow.AccountStatus, <-chan error, error) {
1589+
request := &executiondata.SubscribeAccountStatusesFromLatestBlockRequest{
1590+
EventEncodingVersion: c.eventEncoding,
1591+
}
1592+
request.Filter = &executiondata.StatusFilter{
1593+
EventType: filter.EventTypes,
1594+
Address: filter.Addresses,
1595+
}
1596+
1597+
subscribeClient, err := c.executionDataClient.SubscribeAccountStatusesFromLatestBlock(ctx, request, opts...)
1598+
if err != nil {
1599+
return nil, nil, newRPCError(err)
1600+
}
1601+
1602+
accountStatutesChan := make(chan flow.AccountStatus)
1603+
errChan := make(chan error)
1604+
1605+
go func() {
1606+
defer close(accountStatutesChan)
1607+
defer close(errChan)
1608+
receiveAccountStatusesFromStream(ctx, subscribeClient, accountStatutesChan, errChan)
1609+
}()
1610+
1611+
return accountStatutesChan, errChan, nil
1612+
}
1613+
1614+
func receiveAccountStatusesFromStream[Stream interface {
1615+
Recv() (*executiondata.SubscribeAccountStatusesResponse, error)
1616+
}](
1617+
ctx context.Context,
1618+
stream Stream,
1619+
accountStatutesChan chan<- flow.AccountStatus,
1620+
errChan chan<- error,
1621+
) {
1622+
sendErr := func(err error) {
1623+
select {
1624+
case <-ctx.Done():
1625+
case errChan <- err:
1626+
}
1627+
}
1628+
1629+
var nextExpectedMsgIndex uint64
1630+
for {
1631+
accountStatusResponse, err := stream.Recv()
1632+
if err != nil {
1633+
if err == io.EOF {
1634+
// End of stream, return gracefully
1635+
return
1636+
}
1637+
1638+
sendErr(fmt.Errorf("error receiving account status: %w", err))
1639+
return
1640+
}
1641+
1642+
accountStatus, err := convert.MessageToAccountStatus(accountStatusResponse)
1643+
if err != nil {
1644+
sendErr(fmt.Errorf("error converting message to account status: %w", err))
1645+
return
1646+
}
1647+
1648+
if accountStatus.MessageIndex != nextExpectedMsgIndex {
1649+
sendErr(fmt.Errorf("message received out of order"))
1650+
return
1651+
}
1652+
nextExpectedMsgIndex = accountStatus.MessageIndex + 1
1653+
1654+
select {
1655+
case <-ctx.Done():
1656+
return
1657+
case accountStatutesChan <- accountStatus:
1658+
}
1659+
}
1660+
}
1661+
15201662
func (c *BaseClient) SubscribeBlockDigestsFromStartBlockID(
15211663
ctx context.Context,
15221664
startBlockID flow.Identifier,

0 commit comments

Comments
 (0)