Skip to content

Commit

Permalink
[Miner] feat: add TxClient (#94)
Browse files Browse the repository at this point in the history
* chore: add `TxClient` interface

* chore: add option support to `ReplayObservable`

* feat: add `txClient` implementation

* test: `txClient`

* test: tx client integration

* chore: s/tx/transaction/g

* chore: update pkg README.md template

* wip: client pkg README

* docs: fix client pkg godoc comment

* fix: flakey test

* chore: dial back godoc comments 😅

* chore: revise (and move to godoc.go) `testblock` & `testeventsquery` pkg godoc comment

* chore: update go.mod

* chore: refactor & condense godoc comments

* chore: fix import paths post-update

* chore: review feedback improvements

* docs: update client README.md

* docs: add `tx query` usage association between `txContext` & `Blockchain`

* docs: add TOC

* chore: review feedback improvements

Co-authored-by: Daniel Olshansky <[email protected]>

* docs: improve godoc comments & client README.md

---------

Co-authored-by: Daniel Olshansky <[email protected]>
  • Loading branch information
bryanchriswhite and Olshansk committed Nov 7, 2023
1 parent dc8a37d commit 2c940f4
Show file tree
Hide file tree
Showing 20 changed files with 1,515 additions and 154 deletions.
147 changes: 147 additions & 0 deletions docs/pkg/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Package `client`

## Table of Contents

- [Overview](#overview)
- [Features](#features)
- [Architecture Overview](#architecture-overview)
- [Component Diagram Legend](#component-diagram-legend)
- [Clients Dependency Tree](#clients-dependency-tree)
- [Network Interaction](#network-interaction)
- [Installation](#installation)
- [Usage](#usage)
- [Basic Example](#basic-example)
- [Advanced Usage](#advanced-usage)
- [Configuration](#configuration)
- [API Reference](#api-reference)
- [Best Practices](#best-practices)
- [FAQ](#faq)


## Overview

The `client` package exposes go APIs to facilitate interactions with the Pocket network.
It includes lower-level interfaces for working with transactions and subscribing to events generally, as well as higher-level interfaces for tracking blocks and broadcasting protocol-specific transactions.

## Features

| Interface | Description |
|-------------------------|----------------------------------------------------------------------------------------------------|
| **`SupplierClient`** | A high-level client for use by the "supplier" actor. |
| **`TxClient`** | A high-level client used to build, sign, and broadcast transaction from cosmos-sdk messages. |
| **`TxContext`** | Abstracts and encapsulates the transaction building, signing, encoding, and broadcasting concerns. |
| **`BlockClient`** | Exposes methods for receiving notifications about newly committed blocks. |
| **`EventsQueryClient`** | Encapsulates blockchain event subscriptions. |
| **`Connection`** | A transport agnostic communication channel for sending and receiving messages. |
| **`Dialer`** | Abstracts the establishment of connections. |

## Architecture Overview

```mermaid
---
title: Component Diagram Legend
---
flowchart
c[Component]
d[Dependency Component]
s[[Subcomponent]]
r[Remote Component]
c --"direct usage via #DependencyMethod()"--> d
c -."usage via network I/O".-> r
c --> s
```

> **Figure 1**: A legend for the component diagrams in this document.
```mermaid
---
title: Clients Dependency Tree
---
flowchart
sup[SupplierClient]
tx[TxClient]
txctx[[TxContext]]
bl[BlockClient]
evt[EventsQueryClient]
conn[[Connection]]
dial[[Dialer]]
sup --"#SignAndBroadcast()"--> tx
tx --"#CommittedBlocksSequence()"--> bl
tx --"#BroadcastTx"--> txctx
tx --"#EventsBytes()"--> evt
bl --"#EventsBytes()"--> evt
evt --> conn
evt --"#DialContext()"--> dial
dial --"(returns)"--> conn
```

> **Figure 2**: An overview which articulates the dependency relationships between the various client interfaces and their subcompnents.
```mermaid
---
title: Network Interaction
---
flowchart
txctx[[TxContext]]
conn[[Connection]]
dial[[Dialer]]
chain[Blockchain]
conn <-."subscribed events".-> chain
dial -."RPC subscribe".-> chain
txctx -."tx broadcast".-> chain
txctx -."tx query".-> chain
```

> **Figure 3**: An overview of how client subcomponents interact with the network.
## Installation

```bash
go get github.com/pokt-network/poktroll/pkg/client
```

## Usage

### Basic Example

```go
// TODO: Code example showcasing the use of TxClient or any other primary interface.
```

### Advanced Usage

```go
// TODO: Example illustrating advanced features or edge cases of the package.
```

### Configuration

- **TxClientOption**: Function type that modifies the `TxClient` allowing for flexible and optional configurations.
- **EventsQueryClientOption**: Modifies the `EventsQueryClient` to apply custom behaviors or configurations.

## API Reference

For the complete API details, see the [godoc](https://pkg.go.dev/github.com/pokt-network/poktroll/pkg/client).

## Best Practices

- **Use Abstractions**: Instead of directly communicating with blockchain platforms, leverage the provided interfaces for consistent and error-free interactions.
- **Stay Updated**: With evolving blockchain technologies, ensure to keep the package updated for any new features or security patches.

## FAQ

#### How does the `TxClient` interface differ from `TxContext`?

While `TxClient` is centered around signing and broadcasting transactions, `TxContext` consolidates operational dependencies for the transaction lifecycle, like building, encoding, and querying.

#### Can I extend or customize the provided interfaces?

Yes, the package is designed with modularity in mind. You can either implement the interfaces based on your requirements or extend them for additional functionalities.
21 changes: 2 additions & 19 deletions docs/template/pkg/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,7 @@ If the package can be configured in some way, describe it here:

## API Reference

While `godoc` will provide the detailed API reference, you can highlight or briefly describe key functions, types, or methods here.

- `FunctionOrType1()`: A short description of its purpose.
- `FunctionOrType2(param Type)`: Another brief description.

For the complete API details, see the [godoc](https://pkg.go.dev/github.com/yourusername/yourproject/[PackageName]).
For the complete API details, see the [godoc](https://pkg.go.dev/github.com/pokt-network/poktroll/[PackageName]).

## Best Practices

Expand All @@ -90,16 +85,4 @@ Answer for question 1.

#### Question 2?

Answer for question 2.

## Contributing

Briefly describe how others can contribute to this package. Link to the main contributing guide if you have one.

## Changelog

For detailed release notes, see the [CHANGELOG](../CHANGELOG.md) at the root level or link to a separate CHANGELOG specific to this package.

## License

This package is released under the XYZ License. For more information, see the [LICENSE](../LICENSE) file at the root level.
Answer for question 2.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ require (
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/hashicorp/golang-lru/v2 v2.0.2 // indirect
Expand Down
3 changes: 1 addition & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -936,9 +936,8 @@ github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoD
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
Expand Down
73 changes: 73 additions & 0 deletions internal/testclient/testblock/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ import (
"testing"

"cosmossdk.io/depinject"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"

"github.com/pokt-network/poktroll/internal/mocks/mockclient"
"github.com/pokt-network/poktroll/internal/testclient"
"github.com/pokt-network/poktroll/internal/testclient/testeventsquery"
"github.com/pokt-network/poktroll/pkg/client"
"github.com/pokt-network/poktroll/pkg/client/block"
"github.com/pokt-network/poktroll/pkg/observable/channel"
)

// NewLocalnetClient creates and returns a new BlockClient that's configured for
// use with the localnet sequencer.
func NewLocalnetClient(ctx context.Context, t *testing.T) client.BlockClient {
t.Helper()

Expand All @@ -25,3 +30,71 @@ func NewLocalnetClient(ctx context.Context, t *testing.T) client.BlockClient {

return bClient
}

// NewOneTimeCommittedBlocksSequenceBlockClient creates a new mock BlockClient.
// This mock BlockClient will expect a call to CommittedBlocksSequence, and
// when that call is made, it returns a new BlocksObservable that is notified of
// blocks sent on the given blocksPublishCh.
// blocksPublishCh is the channel the caller can use to publish blocks the observable.
func NewOneTimeCommittedBlocksSequenceBlockClient(
t *testing.T,
blocksPublishCh chan client.Block,
) *mockclient.MockBlockClient {
t.Helper()

// Create a mock for the block client which expects the LatestBlock method to be called any number of times.
blockClientMock := NewAnyTimeLatestBlockBlockClient(t, nil, 0)

// Set up the mock expectation for the CommittedBlocksSequence method. When
// the method is called, it returns a new replay observable that publishes
// blocks sent on the given blocksPublishCh.
blockClientMock.EXPECT().CommittedBlocksSequence(
gomock.AssignableToTypeOf(context.Background()),
).DoAndReturn(func(ctx context.Context) client.BlocksObservable {
// Create a new replay observable with a replay buffer size of 1. Blocks
// are published to this observable via the provided blocksPublishCh.
withPublisherOpt := channel.WithPublisher(blocksPublishCh)
obs, _ := channel.NewReplayObservable[client.Block](
ctx, 1, withPublisherOpt,
)
return obs
})

return blockClientMock
}

// NewAnyTimeLatestBlockBlockClient creates a mock BlockClient that expects
// calls to the LatestBlock method any number of times. When the LatestBlock
// method is called, it returns a mock Block with the provided hash and height.
func NewAnyTimeLatestBlockBlockClient(
t *testing.T,
hash []byte,
height int64,
) *mockclient.MockBlockClient {
t.Helper()
ctrl := gomock.NewController(t)

// Create a mock block that returns the provided hash and height.
blockMock := NewAnyTimesBlock(t, hash, height)
// Create a mock block client that expects calls to LatestBlock method and
// returns the mock block.
blockClientMock := mockclient.NewMockBlockClient(ctrl)
blockClientMock.EXPECT().LatestBlock(gomock.Any()).Return(blockMock).AnyTimes()

return blockClientMock
}

// NewAnyTimesBlock creates a mock Block that expects calls to Height and Hash
// methods any number of times. When the methods are called, they return the
// provided height and hash respectively.
func NewAnyTimesBlock(t *testing.T, hash []byte, height int64) *mockclient.MockBlock {
t.Helper()
ctrl := gomock.NewController(t)

// Create a mock block that returns the provided hash and height AnyTimes.
blockMock := mockclient.NewMockBlock(ctrl)
blockMock.EXPECT().Height().Return(height).AnyTimes()
blockMock.EXPECT().Hash().Return(hash).AnyTimes()

return blockMock
}
4 changes: 4 additions & 0 deletions internal/testclient/testblock/godoc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Package testblock provides helper functions for constructing real (e.g. localnet)
// and mock BlockClient objects with pre-configured and/or parameterized call
// arguments, return value(s), and/or expectations thereof. Intended for use in tests.
package testblock
61 changes: 59 additions & 2 deletions internal/testclient/testeventsquery/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package testeventsquery

import (
"context"
"fmt"
"testing"
"time"

cosmoskeyring "github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"

"github.com/pokt-network/poktroll/internal/mocks/mockclient"
"github.com/pokt-network/poktroll/internal/testclient"
Expand All @@ -15,14 +18,68 @@ import (
"github.com/pokt-network/poktroll/pkg/observable/channel"
)

// NewLocalnetClient returns a new events query client which is configured to
// connect to the localnet sequencer.
// NewLocalnetClient creates and returns a new events query client that's configured
// for use with the localnet sequencer. Any options provided are applied to the client.
func NewLocalnetClient(t *testing.T, opts ...client.EventsQueryClientOption) client.EventsQueryClient {
t.Helper()

return eventsquery.NewEventsQueryClient(testclient.CometLocalWebsocketURL, opts...)
}

// NewOneTimeEventsQuery creates a mock of the EventsQueryClient which expects
// a single call to the EventsBytes method. query is the query string which is
// expected to be received by that call.
// It returns a mock client whose event bytes method constructs a new observable.
// The caller can simulate blockchain events by sending on the value publishCh
// points to, which is set by this helper function.
func NewOneTimeEventsQuery(
ctx context.Context,
t *testing.T,
query string,
publishCh *chan<- either.Bytes,
) *mockclient.MockEventsQueryClient {
t.Helper()
ctrl := gomock.NewController(t)

eventsQueryClient := mockclient.NewMockEventsQueryClient(ctrl)
eventsQueryClient.EXPECT().EventsBytes(gomock.Eq(ctx), gomock.Eq(query)).
DoAndReturn(func(
ctx context.Context,
query string,
) (eventsBzObservable client.EventsBytesObservable, err error) {
eventsBzObservable, *publishCh = channel.NewObservable[either.Bytes]()
return eventsBzObservable, nil
}).Times(1)
return eventsQueryClient
}

// NewOneTimeTxEventsQueryClient creates a mock of the Events that expects
// a single call to the EventsBytes method where the query is for transaction
// events for sender address matching that of the given key.
// The caller can simulate blockchain events by sending on the value publishCh
// points to, which is set by this helper function.
func NewOneTimeTxEventsQueryClient(
ctx context.Context,
t *testing.T,
key *cosmoskeyring.Record,
publishCh *chan<- either.Bytes,
) *mockclient.MockEventsQueryClient {
t.Helper()

signingAddr, err := key.GetAddress()
require.NoError(t, err)

expectedEventsQuery := fmt.Sprintf(
"tm.event='Tx' AND message.sender='%s'",
signingAddr,
)
return NewOneTimeEventsQuery(
ctx, t,
expectedEventsQuery,
publishCh,
)
}

// NewAnyTimesEventsBytesEventsQueryClient returns a new events query client which
// is configured to return the expected event bytes when queried with the expected
// query, any number of times. The returned client also expects to be closed once.
Expand Down
5 changes: 5 additions & 0 deletions internal/testclient/testeventsquery/godoc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Package testeventsquery provides helper functions for constructing real
// (e.g. localnet) and mock EventsQueryClient objects with pre-configured and/or
// parameterized call arguments, return value(s), and/or expectations thereof.
// Intended for use in tests.
package testeventsquery
Loading

0 comments on commit 2c940f4

Please sign in to comment.