Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Miner] feat: add TxClient #94

Merged
merged 24 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
69a9b00
chore: add `TxClient` interface
bryanchriswhite Oct 30, 2023
eddc545
chore: add option support to `ReplayObservable`
bryanchriswhite Oct 31, 2023
a96d9c4
feat: add `txClient` implementation
bryanchriswhite Oct 30, 2023
b84f7e6
test: `txClient`
bryanchriswhite Oct 30, 2023
1e0aa3a
test: tx client integration
bryanchriswhite Oct 31, 2023
0cc7dac
chore: s/tx/transaction/g
bryanchriswhite Nov 2, 2023
29da6d1
chore: update pkg README.md template
bryanchriswhite Nov 2, 2023
1cfa3dc
wip: client pkg README
bryanchriswhite Nov 2, 2023
f5e9737
docs: fix client pkg godoc comment
bryanchriswhite Nov 3, 2023
e136136
fix: flakey test
bryanchriswhite Nov 3, 2023
833968c
chore: dial back godoc comments 😅
bryanchriswhite Nov 3, 2023
62b5111
chore: revise (and move to godoc.go) `testblock` & `testeventsquery` …
bryanchriswhite Nov 6, 2023
37abd88
chore: update go.mod
bryanchriswhite Nov 6, 2023
d39f3d7
chore: refactor & condense godoc comments
bryanchriswhite Nov 6, 2023
c175d19
Merge remote-tracking branch 'pokt/main' into feat/tx-client
bryanchriswhite Nov 6, 2023
338b66e
chore: fix import paths post-update
bryanchriswhite Nov 6, 2023
b67bd2c
chore: review feedback improvements
bryanchriswhite Nov 6, 2023
c795499
docs: update client README.md
bryanchriswhite Nov 6, 2023
5693421
Merge remote-tracking branch 'pokt/main' into feat/tx-client
bryanchriswhite Nov 6, 2023
d58f88e
docs: add `tx query` usage association between `txContext` & `Blockch…
bryanchriswhite Nov 6, 2023
daf3668
docs: add TOC
bryanchriswhite Nov 6, 2023
71c407c
chore: review feedback improvements
bryanchriswhite Nov 7, 2023
baca6ef
docs: improve godoc comments & client README.md
bryanchriswhite Nov 7, 2023
4b9f156
Merge branch 'main' into feat/tx-client
bryanchriswhite Nov 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions docs/pkg/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Package `client`

bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved
> Standardized interfaces facilitating interactions with blockchain functionalities.

## Overview

The `client` package serves as a foundational layer for applications aiming to integrate with various blockchain platforms. It abstracts the complexities of sending, receiving, and querying blockchain data, ensuring a consistent experience irrespective of the underlying blockchain:

- **Simplifies Blockchain Interactions**: Provides a clear interface for initiating transactions and querying blockchain events without dealing with platform-specific quirks.
- **Modular and Extendable**: Designed with separation of concerns in mind, allowing developers to customize or replace components as necessary.
- **Unified Communication**: Regardless of the blockchain in use, the interfaces offer a standard way to communicate, streamlining the development process.

## Architecture Diagrams
bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved

Visual representations often make it easier to understand the design and flow of a package. Diagrams specific to this package can be added here.

```mermaid
---
title: Clients Dependency Tree
---
flowchart

sup[SupplierClient]
tx[TxClient]
bl[BlockClient]
evt[EventsQueryClient]

sup --"#SignAndBroadcast()"--> tx

tx --"#CommittedBlocksSequence()"--> bl
tx --"#EventsBytes()"--> evt
bl --"#EventsBytes()"--> evt
```

> **Figure 1**: An overview diagram showing the interaction between the various interfaces and the blockchain.

```mermaid
---
title: TxClient Dependency Graph
---


flowchart

subgraph tclient[TxClient]
tctx[TxContext]
builder[TxBuilder]
keyring[Keyring]

bclient[BlockClient]
eclient[EventsQueryClient]

tclient_internal((_))
end

chain[Blockchain]

tctx --"#GetKeyring()"--> keyring
tctx --"#SignTx()"--> builder
tctx --"#EncodeTx()"--> builder
tctx -."#BroadcastTx()".-> chain
tctx -."#QueryTx()".-> chain

