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

feat(client/v2): broadcast logic #22282

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
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
1 change: 1 addition & 0 deletions client/v2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
* [#18461](https://github.com/cosmos/cosmos-sdk/pull/18461) Support governance proposals.
* [#20623](https://github.com/cosmos/cosmos-sdk/pull/20623) Introduce client/v2 tx factory.
* [#20623](https://github.com/cosmos/cosmos-sdk/pull/20623) Extend client/v2 keyring interface with `KeyType` and `KeyInfo`.
* [#22282](https://github.com/cosmos/cosmos-sdk/pull/22282) Added custom broadcast logic.

### Improvements

Expand Down
5 changes: 3 additions & 2 deletions client/v2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/cosmos/cosmos-sdk v0.53.0
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
go.uber.org/mock v0.4.0
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1
gotest.tools/v3 v3.5.1
Expand All @@ -27,7 +28,7 @@ require (
cosmossdk.io/errors v1.0.1 // indirect
cosmossdk.io/log v1.4.1 // indirect
cosmossdk.io/math v1.3.0
cosmossdk.io/schema v0.3.1-0.20240930054013-7c6e0388a3f9 // indirect
cosmossdk.io/schema v0.3.1-0.20241010135032-192601639cac // indirect
cosmossdk.io/store v1.1.1-0.20240418092142-896cdf1971bc // indirect
cosmossdk.io/x/staking v0.0.0-00010101000000-000000000000 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
Expand All @@ -47,7 +48,7 @@ require (
github.com/cockroachdb/pebble v1.1.2 // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/cometbft/cometbft v1.0.0-rc1.0.20240908111210-ab0be101882f // indirect
github.com/cometbft/cometbft v1.0.0-rc1.0.20240908111210-ab0be101882f
github.com/cometbft/cometbft-db v0.15.0 // indirect
github.com/cometbft/cometbft/api v1.0.0-rc.1 // indirect
github.com/cosmos/btcutil v1.0.5 // indirect
Expand Down
4 changes: 2 additions & 2 deletions client/v2/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM=
cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU=
cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE=
cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k=
cosmossdk.io/schema v0.3.1-0.20240930054013-7c6e0388a3f9 h1:DmOoS/1PeY6Ih0hAVlJ69kLMUrLV+TCbfICrZtB1vdU=
cosmossdk.io/schema v0.3.1-0.20240930054013-7c6e0388a3f9/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ=
cosmossdk.io/schema v0.3.1-0.20241010135032-192601639cac h1:3joNZZWZ3k7fMsrBDL1ktuQ2xQwYLZOaDhkruadDFmc=
cosmossdk.io/schema v0.3.1-0.20241010135032-192601639cac/go.mod h1:RDAhxIeNB4bYqAlF4NBJwRrgtnciMcyyg0DOKnhNZQQ=
cosmossdk.io/x/protocolpool v0.0.0-20230925135524-a1bc045b3190 h1:XQJj9Dv9Gtze0l2TF79BU5lkP6MkUveTUuKICmxoz+o=
cosmossdk.io/x/protocolpool v0.0.0-20230925135524-a1bc045b3190/go.mod h1:7WUGupOvmlHJoIMBz1JbObQxeo6/TDiuDBxmtod8HRg=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
Expand Down
79 changes: 79 additions & 0 deletions client/v2/internal/broadcast/broadcaster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package broadcast

import (
"context"
"fmt"
)

const (
// BroadcastSync defines a tx broadcasting mode where the client waits for
// a CheckTx execution response only.
BroadcastSync = "sync"
// BroadcastAsync defines a tx broadcasting mode where the client returns
// immediately.
BroadcastAsync = "async"

// cometBftConsensus is the identifier for the CometBFT consensus engine.
cometBFTConsensus = "comet"
)

type (
// Broadcaster defines an interface for broadcasting transactions to the consensus engine.
Broadcaster interface {
JulianToledano marked this conversation as resolved.
Show resolved Hide resolved
// Broadcast sends a transaction to the network and returns the result.
//
// It returns a byte slice containing the formatted result that will be
// passed to the output writer, and an error if the broadcast failed.
Broadcast(ctx context.Context, txBytes []byte) ([]byte, error)

// Consensus returns the consensus engine identifier for this Broadcaster.
Consensus() string
}

// NewBroadcasterFn is a function type for creating Broadcaster instances.
NewBroadcasterFn func(url string, opts ...Option) (Broadcaster, error)

// BroadcasterFactory defines an interface for creating and registering Broadcasters.
BroadcasterFactory interface {
// Register adds a new BroadcasterCreator for a given consensus type to the factory.
Register(consensus string, creator NewBroadcasterFn)
// Create instantiates a new Broadcaster based on the given consensus type, URL, and options.
// It returns the created Broadcaster and any error encountered during creation.
Create(ctx context.Context, consensus, url string, opts ...Option) (Broadcaster, error)
}

// Option is a function that configures a Broadcaster.
Option func(Broadcaster)
)

var _ BroadcasterFactory = Factory{}

// Factory is a factory for creating Broadcaster instances.
type Factory struct {
engines map[string]NewBroadcasterFn
}

// Create creates a new Broadcaster based on the given consensus type.
func (f Factory) Create(_ context.Context, consensus, url string, opts ...Option) (Broadcaster, error) {
creator, ok := f.engines[consensus]
if !ok {
return nil, fmt.Errorf("invalid consensus type: %s", consensus)
}
return creator(url, opts...)
}

// Register adds a new BroadcasterCreator for a given consensus type to the factory.
func (f Factory) Register(consensus string, creator NewBroadcasterFn) {
f.engines[consensus] = creator
}

// NewFactory creates and returns a new Factory instance with a default CometBFT broadcaster.
func NewFactory() Factory {
return Factory{
engines: map[string]NewBroadcasterFn{
cometBFTConsensus: func(url string, opts ...Option) (Broadcaster, error) {
return NewCometBFTBroadcaster(url, opts...)
},
},
}
}
83 changes: 83 additions & 0 deletions client/v2/internal/broadcast/broadcaster_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package broadcast

import (
"context"
"testing"

"github.com/stretchr/testify/require"
)

type testBFT struct{}

func (t testBFT) Broadcast(_ context.Context, _ []byte) ([]byte, error) {
return nil, nil
}

func (t testBFT) Consensus() string {
return "testBFT"
}

func Test_newBroadcaster(t *testing.T) {
f := NewFactory()
tests := []struct {
name string
consensus string
opts []Option
want Broadcaster
wantErr bool
}{
{
name: "comet",
consensus: "comet",
opts: []Option{
withMode(BroadcastSync),
},
want: &CometBFTBroadcaster{},
},
{
name: "unsupported_consensus",
consensus: "unsupported",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := f.Create(context.Background(), tt.consensus, "localhost:26657", tt.opts...)
if tt.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.NotNil(t, got)
require.IsType(t, tt.want, got)
}
})
}
}

func TestFactory_Register(t *testing.T) {
tests := []struct {
name string
consensus string
creator NewBroadcasterFn
}{
{
name: "register new broadcaster",
consensus: "testBFT",
creator: func(url string, opts ...Option) (Broadcaster, error) {
return testBFT{}, nil
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := NewFactory()
f.Register(tt.consensus, tt.creator)

b, err := f.Create(context.Background(), tt.consensus, "localhost:26657")
require.NoError(t, err)
require.NotNil(t, b)

require.Equal(t, tt.consensus, b.Consensus())
})
}
}
Loading
Loading