-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Miner] feat: add supplier client (#42)
* 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 * refactor: consolidate keyring errors & helpers * refactor: keyring test helpers * fix: flakey test * chore: dial back godoc comments 😅 * chore: add `SupplierClient` interface * feat: add supplier client implementation * test: supplier test helpers * test: supplier client tests * test: supplier client integration test * chore: update go.mod * trigger CI * 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: add godoc comment
- Loading branch information
1 parent
aecdf18
commit 5b3fd95
Showing
11 changed files
with
557 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package testsupplier | ||
|
||
import ( | ||
"testing" | ||
|
||
"cosmossdk.io/depinject" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/pokt-network/poktroll/internal/testclient/testtx" | ||
"github.com/pokt-network/poktroll/pkg/client" | ||
"github.com/pokt-network/poktroll/pkg/client/supplier" | ||
"github.com/pokt-network/poktroll/pkg/client/tx" | ||
) | ||
|
||
// NewLocalnetClient creates and returns a new supplier client that connects to | ||
// the localnet sequencer. | ||
func NewLocalnetClient( | ||
t *testing.T, | ||
signingKeyName string, | ||
) client.SupplierClient { | ||
t.Helper() | ||
|
||
txClientOpt := tx.WithSigningKeyName(signingKeyName) | ||
supplierClientOpt := supplier.WithSigningKeyName(signingKeyName) | ||
|
||
txCtx := testtx.NewLocalnetContext(t) | ||
txClient := testtx.NewLocalnetClient(t, txClientOpt) | ||
|
||
deps := depinject.Supply( | ||
txCtx, | ||
txClient, | ||
) | ||
|
||
supplierClient, err := supplier.NewSupplierClient(deps, supplierClientOpt) | ||
require.NoError(t, err) | ||
return supplierClient | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package testtx | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
|
||
"cosmossdk.io/depinject" | ||
cosmostypes "github.com/cosmos/cosmos-sdk/types" | ||
"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/testblock" | ||
"github.com/pokt-network/poktroll/internal/testclient/testeventsquery" | ||
"github.com/pokt-network/poktroll/pkg/client" | ||
"github.com/pokt-network/poktroll/pkg/client/tx" | ||
"github.com/pokt-network/poktroll/pkg/either" | ||
) | ||
|
||
type signAndBroadcastFn func(context.Context, cosmostypes.Msg) either.AsyncError | ||
|
||
// TODO_CONSIDERATION: functions like these (NewLocalnetXXX) could probably accept | ||
// and return depinject.Config arguments to support shared dependencies. | ||
|
||
// NewLocalnetClient creates and returns a new client for use with the localnet | ||
// sequencer. | ||
func NewLocalnetClient(t *testing.T, opts ...client.TxClientOption) client.TxClient { | ||
t.Helper() | ||
|
||
ctx := context.Background() | ||
txCtx := NewLocalnetContext(t) | ||
eventsQueryClient := testeventsquery.NewLocalnetClient(t) | ||
blockClient := testblock.NewLocalnetClient(ctx, t) | ||
|
||
deps := depinject.Supply( | ||
txCtx, | ||
eventsQueryClient, | ||
blockClient, | ||
) | ||
|
||
txClient, err := tx.NewTxClient(ctx, deps, opts...) | ||
require.NoError(t, err) | ||
|
||
return txClient | ||
} | ||
|
||
// NewOneTimeDelayedSignAndBroadcastTxClient constructs a mock TxClient with the | ||
// expectation to perform a SignAndBroadcast operation with a specified delay. | ||
func NewOneTimeDelayedSignAndBroadcastTxClient( | ||
t *testing.T, | ||
delay time.Duration, | ||
) *mockclient.MockTxClient { | ||
t.Helper() | ||
|
||
signAndBroadcast := newSignAndBroadcastSucceedsDelayed(delay) | ||
return NewOneTimeSignAndBroadcastTxClient(t, signAndBroadcast) | ||
} | ||
|
||
// NewOneTimeSignAndBroadcastTxClient constructs a mock TxClient with the | ||
// expectation to perform a SignAndBroadcast operation, which will call and receive | ||
// the return from the given signAndBroadcast function. | ||
func NewOneTimeSignAndBroadcastTxClient( | ||
t *testing.T, | ||
signAndBroadcast signAndBroadcastFn, | ||
) *mockclient.MockTxClient { | ||
t.Helper() | ||
|
||
var ctrl = gomock.NewController(t) | ||
|
||
txClient := mockclient.NewMockTxClient(ctrl) | ||
txClient.EXPECT().SignAndBroadcast( | ||
gomock.AssignableToTypeOf(context.Background()), | ||
gomock.Any(), | ||
).DoAndReturn(signAndBroadcast).Times(1) | ||
|
||
return txClient | ||
} | ||
|
||
// newSignAndBroadcastSucceedsDelayed returns a signAndBroadcastFn that succeeds | ||
// after the given delay. | ||
func newSignAndBroadcastSucceedsDelayed(delay time.Duration) signAndBroadcastFn { | ||
return func(ctx context.Context, msg cosmostypes.Msg) either.AsyncError { | ||
errCh := make(chan error) | ||
|
||
go func() { | ||
time.Sleep(delay) | ||
close(errCh) | ||
}() | ||
|
||
return either.AsyncErr(errCh) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package supplier | ||
|
||
import ( | ||
"context" | ||
|
||
"cosmossdk.io/depinject" | ||
cosmostypes "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/pokt-network/smt" | ||
|
||
"github.com/pokt-network/poktroll/pkg/client" | ||
"github.com/pokt-network/poktroll/pkg/client/keyring" | ||
sessiontypes "github.com/pokt-network/poktroll/x/session/types" | ||
suppliertypes "github.com/pokt-network/poktroll/x/supplier/types" | ||
) | ||
|
||
var _ client.SupplierClient = (*supplierClient)(nil) | ||
|
||
// supplierClient | ||
type supplierClient struct { | ||
signingKeyName string | ||
signingKeyAddr cosmostypes.AccAddress | ||
|
||
txClient client.TxClient | ||
txCtx client.TxContext | ||
} | ||
|
||
// NewSupplierClient constructs a new SupplierClient with the given dependencies | ||
// and options. If a signingKeyName is not configured, an error will be returned. | ||
// | ||
// Required dependencies: | ||
// - client.TxClient | ||
// - client.TxContext | ||
// | ||
// Available options: | ||
// - WithSigningKeyName | ||
func NewSupplierClient( | ||
deps depinject.Config, | ||
opts ...client.SupplierClientOption, | ||
) (*supplierClient, error) { | ||
sClient := &supplierClient{} | ||
|
||
if err := depinject.Inject( | ||
deps, | ||
&sClient.txClient, | ||
&sClient.txCtx, | ||
); err != nil { | ||
return nil, err | ||
} | ||
|
||
for _, opt := range opts { | ||
opt(sClient) | ||
} | ||
|
||
if err := sClient.validateConfigAndSetDefaults(); err != nil { | ||
return nil, err | ||
} | ||
|
||
return sClient, nil | ||
} | ||
|
||
// SubmitProof constructs a submit proof message then signs and broadcasts it | ||
// to the network via #txClient. It blocks until the transaction is included in | ||
// a block or times out. | ||
func (sClient *supplierClient) SubmitProof( | ||
ctx context.Context, | ||
sessionHeader sessiontypes.SessionHeader, | ||
proof *smt.SparseMerkleClosestProof, | ||
) error { | ||
proofBz, err := proof.Marshal() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
msg := &suppliertypes.MsgSubmitProof{ | ||
SupplierAddress: sClient.signingKeyAddr.String(), | ||
SessionHeader: &sessionHeader, | ||
Proof: proofBz, | ||
} | ||
eitherErr := sClient.txClient.SignAndBroadcast(ctx, msg) | ||
err, errCh := eitherErr.SyncOrAsyncError() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return <-errCh | ||
} | ||
|
||
// CreateClaim constructs a creates claim message then signs and broadcasts it | ||
// to the network via #txClient. It blocks until the transaction is included in | ||
// a block or times out. | ||
func (sClient *supplierClient) CreateClaim( | ||
ctx context.Context, | ||
sessionHeader sessiontypes.SessionHeader, | ||
rootHash []byte, | ||
) error { | ||
msg := &suppliertypes.MsgCreateClaim{ | ||
SupplierAddress: sClient.signingKeyAddr.String(), | ||
SessionHeader: &sessionHeader, | ||
RootHash: rootHash, | ||
} | ||
eitherErr := sClient.txClient.SignAndBroadcast(ctx, msg) | ||
err, errCh := eitherErr.SyncOrAsyncError() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = <-errCh | ||
return err | ||
} | ||
|
||
// validateConfigAndSetDefaults attempts to get the address from the keyring | ||
// corresponding to the key whose name matches the configured signingKeyName. | ||
// If signingKeyName is empty or the keyring does not contain the corresponding | ||
// key, an error is returned. | ||
func (sClient *supplierClient) validateConfigAndSetDefaults() error { | ||
signingAddr, err := keyring.KeyNameToAddr( | ||
sClient.signingKeyName, | ||
sClient.txCtx.GetKeyring(), | ||
) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
sClient.signingKeyAddr = signingAddr | ||
|
||
return nil | ||
} |
Oops, something went wrong.