eclient -."websocket connection".-> chain
bclient --"committed block subscription"--> eclient
tclient_internal --"own tx subscription"--> eclient
```

> **Figure 2**: A focused look at how `TxClient` functions and interacts with the underlying blockchain.

## Installation

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

## Features

- **TxClient Interface**: A streamlined way to sign and broadcast multiple messages as part of a blockchain transaction.
- **BlockClient Interface**: Notifications about newly committed blocks and access to the latest block.
- **EventsQueryClient**: Enables subscription to chain event messages.
- **Connection Interface**: A transport agnostic communication channel for sending and receiving messages.
- **Dialer**: Simplifies the establishment of connections.

## Usage

### Basic Example

```go
// Code example showcasing the use of TxClient or any other primary interface.
bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved
```

### Advanced Usage

```go
// Example illustrating advanced features or edge cases of the package.
bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved
```

### 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).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotta say this is pretty nice


## 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.
115 changes: 115 additions & 0 deletions internal/testclient/testblock/client.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,43 @@
// Package testblock provides utilities and mock clients to facilitate testing
// interactions with blockchain-related functionality. It includes tools for creating mock
// BlockClients, Block observables, and mock blocks tailored for specific testing scenarios.
// The package is designed to help ensure that tests around blockchain functionality are
// robust, using mock implementations to replicate expected behaviors in controlled environments.
//
// Given its role in testing, the testblock package leverages several other testing
// packages and libraries, such as gomock, testify, and internal testing clients
// from the pocket project.
package testblock

import (
"context"
"testing"

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

"pocket/internal/mocks/mockclient"
"pocket/internal/testclient"
"pocket/internal/testclient/testeventsquery"
"pocket/pkg/client"
"pocket/pkg/client/block"
"pocket/pkg/observable/channel"
)

// NewLocalnetClient creates and returns a new BlockClient for localnet testing
// environments.
//
// Parameters:
// - ctx: The context for creating the client.
// - t: The testing.T instance for assertions.
//
// The function initializes a localnet query client, ensures its successful creation, and then
// proceeds to set up the dependencies required to instantiate a new BlockClient.
// The final BlockClient instance connects to the CometLocalWebsocketURL endpoint.
//
// Returns:
// - A new instance of client.BlockClient configured for localnet interactions.
func NewLocalnetClient(ctx context.Context, t *testing.T) client.BlockClient {
t.Helper()

Expand All @@ -25,3 +50,93 @@ 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.
//
// Parameters:
// - t: *testing.T - The test instance.
// - blocksPublishCh: chan client.Block - The channel from which blocks are published to the observable.
//
// Returns:
// - *mockclient.MockBlockClient: The mock block client.
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.
//
// Parameters:
// - t: *testing.T - The test instance.
// - hash: []byte - The expected hash value that the mock Block should return.
// - height: int64 - The expected block height that the mock Block should return.
//
// Returns:
// - *mockclient.MockBlockClient: The mock block client.
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.
//
// Parameters:
// - t: *testing.T - The test instance.
// - hash: []byte - The expected hash value that the mock Block should return.
// - height: int64 - The expected block height that the mock Block should return.
//
// Returns:
// - *mockclient.MockBlock: The mock block.
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
}
85 changes: 83 additions & 2 deletions internal/testclient/testeventsquery/client.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,98 @@
// Package testeventsquery provides helper methods and mocks for testing events query functionality.
package testeventsquery

import (
"context"
"fmt"
"testing"

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

"pocket/internal/mocks/mockclient"
"pocket/internal/testclient"
"pocket/pkg/client"
eventsquery "pocket/pkg/client/events_query"
"pocket/pkg/either"
"pocket/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 connects to the
// localnet sequencer. It leverages the NewEventsQueryClient from the eventsquery package.
//
// Parameters:
// t: Testing object which is passed for marking helper.
// opts: Any additional options to configure the events query client.
//
// Returns: An instance of EventsQueryClient which connects to the localnet sequencer.
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. It returns a mock client that emits event bytes to the provided publish channel.
//
// Parameters:
bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved
// ctx: Context object to define the context for the operation.
// t: Testing object.
// query: The query string for which event bytes are fetched.
// publishCh: Channel to which the event bytes are published.
//
// Returns: A mock instance of EventsQueryClient which behaves as described.
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 initializes a new MockEventsQueryClient that is set up to
// query for transaction events for a specific message sender. This is useful for tests where
// you want to listen for a one-time event related to a specific sender.
//
// Parameters:
// - ctx: The context to pass to the client.
// - t: The testing.T instance for assertions.
// - key: The keyring record from which the signing address is derived.
// - publishCh: A channel where the events are published.
//
// Returns:
// - A new instance of mockclient.MockEventsQueryClient set up for the specific query.
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,
)
}
Loading