mirror of
https://github.com/tendermint/tendermint.git
synced 2026-02-06 03:50:46 +00:00
Merge remote-tracking branch 'origin' into jasmina/4457-blocksync-verification_part1
This commit is contained in:
2
.github/workflows/linkchecker.yml
vendored
2
.github/workflows/linkchecker.yml
vendored
@@ -7,6 +7,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@1.0.14
|
||||
- uses: creachadair/github-action-markdown-link-check@master
|
||||
with:
|
||||
folder-path: "docs"
|
||||
|
||||
4
go.mod
4
go.mod
@@ -41,8 +41,8 @@ require (
|
||||
github.com/creachadair/atomicfile v0.2.5
|
||||
github.com/creachadair/taskgroup v0.3.2
|
||||
github.com/golangci/golangci-lint v1.45.2
|
||||
github.com/google/go-cmp v0.5.7
|
||||
github.com/vektra/mockery/v2 v2.12.0
|
||||
github.com/google/go-cmp v0.5.8
|
||||
github.com/vektra/mockery/v2 v2.12.1
|
||||
gotest.tools v2.2.0+incompatible
|
||||
)
|
||||
|
||||
|
||||
7
go.sum
7
go.sum
@@ -449,8 +449,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@@ -1056,8 +1057,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
|
||||
github.com/valyala/fasthttp v1.30.0/go.mod h1:2rsYD01CKFrjjsvFxx75KlEUNpWNBY9JWD3K/7o2Cus=
|
||||
github.com/valyala/quicktemplate v1.7.0/go.mod h1:sqKJnoaOF88V07vkO+9FL8fb9uZg/VPSJnLYn+LmLk8=
|
||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||
github.com/vektra/mockery/v2 v2.12.0 h1:g3lq1ni5swlT9y6jYBx/CITVpwg3vsWrhKIuIweAxYI=
|
||||
github.com/vektra/mockery/v2 v2.12.0/go.mod h1:8vf4KDDUptfkyypzdHLuE7OE2xA7Gdt60WgIS8PgD+U=
|
||||
github.com/vektra/mockery/v2 v2.12.1 h1:BAJk2fGjVg/P9Fi+BxZD1/ZeKTOclpeAb/SKCc12zXc=
|
||||
github.com/vektra/mockery/v2 v2.12.1/go.mod h1:8vf4KDDUptfkyypzdHLuE7OE2xA7Gdt60WgIS8PgD+U=
|
||||
github.com/viki-org/dnscache v0.0.0-20130720023526-c70c1f23c5d8/go.mod h1:dniwbG03GafCjFohMDmz6Zc6oCuiqgH6tGNyXTkHzXE=
|
||||
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||
|
||||
@@ -38,16 +38,16 @@ func Routes(cfg config.RPCConfig, s state.Store, bs state.BlockStore, es []index
|
||||
Logger: logger,
|
||||
}
|
||||
return core.RoutesMap{
|
||||
"blockchain": server.NewRPCFunc(env.BlockchainInfo, "minHeight", "maxHeight"),
|
||||
"consensus_params": server.NewRPCFunc(env.ConsensusParams, "height"),
|
||||
"block": server.NewRPCFunc(env.Block, "height"),
|
||||
"block_by_hash": server.NewRPCFunc(env.BlockByHash, "hash"),
|
||||
"block_results": server.NewRPCFunc(env.BlockResults, "height"),
|
||||
"commit": server.NewRPCFunc(env.Commit, "height"),
|
||||
"validators": server.NewRPCFunc(env.Validators, "height", "page", "per_page"),
|
||||
"tx": server.NewRPCFunc(env.Tx, "hash", "prove"),
|
||||
"tx_search": server.NewRPCFunc(env.TxSearch, "query", "prove", "page", "per_page", "order_by"),
|
||||
"block_search": server.NewRPCFunc(env.BlockSearch, "query", "page", "per_page", "order_by"),
|
||||
"blockchain": server.NewRPCFunc(env.BlockchainInfo),
|
||||
"consensus_params": server.NewRPCFunc(env.ConsensusParams),
|
||||
"block": server.NewRPCFunc(env.Block),
|
||||
"block_by_hash": server.NewRPCFunc(env.BlockByHash),
|
||||
"block_results": server.NewRPCFunc(env.BlockResults),
|
||||
"commit": server.NewRPCFunc(env.Commit),
|
||||
"validators": server.NewRPCFunc(env.Validators),
|
||||
"tx": server.NewRPCFunc(env.Tx),
|
||||
"tx_search": server.NewRPCFunc(env.TxSearch),
|
||||
"block_search": server.NewRPCFunc(env.BlockSearch),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ func ParseNodeAddress(urlString string) (NodeAddress, error) {
|
||||
|
||||
// Resolve resolves a NodeAddress into a set of Endpoints, by expanding
|
||||
// out a DNS hostname to IP addresses.
|
||||
func (a NodeAddress) Resolve(ctx context.Context) ([]Endpoint, error) {
|
||||
func (a NodeAddress) Resolve(ctx context.Context) ([]*Endpoint, error) {
|
||||
if a.Protocol == "" {
|
||||
return nil, errors.New("address has no protocol")
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func (a NodeAddress) Resolve(ctx context.Context) ([]Endpoint, error) {
|
||||
if a.NodeID == "" {
|
||||
return nil, errors.New("local address has no node ID")
|
||||
}
|
||||
return []Endpoint{{
|
||||
return []*Endpoint{{
|
||||
Protocol: a.Protocol,
|
||||
Path: string(a.NodeID),
|
||||
}}, nil
|
||||
@@ -119,9 +119,9 @@ func (a NodeAddress) Resolve(ctx context.Context) ([]Endpoint, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
endpoints := make([]Endpoint, len(ips))
|
||||
endpoints := make([]*Endpoint, len(ips))
|
||||
for i, ip := range ips {
|
||||
endpoints[i] = Endpoint{
|
||||
endpoints[i] = &Endpoint{
|
||||
Protocol: a.Protocol,
|
||||
IP: ip,
|
||||
Port: a.Port,
|
||||
|
||||
@@ -210,71 +210,71 @@ func TestNodeAddress_Resolve(t *testing.T) {
|
||||
|
||||
testcases := []struct {
|
||||
address p2p.NodeAddress
|
||||
expect p2p.Endpoint
|
||||
expect *p2p.Endpoint
|
||||
ok bool
|
||||
}{
|
||||
// Valid networked addresses (with hostname).
|
||||
{
|
||||
p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1", Port: 80, Path: "/path"},
|
||||
p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1), Port: 80, Path: "/path"},
|
||||
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1), Port: 80, Path: "/path"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
p2p.NodeAddress{Protocol: "tcp", Hostname: "localhost", Port: 80, Path: "/path"},
|
||||
p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1), Port: 80, Path: "/path"},
|
||||
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1), Port: 80, Path: "/path"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
p2p.NodeAddress{Protocol: "tcp", Hostname: "localhost", Port: 80, Path: "/path"},
|
||||
p2p.Endpoint{Protocol: "tcp", IP: net.IPv6loopback, Port: 80, Path: "/path"},
|
||||
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv6loopback, Port: 80, Path: "/path"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1"},
|
||||
p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1)},
|
||||
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(127, 0, 0, 1)},
|
||||
true,
|
||||
},
|
||||
{
|
||||
p2p.NodeAddress{Protocol: "tcp", Hostname: "::1"},
|
||||
p2p.Endpoint{Protocol: "tcp", IP: net.IPv6loopback},
|
||||
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv6loopback},
|
||||
true,
|
||||
},
|
||||
{
|
||||
p2p.NodeAddress{Protocol: "tcp", Hostname: "8.8.8.8"},
|
||||
p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(8, 8, 8, 8)},
|
||||
&p2p.Endpoint{Protocol: "tcp", IP: net.IPv4(8, 8, 8, 8)},
|
||||
true,
|
||||
},
|
||||
{
|
||||
p2p.NodeAddress{Protocol: "tcp", Hostname: "2001:0db8::ff00:0042:8329"},
|
||||
p2p.Endpoint{Protocol: "tcp", IP: []byte{
|
||||
&p2p.Endpoint{Protocol: "tcp", IP: []byte{
|
||||
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x42, 0x83, 0x29}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
p2p.NodeAddress{Protocol: "tcp", Hostname: "some.missing.host.tendermint.com"},
|
||||
p2p.Endpoint{},
|
||||
&p2p.Endpoint{},
|
||||
false,
|
||||
},
|
||||
|
||||
// Valid non-networked addresses.
|
||||
{
|
||||
p2p.NodeAddress{Protocol: "memory", NodeID: id},
|
||||
p2p.Endpoint{Protocol: "memory", Path: string(id)},
|
||||
&p2p.Endpoint{Protocol: "memory", Path: string(id)},
|
||||
true,
|
||||
},
|
||||
{
|
||||
p2p.NodeAddress{Protocol: "memory", NodeID: id, Path: string(id)},
|
||||
p2p.Endpoint{Protocol: "memory", Path: string(id)},
|
||||
&p2p.Endpoint{Protocol: "memory", Path: string(id)},
|
||||
true,
|
||||
},
|
||||
|
||||
// Invalid addresses.
|
||||
{p2p.NodeAddress{}, p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{Hostname: "127.0.0.1"}, p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1:80"}, p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{Protocol: "memory"}, p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{Protocol: "memory", Path: string(id)}, p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{Protocol: "tcp", Hostname: "💥"}, p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{}, &p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{Hostname: "127.0.0.1"}, &p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{Protocol: "tcp", Hostname: "127.0.0.1:80"}, &p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{Protocol: "memory"}, &p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{Protocol: "memory", Path: string(id)}, &p2p.Endpoint{}, false},
|
||||
{p2p.NodeAddress{Protocol: "tcp", Hostname: "💥"}, &p2p.Endpoint{}, false},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
tc := tc
|
||||
|
||||
@@ -62,11 +62,11 @@ func (_m *Transport) Close() error {
|
||||
}
|
||||
|
||||
// Dial provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Transport) Dial(_a0 context.Context, _a1 p2p.Endpoint) (p2p.Connection, error) {
|
||||
func (_m *Transport) Dial(_a0 context.Context, _a1 *p2p.Endpoint) (p2p.Connection, error) {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
var r0 p2p.Connection
|
||||
if rf, ok := ret.Get(0).(func(context.Context, p2p.Endpoint) p2p.Connection); ok {
|
||||
if rf, ok := ret.Get(0).(func(context.Context, *p2p.Endpoint) p2p.Connection); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
@@ -75,7 +75,7 @@ func (_m *Transport) Dial(_a0 context.Context, _a1 p2p.Endpoint) (p2p.Connection
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, p2p.Endpoint) error); ok {
|
||||
if rf, ok := ret.Get(1).(func(context.Context, *p2p.Endpoint) error); ok {
|
||||
r1 = rf(_a0, _a1)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
@@ -84,28 +84,35 @@ func (_m *Transport) Dial(_a0 context.Context, _a1 p2p.Endpoint) (p2p.Connection
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Endpoints provides a mock function with given fields:
|
||||
func (_m *Transport) Endpoints() []p2p.Endpoint {
|
||||
// Endpoint provides a mock function with given fields:
|
||||
func (_m *Transport) Endpoint() (*p2p.Endpoint, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 []p2p.Endpoint
|
||||
if rf, ok := ret.Get(0).(func() []p2p.Endpoint); ok {
|
||||
var r0 *p2p.Endpoint
|
||||
if rf, ok := ret.Get(0).(func() *p2p.Endpoint); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]p2p.Endpoint)
|
||||
r0 = ret.Get(0).(*p2p.Endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Listen provides a mock function with given fields: _a0
|
||||
func (_m *Transport) Listen(_a0 p2p.Endpoint) error {
|
||||
func (_m *Transport) Listen(_a0 *p2p.Endpoint) error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(p2p.Endpoint) error); ok {
|
||||
if rf, ok := ret.Get(0).(func(*p2p.Endpoint) error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
|
||||
@@ -247,7 +247,9 @@ func (n *Network) MakeNode(ctx context.Context, t *testing.T, opts NodeOptions)
|
||||
}
|
||||
|
||||
transport := n.memoryNetwork.CreateTransport(nodeID)
|
||||
require.Len(t, transport.Endpoints(), 1, "transport not listening on 1 endpoint")
|
||||
ep, err := transport.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ep, "transport not listening an endpoint")
|
||||
|
||||
peerManager, err := p2p.NewPeerManager(nodeID, dbm.NewMemDB(), p2p.PeerManagerOptions{
|
||||
MinRetryTime: 10 * time.Millisecond,
|
||||
@@ -264,8 +266,8 @@ func (n *Network) MakeNode(ctx context.Context, t *testing.T, opts NodeOptions)
|
||||
privKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &nodeInfo },
|
||||
[]p2p.Transport{transport},
|
||||
transport.Endpoints(),
|
||||
transport,
|
||||
ep,
|
||||
p2p.RouterOptions{DialSleep: func(_ context.Context) {}},
|
||||
)
|
||||
|
||||
@@ -284,7 +286,7 @@ func (n *Network) MakeNode(ctx context.Context, t *testing.T, opts NodeOptions)
|
||||
return &Node{
|
||||
NodeID: nodeID,
|
||||
NodeInfo: nodeInfo,
|
||||
NodeAddress: transport.Endpoints()[0].NodeAddress(nodeID),
|
||||
NodeAddress: ep.NodeAddress(nodeID),
|
||||
PrivKey: privKey,
|
||||
Router: router,
|
||||
PeerManager: peerManager,
|
||||
@@ -304,7 +306,6 @@ func (n *Node) MakeChannel(
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
channel, err := n.Router.OpenChannel(ctx, chDesc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, n.Router.NodeInfo().Channels, byte(chDesc.ID))
|
||||
t.Cleanup(func() {
|
||||
RequireEmpty(ctx, t, channel)
|
||||
cancel()
|
||||
|
||||
@@ -148,15 +148,14 @@ type Router struct {
|
||||
*service.BaseService
|
||||
logger log.Logger
|
||||
|
||||
metrics *Metrics
|
||||
options RouterOptions
|
||||
privKey crypto.PrivKey
|
||||
peerManager *PeerManager
|
||||
chDescs []*ChannelDescriptor
|
||||
transports []Transport
|
||||
endpoints []Endpoint
|
||||
connTracker connectionTracker
|
||||
protocolTransports map[Protocol]Transport
|
||||
metrics *Metrics
|
||||
options RouterOptions
|
||||
privKey crypto.PrivKey
|
||||
peerManager *PeerManager
|
||||
chDescs []*ChannelDescriptor
|
||||
transport Transport
|
||||
endpoint *Endpoint
|
||||
connTracker connectionTracker
|
||||
|
||||
peerMtx sync.RWMutex
|
||||
peerQueues map[types.NodeID]queue // outbound messages per peer for all channels
|
||||
@@ -182,8 +181,8 @@ func NewRouter(
|
||||
privKey crypto.PrivKey,
|
||||
peerManager *PeerManager,
|
||||
nodeInfoProducer func() *types.NodeInfo,
|
||||
transports []Transport,
|
||||
endpoints []Endpoint,
|
||||
transport Transport,
|
||||
endpoint *Endpoint,
|
||||
options RouterOptions,
|
||||
) (*Router, error) {
|
||||
|
||||
@@ -200,28 +199,19 @@ func NewRouter(
|
||||
options.MaxIncomingConnectionAttempts,
|
||||
options.IncomingConnectionWindow,
|
||||
),
|
||||
chDescs: make([]*ChannelDescriptor, 0),
|
||||
transports: transports,
|
||||
endpoints: endpoints,
|
||||
protocolTransports: map[Protocol]Transport{},
|
||||
peerManager: peerManager,
|
||||
options: options,
|
||||
channelQueues: map[ChannelID]queue{},
|
||||
channelMessages: map[ChannelID]proto.Message{},
|
||||
peerQueues: map[types.NodeID]queue{},
|
||||
peerChannels: make(map[types.NodeID]ChannelIDSet),
|
||||
chDescs: make([]*ChannelDescriptor, 0),
|
||||
transport: transport,
|
||||
endpoint: endpoint,
|
||||
peerManager: peerManager,
|
||||
options: options,
|
||||
channelQueues: map[ChannelID]queue{},
|
||||
channelMessages: map[ChannelID]proto.Message{},
|
||||
peerQueues: map[types.NodeID]queue{},
|
||||
peerChannels: make(map[types.NodeID]ChannelIDSet),
|
||||
}
|
||||
|
||||
router.BaseService = service.NewBaseService(logger, "router", router)
|
||||
|
||||
for _, transport := range transports {
|
||||
for _, protocol := range transport.Protocols() {
|
||||
if _, ok := router.protocolTransports[protocol]; !ok {
|
||||
router.protocolTransports[protocol] = transport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return router, nil
|
||||
}
|
||||
|
||||
@@ -286,9 +276,7 @@ func (r *Router) OpenChannel(ctx context.Context, chDesc *ChannelDescriptor) (*C
|
||||
// add the channel to the nodeInfo if it's not already there.
|
||||
r.nodeInfoProducer().AddChannel(uint16(chDesc.ID))
|
||||
|
||||
for _, t := range r.transports {
|
||||
t.AddChannelDescriptors([]*ChannelDescriptor{chDesc})
|
||||
}
|
||||
r.transport.AddChannelDescriptors([]*ChannelDescriptor{chDesc})
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
@@ -670,12 +658,6 @@ func (r *Router) dialPeer(ctx context.Context, address NodeAddress) (Connection,
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
transport, ok := r.protocolTransports[endpoint.Protocol]
|
||||
if !ok {
|
||||
r.logger.Error("no transport found for protocol", "endpoint", endpoint)
|
||||
continue
|
||||
}
|
||||
|
||||
dialCtx := ctx
|
||||
if r.options.DialTimeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
@@ -690,7 +672,7 @@ func (r *Router) dialPeer(ctx context.Context, address NodeAddress) (Connection,
|
||||
// by the peer's endpoint, since e.g. a peer on 192.168.0.0 can reach us
|
||||
// on a private address on this endpoint, but a peer on the public
|
||||
// Internet can't and needs a different public address.
|
||||
conn, err := transport.Dial(dialCtx, endpoint)
|
||||
conn, err := r.transport.Dial(dialCtx, endpoint)
|
||||
if err != nil {
|
||||
r.logger.Error("failed to dial endpoint", "peer", address.NodeID, "endpoint", endpoint, "err", err)
|
||||
} else {
|
||||
@@ -731,7 +713,7 @@ func (r *Router) handshakePeer(
|
||||
return peerInfo, fmt.Errorf("expected to connect with peer %q, got %q",
|
||||
expectID, peerInfo.NodeID)
|
||||
}
|
||||
if err := r.nodeInfoProducer().CompatibleWith(peerInfo); err != nil {
|
||||
if err := nodeInfo.CompatibleWith(peerInfo); err != nil {
|
||||
return peerInfo, ErrRejected{
|
||||
err: err,
|
||||
id: peerInfo.ID(),
|
||||
@@ -929,11 +911,6 @@ func (r *Router) evictPeers(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// NodeInfo returns a copy of the current NodeInfo. Used for testing.
|
||||
func (r *Router) NodeInfo() types.NodeInfo {
|
||||
return r.nodeInfoProducer().Copy()
|
||||
}
|
||||
|
||||
func (r *Router) setupQueueFactory(ctx context.Context) error {
|
||||
qf, err := r.createQueueFactory(ctx)
|
||||
if err != nil {
|
||||
@@ -950,29 +927,13 @@ func (r *Router) OnStart(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, transport := range r.transports {
|
||||
for _, endpoint := range r.endpoints {
|
||||
if err := transport.Listen(endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := r.transport.Listen(r.endpoint); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nodeInfo := r.nodeInfoProducer()
|
||||
r.logger.Info(
|
||||
"starting router",
|
||||
"node_id", nodeInfo.NodeID,
|
||||
"channels", nodeInfo.Channels,
|
||||
"listen_addr", nodeInfo.ListenAddr,
|
||||
"transports", len(r.transports),
|
||||
)
|
||||
|
||||
go r.dialPeers(ctx)
|
||||
go r.evictPeers(ctx)
|
||||
|
||||
for _, transport := range r.transports {
|
||||
go r.acceptPeers(ctx, transport)
|
||||
}
|
||||
go r.acceptPeers(ctx, r.transport)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -985,10 +946,8 @@ func (r *Router) OnStart(ctx context.Context) error {
|
||||
// sender's responsibility.
|
||||
func (r *Router) OnStop() {
|
||||
// Close transport listeners (unblocks Accept calls).
|
||||
for _, transport := range r.transports {
|
||||
if err := transport.Close(); err != nil {
|
||||
r.logger.Error("failed to close transport", "transport", transport, "err", err)
|
||||
}
|
||||
if err := r.transport.Close(); err != nil {
|
||||
r.logger.Error("failed to close transport", "err", err)
|
||||
}
|
||||
|
||||
// Collect all remaining queues, and wait for them to close.
|
||||
|
||||
@@ -105,14 +105,16 @@ func TestRouter_Channel_Basic(t *testing.T) {
|
||||
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
testnet := p2ptest.MakeNetwork(ctx, t, p2ptest.NetworkOptions{NumNodes: 1})
|
||||
|
||||
router, err := p2p.NewRouter(
|
||||
log.NewNopLogger(),
|
||||
p2p.NopMetrics(),
|
||||
selfKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &selfInfo },
|
||||
nil,
|
||||
nil,
|
||||
testnet.RandomNode().Transport,
|
||||
&p2p.Endpoint{},
|
||||
p2p.RouterOptions{},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
@@ -126,7 +128,6 @@ func TestRouter_Channel_Basic(t *testing.T) {
|
||||
|
||||
channel, err := router.OpenChannel(chctx, chDesc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, router.NodeInfo().Channels, byte(chDesc.ID))
|
||||
require.NotNil(t, channel)
|
||||
|
||||
// Opening the same channel again should fail.
|
||||
@@ -136,9 +137,7 @@ func TestRouter_Channel_Basic(t *testing.T) {
|
||||
// Opening a different channel should work.
|
||||
chDesc2 := &p2p.ChannelDescriptor{ID: 2, MessageType: &p2ptest.Message{}}
|
||||
_, err = router.OpenChannel(ctx, chDesc2)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, router.NodeInfo().Channels, byte(chDesc2.ID))
|
||||
|
||||
// Closing the channel, then opening it again should be fine.
|
||||
chcancel()
|
||||
@@ -396,10 +395,10 @@ func TestRouter_AcceptPeers(t *testing.T) {
|
||||
|
||||
mockTransport := &mocks.Transport{}
|
||||
mockTransport.On("String").Maybe().Return("mock")
|
||||
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
|
||||
mockTransport.On("Close").Return(nil).Maybe()
|
||||
mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil)
|
||||
mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, io.EOF)
|
||||
mockTransport.On("Listen", mock.Anything).Return(nil)
|
||||
|
||||
// Set up and start the router.
|
||||
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
|
||||
@@ -413,7 +412,7 @@ func TestRouter_AcceptPeers(t *testing.T) {
|
||||
selfKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &selfInfo },
|
||||
[]p2p.Transport{mockTransport},
|
||||
mockTransport,
|
||||
nil,
|
||||
p2p.RouterOptions{},
|
||||
)
|
||||
@@ -453,9 +452,9 @@ func TestRouter_AcceptPeers_Error(t *testing.T) {
|
||||
// the router from calling Accept again.
|
||||
mockTransport := &mocks.Transport{}
|
||||
mockTransport.On("String").Maybe().Return("mock")
|
||||
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
|
||||
mockTransport.On("Accept", mock.Anything).Once().Return(nil, errors.New("boom"))
|
||||
mockTransport.On("Close").Return(nil)
|
||||
mockTransport.On("Listen", mock.Anything).Return(nil)
|
||||
|
||||
// Set up and start the router.
|
||||
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
|
||||
@@ -467,7 +466,7 @@ func TestRouter_AcceptPeers_Error(t *testing.T) {
|
||||
selfKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &selfInfo },
|
||||
[]p2p.Transport{mockTransport},
|
||||
mockTransport,
|
||||
nil,
|
||||
p2p.RouterOptions{},
|
||||
)
|
||||
@@ -490,9 +489,9 @@ func TestRouter_AcceptPeers_ErrorEOF(t *testing.T) {
|
||||
// the router from calling Accept again.
|
||||
mockTransport := &mocks.Transport{}
|
||||
mockTransport.On("String").Maybe().Return("mock")
|
||||
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
|
||||
mockTransport.On("Accept", mock.Anything).Once().Return(nil, io.EOF)
|
||||
mockTransport.On("Close").Return(nil)
|
||||
mockTransport.On("Listen", mock.Anything).Return(nil)
|
||||
|
||||
// Set up and start the router.
|
||||
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
|
||||
@@ -504,7 +503,7 @@ func TestRouter_AcceptPeers_ErrorEOF(t *testing.T) {
|
||||
selfKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &selfInfo },
|
||||
[]p2p.Transport{mockTransport},
|
||||
mockTransport,
|
||||
nil,
|
||||
p2p.RouterOptions{},
|
||||
)
|
||||
@@ -538,12 +537,12 @@ func TestRouter_AcceptPeers_HeadOfLineBlocking(t *testing.T) {
|
||||
|
||||
mockTransport := &mocks.Transport{}
|
||||
mockTransport.On("String").Maybe().Return("mock")
|
||||
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
|
||||
mockTransport.On("Close").Return(nil)
|
||||
mockTransport.On("Accept", mock.Anything).Times(3).Run(func(_ mock.Arguments) {
|
||||
acceptCh <- true
|
||||
}).Return(mockConnection, nil)
|
||||
mockTransport.On("Accept", mock.Anything).Once().Return(nil, io.EOF)
|
||||
mockTransport.On("Listen", mock.Anything).Return(nil)
|
||||
|
||||
// Set up and start the router.
|
||||
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
|
||||
@@ -555,7 +554,7 @@ func TestRouter_AcceptPeers_HeadOfLineBlocking(t *testing.T) {
|
||||
selfKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &selfInfo },
|
||||
[]p2p.Transport{mockTransport},
|
||||
mockTransport,
|
||||
nil,
|
||||
p2p.RouterOptions{},
|
||||
)
|
||||
@@ -611,7 +610,7 @@ func TestRouter_DialPeers(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
address := p2p.NodeAddress{Protocol: "mock", NodeID: tc.dialID}
|
||||
endpoint := p2p.Endpoint{Protocol: "mock", Path: string(tc.dialID)}
|
||||
endpoint := &p2p.Endpoint{Protocol: "mock", Path: string(tc.dialID)}
|
||||
|
||||
// Set up a mock transport that handshakes.
|
||||
connCtx, connCancel := context.WithCancel(context.Background())
|
||||
@@ -629,8 +628,8 @@ func TestRouter_DialPeers(t *testing.T) {
|
||||
|
||||
mockTransport := &mocks.Transport{}
|
||||
mockTransport.On("String").Maybe().Return("mock")
|
||||
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
|
||||
mockTransport.On("Close").Return(nil).Maybe()
|
||||
mockTransport.On("Listen", mock.Anything).Return(nil)
|
||||
mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, io.EOF)
|
||||
if tc.dialErr == nil {
|
||||
mockTransport.On("Dial", mock.Anything, endpoint).Once().Return(mockConnection, nil)
|
||||
@@ -658,7 +657,7 @@ func TestRouter_DialPeers(t *testing.T) {
|
||||
selfKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &selfInfo },
|
||||
[]p2p.Transport{mockTransport},
|
||||
mockTransport,
|
||||
nil,
|
||||
p2p.RouterOptions{},
|
||||
)
|
||||
@@ -711,11 +710,11 @@ func TestRouter_DialPeers_Parallel(t *testing.T) {
|
||||
|
||||
mockTransport := &mocks.Transport{}
|
||||
mockTransport.On("String").Maybe().Return("mock")
|
||||
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
|
||||
mockTransport.On("Close").Return(nil)
|
||||
mockTransport.On("Listen", mock.Anything).Return(nil)
|
||||
mockTransport.On("Accept", mock.Anything).Once().Return(nil, io.EOF)
|
||||
for _, address := range []p2p.NodeAddress{a, b, c} {
|
||||
endpoint := p2p.Endpoint{Protocol: address.Protocol, Path: string(address.NodeID)}
|
||||
endpoint := &p2p.Endpoint{Protocol: address.Protocol, Path: string(address.NodeID)}
|
||||
mockTransport.On("Dial", mock.Anything, endpoint).Run(func(_ mock.Arguments) {
|
||||
dialCh <- true
|
||||
}).Return(mockConnection, nil)
|
||||
@@ -743,7 +742,7 @@ func TestRouter_DialPeers_Parallel(t *testing.T) {
|
||||
selfKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &selfInfo },
|
||||
[]p2p.Transport{mockTransport},
|
||||
mockTransport,
|
||||
nil,
|
||||
p2p.RouterOptions{
|
||||
DialSleep: func(_ context.Context) {},
|
||||
@@ -800,10 +799,10 @@ func TestRouter_EvictPeers(t *testing.T) {
|
||||
|
||||
mockTransport := &mocks.Transport{}
|
||||
mockTransport.On("String").Maybe().Return("mock")
|
||||
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
|
||||
mockTransport.On("Close").Return(nil)
|
||||
mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil)
|
||||
mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, io.EOF)
|
||||
mockTransport.On("Listen", mock.Anything).Return(nil)
|
||||
|
||||
// Set up and start the router.
|
||||
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
|
||||
@@ -817,7 +816,7 @@ func TestRouter_EvictPeers(t *testing.T) {
|
||||
selfKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &selfInfo },
|
||||
[]p2p.Transport{mockTransport},
|
||||
mockTransport,
|
||||
nil,
|
||||
p2p.RouterOptions{},
|
||||
)
|
||||
@@ -864,10 +863,10 @@ func TestRouter_ChannelCompatability(t *testing.T) {
|
||||
|
||||
mockTransport := &mocks.Transport{}
|
||||
mockTransport.On("String").Maybe().Return("mock")
|
||||
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
|
||||
mockTransport.On("Close").Return(nil)
|
||||
mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil)
|
||||
mockTransport.On("Accept", mock.Anything).Once().Return(nil, io.EOF)
|
||||
mockTransport.On("Listen", mock.Anything).Return(nil)
|
||||
|
||||
// Set up and start the router.
|
||||
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
|
||||
@@ -879,7 +878,7 @@ func TestRouter_ChannelCompatability(t *testing.T) {
|
||||
selfKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &selfInfo },
|
||||
[]p2p.Transport{mockTransport},
|
||||
mockTransport,
|
||||
nil,
|
||||
p2p.RouterOptions{},
|
||||
)
|
||||
@@ -917,10 +916,10 @@ func TestRouter_DontSendOnInvalidChannel(t *testing.T) {
|
||||
mockTransport := &mocks.Transport{}
|
||||
mockTransport.On("AddChannelDescriptors", mock.Anything).Return()
|
||||
mockTransport.On("String").Maybe().Return("mock")
|
||||
mockTransport.On("Protocols").Return([]p2p.Protocol{"mock"})
|
||||
mockTransport.On("Close").Return(nil)
|
||||
mockTransport.On("Accept", mock.Anything).Once().Return(mockConnection, nil)
|
||||
mockTransport.On("Accept", mock.Anything).Maybe().Return(nil, io.EOF)
|
||||
mockTransport.On("Listen", mock.Anything).Return(nil)
|
||||
|
||||
// Set up and start the router.
|
||||
peerManager, err := p2p.NewPeerManager(selfID, dbm.NewMemDB(), p2p.PeerManagerOptions{})
|
||||
@@ -934,7 +933,7 @@ func TestRouter_DontSendOnInvalidChannel(t *testing.T) {
|
||||
selfKey,
|
||||
peerManager,
|
||||
func() *types.NodeInfo { return &selfInfo },
|
||||
[]p2p.Transport{mockTransport},
|
||||
mockTransport,
|
||||
nil,
|
||||
p2p.RouterOptions{},
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ type Protocol string
|
||||
// Transport is a connection-oriented mechanism for exchanging data with a peer.
|
||||
type Transport interface {
|
||||
// Listen starts the transport on the specified endpoint.
|
||||
Listen(Endpoint) error
|
||||
Listen(*Endpoint) error
|
||||
|
||||
// Protocols returns the protocols supported by the transport. The Router
|
||||
// uses this to pick a transport for an Endpoint.
|
||||
@@ -34,7 +34,7 @@ type Transport interface {
|
||||
//
|
||||
// How to listen is transport-dependent, e.g. MConnTransport uses Listen() while
|
||||
// MemoryTransport starts listening via MemoryNetwork.CreateTransport().
|
||||
Endpoints() []Endpoint
|
||||
Endpoint() (*Endpoint, error)
|
||||
|
||||
// Accept waits for the next inbound connection on a listening endpoint, blocking
|
||||
// until either a connection is available or the transport is closed. On closure,
|
||||
@@ -42,7 +42,7 @@ type Transport interface {
|
||||
Accept(context.Context) (Connection, error)
|
||||
|
||||
// Dial creates an outbound connection to an endpoint.
|
||||
Dial(context.Context, Endpoint) (Connection, error)
|
||||
Dial(context.Context, *Endpoint) (Connection, error)
|
||||
|
||||
// Close stops accepting new connections, but does not close active connections.
|
||||
Close() error
|
||||
@@ -129,13 +129,13 @@ type Endpoint struct {
|
||||
}
|
||||
|
||||
// NewEndpoint constructs an Endpoint from a types.NetAddress structure.
|
||||
func NewEndpoint(addr string) (Endpoint, error) {
|
||||
func NewEndpoint(addr string) (*Endpoint, error) {
|
||||
ip, port, err := types.ParseAddressString(addr)
|
||||
if err != nil {
|
||||
return Endpoint{}, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Endpoint{
|
||||
return &Endpoint{
|
||||
Protocol: MConnProtocol,
|
||||
IP: ip,
|
||||
Port: port,
|
||||
|
||||
@@ -78,25 +78,25 @@ func (m *MConnTransport) Protocols() []Protocol {
|
||||
return []Protocol{MConnProtocol, TCPProtocol}
|
||||
}
|
||||
|
||||
// Endpoints implements Transport.
|
||||
func (m *MConnTransport) Endpoints() []Endpoint {
|
||||
// Endpoint implements Transport.
|
||||
func (m *MConnTransport) Endpoint() (*Endpoint, error) {
|
||||
if m.listener == nil {
|
||||
return []Endpoint{}
|
||||
return nil, errors.New("listenter not defined")
|
||||
}
|
||||
select {
|
||||
case <-m.doneCh:
|
||||
return []Endpoint{}
|
||||
return nil, errors.New("transport closed")
|
||||
default:
|
||||
}
|
||||
|
||||
endpoint := Endpoint{
|
||||
endpoint := &Endpoint{
|
||||
Protocol: MConnProtocol,
|
||||
}
|
||||
if addr, ok := m.listener.Addr().(*net.TCPAddr); ok {
|
||||
endpoint.IP = addr.IP
|
||||
endpoint.Port = uint16(addr.Port)
|
||||
}
|
||||
return []Endpoint{endpoint}
|
||||
return endpoint, nil
|
||||
}
|
||||
|
||||
// Listen asynchronously listens for inbound connections on the given endpoint.
|
||||
@@ -106,7 +106,7 @@ func (m *MConnTransport) Endpoints() []Endpoint {
|
||||
// FIXME: Listen currently only supports listening on a single endpoint, it
|
||||
// might be useful to support listening on multiple addresses (e.g. IPv4 and
|
||||
// IPv6, or a private and public address) via multiple Listen() calls.
|
||||
func (m *MConnTransport) Listen(endpoint Endpoint) error {
|
||||
func (m *MConnTransport) Listen(endpoint *Endpoint) error {
|
||||
if m.listener != nil {
|
||||
return errors.New("transport is already listening")
|
||||
}
|
||||
@@ -170,7 +170,7 @@ func (m *MConnTransport) Accept(ctx context.Context) (Connection, error) {
|
||||
}
|
||||
|
||||
// Dial implements Transport.
|
||||
func (m *MConnTransport) Dial(ctx context.Context, endpoint Endpoint) (Connection, error) {
|
||||
func (m *MConnTransport) Dial(ctx context.Context, endpoint *Endpoint) (Connection, error) {
|
||||
if err := m.validateEndpoint(endpoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -217,7 +217,7 @@ func (m *MConnTransport) AddChannelDescriptors(channelDesc []*ChannelDescriptor)
|
||||
}
|
||||
|
||||
// validateEndpoint validates an endpoint.
|
||||
func (m *MConnTransport) validateEndpoint(endpoint Endpoint) error {
|
||||
func (m *MConnTransport) validateEndpoint(endpoint *Endpoint) error {
|
||||
if err := endpoint.Validate(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func init() {
|
||||
[]*p2p.ChannelDescriptor{{ID: chID, Priority: 1}},
|
||||
p2p.MConnTransportOptions{},
|
||||
)
|
||||
err := transport.Listen(p2p.Endpoint{
|
||||
err := transport.Listen(&p2p.Endpoint{
|
||||
Protocol: p2p.MConnProtocol,
|
||||
IP: net.IPv4(127, 0, 0, 1),
|
||||
Port: 0, // assign a random port
|
||||
@@ -73,13 +73,14 @@ func TestMConnTransport_AcceptMaxAcceptedConnections(t *testing.T) {
|
||||
t.Cleanup(func() {
|
||||
_ = transport.Close()
|
||||
})
|
||||
err := transport.Listen(p2p.Endpoint{
|
||||
err := transport.Listen(&p2p.Endpoint{
|
||||
Protocol: p2p.MConnProtocol,
|
||||
IP: net.IPv4(127, 0, 0, 1),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, transport.Endpoints())
|
||||
endpoint := transport.Endpoints()[0]
|
||||
endpoint, err := transport.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, endpoint)
|
||||
|
||||
// Start a goroutine to just accept any connections.
|
||||
acceptCh := make(chan p2p.Connection, 10)
|
||||
@@ -132,20 +133,20 @@ func TestMConnTransport_Listen(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
testcases := []struct {
|
||||
endpoint p2p.Endpoint
|
||||
endpoint *p2p.Endpoint
|
||||
ok bool
|
||||
}{
|
||||
// Valid v4 and v6 addresses, with mconn and tcp protocols.
|
||||
{p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4zero}, true},
|
||||
{p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4(127, 0, 0, 1)}, true},
|
||||
{p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv6zero}, true},
|
||||
{p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv6loopback}, true},
|
||||
{p2p.Endpoint{Protocol: p2p.TCPProtocol, IP: net.IPv4zero}, true},
|
||||
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4zero}, true},
|
||||
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4(127, 0, 0, 1)}, true},
|
||||
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv6zero}, true},
|
||||
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv6loopback}, true},
|
||||
{&p2p.Endpoint{Protocol: p2p.TCPProtocol, IP: net.IPv4zero}, true},
|
||||
|
||||
// Invalid endpoints.
|
||||
{p2p.Endpoint{}, false},
|
||||
{p2p.Endpoint{Protocol: p2p.MConnProtocol, Path: "foo"}, false},
|
||||
{p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4zero, Path: "foo"}, false},
|
||||
{&p2p.Endpoint{}, false},
|
||||
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, Path: "foo"}, false},
|
||||
{&p2p.Endpoint{Protocol: p2p.MConnProtocol, IP: net.IPv4zero, Path: "foo"}, false},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
tc := tc
|
||||
@@ -160,10 +161,12 @@ func TestMConnTransport_Listen(t *testing.T) {
|
||||
)
|
||||
|
||||
// Transport should not listen on any endpoints yet.
|
||||
require.Empty(t, transport.Endpoints())
|
||||
endpoint, err := transport.Endpoint()
|
||||
require.Error(t, err)
|
||||
require.Nil(t, endpoint)
|
||||
|
||||
// Start listening, and check any expected errors.
|
||||
err := transport.Listen(tc.endpoint)
|
||||
err = transport.Listen(tc.endpoint)
|
||||
if !tc.ok {
|
||||
require.Error(t, err)
|
||||
return
|
||||
@@ -171,9 +174,9 @@ func TestMConnTransport_Listen(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check the endpoint.
|
||||
endpoints := transport.Endpoints()
|
||||
require.Len(t, endpoints, 1)
|
||||
endpoint := endpoints[0]
|
||||
endpoint, err = transport.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, endpoint)
|
||||
|
||||
require.Equal(t, p2p.MConnProtocol, endpoint.Protocol)
|
||||
if tc.endpoint.IP.IsUnspecified() {
|
||||
|
||||
@@ -119,7 +119,7 @@ func (t *MemoryTransport) String() string {
|
||||
return string(MemoryProtocol)
|
||||
}
|
||||
|
||||
func (*MemoryTransport) Listen(Endpoint) error { return nil }
|
||||
func (*MemoryTransport) Listen(*Endpoint) error { return nil }
|
||||
|
||||
func (t *MemoryTransport) AddChannelDescriptors([]*ChannelDescriptor) {}
|
||||
|
||||
@@ -129,19 +129,19 @@ func (t *MemoryTransport) Protocols() []Protocol {
|
||||
}
|
||||
|
||||
// Endpoints implements Transport.
|
||||
func (t *MemoryTransport) Endpoints() []Endpoint {
|
||||
func (t *MemoryTransport) Endpoint() (*Endpoint, error) {
|
||||
if n := t.network.GetTransport(t.nodeID); n == nil {
|
||||
return []Endpoint{}
|
||||
return nil, errors.New("node not defined")
|
||||
}
|
||||
|
||||
return []Endpoint{{
|
||||
return &Endpoint{
|
||||
Protocol: MemoryProtocol,
|
||||
Path: string(t.nodeID),
|
||||
// An arbitrary IP and port is used in order for the pex
|
||||
// reactor to be able to send addresses to one another.
|
||||
IP: net.IPv4zero,
|
||||
Port: 0,
|
||||
}}
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Accept implements Transport.
|
||||
@@ -158,7 +158,7 @@ func (t *MemoryTransport) Accept(ctx context.Context) (Connection, error) {
|
||||
}
|
||||
|
||||
// Dial implements Transport.
|
||||
func (t *MemoryTransport) Dial(ctx context.Context, endpoint Endpoint) (Connection, error) {
|
||||
func (t *MemoryTransport) Dial(ctx context.Context, endpoint *Endpoint) (Connection, error) {
|
||||
if endpoint.Protocol != MemoryProtocol {
|
||||
return nil, fmt.Errorf("invalid protocol %q", endpoint.Protocol)
|
||||
}
|
||||
|
||||
@@ -87,9 +87,9 @@ func TestTransport_DialEndpoints(t *testing.T) {
|
||||
|
||||
withTransports(ctx, t, func(ctx context.Context, t *testing.T, makeTransport transportFactory) {
|
||||
a := makeTransport(t)
|
||||
endpoints := a.Endpoints()
|
||||
require.NotEmpty(t, endpoints)
|
||||
endpoint := endpoints[0]
|
||||
endpoint, err := a.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, endpoint)
|
||||
|
||||
// Spawn a goroutine to simply accept any connections until closed.
|
||||
go func() {
|
||||
@@ -108,19 +108,19 @@ func TestTransport_DialEndpoints(t *testing.T) {
|
||||
require.NoError(t, conn.Close())
|
||||
|
||||
// Dialing empty endpoint should error.
|
||||
_, err = a.Dial(ctx, p2p.Endpoint{})
|
||||
_, err = a.Dial(ctx, &p2p.Endpoint{})
|
||||
require.Error(t, err)
|
||||
|
||||
// Dialing without protocol should error.
|
||||
noProtocol := endpoint
|
||||
noProtocol := *endpoint
|
||||
noProtocol.Protocol = ""
|
||||
_, err = a.Dial(ctx, noProtocol)
|
||||
_, err = a.Dial(ctx, &noProtocol)
|
||||
require.Error(t, err)
|
||||
|
||||
// Dialing with invalid protocol should error.
|
||||
fooProtocol := endpoint
|
||||
fooProtocol := *endpoint
|
||||
fooProtocol.Protocol = "foo"
|
||||
_, err = a.Dial(ctx, fooProtocol)
|
||||
_, err = a.Dial(ctx, &fooProtocol)
|
||||
require.Error(t, err)
|
||||
|
||||
// Tests for networked endpoints (with IP).
|
||||
@@ -129,11 +129,12 @@ func TestTransport_DialEndpoints(t *testing.T) {
|
||||
tc := tc
|
||||
t.Run(tc.ip.String(), func(t *testing.T) {
|
||||
e := endpoint
|
||||
require.NotNil(t, e)
|
||||
e.IP = tc.ip
|
||||
conn, err := a.Dial(ctx, e)
|
||||
if tc.ok {
|
||||
require.NoError(t, conn.Close())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, conn.Close())
|
||||
} else {
|
||||
require.Error(t, err, "endpoint=%s", e)
|
||||
}
|
||||
@@ -167,16 +168,18 @@ func TestTransport_Dial(t *testing.T) {
|
||||
a := makeTransport(t)
|
||||
b := makeTransport(t)
|
||||
|
||||
require.NotEmpty(t, a.Endpoints())
|
||||
require.NotEmpty(t, b.Endpoints())
|
||||
aEndpoint := a.Endpoints()[0]
|
||||
bEndpoint := b.Endpoints()[0]
|
||||
aEndpoint, err := a.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, aEndpoint)
|
||||
bEndpoint, err := b.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, bEndpoint)
|
||||
|
||||
// Context cancellation should error. We can't test timeouts since we'd
|
||||
// need a non-responsive endpoint.
|
||||
cancelCtx, cancel := context.WithCancel(ctx)
|
||||
cancel()
|
||||
_, err := a.Dial(cancelCtx, bEndpoint)
|
||||
_, err = a.Dial(cancelCtx, bEndpoint)
|
||||
require.Error(t, err)
|
||||
|
||||
// Unavailable endpoint should error.
|
||||
@@ -210,21 +213,26 @@ func TestTransport_Endpoints(t *testing.T) {
|
||||
b := makeTransport(t)
|
||||
|
||||
// Both transports return valid and different endpoints.
|
||||
aEndpoints := a.Endpoints()
|
||||
bEndpoints := b.Endpoints()
|
||||
require.NotEmpty(t, aEndpoints)
|
||||
require.NotEmpty(t, bEndpoints)
|
||||
require.NotEqual(t, aEndpoints, bEndpoints)
|
||||
for _, endpoint := range append(aEndpoints, bEndpoints...) {
|
||||
aEndpoint, err := a.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, aEndpoint)
|
||||
bEndpoint, err := b.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, bEndpoint)
|
||||
require.NotEqual(t, aEndpoint, bEndpoint)
|
||||
for _, endpoint := range []*p2p.Endpoint{aEndpoint, bEndpoint} {
|
||||
err := endpoint.Validate()
|
||||
require.NoError(t, err, "invalid endpoint %q", endpoint)
|
||||
}
|
||||
|
||||
// When closed, the transport should no longer return any endpoints.
|
||||
err := a.Close()
|
||||
require.NoError(t, a.Close())
|
||||
aEndpoint, err = a.Endpoint()
|
||||
require.Error(t, err)
|
||||
require.Nil(t, aEndpoint)
|
||||
bEndpoint, err = b.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, a.Endpoints())
|
||||
require.NotEmpty(t, b.Endpoints())
|
||||
require.NotNil(t, bEndpoint)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -235,13 +243,12 @@ func TestTransport_Protocols(t *testing.T) {
|
||||
withTransports(ctx, t, func(ctx context.Context, t *testing.T, makeTransport transportFactory) {
|
||||
a := makeTransport(t)
|
||||
protocols := a.Protocols()
|
||||
endpoints := a.Endpoints()
|
||||
endpoint, err := a.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, protocols)
|
||||
require.NotEmpty(t, endpoints)
|
||||
require.NotNil(t, endpoint)
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
require.Contains(t, protocols, endpoint.Protocol)
|
||||
}
|
||||
require.Contains(t, protocols, endpoint.Protocol)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -595,8 +602,9 @@ func TestEndpoint_Validate(t *testing.T) {
|
||||
func dialAccept(ctx context.Context, t *testing.T, a, b p2p.Transport) (p2p.Connection, p2p.Connection) {
|
||||
t.Helper()
|
||||
|
||||
endpoints := b.Endpoints()
|
||||
require.NotEmpty(t, endpoints, "peer not listening on any endpoints")
|
||||
endpoint, err := b.Endpoint()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, endpoint, "peer not listening on any endpoints")
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second)
|
||||
defer cancel()
|
||||
@@ -609,7 +617,7 @@ func dialAccept(ctx context.Context, t *testing.T, a, b p2p.Transport) (p2p.Conn
|
||||
acceptCh <- conn
|
||||
}()
|
||||
|
||||
dialConn, err := a.Dial(ctx, endpoints[0])
|
||||
dialConn, err := a.Dial(ctx, endpoint)
|
||||
require.NoError(t, err)
|
||||
|
||||
acceptConn := <-acceptCh
|
||||
|
||||
@@ -5,24 +5,17 @@ import (
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/internal/proxy"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
)
|
||||
|
||||
// ABCIQuery queries the application for some information.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/ABCI/abci_query
|
||||
func (env *Environment) ABCIQuery(
|
||||
ctx context.Context,
|
||||
path string,
|
||||
data bytes.HexBytes,
|
||||
height int64,
|
||||
prove bool,
|
||||
) (*coretypes.ResultABCIQuery, error) {
|
||||
func (env *Environment) ABCIQuery(ctx context.Context, req *coretypes.RequestABCIQuery) (*coretypes.ResultABCIQuery, error) {
|
||||
resQuery, err := env.ProxyApp.Query(ctx, &abci.RequestQuery{
|
||||
Path: path,
|
||||
Data: data,
|
||||
Height: height,
|
||||
Prove: prove,
|
||||
Path: req.Path,
|
||||
Data: req.Data,
|
||||
Height: int64(req.Height),
|
||||
Prove: req.Prove,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
tmquery "github.com/tendermint/tendermint/internal/pubsub/query"
|
||||
"github.com/tendermint/tendermint/internal/state/indexer"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
@@ -23,17 +22,15 @@ import (
|
||||
// order (highest first).
|
||||
//
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/blockchain
|
||||
func (env *Environment) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) {
|
||||
|
||||
const limit int64 = 20
|
||||
|
||||
var err error
|
||||
minHeight, maxHeight, err = filterMinMax(
|
||||
func (env *Environment) BlockchainInfo(ctx context.Context, req *coretypes.RequestBlockchainInfo) (*coretypes.ResultBlockchainInfo, error) {
|
||||
const limit = 20
|
||||
minHeight, maxHeight, err := filterMinMax(
|
||||
env.BlockStore.Base(),
|
||||
env.BlockStore.Height(),
|
||||
minHeight,
|
||||
maxHeight,
|
||||
limit)
|
||||
int64(req.MinHeight),
|
||||
int64(req.MaxHeight),
|
||||
limit,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -90,8 +87,8 @@ func filterMinMax(base, height, min, max, limit int64) (int64, int64, error) {
|
||||
// Block gets block at a given height.
|
||||
// If no height is provided, it will fetch the latest block.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/block
|
||||
func (env *Environment) Block(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlock, error) {
|
||||
height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
|
||||
func (env *Environment) Block(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultBlock, error) {
|
||||
height, err := env.getHeight(env.BlockStore.Height(), (*int64)(req.Height))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -107,12 +104,8 @@ func (env *Environment) Block(ctx context.Context, heightPtr *int64) (*coretypes
|
||||
|
||||
// BlockByHash gets block by hash.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/block_by_hash
|
||||
func (env *Environment) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error) {
|
||||
// N.B. The hash parameter is HexBytes so that the reflective parameter
|
||||
// decoding logic in the HTTP service will correctly translate from JSON.
|
||||
// See https://github.com/tendermint/tendermint/issues/6802 for context.
|
||||
|
||||
block := env.BlockStore.LoadBlockByHash(hash)
|
||||
func (env *Environment) BlockByHash(ctx context.Context, req *coretypes.RequestBlockByHash) (*coretypes.ResultBlock, error) {
|
||||
block := env.BlockStore.LoadBlockByHash(req.Hash)
|
||||
if block == nil {
|
||||
return &coretypes.ResultBlock{BlockID: types.BlockID{}, Block: nil}, nil
|
||||
}
|
||||
@@ -124,8 +117,8 @@ func (env *Environment) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*
|
||||
// Header gets block header at a given height.
|
||||
// If no height is provided, it will fetch the latest header.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/header
|
||||
func (env *Environment) Header(ctx context.Context, heightPtr *int64) (*coretypes.ResultHeader, error) {
|
||||
height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
|
||||
func (env *Environment) Header(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultHeader, error) {
|
||||
height, err := env.getHeight(env.BlockStore.Height(), (*int64)(req.Height))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -140,12 +133,8 @@ func (env *Environment) Header(ctx context.Context, heightPtr *int64) (*coretype
|
||||
|
||||
// HeaderByHash gets header by hash.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/header_by_hash
|
||||
func (env *Environment) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) {
|
||||
// N.B. The hash parameter is HexBytes so that the reflective parameter
|
||||
// decoding logic in the HTTP service will correctly translate from JSON.
|
||||
// See https://github.com/tendermint/tendermint/issues/6802 for context.
|
||||
|
||||
blockMeta := env.BlockStore.LoadBlockMetaByHash(hash)
|
||||
func (env *Environment) HeaderByHash(ctx context.Context, req *coretypes.RequestBlockByHash) (*coretypes.ResultHeader, error) {
|
||||
blockMeta := env.BlockStore.LoadBlockMetaByHash(req.Hash)
|
||||
if blockMeta == nil {
|
||||
return &coretypes.ResultHeader{}, nil
|
||||
}
|
||||
@@ -156,8 +145,8 @@ func (env *Environment) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (
|
||||
// Commit gets block commit at a given height.
|
||||
// If no height is provided, it will fetch the commit for the latest block.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/commit
|
||||
func (env *Environment) Commit(ctx context.Context, heightPtr *int64) (*coretypes.ResultCommit, error) {
|
||||
height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
|
||||
func (env *Environment) Commit(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultCommit, error) {
|
||||
height, err := env.getHeight(env.BlockStore.Height(), (*int64)(req.Height))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -192,8 +181,8 @@ func (env *Environment) Commit(ctx context.Context, heightPtr *int64) (*coretype
|
||||
//
|
||||
// Results are for the height of the block containing the txs.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/block_results
|
||||
func (env *Environment) BlockResults(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlockResults, error) {
|
||||
height, err := env.getHeight(env.BlockStore.Height(), heightPtr)
|
||||
func (env *Environment) BlockResults(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultBlockResults, error) {
|
||||
height, err := env.getHeight(env.BlockStore.Height(), (*int64)(req.Height))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -218,20 +207,13 @@ func (env *Environment) BlockResults(ctx context.Context, heightPtr *int64) (*co
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BlockSearch searches for a paginated set of blocks matching the provided
|
||||
// query.
|
||||
func (env *Environment) BlockSearch(
|
||||
ctx context.Context,
|
||||
query string,
|
||||
pagePtr, perPagePtr *int,
|
||||
orderBy string,
|
||||
) (*coretypes.ResultBlockSearch, error) {
|
||||
|
||||
// BlockSearch searches for a paginated set of blocks matching the provided query.
|
||||
func (env *Environment) BlockSearch(ctx context.Context, req *coretypes.RequestBlockSearch) (*coretypes.ResultBlockSearch, error) {
|
||||
if !indexer.KVSinkEnabled(env.EventSinks) {
|
||||
return nil, fmt.Errorf("block searching is disabled due to no kvEventSink")
|
||||
}
|
||||
|
||||
q, err := tmquery.New(query)
|
||||
q, err := tmquery.New(req.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -249,7 +231,7 @@ func (env *Environment) BlockSearch(
|
||||
}
|
||||
|
||||
// sort results (must be done before pagination)
|
||||
switch orderBy {
|
||||
switch req.OrderBy {
|
||||
case "desc", "":
|
||||
sort.Slice(results, func(i, j int) bool { return results[i] > results[j] })
|
||||
|
||||
@@ -262,9 +244,9 @@ func (env *Environment) BlockSearch(
|
||||
|
||||
// paginate results
|
||||
totalCount := len(results)
|
||||
perPage := env.validatePerPage(perPagePtr)
|
||||
perPage := env.validatePerPage(req.PerPage.IntPtr())
|
||||
|
||||
page, err := validatePage(pagePtr, perPage, totalCount)
|
||||
page, err := validatePage(req.Page.IntPtr(), perPage, totalCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -109,7 +109,9 @@ func TestBlockResults(t *testing.T) {
|
||||
|
||||
ctx := context.Background()
|
||||
for _, tc := range testCases {
|
||||
res, err := env.BlockResults(ctx, &tc.height)
|
||||
res, err := env.BlockResults(ctx, &coretypes.RequestBlockInfo{
|
||||
Height: (*coretypes.Int64)(&tc.height),
|
||||
})
|
||||
if tc.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
|
||||
@@ -14,10 +14,9 @@ import (
|
||||
// for the validators in the set as used in computing their Merkle root.
|
||||
//
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/validators
|
||||
func (env *Environment) Validators(ctx context.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*coretypes.ResultValidators, error) {
|
||||
|
||||
func (env *Environment) Validators(ctx context.Context, req *coretypes.RequestValidators) (*coretypes.ResultValidators, error) {
|
||||
// The latest validator that we know is the NextValidator of the last block.
|
||||
height, err := env.getHeight(env.latestUncommittedHeight(), heightPtr)
|
||||
height, err := env.getHeight(env.latestUncommittedHeight(), (*int64)(req.Height))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -28,8 +27,8 @@ func (env *Environment) Validators(ctx context.Context, heightPtr *int64, pagePt
|
||||
}
|
||||
|
||||
totalCount := len(validators.Validators)
|
||||
perPage := env.validatePerPage(perPagePtr)
|
||||
page, err := validatePage(pagePtr, perPage, totalCount)
|
||||
perPage := env.validatePerPage(req.PerPage.IntPtr())
|
||||
page, err := validatePage(req.Page.IntPtr(), perPage, totalCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -42,7 +41,8 @@ func (env *Environment) Validators(ctx context.Context, heightPtr *int64, pagePt
|
||||
BlockHeight: height,
|
||||
Validators: v,
|
||||
Count: len(v),
|
||||
Total: totalCount}, nil
|
||||
Total: totalCount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DumpConsensusState dumps consensus state.
|
||||
@@ -99,11 +99,10 @@ func (env *Environment) GetConsensusState(ctx context.Context) (*coretypes.Resul
|
||||
// ConsensusParams gets the consensus parameters at the given block height.
|
||||
// If no height is provided, it will fetch the latest consensus params.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/consensus_params
|
||||
func (env *Environment) ConsensusParams(ctx context.Context, heightPtr *int64) (*coretypes.ResultConsensusParams, error) {
|
||||
|
||||
// The latest consensus params that we know is the consensus params after the
|
||||
// last block.
|
||||
height, err := env.getHeight(env.latestUncommittedHeight(), heightPtr)
|
||||
func (env *Environment) ConsensusParams(ctx context.Context, req *coretypes.RequestConsensusParams) (*coretypes.ResultConsensusParams, error) {
|
||||
// The latest consensus params that we know is the consensus params after
|
||||
// the last block.
|
||||
height, err := env.getHeight(env.latestUncommittedHeight(), (*int64)(req.Height))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const (
|
||||
|
||||
// Subscribe for events via WebSocket.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Websocket/subscribe
|
||||
func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes.ResultSubscribe, error) {
|
||||
func (env *Environment) Subscribe(ctx context.Context, req *coretypes.RequestSubscribe) (*coretypes.ResultSubscribe, error) {
|
||||
callInfo := rpctypes.GetCallInfo(ctx)
|
||||
addr := callInfo.RemoteAddr()
|
||||
|
||||
@@ -34,15 +34,15 @@ func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes
|
||||
return nil, fmt.Errorf("max_subscription_clients %d reached", env.Config.MaxSubscriptionClients)
|
||||
} else if env.EventBus.NumClientSubscriptions(addr) >= env.Config.MaxSubscriptionsPerClient {
|
||||
return nil, fmt.Errorf("max_subscriptions_per_client %d reached", env.Config.MaxSubscriptionsPerClient)
|
||||
} else if len(query) > maxQueryLength {
|
||||
} else if len(req.Query) > maxQueryLength {
|
||||
return nil, errors.New("maximum query length exceeded")
|
||||
}
|
||||
|
||||
env.Logger.Info("WARNING: Websocket subscriptions are deprecated and will be removed " +
|
||||
"in Tendermint v0.37. See https://tinyurl.com/adr075 for more information.")
|
||||
env.Logger.Info("Subscribe to query", "remote", addr, "query", query)
|
||||
env.Logger.Info("Subscribe to query", "remote", addr, "query", req.Query)
|
||||
|
||||
q, err := tmquery.New(query)
|
||||
q, err := tmquery.New(req.Query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse query: %w", err)
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes
|
||||
|
||||
// We have a message to deliver to the client.
|
||||
resp := callInfo.RPCRequest.MakeResponse(&coretypes.ResultEvent{
|
||||
Query: query,
|
||||
Query: req.Query,
|
||||
Data: msg.Data(),
|
||||
Events: msg.Events(),
|
||||
})
|
||||
@@ -102,15 +102,15 @@ func (env *Environment) Subscribe(ctx context.Context, query string) (*coretypes
|
||||
|
||||
// Unsubscribe from events via WebSocket.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Websocket/unsubscribe
|
||||
func (env *Environment) Unsubscribe(ctx context.Context, query string) (*coretypes.ResultUnsubscribe, error) {
|
||||
func (env *Environment) Unsubscribe(ctx context.Context, req *coretypes.RequestUnsubscribe) (*coretypes.ResultUnsubscribe, error) {
|
||||
args := tmpubsub.UnsubscribeArgs{Subscriber: rpctypes.GetCallInfo(ctx).RemoteAddr()}
|
||||
env.Logger.Info("Unsubscribe from query", "remote", args.Subscriber, "subscription", query)
|
||||
env.Logger.Info("Unsubscribe from query", "remote", args.Subscriber, "subscription", req.Query)
|
||||
|
||||
var err error
|
||||
args.Query, err = tmquery.New(query)
|
||||
args.Query, err = tmquery.New(req.Query)
|
||||
|
||||
if err != nil {
|
||||
args.ID = query
|
||||
args.ID = req.Query
|
||||
}
|
||||
|
||||
err = env.EventBus.Unsubscribe(ctx, args)
|
||||
@@ -148,17 +148,13 @@ func (env *Environment) UnsubscribeAll(ctx context.Context) (*coretypes.ResultUn
|
||||
// If maxItems ≤ 0, a default positive number of events is chosen. The values
|
||||
// of maxItems and waitTime may be capped to sensible internal maxima without
|
||||
// reporting an error to the caller.
|
||||
func (env *Environment) Events(ctx context.Context,
|
||||
filter *coretypes.EventFilter,
|
||||
maxItems int,
|
||||
before, after cursor.Cursor,
|
||||
waitTime time.Duration,
|
||||
) (*coretypes.ResultEvents, error) {
|
||||
func (env *Environment) Events(ctx context.Context, req *coretypes.RequestEvents) (*coretypes.ResultEvents, error) {
|
||||
if env.EventLog == nil {
|
||||
return nil, errors.New("the event log is not enabled")
|
||||
}
|
||||
|
||||
// Parse and validate parameters.
|
||||
maxItems := req.MaxItems
|
||||
if maxItems <= 0 {
|
||||
maxItems = 10
|
||||
} else if maxItems > 100 {
|
||||
@@ -167,6 +163,8 @@ func (env *Environment) Events(ctx context.Context,
|
||||
|
||||
const minWaitTime = 1 * time.Second
|
||||
const maxWaitTime = 30 * time.Second
|
||||
|
||||
waitTime := req.WaitTime
|
||||
if waitTime < minWaitTime {
|
||||
waitTime = minWaitTime
|
||||
} else if waitTime > maxWaitTime {
|
||||
@@ -174,14 +172,22 @@ func (env *Environment) Events(ctx context.Context,
|
||||
}
|
||||
|
||||
query := tmquery.All
|
||||
if filter != nil && filter.Query != "" {
|
||||
q, err := tmquery.New(filter.Query)
|
||||
if req.Filter != nil && req.Filter.Query != "" {
|
||||
q, err := tmquery.New(req.Filter.Query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter query: %w", err)
|
||||
}
|
||||
query = q
|
||||
}
|
||||
|
||||
var before, after cursor.Cursor
|
||||
if err := before.UnmarshalText([]byte(req.Before)); err != nil {
|
||||
return nil, fmt.Errorf("invalid cursor %q: %w", req.Before, err)
|
||||
}
|
||||
if err := after.UnmarshalText([]byte(req.After)); err != nil {
|
||||
return nil, fmt.Errorf("invalid cursor %q: %w", req.After, err)
|
||||
}
|
||||
|
||||
var info eventlog.Info
|
||||
var items []*eventlog.Item
|
||||
var err error
|
||||
|
||||
@@ -9,18 +9,15 @@ import (
|
||||
|
||||
// BroadcastEvidence broadcasts evidence of the misbehavior.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Evidence/broadcast_evidence
|
||||
func (env *Environment) BroadcastEvidence(
|
||||
ctx context.Context,
|
||||
ev coretypes.Evidence,
|
||||
) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
if ev.Value == nil {
|
||||
func (env *Environment) BroadcastEvidence(ctx context.Context, req *coretypes.RequestBroadcastEvidence) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
if req.Evidence == nil {
|
||||
return nil, fmt.Errorf("%w: no evidence was provided", coretypes.ErrInvalidRequest)
|
||||
}
|
||||
if err := ev.Value.ValidateBasic(); err != nil {
|
||||
if err := req.Evidence.ValidateBasic(); err != nil {
|
||||
return nil, fmt.Errorf("evidence.ValidateBasic failed: %w", err)
|
||||
}
|
||||
if err := env.EvidencePool.AddEvidence(ctx, ev.Value); err != nil {
|
||||
if err := env.EvidencePool.AddEvidence(ctx, req.Evidence); err != nil {
|
||||
return nil, fmt.Errorf("failed to add evidence: %w", err)
|
||||
}
|
||||
return &coretypes.ResultBroadcastEvidence{Hash: ev.Value.Hash()}, nil
|
||||
return &coretypes.ResultBroadcastEvidence{Hash: req.Evidence.Hash()}, nil
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/tendermint/tendermint/internal/state/indexer"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -21,23 +20,23 @@ import (
|
||||
// BroadcastTxAsync returns right away, with no response. Does not wait for
|
||||
// CheckTx nor DeliverTx results.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_async
|
||||
func (env *Environment) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
|
||||
err := env.Mempool.CheckTx(ctx, tx, nil, mempool.TxInfo{})
|
||||
func (env *Environment) BroadcastTxAsync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error) {
|
||||
err := env.Mempool.CheckTx(ctx, req.Tx, nil, mempool.TxInfo{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &coretypes.ResultBroadcastTx{Hash: tx.Hash()}, nil
|
||||
return &coretypes.ResultBroadcastTx{Hash: req.Tx.Hash()}, nil
|
||||
}
|
||||
|
||||
// BroadcastTxSync returns with the response from CheckTx. Does not wait for
|
||||
// DeliverTx result.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_sync
|
||||
func (env *Environment) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
|
||||
func (env *Environment) BroadcastTxSync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error) {
|
||||
resCh := make(chan *abci.ResponseCheckTx, 1)
|
||||
err := env.Mempool.CheckTx(
|
||||
ctx,
|
||||
tx,
|
||||
req.Tx,
|
||||
func(res *abci.ResponseCheckTx) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -60,19 +59,18 @@ func (env *Environment) BroadcastTxSync(ctx context.Context, tx types.Tx) (*core
|
||||
Log: r.Log,
|
||||
Codespace: r.Codespace,
|
||||
MempoolError: r.MempoolError,
|
||||
Hash: tx.Hash(),
|
||||
Hash: req.Tx.Hash(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// BroadcastTxCommit returns with the responses from CheckTx and DeliverTx.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Tx/broadcast_tx_commit
|
||||
func (env *Environment) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) {
|
||||
func (env *Environment) BroadcastTxCommit(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTxCommit, error) {
|
||||
resCh := make(chan *abci.ResponseCheckTx, 1)
|
||||
err := env.Mempool.CheckTx(
|
||||
ctx,
|
||||
tx,
|
||||
req.Tx,
|
||||
func(res *abci.ResponseCheckTx) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -92,14 +90,14 @@ func (env *Environment) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*co
|
||||
if r.Code != abci.CodeTypeOK {
|
||||
return &coretypes.ResultBroadcastTxCommit{
|
||||
CheckTx: *r,
|
||||
Hash: tx.Hash(),
|
||||
Hash: req.Tx.Hash(),
|
||||
}, fmt.Errorf("transaction encountered error (%s)", r.MempoolError)
|
||||
}
|
||||
|
||||
if !indexer.KVSinkEnabled(env.EventSinks) {
|
||||
return &coretypes.ResultBroadcastTxCommit{
|
||||
CheckTx: *r,
|
||||
Hash: tx.Hash(),
|
||||
Hash: req.Tx.Hash(),
|
||||
},
|
||||
errors.New("cannot confirm transaction because kvEventSink is not enabled")
|
||||
}
|
||||
@@ -118,11 +116,14 @@ func (env *Environment) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*co
|
||||
"err", err)
|
||||
return &coretypes.ResultBroadcastTxCommit{
|
||||
CheckTx: *r,
|
||||
Hash: tx.Hash(),
|
||||
Hash: req.Tx.Hash(),
|
||||
}, fmt.Errorf("timeout waiting for commit of tx %s (%s)",
|
||||
tx.Hash(), time.Since(startAt))
|
||||
req.Tx.Hash(), time.Since(startAt))
|
||||
case <-timer.C:
|
||||
txres, err := env.Tx(ctx, tx.Hash(), false)
|
||||
txres, err := env.Tx(ctx, &coretypes.RequestTx{
|
||||
Hash: req.Tx.Hash(),
|
||||
Prove: false,
|
||||
})
|
||||
if err != nil {
|
||||
jitter := 100*time.Millisecond + time.Duration(rand.Int63n(int64(time.Second))) // nolint: gosec
|
||||
backoff := 100 * time.Duration(count) * time.Millisecond
|
||||
@@ -133,7 +134,7 @@ func (env *Environment) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*co
|
||||
return &coretypes.ResultBroadcastTxCommit{
|
||||
CheckTx: *r,
|
||||
TxResult: txres.TxResult,
|
||||
Hash: tx.Hash(),
|
||||
Hash: req.Tx.Hash(),
|
||||
Height: txres.Height,
|
||||
}, nil
|
||||
}
|
||||
@@ -143,10 +144,10 @@ func (env *Environment) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*co
|
||||
|
||||
// UnconfirmedTxs gets unconfirmed transactions from the mempool in order of priority
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/unconfirmed_txs
|
||||
func (env *Environment) UnconfirmedTxs(ctx context.Context, pagePtr, perPagePtr *int) (*coretypes.ResultUnconfirmedTxs, error) {
|
||||
func (env *Environment) UnconfirmedTxs(ctx context.Context, req *coretypes.RequestUnconfirmedTxs) (*coretypes.ResultUnconfirmedTxs, error) {
|
||||
totalCount := env.Mempool.Size()
|
||||
perPage := env.validatePerPage(perPagePtr)
|
||||
page, err := validatePage(pagePtr, perPage, totalCount)
|
||||
perPage := env.validatePerPage(req.PerPage.IntPtr())
|
||||
page, err := validatePage(req.Page.IntPtr(), perPage, totalCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -160,7 +161,8 @@ func (env *Environment) UnconfirmedTxs(ctx context.Context, pagePtr, perPagePtr
|
||||
Count: len(result),
|
||||
Total: totalCount,
|
||||
TotalBytes: env.Mempool.SizeBytes(),
|
||||
Txs: result}, nil
|
||||
Txs: result,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NumUnconfirmedTxs gets number of unconfirmed transactions.
|
||||
@@ -175,14 +177,14 @@ func (env *Environment) NumUnconfirmedTxs(ctx context.Context) (*coretypes.Resul
|
||||
// CheckTx checks the transaction without executing it. The transaction won't
|
||||
// be added to the mempool either.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Tx/check_tx
|
||||
func (env *Environment) CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultCheckTx, error) {
|
||||
res, err := env.ProxyApp.CheckTx(ctx, &abci.RequestCheckTx{Tx: tx})
|
||||
func (env *Environment) CheckTx(ctx context.Context, req *coretypes.RequestCheckTx) (*coretypes.ResultCheckTx, error) {
|
||||
res, err := env.ProxyApp.CheckTx(ctx, &abci.RequestCheckTx{Tx: req.Tx})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &coretypes.ResultCheckTx{ResponseCheckTx: *res}, nil
|
||||
}
|
||||
|
||||
func (env *Environment) RemoveTx(ctx context.Context, txkey types.TxKey) error {
|
||||
return env.Mempool.RemoveTxByKey(txkey)
|
||||
func (env *Environment) RemoveTx(ctx context.Context, req *coretypes.RequestRemoveTx) error {
|
||||
return env.Mempool.RemoveTxByKey(req.TxKey)
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func (env *Environment) Genesis(ctx context.Context) (*coretypes.ResultGenesis,
|
||||
return &coretypes.ResultGenesis{Genesis: env.GenDoc}, nil
|
||||
}
|
||||
|
||||
func (env *Environment) GenesisChunked(ctx context.Context, chunk uint) (*coretypes.ResultGenesisChunk, error) {
|
||||
func (env *Environment) GenesisChunked(ctx context.Context, req *coretypes.RequestGenesisChunked) (*coretypes.ResultGenesisChunk, error) {
|
||||
if env.genChunks == nil {
|
||||
return nil, fmt.Errorf("service configuration error, genesis chunks are not initialized")
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func (env *Environment) GenesisChunked(ctx context.Context, chunk uint) (*corety
|
||||
return nil, fmt.Errorf("service configuration error, there are no chunks")
|
||||
}
|
||||
|
||||
id := int(chunk)
|
||||
id := int(req.Chunk)
|
||||
|
||||
if id > len(env.genChunks)-1 {
|
||||
return nil, fmt.Errorf("there are %d chunks, %d is invalid", len(env.genChunks)-1, id)
|
||||
|
||||
@@ -2,13 +2,9 @@ package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/internal/eventlog/cursor"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
rpc "github.com/tendermint/tendermint/rpc/jsonrpc/server"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// TODO: better system than "unsafe" prefix
|
||||
@@ -32,47 +28,47 @@ func NewRoutesMap(svc RPCService, opts *RouteOptions) RoutesMap {
|
||||
out := RoutesMap{
|
||||
// Event subscription. Note that subscribe, unsubscribe, and
|
||||
// unsubscribe_all are only available via the websocket endpoint.
|
||||
"events": rpc.NewRPCFunc(svc.Events, "filter", "maxItems", "before", "after", "waitTime"),
|
||||
"subscribe": rpc.NewWSRPCFunc(svc.Subscribe, "query"),
|
||||
"unsubscribe": rpc.NewWSRPCFunc(svc.Unsubscribe, "query"),
|
||||
"events": rpc.NewRPCFunc(svc.Events),
|
||||
"subscribe": rpc.NewWSRPCFunc(svc.Subscribe),
|
||||
"unsubscribe": rpc.NewWSRPCFunc(svc.Unsubscribe),
|
||||
"unsubscribe_all": rpc.NewWSRPCFunc(svc.UnsubscribeAll),
|
||||
|
||||
// info API
|
||||
"health": rpc.NewRPCFunc(svc.Health),
|
||||
"status": rpc.NewRPCFunc(svc.Status),
|
||||
"net_info": rpc.NewRPCFunc(svc.NetInfo),
|
||||
"blockchain": rpc.NewRPCFunc(svc.BlockchainInfo, "minHeight", "maxHeight"),
|
||||
"blockchain": rpc.NewRPCFunc(svc.BlockchainInfo),
|
||||
"genesis": rpc.NewRPCFunc(svc.Genesis),
|
||||
"genesis_chunked": rpc.NewRPCFunc(svc.GenesisChunked, "chunk"),
|
||||
"header": rpc.NewRPCFunc(svc.Header, "height"),
|
||||
"header_by_hash": rpc.NewRPCFunc(svc.HeaderByHash, "hash"),
|
||||
"block": rpc.NewRPCFunc(svc.Block, "height"),
|
||||
"block_by_hash": rpc.NewRPCFunc(svc.BlockByHash, "hash"),
|
||||
"block_results": rpc.NewRPCFunc(svc.BlockResults, "height"),
|
||||
"commit": rpc.NewRPCFunc(svc.Commit, "height"),
|
||||
"check_tx": rpc.NewRPCFunc(svc.CheckTx, "tx"),
|
||||
"remove_tx": rpc.NewRPCFunc(svc.RemoveTx, "txkey"),
|
||||
"tx": rpc.NewRPCFunc(svc.Tx, "hash", "prove"),
|
||||
"tx_search": rpc.NewRPCFunc(svc.TxSearch, "query", "prove", "page", "per_page", "order_by"),
|
||||
"block_search": rpc.NewRPCFunc(svc.BlockSearch, "query", "page", "per_page", "order_by"),
|
||||
"validators": rpc.NewRPCFunc(svc.Validators, "height", "page", "per_page"),
|
||||
"genesis_chunked": rpc.NewRPCFunc(svc.GenesisChunked),
|
||||
"header": rpc.NewRPCFunc(svc.Header),
|
||||
"header_by_hash": rpc.NewRPCFunc(svc.HeaderByHash),
|
||||
"block": rpc.NewRPCFunc(svc.Block),
|
||||
"block_by_hash": rpc.NewRPCFunc(svc.BlockByHash),
|
||||
"block_results": rpc.NewRPCFunc(svc.BlockResults),
|
||||
"commit": rpc.NewRPCFunc(svc.Commit),
|
||||
"check_tx": rpc.NewRPCFunc(svc.CheckTx),
|
||||
"remove_tx": rpc.NewRPCFunc(svc.RemoveTx),
|
||||
"tx": rpc.NewRPCFunc(svc.Tx),
|
||||
"tx_search": rpc.NewRPCFunc(svc.TxSearch),
|
||||
"block_search": rpc.NewRPCFunc(svc.BlockSearch),
|
||||
"validators": rpc.NewRPCFunc(svc.Validators),
|
||||
"dump_consensus_state": rpc.NewRPCFunc(svc.DumpConsensusState),
|
||||
"consensus_state": rpc.NewRPCFunc(svc.GetConsensusState),
|
||||
"consensus_params": rpc.NewRPCFunc(svc.ConsensusParams, "height"),
|
||||
"unconfirmed_txs": rpc.NewRPCFunc(svc.UnconfirmedTxs, "page", "per_page"),
|
||||
"consensus_params": rpc.NewRPCFunc(svc.ConsensusParams),
|
||||
"unconfirmed_txs": rpc.NewRPCFunc(svc.UnconfirmedTxs),
|
||||
"num_unconfirmed_txs": rpc.NewRPCFunc(svc.NumUnconfirmedTxs),
|
||||
|
||||
// tx broadcast API
|
||||
"broadcast_tx_commit": rpc.NewRPCFunc(svc.BroadcastTxCommit, "tx"),
|
||||
"broadcast_tx_sync": rpc.NewRPCFunc(svc.BroadcastTxSync, "tx"),
|
||||
"broadcast_tx_async": rpc.NewRPCFunc(svc.BroadcastTxAsync, "tx"),
|
||||
"broadcast_tx_commit": rpc.NewRPCFunc(svc.BroadcastTxCommit),
|
||||
"broadcast_tx_sync": rpc.NewRPCFunc(svc.BroadcastTxSync),
|
||||
"broadcast_tx_async": rpc.NewRPCFunc(svc.BroadcastTxAsync),
|
||||
|
||||
// abci API
|
||||
"abci_query": rpc.NewRPCFunc(svc.ABCIQuery, "path", "data", "height", "prove"),
|
||||
"abci_query": rpc.NewRPCFunc(svc.ABCIQuery),
|
||||
"abci_info": rpc.NewRPCFunc(svc.ABCIInfo),
|
||||
|
||||
// evidence API
|
||||
"broadcast_evidence": rpc.NewRPCFunc(svc.BroadcastEvidence, "evidence"),
|
||||
"broadcast_evidence": rpc.NewRPCFunc(svc.BroadcastEvidence),
|
||||
}
|
||||
if u, ok := svc.(RPCUnsafe); ok && opts.Unsafe {
|
||||
out["unsafe_flush_mempool"] = rpc.NewRPCFunc(u.UnsafeFlushMempool)
|
||||
@@ -84,38 +80,38 @@ func NewRoutesMap(svc RPCService, opts *RouteOptions) RoutesMap {
|
||||
// implementation, for use in constructing a routing table.
|
||||
type RPCService interface {
|
||||
ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo, error)
|
||||
ABCIQuery(ctx context.Context, path string, data bytes.HexBytes, height int64, prove bool) (*coretypes.ResultABCIQuery, error)
|
||||
Block(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlock, error)
|
||||
BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error)
|
||||
BlockResults(ctx context.Context, heightPtr *int64) (*coretypes.ResultBlockResults, error)
|
||||
BlockSearch(ctx context.Context, query string, pagePtr, perPagePtr *int, orderBy string) (*coretypes.ResultBlockSearch, error)
|
||||
BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error)
|
||||
BroadcastEvidence(ctx context.Context, ev coretypes.Evidence) (*coretypes.ResultBroadcastEvidence, error)
|
||||
BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error)
|
||||
BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error)
|
||||
BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error)
|
||||
CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultCheckTx, error)
|
||||
Commit(ctx context.Context, heightPtr *int64) (*coretypes.ResultCommit, error)
|
||||
ConsensusParams(ctx context.Context, heightPtr *int64) (*coretypes.ResultConsensusParams, error)
|
||||
ABCIQuery(ctx context.Context, req *coretypes.RequestABCIQuery) (*coretypes.ResultABCIQuery, error)
|
||||
Block(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultBlock, error)
|
||||
BlockByHash(ctx context.Context, req *coretypes.RequestBlockByHash) (*coretypes.ResultBlock, error)
|
||||
BlockResults(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultBlockResults, error)
|
||||
BlockSearch(ctx context.Context, req *coretypes.RequestBlockSearch) (*coretypes.ResultBlockSearch, error)
|
||||
BlockchainInfo(ctx context.Context, req *coretypes.RequestBlockchainInfo) (*coretypes.ResultBlockchainInfo, error)
|
||||
BroadcastEvidence(ctx context.Context, req *coretypes.RequestBroadcastEvidence) (*coretypes.ResultBroadcastEvidence, error)
|
||||
BroadcastTxAsync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error)
|
||||
BroadcastTxCommit(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTxCommit, error)
|
||||
BroadcastTxSync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error)
|
||||
CheckTx(ctx context.Context, req *coretypes.RequestCheckTx) (*coretypes.ResultCheckTx, error)
|
||||
Commit(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultCommit, error)
|
||||
ConsensusParams(ctx context.Context, req *coretypes.RequestConsensusParams) (*coretypes.ResultConsensusParams, error)
|
||||
DumpConsensusState(ctx context.Context) (*coretypes.ResultDumpConsensusState, error)
|
||||
Events(ctx context.Context, filter *coretypes.EventFilter, maxItems int, before, after cursor.Cursor, waitTime time.Duration) (*coretypes.ResultEvents, error)
|
||||
Events(ctx context.Context, req *coretypes.RequestEvents) (*coretypes.ResultEvents, error)
|
||||
Genesis(ctx context.Context) (*coretypes.ResultGenesis, error)
|
||||
GenesisChunked(ctx context.Context, chunk uint) (*coretypes.ResultGenesisChunk, error)
|
||||
GenesisChunked(ctx context.Context, req *coretypes.RequestGenesisChunked) (*coretypes.ResultGenesisChunk, error)
|
||||
GetConsensusState(ctx context.Context) (*coretypes.ResultConsensusState, error)
|
||||
Header(ctx context.Context, heightPtr *int64) (*coretypes.ResultHeader, error)
|
||||
HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error)
|
||||
Header(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultHeader, error)
|
||||
HeaderByHash(ctx context.Context, req *coretypes.RequestBlockByHash) (*coretypes.ResultHeader, error)
|
||||
Health(ctx context.Context) (*coretypes.ResultHealth, error)
|
||||
NetInfo(ctx context.Context) (*coretypes.ResultNetInfo, error)
|
||||
NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error)
|
||||
RemoveTx(ctx context.Context, txkey types.TxKey) error
|
||||
RemoveTx(ctx context.Context, req *coretypes.RequestRemoveTx) error
|
||||
Status(ctx context.Context) (*coretypes.ResultStatus, error)
|
||||
Subscribe(ctx context.Context, query string) (*coretypes.ResultSubscribe, error)
|
||||
Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coretypes.ResultTx, error)
|
||||
TxSearch(ctx context.Context, query string, prove bool, pagePtr, perPagePtr *int, orderBy string) (*coretypes.ResultTxSearch, error)
|
||||
UnconfirmedTxs(ctx context.Context, page, perPage *int) (*coretypes.ResultUnconfirmedTxs, error)
|
||||
Unsubscribe(ctx context.Context, query string) (*coretypes.ResultUnsubscribe, error)
|
||||
Subscribe(ctx context.Context, req *coretypes.RequestSubscribe) (*coretypes.ResultSubscribe, error)
|
||||
Tx(ctx context.Context, req *coretypes.RequestTx) (*coretypes.ResultTx, error)
|
||||
TxSearch(ctx context.Context, req *coretypes.RequestTxSearch) (*coretypes.ResultTxSearch, error)
|
||||
UnconfirmedTxs(ctx context.Context, req *coretypes.RequestUnconfirmedTxs) (*coretypes.ResultUnconfirmedTxs, error)
|
||||
Unsubscribe(ctx context.Context, req *coretypes.RequestUnsubscribe) (*coretypes.ResultUnsubscribe, error)
|
||||
UnsubscribeAll(ctx context.Context) (*coretypes.ResultUnsubscribe, error)
|
||||
Validators(ctx context.Context, heightPtr *int64, pagePtr, perPagePtr *int) (*coretypes.ResultValidators, error)
|
||||
Validators(ctx context.Context, req *coretypes.RequestValidators) (*coretypes.ResultValidators, error)
|
||||
}
|
||||
|
||||
// RPCUnsafe defines the set of "unsafe" methods that may optionally be
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
tmquery "github.com/tendermint/tendermint/internal/pubsub/query"
|
||||
"github.com/tendermint/tendermint/internal/state/indexer"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
tmmath "github.com/tendermint/tendermint/libs/math"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
@@ -18,32 +17,27 @@ import (
|
||||
// transaction is in the mempool, invalidated, or was not sent in the first
|
||||
// place.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/tx
|
||||
func (env *Environment) Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coretypes.ResultTx, error) {
|
||||
func (env *Environment) Tx(ctx context.Context, req *coretypes.RequestTx) (*coretypes.ResultTx, error) {
|
||||
// if index is disabled, return error
|
||||
|
||||
// N.B. The hash parameter is HexBytes so that the reflective parameter
|
||||
// decoding logic in the HTTP service will correctly translate from JSON.
|
||||
// See https://github.com/tendermint/tendermint/issues/6802 for context.
|
||||
|
||||
if !indexer.KVSinkEnabled(env.EventSinks) {
|
||||
return nil, errors.New("transaction querying is disabled due to no kvEventSink")
|
||||
}
|
||||
|
||||
for _, sink := range env.EventSinks {
|
||||
if sink.Type() == indexer.KV {
|
||||
r, err := sink.GetTxByHash(hash)
|
||||
r, err := sink.GetTxByHash(req.Hash)
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("tx (%X) not found, err: %w", hash, err)
|
||||
return nil, fmt.Errorf("tx (%X) not found, err: %w", req.Hash, err)
|
||||
}
|
||||
|
||||
var proof types.TxProof
|
||||
if prove {
|
||||
if req.Prove {
|
||||
block := env.BlockStore.LoadBlock(r.Height)
|
||||
proof = block.Data.Txs.Proof(int(r.Index))
|
||||
}
|
||||
|
||||
return &coretypes.ResultTx{
|
||||
Hash: hash,
|
||||
Hash: req.Hash,
|
||||
Height: r.Height,
|
||||
Index: r.Index,
|
||||
TxResult: r.Result,
|
||||
@@ -59,21 +53,14 @@ func (env *Environment) Tx(ctx context.Context, hash bytes.HexBytes, prove bool)
|
||||
// TxSearch allows you to query for multiple transactions results. It returns a
|
||||
// list of transactions (maximum ?per_page entries) and the total count.
|
||||
// More: https://docs.tendermint.com/master/rpc/#/Info/tx_search
|
||||
func (env *Environment) TxSearch(
|
||||
ctx context.Context,
|
||||
query string,
|
||||
prove bool,
|
||||
pagePtr, perPagePtr *int,
|
||||
orderBy string,
|
||||
) (*coretypes.ResultTxSearch, error) {
|
||||
|
||||
func (env *Environment) TxSearch(ctx context.Context, req *coretypes.RequestTxSearch) (*coretypes.ResultTxSearch, error) {
|
||||
if !indexer.KVSinkEnabled(env.EventSinks) {
|
||||
return nil, fmt.Errorf("transaction searching is disabled due to no kvEventSink")
|
||||
} else if len(query) > maxQueryLength {
|
||||
} else if len(req.Query) > maxQueryLength {
|
||||
return nil, errors.New("maximum query length exceeded")
|
||||
}
|
||||
|
||||
q, err := tmquery.New(query)
|
||||
q, err := tmquery.New(req.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -86,7 +73,7 @@ func (env *Environment) TxSearch(
|
||||
}
|
||||
|
||||
// sort results (must be done before pagination)
|
||||
switch orderBy {
|
||||
switch req.OrderBy {
|
||||
case "desc", "":
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
if results[i].Height == results[j].Height {
|
||||
@@ -107,9 +94,9 @@ func (env *Environment) TxSearch(
|
||||
|
||||
// paginate results
|
||||
totalCount := len(results)
|
||||
perPage := env.validatePerPage(perPagePtr)
|
||||
perPage := env.validatePerPage(req.PerPage.IntPtr())
|
||||
|
||||
page, err := validatePage(pagePtr, perPage, totalCount)
|
||||
page, err := validatePage(req.Page.IntPtr(), perPage, totalCount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -122,7 +109,7 @@ func (env *Environment) TxSearch(
|
||||
r := results[i]
|
||||
|
||||
var proof types.TxProof
|
||||
if prove {
|
||||
if req.Prove {
|
||||
block := env.BlockStore.LoadBlock(r.Height)
|
||||
proof = block.Data.Txs.Proof(int(r.Index))
|
||||
}
|
||||
|
||||
@@ -2,60 +2,148 @@ package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/internal/eventlog/cursor"
|
||||
tmbytes "github.com/tendermint/tendermint/libs/bytes"
|
||||
lrpc "github.com/tendermint/tendermint/light/rpc"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
)
|
||||
|
||||
// proxyService wraps a light RPC client to export the RPC service interfaces.
|
||||
// This is needed because the service and the client use different signatures
|
||||
// for some of the methods.
|
||||
// The interfaces are implemented by delegating to the underlying node via the
|
||||
// specified client.
|
||||
type proxyService struct {
|
||||
*lrpc.Client
|
||||
Client *lrpc.Client
|
||||
}
|
||||
|
||||
func (p proxyService) ABCIQuery(ctx context.Context, path string, data tmbytes.HexBytes, height int64, prove bool) (*coretypes.ResultABCIQuery, error) {
|
||||
return p.ABCIQueryWithOptions(ctx, path, data, rpcclient.ABCIQueryOptions{
|
||||
Height: height,
|
||||
Prove: prove,
|
||||
func (p proxyService) ABCIInfo(ctx context.Context) (*coretypes.ResultABCIInfo, error) { panic("ok") }
|
||||
|
||||
func (p proxyService) ABCIQuery(ctx context.Context, req *coretypes.RequestABCIQuery) (*coretypes.ResultABCIQuery, error) {
|
||||
return p.Client.ABCIQueryWithOptions(ctx, req.Path, req.Data, rpcclient.ABCIQueryOptions{
|
||||
Height: int64(req.Height),
|
||||
Prove: req.Prove,
|
||||
})
|
||||
}
|
||||
|
||||
func (p proxyService) Block(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultBlock, error) {
|
||||
return p.Client.Block(ctx, (*int64)(req.Height))
|
||||
}
|
||||
|
||||
func (p proxyService) BlockByHash(ctx context.Context, req *coretypes.RequestBlockByHash) (*coretypes.ResultBlock, error) {
|
||||
return p.Client.BlockByHash(ctx, req.Hash)
|
||||
}
|
||||
|
||||
func (p proxyService) BlockResults(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultBlockResults, error) {
|
||||
return p.Client.BlockResults(ctx, (*int64)(req.Height))
|
||||
}
|
||||
|
||||
func (p proxyService) BlockSearch(ctx context.Context, req *coretypes.RequestBlockSearch) (*coretypes.ResultBlockSearch, error) {
|
||||
return p.Client.BlockSearch(ctx, req.Query, req.Page.IntPtr(), req.PerPage.IntPtr(), req.OrderBy)
|
||||
}
|
||||
|
||||
func (p proxyService) BlockchainInfo(ctx context.Context, req *coretypes.RequestBlockchainInfo) (*coretypes.ResultBlockchainInfo, error) {
|
||||
return p.Client.BlockchainInfo(ctx, int64(req.MinHeight), int64(req.MaxHeight))
|
||||
}
|
||||
|
||||
func (p proxyService) BroadcastEvidence(ctx context.Context, req *coretypes.RequestBroadcastEvidence) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
return p.Client.BroadcastEvidence(ctx, req.Evidence)
|
||||
}
|
||||
|
||||
func (p proxyService) BroadcastTxAsync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error) {
|
||||
return p.Client.BroadcastTxAsync(ctx, req.Tx)
|
||||
}
|
||||
|
||||
func (p proxyService) BroadcastTxCommit(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTxCommit, error) {
|
||||
return p.Client.BroadcastTxCommit(ctx, req.Tx)
|
||||
}
|
||||
|
||||
func (p proxyService) BroadcastTxSync(ctx context.Context, req *coretypes.RequestBroadcastTx) (*coretypes.ResultBroadcastTx, error) {
|
||||
return p.Client.BroadcastTxSync(ctx, req.Tx)
|
||||
}
|
||||
|
||||
func (p proxyService) CheckTx(ctx context.Context, req *coretypes.RequestCheckTx) (*coretypes.ResultCheckTx, error) {
|
||||
return p.Client.CheckTx(ctx, req.Tx)
|
||||
}
|
||||
|
||||
func (p proxyService) Commit(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultCommit, error) {
|
||||
return p.Client.Commit(ctx, (*int64)(req.Height))
|
||||
}
|
||||
|
||||
func (p proxyService) ConsensusParams(ctx context.Context, req *coretypes.RequestConsensusParams) (*coretypes.ResultConsensusParams, error) {
|
||||
return p.Client.ConsensusParams(ctx, (*int64)(req.Height))
|
||||
}
|
||||
|
||||
func (p proxyService) DumpConsensusState(ctx context.Context) (*coretypes.ResultDumpConsensusState, error) {
|
||||
return p.Client.DumpConsensusState(ctx)
|
||||
}
|
||||
|
||||
func (p proxyService) Events(ctx context.Context, req *coretypes.RequestEvents) (*coretypes.ResultEvents, error) {
|
||||
return p.Client.Events(ctx, req)
|
||||
}
|
||||
|
||||
func (p proxyService) Genesis(ctx context.Context) (*coretypes.ResultGenesis, error) {
|
||||
return p.Client.Genesis(ctx)
|
||||
}
|
||||
|
||||
func (p proxyService) GenesisChunked(ctx context.Context, req *coretypes.RequestGenesisChunked) (*coretypes.ResultGenesisChunk, error) {
|
||||
return p.Client.GenesisChunked(ctx, uint(req.Chunk))
|
||||
}
|
||||
|
||||
func (p proxyService) GetConsensusState(ctx context.Context) (*coretypes.ResultConsensusState, error) {
|
||||
return p.ConsensusState(ctx)
|
||||
return p.Client.ConsensusState(ctx)
|
||||
}
|
||||
|
||||
func (p proxyService) Events(ctx context.Context,
|
||||
filter *coretypes.EventFilter,
|
||||
maxItems int,
|
||||
before, after cursor.Cursor,
|
||||
waitTime time.Duration,
|
||||
) (*coretypes.ResultEvents, error) {
|
||||
return p.Client.Events(ctx, &coretypes.RequestEvents{
|
||||
Filter: filter,
|
||||
MaxItems: maxItems,
|
||||
Before: before.String(),
|
||||
After: after.String(),
|
||||
WaitTime: waitTime,
|
||||
})
|
||||
func (p proxyService) Header(ctx context.Context, req *coretypes.RequestBlockInfo) (*coretypes.ResultHeader, error) {
|
||||
return p.Client.Header(ctx, (*int64)(req.Height))
|
||||
}
|
||||
|
||||
func (p proxyService) Subscribe(ctx context.Context, query string) (*coretypes.ResultSubscribe, error) {
|
||||
return p.SubscribeWS(ctx, query)
|
||||
func (p proxyService) HeaderByHash(ctx context.Context, req *coretypes.RequestBlockByHash) (*coretypes.ResultHeader, error) {
|
||||
return p.Client.HeaderByHash(ctx, req.Hash)
|
||||
}
|
||||
|
||||
func (p proxyService) Unsubscribe(ctx context.Context, query string) (*coretypes.ResultUnsubscribe, error) {
|
||||
return p.UnsubscribeWS(ctx, query)
|
||||
func (p proxyService) Health(ctx context.Context) (*coretypes.ResultHealth, error) {
|
||||
return p.Client.Health(ctx)
|
||||
}
|
||||
|
||||
func (p proxyService) NetInfo(ctx context.Context) (*coretypes.ResultNetInfo, error) {
|
||||
return p.Client.NetInfo(ctx)
|
||||
}
|
||||
|
||||
func (p proxyService) NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error) {
|
||||
return p.Client.NumUnconfirmedTxs(ctx)
|
||||
}
|
||||
|
||||
func (p proxyService) RemoveTx(ctx context.Context, req *coretypes.RequestRemoveTx) error {
|
||||
return p.Client.RemoveTx(ctx, req.TxKey)
|
||||
}
|
||||
|
||||
func (p proxyService) Status(ctx context.Context) (*coretypes.ResultStatus, error) {
|
||||
return p.Client.Status(ctx)
|
||||
}
|
||||
|
||||
func (p proxyService) Subscribe(ctx context.Context, req *coretypes.RequestSubscribe) (*coretypes.ResultSubscribe, error) {
|
||||
return p.Client.SubscribeWS(ctx, req.Query)
|
||||
}
|
||||
|
||||
func (p proxyService) Tx(ctx context.Context, req *coretypes.RequestTx) (*coretypes.ResultTx, error) {
|
||||
return p.Client.Tx(ctx, req.Hash, req.Prove)
|
||||
}
|
||||
|
||||
func (p proxyService) TxSearch(ctx context.Context, req *coretypes.RequestTxSearch) (*coretypes.ResultTxSearch, error) {
|
||||
return p.Client.TxSearch(ctx, req.Query, req.Prove, req.Page.IntPtr(), req.PerPage.IntPtr(), req.OrderBy)
|
||||
}
|
||||
|
||||
func (p proxyService) UnconfirmedTxs(ctx context.Context, req *coretypes.RequestUnconfirmedTxs) (*coretypes.ResultUnconfirmedTxs, error) {
|
||||
return p.Client.UnconfirmedTxs(ctx, req.Page.IntPtr(), req.PerPage.IntPtr())
|
||||
}
|
||||
|
||||
func (p proxyService) Unsubscribe(ctx context.Context, req *coretypes.RequestUnsubscribe) (*coretypes.ResultUnsubscribe, error) {
|
||||
return p.Client.UnsubscribeWS(ctx, req.Query)
|
||||
}
|
||||
|
||||
func (p proxyService) UnsubscribeAll(ctx context.Context) (*coretypes.ResultUnsubscribe, error) {
|
||||
return p.UnsubscribeAllWS(ctx)
|
||||
return p.Client.UnsubscribeAllWS(ctx)
|
||||
}
|
||||
|
||||
func (p proxyService) BroadcastEvidence(ctx context.Context, ev coretypes.Evidence) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
return p.Client.BroadcastEvidence(ctx, ev.Value)
|
||||
func (p proxyService) Validators(ctx context.Context, req *coretypes.RequestValidators) (*coretypes.ResultValidators, error) {
|
||||
return p.Client.Validators(ctx, (*int64)(req.Height), req.Page.IntPtr(), req.PerPage.IntPtr())
|
||||
}
|
||||
|
||||
38
node/node.go
38
node/node.go
@@ -488,17 +488,6 @@ func (n *nodeImpl) OnStart(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
n.rpcEnv.NodeInfo = n.nodeInfo
|
||||
// Start the RPC server before the P2P server
|
||||
// so we can eg. receive txs for the first block
|
||||
if n.config.RPC.ListenAddress != "" {
|
||||
var err error
|
||||
n.rpcListeners, err = n.rpcEnv.StartService(ctx, n.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if n.config.Instrumentation.Prometheus && n.config.Instrumentation.PrometheusListenAddr != "" {
|
||||
n.prometheusSrv = n.startPrometheusServer(ctx, n.config.Instrumentation.PrometheusListenAddr)
|
||||
}
|
||||
@@ -515,12 +504,31 @@ func (n *nodeImpl) OnStart(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
n.rpcEnv.NodeInfo = n.nodeInfo
|
||||
// Start the RPC server before the P2P server
|
||||
// so we can eg. receive txs for the first block
|
||||
if n.config.RPC.ListenAddress != "" {
|
||||
var err error
|
||||
n.rpcListeners, err = n.rpcEnv.StartService(ctx, n.config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnStop stops the Node. It implements service.Service.
|
||||
func (n *nodeImpl) OnStop() {
|
||||
n.logger.Info("Stopping Node")
|
||||
// stop the listeners / external services first
|
||||
for _, l := range n.rpcListeners {
|
||||
n.logger.Info("Closing rpc listener", "listener", l)
|
||||
if err := l.Close(); err != nil {
|
||||
n.logger.Error("error closing listener", "listener", l, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, es := range n.eventSinks {
|
||||
if err := es.Stop(); err != nil {
|
||||
n.logger.Error("failed to stop event sink", "err", err)
|
||||
@@ -534,14 +542,6 @@ func (n *nodeImpl) OnStop() {
|
||||
n.router.Wait()
|
||||
n.rpcEnv.IsListening = false
|
||||
|
||||
// finally stop the listeners / external services
|
||||
for _, l := range n.rpcListeners {
|
||||
n.logger.Info("Closing rpc listener", "listener", l)
|
||||
if err := l.Close(); err != nil {
|
||||
n.logger.Error("error closing listener", "listener", l, "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
if pvsc, ok := n.privValidator.(service.Service); ok {
|
||||
pvsc.Wait()
|
||||
}
|
||||
|
||||
@@ -310,8 +310,8 @@ func createRouter(
|
||||
nodeKey.PrivKey,
|
||||
peerManager,
|
||||
nodeInfoProducer,
|
||||
[]p2p.Transport{transport},
|
||||
[]p2p.Endpoint{ep},
|
||||
transport,
|
||||
ep,
|
||||
getRouterConfig(cfg, appClient),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -103,13 +103,16 @@ func TestMinPollTime(t *testing.T) {
|
||||
// wait time and reports no events.
|
||||
ctx := context.Background()
|
||||
filter := &coretypes.EventFilter{Query: `tm.event = 'good'`}
|
||||
var zero cursor.Cursor
|
||||
|
||||
t.Run("NoneMatch", func(t *testing.T) {
|
||||
start := time.Now()
|
||||
|
||||
// Request a very short delay, and affirm we got the server's minimum.
|
||||
rsp, err := s.env.Events(ctx, filter, 1, zero, zero, 10*time.Millisecond)
|
||||
rsp, err := s.env.Events(ctx, &coretypes.RequestEvents{
|
||||
Filter: filter,
|
||||
MaxItems: 1,
|
||||
WaitTime: 10 * time.Millisecond,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Events failed: %v", err)
|
||||
} else if elapsed := time.Since(start); elapsed < time.Second {
|
||||
@@ -128,7 +131,11 @@ func TestMinPollTime(t *testing.T) {
|
||||
// Request a long-ish delay and affirm we don't block for it.
|
||||
// Check for this by ensuring we return sooner than the minimum delay,
|
||||
// since we don't know the exact timing.
|
||||
rsp, err := s.env.Events(ctx, filter, 1, zero, zero, 10*time.Second)
|
||||
rsp, err := s.env.Events(ctx, &coretypes.RequestEvents{
|
||||
Filter: filter,
|
||||
MaxItems: 1,
|
||||
WaitTime: 10 * time.Second,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Events failed: %v", err)
|
||||
} else if elapsed := time.Since(start); elapsed > 500*time.Millisecond {
|
||||
@@ -263,12 +270,5 @@ func (s *streamTester) advance(d time.Duration) { s.clock += int64(d) }
|
||||
// environment as if it were a local RPC client. This works because the Events
|
||||
// method only requires the event log, the other fields are unused.
|
||||
func (s *streamTester) Events(ctx context.Context, req *coretypes.RequestEvents) (*coretypes.ResultEvents, error) {
|
||||
var before, after cursor.Cursor
|
||||
if err := before.UnmarshalText([]byte(req.Before)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := after.UnmarshalText([]byte(req.After)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.env.Events(ctx, req.Filter, req.MaxItems, before, after, req.WaitTime)
|
||||
return s.env.Events(ctx, req)
|
||||
}
|
||||
|
||||
@@ -213,10 +213,10 @@ func (c *baseRPCClient) ABCIQuery(ctx context.Context, path string, data bytes.H
|
||||
|
||||
func (c *baseRPCClient) ABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes, opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) {
|
||||
result := new(coretypes.ResultABCIQuery)
|
||||
if err := c.caller.Call(ctx, "abci_query", abciQueryArgs{
|
||||
if err := c.caller.Call(ctx, "abci_query", &coretypes.RequestABCIQuery{
|
||||
Path: path,
|
||||
Data: data,
|
||||
Height: opts.Height,
|
||||
Height: coretypes.Int64(opts.Height),
|
||||
Prove: opts.Prove,
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
@@ -226,7 +226,9 @@ func (c *baseRPCClient) ABCIQueryWithOptions(ctx context.Context, path string, d
|
||||
|
||||
func (c *baseRPCClient) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) {
|
||||
result := new(coretypes.ResultBroadcastTxCommit)
|
||||
if err := c.caller.Call(ctx, "broadcast_tx_commit", txArgs{Tx: tx}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "broadcast_tx_commit", &coretypes.RequestBroadcastTx{
|
||||
Tx: tx,
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -242,7 +244,7 @@ func (c *baseRPCClient) BroadcastTxSync(ctx context.Context, tx types.Tx) (*core
|
||||
|
||||
func (c *baseRPCClient) broadcastTX(ctx context.Context, route string, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
|
||||
result := new(coretypes.ResultBroadcastTx)
|
||||
if err := c.caller.Call(ctx, route, txArgs{Tx: tx}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, route, &coretypes.RequestBroadcastTx{Tx: tx}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -251,7 +253,10 @@ func (c *baseRPCClient) broadcastTX(ctx context.Context, route string, tx types.
|
||||
func (c *baseRPCClient) UnconfirmedTxs(ctx context.Context, page *int, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) {
|
||||
result := new(coretypes.ResultUnconfirmedTxs)
|
||||
|
||||
if err := c.caller.Call(ctx, "unconfirmed_txs", unconfirmedArgs{Page: page, PerPage: perPage}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "unconfirmed_txs", &coretypes.RequestUnconfirmedTxs{
|
||||
Page: coretypes.Int64Ptr(page),
|
||||
PerPage: coretypes.Int64Ptr(perPage),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -267,14 +272,14 @@ func (c *baseRPCClient) NumUnconfirmedTxs(ctx context.Context) (*coretypes.Resul
|
||||
|
||||
func (c *baseRPCClient) CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultCheckTx, error) {
|
||||
result := new(coretypes.ResultCheckTx)
|
||||
if err := c.caller.Call(ctx, "check_tx", txArgs{Tx: tx}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "check_tx", &coretypes.RequestCheckTx{Tx: tx}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *baseRPCClient) RemoveTx(ctx context.Context, txKey types.TxKey) error {
|
||||
if err := c.caller.Call(ctx, "remove_tx", txKeyArgs{TxKey: txKey[:]}, nil); err != nil {
|
||||
if err := c.caller.Call(ctx, "remove_tx", &coretypes.RequestRemoveTx{TxKey: txKey}, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -306,7 +311,9 @@ func (c *baseRPCClient) ConsensusState(ctx context.Context) (*coretypes.ResultCo
|
||||
|
||||
func (c *baseRPCClient) ConsensusParams(ctx context.Context, height *int64) (*coretypes.ResultConsensusParams, error) {
|
||||
result := new(coretypes.ResultConsensusParams)
|
||||
if err := c.caller.Call(ctx, "consensus_params", heightArgs{Height: height}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "consensus_params", &coretypes.RequestConsensusParams{
|
||||
Height: (*coretypes.Int64)(height),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -330,9 +337,9 @@ func (c *baseRPCClient) Health(ctx context.Context) (*coretypes.ResultHealth, er
|
||||
|
||||
func (c *baseRPCClient) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) {
|
||||
result := new(coretypes.ResultBlockchainInfo)
|
||||
if err := c.caller.Call(ctx, "blockchain", blockchainInfoArgs{
|
||||
MinHeight: minHeight,
|
||||
MaxHeight: maxHeight,
|
||||
if err := c.caller.Call(ctx, "blockchain", &coretypes.RequestBlockchainInfo{
|
||||
MinHeight: coretypes.Int64(minHeight),
|
||||
MaxHeight: coretypes.Int64(maxHeight),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -349,7 +356,9 @@ func (c *baseRPCClient) Genesis(ctx context.Context) (*coretypes.ResultGenesis,
|
||||
|
||||
func (c *baseRPCClient) GenesisChunked(ctx context.Context, id uint) (*coretypes.ResultGenesisChunk, error) {
|
||||
result := new(coretypes.ResultGenesisChunk)
|
||||
if err := c.caller.Call(ctx, "genesis_chunked", genesisChunkArgs{Chunk: id}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "genesis_chunked", &coretypes.RequestGenesisChunked{
|
||||
Chunk: coretypes.Int64(id),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -357,7 +366,9 @@ func (c *baseRPCClient) GenesisChunked(ctx context.Context, id uint) (*coretypes
|
||||
|
||||
func (c *baseRPCClient) Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error) {
|
||||
result := new(coretypes.ResultBlock)
|
||||
if err := c.caller.Call(ctx, "block", heightArgs{Height: height}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "block", &coretypes.RequestBlockInfo{
|
||||
Height: (*coretypes.Int64)(height),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -365,7 +376,7 @@ func (c *baseRPCClient) Block(ctx context.Context, height *int64) (*coretypes.Re
|
||||
|
||||
func (c *baseRPCClient) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error) {
|
||||
result := new(coretypes.ResultBlock)
|
||||
if err := c.caller.Call(ctx, "block_by_hash", hashArgs{Hash: hash}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "block_by_hash", &coretypes.RequestBlockByHash{Hash: hash}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -373,7 +384,9 @@ func (c *baseRPCClient) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*
|
||||
|
||||
func (c *baseRPCClient) BlockResults(ctx context.Context, height *int64) (*coretypes.ResultBlockResults, error) {
|
||||
result := new(coretypes.ResultBlockResults)
|
||||
if err := c.caller.Call(ctx, "block_results", heightArgs{Height: height}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "block_results", &coretypes.RequestBlockInfo{
|
||||
Height: (*coretypes.Int64)(height),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -381,7 +394,9 @@ func (c *baseRPCClient) BlockResults(ctx context.Context, height *int64) (*coret
|
||||
|
||||
func (c *baseRPCClient) Header(ctx context.Context, height *int64) (*coretypes.ResultHeader, error) {
|
||||
result := new(coretypes.ResultHeader)
|
||||
if err := c.caller.Call(ctx, "header", heightArgs{Height: height}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "header", &coretypes.RequestBlockInfo{
|
||||
Height: (*coretypes.Int64)(height),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -389,7 +404,9 @@ func (c *baseRPCClient) Header(ctx context.Context, height *int64) (*coretypes.R
|
||||
|
||||
func (c *baseRPCClient) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) {
|
||||
result := new(coretypes.ResultHeader)
|
||||
if err := c.caller.Call(ctx, "header_by_hash", hashArgs{Hash: hash}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "header_by_hash", &coretypes.RequestBlockByHash{
|
||||
Hash: hash,
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -397,7 +414,9 @@ func (c *baseRPCClient) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (
|
||||
|
||||
func (c *baseRPCClient) Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) {
|
||||
result := new(coretypes.ResultCommit)
|
||||
if err := c.caller.Call(ctx, "commit", heightArgs{Height: height}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "commit", &coretypes.RequestBlockInfo{
|
||||
Height: (*coretypes.Int64)(height),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -405,7 +424,7 @@ func (c *baseRPCClient) Commit(ctx context.Context, height *int64) (*coretypes.R
|
||||
|
||||
func (c *baseRPCClient) Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coretypes.ResultTx, error) {
|
||||
result := new(coretypes.ResultTx)
|
||||
if err := c.caller.Call(ctx, "tx", hashArgs{Hash: hash, Prove: prove}, result); err != nil {
|
||||
if err := c.caller.Call(ctx, "tx", &coretypes.RequestTx{Hash: hash, Prove: prove}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
@@ -413,12 +432,12 @@ func (c *baseRPCClient) Tx(ctx context.Context, hash bytes.HexBytes, prove bool)
|
||||
|
||||
func (c *baseRPCClient) TxSearch(ctx context.Context, query string, prove bool, page, perPage *int, orderBy string) (*coretypes.ResultTxSearch, error) {
|
||||
result := new(coretypes.ResultTxSearch)
|
||||
if err := c.caller.Call(ctx, "tx_search", searchArgs{
|
||||
if err := c.caller.Call(ctx, "tx_search", &coretypes.RequestTxSearch{
|
||||
Query: query,
|
||||
Prove: prove,
|
||||
OrderBy: orderBy,
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
Page: coretypes.Int64Ptr(page),
|
||||
PerPage: coretypes.Int64Ptr(perPage),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -428,11 +447,11 @@ func (c *baseRPCClient) TxSearch(ctx context.Context, query string, prove bool,
|
||||
|
||||
func (c *baseRPCClient) BlockSearch(ctx context.Context, query string, page, perPage *int, orderBy string) (*coretypes.ResultBlockSearch, error) {
|
||||
result := new(coretypes.ResultBlockSearch)
|
||||
if err := c.caller.Call(ctx, "block_search", searchArgs{
|
||||
if err := c.caller.Call(ctx, "block_search", &coretypes.RequestBlockSearch{
|
||||
Query: query,
|
||||
OrderBy: orderBy,
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
Page: coretypes.Int64Ptr(page),
|
||||
PerPage: coretypes.Int64Ptr(perPage),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -442,10 +461,10 @@ func (c *baseRPCClient) BlockSearch(ctx context.Context, query string, page, per
|
||||
|
||||
func (c *baseRPCClient) Validators(ctx context.Context, height *int64, page, perPage *int) (*coretypes.ResultValidators, error) {
|
||||
result := new(coretypes.ResultValidators)
|
||||
if err := c.caller.Call(ctx, "validators", validatorArgs{
|
||||
Height: height,
|
||||
Page: page,
|
||||
PerPage: perPage,
|
||||
if err := c.caller.Call(ctx, "validators", &coretypes.RequestValidators{
|
||||
Height: (*coretypes.Int64)(height),
|
||||
Page: coretypes.Int64Ptr(page),
|
||||
PerPage: coretypes.Int64Ptr(perPage),
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -454,8 +473,8 @@ func (c *baseRPCClient) Validators(ctx context.Context, height *int64, page, per
|
||||
|
||||
func (c *baseRPCClient) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
result := new(coretypes.ResultBroadcastEvidence)
|
||||
if err := c.caller.Call(ctx, "broadcast_evidence", evidenceArgs{
|
||||
Evidence: coretypes.Evidence{Value: ev},
|
||||
if err := c.caller.Call(ctx, "broadcast_evidence", &coretypes.RequestBroadcastEvidence{
|
||||
Evidence: ev,
|
||||
}, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package http
|
||||
|
||||
// The types in this file define the JSON encoding for RPC method parameters
|
||||
// from the client to the server.
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/rpc/coretypes"
|
||||
)
|
||||
|
||||
type abciQueryArgs struct {
|
||||
Path string `json:"path"`
|
||||
Data bytes.HexBytes `json:"data"`
|
||||
Height int64 `json:"height,string"`
|
||||
Prove bool `json:"prove"`
|
||||
}
|
||||
|
||||
type txArgs struct {
|
||||
Tx []byte `json:"tx"`
|
||||
}
|
||||
|
||||
type txKeyArgs struct {
|
||||
TxKey []byte `json:"tx_key"`
|
||||
}
|
||||
|
||||
type unconfirmedArgs struct {
|
||||
Page *int `json:"page,string,omitempty"`
|
||||
PerPage *int `json:"per_page,string,omitempty"`
|
||||
}
|
||||
|
||||
type heightArgs struct {
|
||||
Height *int64 `json:"height,string,omitempty"`
|
||||
}
|
||||
|
||||
type hashArgs struct {
|
||||
Hash bytes.HexBytes `json:"hash"`
|
||||
Prove bool `json:"prove,omitempty"`
|
||||
}
|
||||
|
||||
type blockchainInfoArgs struct {
|
||||
MinHeight int64 `json:"minHeight,string"`
|
||||
MaxHeight int64 `json:"maxHeight,string"`
|
||||
}
|
||||
|
||||
type genesisChunkArgs struct {
|
||||
Chunk uint `json:"chunk,string"`
|
||||
}
|
||||
|
||||
type searchArgs struct {
|
||||
Query string `json:"query"`
|
||||
Prove bool `json:"prove,omitempty"`
|
||||
OrderBy string `json:"order_by,omitempty"`
|
||||
Page *int `json:"page,string,omitempty"`
|
||||
PerPage *int `json:"per_page,string,omitempty"`
|
||||
}
|
||||
|
||||
type validatorArgs struct {
|
||||
Height *int64 `json:"height,string,omitempty"`
|
||||
Page *int `json:"page,string,omitempty"`
|
||||
PerPage *int `json:"per_page,string,omitempty"`
|
||||
}
|
||||
|
||||
type evidenceArgs struct {
|
||||
Evidence coretypes.Evidence `json:"evidence"`
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/internal/eventbus"
|
||||
"github.com/tendermint/tendermint/internal/eventlog/cursor"
|
||||
"github.com/tendermint/tendermint/internal/pubsub"
|
||||
"github.com/tendermint/tendermint/internal/pubsub/query"
|
||||
rpccore "github.com/tendermint/tendermint/internal/rpc/core"
|
||||
@@ -79,23 +78,28 @@ func (c *Local) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes)
|
||||
}
|
||||
|
||||
func (c *Local) ABCIQueryWithOptions(ctx context.Context, path string, data bytes.HexBytes, opts rpcclient.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) {
|
||||
return c.env.ABCIQuery(ctx, path, data, opts.Height, opts.Prove)
|
||||
return c.env.ABCIQuery(ctx, &coretypes.RequestABCIQuery{
|
||||
Path: path, Data: data, Height: coretypes.Int64(opts.Height), Prove: opts.Prove,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Local) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) {
|
||||
return c.env.BroadcastTxCommit(ctx, tx)
|
||||
return c.env.BroadcastTxCommit(ctx, &coretypes.RequestBroadcastTx{Tx: tx})
|
||||
}
|
||||
|
||||
func (c *Local) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
|
||||
return c.env.BroadcastTxAsync(ctx, tx)
|
||||
return c.env.BroadcastTxAsync(ctx, &coretypes.RequestBroadcastTx{Tx: tx})
|
||||
}
|
||||
|
||||
func (c *Local) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
|
||||
return c.env.BroadcastTxSync(ctx, tx)
|
||||
return c.env.BroadcastTxSync(ctx, &coretypes.RequestBroadcastTx{Tx: tx})
|
||||
}
|
||||
|
||||
func (c *Local) UnconfirmedTxs(ctx context.Context, page, perPage *int) (*coretypes.ResultUnconfirmedTxs, error) {
|
||||
return c.env.UnconfirmedTxs(ctx, page, perPage)
|
||||
return c.env.UnconfirmedTxs(ctx, &coretypes.RequestUnconfirmedTxs{
|
||||
Page: coretypes.Int64Ptr(page),
|
||||
PerPage: coretypes.Int64Ptr(perPage),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Local) NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfirmedTxs, error) {
|
||||
@@ -103,7 +107,7 @@ func (c *Local) NumUnconfirmedTxs(ctx context.Context) (*coretypes.ResultUnconfi
|
||||
}
|
||||
|
||||
func (c *Local) CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultCheckTx, error) {
|
||||
return c.env.CheckTx(ctx, tx)
|
||||
return c.env.CheckTx(ctx, &coretypes.RequestCheckTx{Tx: tx})
|
||||
}
|
||||
|
||||
func (c *Local) RemoveTx(ctx context.Context, txKey types.TxKey) error {
|
||||
@@ -123,18 +127,11 @@ func (c *Local) ConsensusState(ctx context.Context) (*coretypes.ResultConsensusS
|
||||
}
|
||||
|
||||
func (c *Local) ConsensusParams(ctx context.Context, height *int64) (*coretypes.ResultConsensusParams, error) {
|
||||
return c.env.ConsensusParams(ctx, height)
|
||||
return c.env.ConsensusParams(ctx, &coretypes.RequestConsensusParams{Height: (*coretypes.Int64)(height)})
|
||||
}
|
||||
|
||||
func (c *Local) Events(ctx context.Context, req *coretypes.RequestEvents) (*coretypes.ResultEvents, error) {
|
||||
var before, after cursor.Cursor
|
||||
if err := before.UnmarshalText([]byte(req.Before)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := after.UnmarshalText([]byte(req.After)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.env.Events(ctx, req.Filter, req.MaxItems, before, after, req.WaitTime)
|
||||
return c.env.Events(ctx, req)
|
||||
}
|
||||
|
||||
func (c *Local) Health(ctx context.Context) (*coretypes.ResultHealth, error) {
|
||||
@@ -142,7 +139,10 @@ func (c *Local) Health(ctx context.Context) (*coretypes.ResultHealth, error) {
|
||||
}
|
||||
|
||||
func (c *Local) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) {
|
||||
return c.env.BlockchainInfo(ctx, minHeight, maxHeight)
|
||||
return c.env.BlockchainInfo(ctx, &coretypes.RequestBlockchainInfo{
|
||||
MinHeight: coretypes.Int64(minHeight),
|
||||
MaxHeight: coretypes.Int64(maxHeight),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Local) Genesis(ctx context.Context) (*coretypes.ResultGenesis, error) {
|
||||
@@ -150,51 +150,66 @@ func (c *Local) Genesis(ctx context.Context) (*coretypes.ResultGenesis, error) {
|
||||
}
|
||||
|
||||
func (c *Local) GenesisChunked(ctx context.Context, id uint) (*coretypes.ResultGenesisChunk, error) {
|
||||
return c.env.GenesisChunked(ctx, id)
|
||||
return c.env.GenesisChunked(ctx, &coretypes.RequestGenesisChunked{Chunk: coretypes.Int64(id)})
|
||||
}
|
||||
|
||||
func (c *Local) Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error) {
|
||||
return c.env.Block(ctx, height)
|
||||
return c.env.Block(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)})
|
||||
}
|
||||
|
||||
func (c *Local) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error) {
|
||||
return c.env.BlockByHash(ctx, hash)
|
||||
return c.env.BlockByHash(ctx, &coretypes.RequestBlockByHash{Hash: hash})
|
||||
}
|
||||
|
||||
func (c *Local) BlockResults(ctx context.Context, height *int64) (*coretypes.ResultBlockResults, error) {
|
||||
return c.env.BlockResults(ctx, height)
|
||||
return c.env.BlockResults(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)})
|
||||
}
|
||||
|
||||
func (c *Local) Header(ctx context.Context, height *int64) (*coretypes.ResultHeader, error) {
|
||||
return c.env.Header(ctx, height)
|
||||
return c.env.Header(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)})
|
||||
}
|
||||
|
||||
func (c *Local) HeaderByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultHeader, error) {
|
||||
return c.env.HeaderByHash(ctx, hash)
|
||||
return c.env.HeaderByHash(ctx, &coretypes.RequestBlockByHash{Hash: hash})
|
||||
}
|
||||
|
||||
func (c *Local) Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) {
|
||||
return c.env.Commit(ctx, height)
|
||||
return c.env.Commit(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)})
|
||||
}
|
||||
|
||||
func (c *Local) Validators(ctx context.Context, height *int64, page, perPage *int) (*coretypes.ResultValidators, error) {
|
||||
return c.env.Validators(ctx, height, page, perPage)
|
||||
return c.env.Validators(ctx, &coretypes.RequestValidators{
|
||||
Height: (*coretypes.Int64)(height),
|
||||
Page: coretypes.Int64Ptr(page),
|
||||
PerPage: coretypes.Int64Ptr(perPage),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Local) Tx(ctx context.Context, hash bytes.HexBytes, prove bool) (*coretypes.ResultTx, error) {
|
||||
return c.env.Tx(ctx, hash, prove)
|
||||
return c.env.Tx(ctx, &coretypes.RequestTx{Hash: hash, Prove: prove})
|
||||
}
|
||||
|
||||
func (c *Local) TxSearch(ctx context.Context, queryString string, prove bool, page, perPage *int, orderBy string) (*coretypes.ResultTxSearch, error) {
|
||||
return c.env.TxSearch(ctx, queryString, prove, page, perPage, orderBy)
|
||||
return c.env.TxSearch(ctx, &coretypes.RequestTxSearch{
|
||||
Query: queryString,
|
||||
Prove: prove,
|
||||
Page: coretypes.Int64Ptr(page),
|
||||
PerPage: coretypes.Int64Ptr(perPage),
|
||||
OrderBy: orderBy,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Local) BlockSearch(ctx context.Context, queryString string, page, perPage *int, orderBy string) (*coretypes.ResultBlockSearch, error) {
|
||||
return c.env.BlockSearch(ctx, queryString, page, perPage, orderBy)
|
||||
return c.env.BlockSearch(ctx, &coretypes.RequestBlockSearch{
|
||||
Query: queryString,
|
||||
Page: coretypes.Int64Ptr(page),
|
||||
PerPage: coretypes.Int64Ptr(perPage),
|
||||
OrderBy: orderBy,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Local) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
return c.env.BroadcastEvidence(ctx, coretypes.Evidence{Value: ev})
|
||||
return c.env.BroadcastEvidence(ctx, &coretypes.RequestBroadcastEvidence{Evidence: ev})
|
||||
}
|
||||
|
||||
func (c *Local) Subscribe(ctx context.Context, subscriber, queryString string, capacity ...int) (<-chan coretypes.ResultEvent, error) {
|
||||
|
||||
@@ -91,23 +91,25 @@ func (c Client) ABCIQueryWithOptions(
|
||||
path string,
|
||||
data bytes.HexBytes,
|
||||
opts client.ABCIQueryOptions) (*coretypes.ResultABCIQuery, error) {
|
||||
return c.env.ABCIQuery(ctx, path, data, opts.Height, opts.Prove)
|
||||
return c.env.ABCIQuery(ctx, &coretypes.RequestABCIQuery{
|
||||
Path: path, Data: data, Height: coretypes.Int64(opts.Height), Prove: opts.Prove,
|
||||
})
|
||||
}
|
||||
|
||||
func (c Client) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTxCommit, error) {
|
||||
return c.env.BroadcastTxCommit(ctx, tx)
|
||||
return c.env.BroadcastTxCommit(ctx, &coretypes.RequestBroadcastTx{Tx: tx})
|
||||
}
|
||||
|
||||
func (c Client) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
|
||||
return c.env.BroadcastTxAsync(ctx, tx)
|
||||
return c.env.BroadcastTxAsync(ctx, &coretypes.RequestBroadcastTx{Tx: tx})
|
||||
}
|
||||
|
||||
func (c Client) BroadcastTxSync(ctx context.Context, tx types.Tx) (*coretypes.ResultBroadcastTx, error) {
|
||||
return c.env.BroadcastTxSync(ctx, tx)
|
||||
return c.env.BroadcastTxSync(ctx, &coretypes.RequestBroadcastTx{Tx: tx})
|
||||
}
|
||||
|
||||
func (c Client) CheckTx(ctx context.Context, tx types.Tx) (*coretypes.ResultCheckTx, error) {
|
||||
return c.env.CheckTx(ctx, tx)
|
||||
return c.env.CheckTx(ctx, &coretypes.RequestCheckTx{Tx: tx})
|
||||
}
|
||||
|
||||
func (c Client) NetInfo(ctx context.Context) (*coretypes.ResultNetInfo, error) {
|
||||
@@ -123,7 +125,7 @@ func (c Client) DumpConsensusState(ctx context.Context) (*coretypes.ResultDumpCo
|
||||
}
|
||||
|
||||
func (c Client) ConsensusParams(ctx context.Context, height *int64) (*coretypes.ResultConsensusParams, error) {
|
||||
return c.env.ConsensusParams(ctx, height)
|
||||
return c.env.ConsensusParams(ctx, &coretypes.RequestConsensusParams{Height: (*coretypes.Int64)(height)})
|
||||
}
|
||||
|
||||
func (c Client) Health(ctx context.Context) (*coretypes.ResultHealth, error) {
|
||||
@@ -131,7 +133,10 @@ func (c Client) Health(ctx context.Context) (*coretypes.ResultHealth, error) {
|
||||
}
|
||||
|
||||
func (c Client) BlockchainInfo(ctx context.Context, minHeight, maxHeight int64) (*coretypes.ResultBlockchainInfo, error) {
|
||||
return c.env.BlockchainInfo(ctx, minHeight, maxHeight)
|
||||
return c.env.BlockchainInfo(ctx, &coretypes.RequestBlockchainInfo{
|
||||
MinHeight: coretypes.Int64(minHeight),
|
||||
MaxHeight: coretypes.Int64(maxHeight),
|
||||
})
|
||||
}
|
||||
|
||||
func (c Client) Genesis(ctx context.Context) (*coretypes.ResultGenesis, error) {
|
||||
@@ -139,21 +144,25 @@ func (c Client) Genesis(ctx context.Context) (*coretypes.ResultGenesis, error) {
|
||||
}
|
||||
|
||||
func (c Client) Block(ctx context.Context, height *int64) (*coretypes.ResultBlock, error) {
|
||||
return c.env.Block(ctx, height)
|
||||
return c.env.Block(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)})
|
||||
}
|
||||
|
||||
func (c Client) BlockByHash(ctx context.Context, hash bytes.HexBytes) (*coretypes.ResultBlock, error) {
|
||||
return c.env.BlockByHash(ctx, hash)
|
||||
return c.env.BlockByHash(ctx, &coretypes.RequestBlockByHash{Hash: hash})
|
||||
}
|
||||
|
||||
func (c Client) Commit(ctx context.Context, height *int64) (*coretypes.ResultCommit, error) {
|
||||
return c.env.Commit(ctx, height)
|
||||
return c.env.Commit(ctx, &coretypes.RequestBlockInfo{Height: (*coretypes.Int64)(height)})
|
||||
}
|
||||
|
||||
func (c Client) Validators(ctx context.Context, height *int64, page, perPage *int) (*coretypes.ResultValidators, error) {
|
||||
return c.env.Validators(ctx, height, page, perPage)
|
||||
return c.env.Validators(ctx, &coretypes.RequestValidators{
|
||||
Height: (*coretypes.Int64)(height),
|
||||
Page: coretypes.Int64Ptr(page),
|
||||
PerPage: coretypes.Int64Ptr(perPage),
|
||||
})
|
||||
}
|
||||
|
||||
func (c Client) BroadcastEvidence(ctx context.Context, ev types.Evidence) (*coretypes.ResultBroadcastEvidence, error) {
|
||||
return c.env.BroadcastEvidence(ctx, coretypes.Evidence{Value: ev})
|
||||
return c.env.BroadcastEvidence(ctx, &coretypes.RequestBroadcastEvidence{Evidence: ev})
|
||||
}
|
||||
|
||||
188
rpc/coretypes/requests.go
Normal file
188
rpc/coretypes/requests.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package coretypes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/internal/jsontypes"
|
||||
"github.com/tendermint/tendermint/libs/bytes"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
type RequestSubscribe struct {
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
type RequestUnsubscribe struct {
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
type RequestBlockchainInfo struct {
|
||||
MinHeight Int64 `json:"minHeight"`
|
||||
MaxHeight Int64 `json:"maxHeight"`
|
||||
}
|
||||
|
||||
type RequestGenesisChunked struct {
|
||||
Chunk Int64 `json:"chunk"`
|
||||
}
|
||||
|
||||
type RequestBlockInfo struct {
|
||||
Height *Int64 `json:"height"`
|
||||
}
|
||||
|
||||
type RequestBlockByHash struct {
|
||||
Hash bytes.HexBytes `json:"hash"`
|
||||
}
|
||||
|
||||
type RequestCheckTx struct {
|
||||
Tx types.Tx `json:"tx"`
|
||||
}
|
||||
|
||||
type RequestRemoveTx struct {
|
||||
TxKey types.TxKey `json:"txkey"`
|
||||
}
|
||||
|
||||
type RequestTx struct {
|
||||
Hash bytes.HexBytes `json:"hash"`
|
||||
Prove bool `json:"prove"`
|
||||
}
|
||||
|
||||
type RequestTxSearch struct {
|
||||
Query string `json:"query"`
|
||||
Prove bool `json:"prove"`
|
||||
Page *Int64 `json:"page"`
|
||||
PerPage *Int64 `json:"per_page"`
|
||||
OrderBy string `json:"order_by"`
|
||||
}
|
||||
|
||||
type RequestBlockSearch struct {
|
||||
Query string `json:"query"`
|
||||
Page *Int64 `json:"page"`
|
||||
PerPage *Int64 `json:"per_page"`
|
||||
OrderBy string `json:"order_by"`
|
||||
}
|
||||
|
||||
type RequestValidators struct {
|
||||
Height *Int64 `json:"height"`
|
||||
Page *Int64 `json:"page"`
|
||||
PerPage *Int64 `json:"per_page"`
|
||||
}
|
||||
|
||||
type RequestConsensusParams struct {
|
||||
Height *Int64 `json:"height"`
|
||||
}
|
||||
|
||||
type RequestUnconfirmedTxs struct {
|
||||
Page *Int64 `json:"page"`
|
||||
PerPage *Int64 `json:"per_page"`
|
||||
}
|
||||
|
||||
type RequestBroadcastTx struct {
|
||||
Tx types.Tx `json:"tx"`
|
||||
}
|
||||
|
||||
type RequestABCIQuery struct {
|
||||
Path string `json:"path"`
|
||||
Data bytes.HexBytes `json:"data"`
|
||||
Height Int64 `json:"height"`
|
||||
Prove bool `json:"prove"`
|
||||
}
|
||||
|
||||
type RequestBroadcastEvidence struct {
|
||||
Evidence types.Evidence
|
||||
}
|
||||
|
||||
type requestBroadcastEvidenceJSON struct {
|
||||
Evidence json.RawMessage `json:"evidence"`
|
||||
}
|
||||
|
||||
func (r RequestBroadcastEvidence) MarshalJSON() ([]byte, error) {
|
||||
ev, err := jsontypes.Marshal(r.Evidence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(requestBroadcastEvidenceJSON{
|
||||
Evidence: ev,
|
||||
})
|
||||
}
|
||||
|
||||
func (r *RequestBroadcastEvidence) UnmarshalJSON(data []byte) error {
|
||||
var val requestBroadcastEvidenceJSON
|
||||
if err := json.Unmarshal(data, &val); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := jsontypes.Unmarshal(val.Evidence, &r.Evidence); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestEvents is the argument for the "/events" RPC endpoint.
|
||||
type RequestEvents struct {
|
||||
// Optional filter spec. If nil or empty, all items are eligible.
|
||||
Filter *EventFilter `json:"filter"`
|
||||
|
||||
// The maximum number of eligible items to return.
|
||||
// If zero or negative, the server will report a default number.
|
||||
MaxItems int `json:"maxItems"`
|
||||
|
||||
// Return only items after this cursor. If empty, the limit is just
|
||||
// before the the beginning of the event log.
|
||||
After string `json:"after"`
|
||||
|
||||
// Return only items before this cursor. If empty, the limit is just
|
||||
// after the head of the event log.
|
||||
Before string `json:"before"`
|
||||
|
||||
// Wait for up to this long for events to be available.
|
||||
WaitTime time.Duration `json:"waitTime"`
|
||||
}
|
||||
|
||||
// An EventFilter specifies which events are selected by an /events request.
|
||||
type EventFilter struct {
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
// Int64 is a wrapper for int64 that encodes to JSON as a string and can be
|
||||
// decoded from either a string or a number value.
|
||||
type Int64 int64
|
||||
|
||||
func (z *Int64) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
if len(data) != 0 && data[0] == '"' {
|
||||
if err := json.Unmarshal(data, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
s = string(data)
|
||||
}
|
||||
v, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*z = Int64(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (z Int64) MarshalJSON() ([]byte, error) {
|
||||
return []byte(strconv.FormatInt(int64(z), 10)), nil
|
||||
}
|
||||
|
||||
// IntPtr returns a pointer to the value of *z as an int, or nil if z == nil.
|
||||
func (z *Int64) IntPtr() *int {
|
||||
if z == nil {
|
||||
return nil
|
||||
}
|
||||
v := int(*z)
|
||||
return &v
|
||||
}
|
||||
|
||||
// Int64Ptr returns an *Int64 that points to the same value as v, or nil.
|
||||
func Int64Ptr(v *int) *Int64 {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
z := Int64(*v)
|
||||
return &z
|
||||
}
|
||||
@@ -357,32 +357,6 @@ type Evidence struct {
|
||||
func (e Evidence) MarshalJSON() ([]byte, error) { return jsontypes.Marshal(e.Value) }
|
||||
func (e *Evidence) UnmarshalJSON(data []byte) error { return jsontypes.Unmarshal(data, &e.Value) }
|
||||
|
||||
// RequestEvents is the argument for the "/events" RPC endpoint.
|
||||
type RequestEvents struct {
|
||||
// Optional filter spec. If nil or empty, all items are eligible.
|
||||
Filter *EventFilter `json:"filter"`
|
||||
|
||||
// The maximum number of eligible items to return.
|
||||
// If zero or negative, the server will report a default number.
|
||||
MaxItems int `json:"maxItems"`
|
||||
|
||||
// Return only items after this cursor. If empty, the limit is just
|
||||
// before the the beginning of the event log.
|
||||
After string `json:"after"`
|
||||
|
||||
// Return only items before this cursor. If empty, the limit is just
|
||||
// after the head of the event log.
|
||||
Before string `json:"before"`
|
||||
|
||||
// Wait for up to this long for events to be available.
|
||||
WaitTime time.Duration `json:"waitTime"`
|
||||
}
|
||||
|
||||
// An EventFilter specifies which events are selected by an /events request.
|
||||
type EventFilter struct {
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
// ResultEvents is the response from the "/events" RPC endpoint.
|
||||
type ResultEvents struct {
|
||||
// The items matching the request parameters, from newest
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
// Define some routes
|
||||
//
|
||||
// var Routes = map[string]*rpcserver.RPCFunc{
|
||||
// "status": rpcserver.NewRPCFunc(Status, "arg"),
|
||||
// "status": rpcserver.NewRPCFunc(Status),
|
||||
// }
|
||||
//
|
||||
// An rpc function:
|
||||
|
||||
@@ -34,49 +34,65 @@ const (
|
||||
testVal = "acbd"
|
||||
)
|
||||
|
||||
type RequestEcho struct {
|
||||
Value string `json:"arg"`
|
||||
}
|
||||
|
||||
type ResultEcho struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type RequestEchoInt struct {
|
||||
Value int `json:"arg"`
|
||||
}
|
||||
|
||||
type ResultEchoInt struct {
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
type RequestEchoBytes struct {
|
||||
Value []byte `json:"arg"`
|
||||
}
|
||||
|
||||
type ResultEchoBytes struct {
|
||||
Value []byte `json:"value"`
|
||||
}
|
||||
|
||||
type RequestEchoDataBytes struct {
|
||||
Value tmbytes.HexBytes `json:"arg"`
|
||||
}
|
||||
|
||||
type ResultEchoDataBytes struct {
|
||||
Value tmbytes.HexBytes `json:"value"`
|
||||
}
|
||||
|
||||
// Define some routes
|
||||
var Routes = map[string]*server.RPCFunc{
|
||||
"echo": server.NewRPCFunc(EchoResult, "arg"),
|
||||
"echo_ws": server.NewWSRPCFunc(EchoWSResult, "arg"),
|
||||
"echo_bytes": server.NewRPCFunc(EchoBytesResult, "arg"),
|
||||
"echo_data_bytes": server.NewRPCFunc(EchoDataBytesResult, "arg"),
|
||||
"echo_int": server.NewRPCFunc(EchoIntResult, "arg"),
|
||||
"echo": server.NewRPCFunc(EchoResult),
|
||||
"echo_ws": server.NewWSRPCFunc(EchoWSResult),
|
||||
"echo_bytes": server.NewRPCFunc(EchoBytesResult),
|
||||
"echo_data_bytes": server.NewRPCFunc(EchoDataBytesResult),
|
||||
"echo_int": server.NewRPCFunc(EchoIntResult),
|
||||
}
|
||||
|
||||
func EchoResult(ctx context.Context, v string) (*ResultEcho, error) {
|
||||
return &ResultEcho{v}, nil
|
||||
func EchoResult(ctx context.Context, v *RequestEcho) (*ResultEcho, error) {
|
||||
return &ResultEcho{v.Value}, nil
|
||||
}
|
||||
|
||||
func EchoWSResult(ctx context.Context, v string) (*ResultEcho, error) {
|
||||
return &ResultEcho{v}, nil
|
||||
func EchoWSResult(ctx context.Context, v *RequestEcho) (*ResultEcho, error) {
|
||||
return &ResultEcho{v.Value}, nil
|
||||
}
|
||||
|
||||
func EchoIntResult(ctx context.Context, v int) (*ResultEchoInt, error) {
|
||||
return &ResultEchoInt{v}, nil
|
||||
func EchoIntResult(ctx context.Context, v *RequestEchoInt) (*ResultEchoInt, error) {
|
||||
return &ResultEchoInt{v.Value}, nil
|
||||
}
|
||||
|
||||
func EchoBytesResult(ctx context.Context, v []byte) (*ResultEchoBytes, error) {
|
||||
return &ResultEchoBytes{v}, nil
|
||||
func EchoBytesResult(ctx context.Context, v *RequestEchoBytes) (*ResultEchoBytes, error) {
|
||||
return &ResultEchoBytes{v.Value}, nil
|
||||
}
|
||||
|
||||
func EchoDataBytesResult(ctx context.Context, v tmbytes.HexBytes) (*ResultEchoDataBytes, error) {
|
||||
return &ResultEchoDataBytes{v}, nil
|
||||
func EchoDataBytesResult(ctx context.Context, v *RequestEchoDataBytes) (*ResultEchoDataBytes, error) {
|
||||
return &ResultEchoDataBytes{v.Value}, nil
|
||||
}
|
||||
|
||||
// launch unix and tcp servers
|
||||
|
||||
@@ -2,15 +2,11 @@ package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
@@ -70,19 +66,11 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.Han
|
||||
RPCRequest: &req,
|
||||
HTTPRequest: hreq,
|
||||
})
|
||||
args, err := parseParams(ctx, rpcFunc, req.Params)
|
||||
result, err := rpcFunc.Call(ctx, req.Params)
|
||||
if err != nil {
|
||||
responses = append(responses,
|
||||
req.MakeErrorf(rpctypes.CodeInvalidParams, "converting JSON parameters: %v", err))
|
||||
continue
|
||||
}
|
||||
|
||||
returns := rpcFunc.f.Call(args)
|
||||
result, err := unreflectResult(returns)
|
||||
if err == nil {
|
||||
responses = append(responses, req.MakeResponse(result))
|
||||
} else {
|
||||
responses = append(responses, req.MakeError(err))
|
||||
} else {
|
||||
responses = append(responses, req.MakeResponse(result))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,103 +112,6 @@ func parseRequests(data []byte) ([]rpctypes.RPCRequest, error) {
|
||||
return reqs, nil
|
||||
}
|
||||
|
||||
// parseParams parses the JSON parameters of rpcReq into the arguments of fn,
|
||||
// returning the corresponding argument values or an error.
|
||||
func parseParams(ctx context.Context, fn *RPCFunc, paramData []byte) ([]reflect.Value, error) {
|
||||
params, err := parseJSONParams(fn, paramData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args := make([]reflect.Value, 1+len(params))
|
||||
args[0] = reflect.ValueOf(ctx)
|
||||
for i, param := range params {
|
||||
ptype := fn.args[i+1]
|
||||
if len(param) == 0 {
|
||||
args[i+1] = reflect.Zero(ptype)
|
||||
continue
|
||||
}
|
||||
|
||||
var pval reflect.Value
|
||||
isPtr := ptype.Kind() == reflect.Ptr
|
||||
if isPtr {
|
||||
pval = reflect.New(ptype.Elem())
|
||||
} else {
|
||||
pval = reflect.New(ptype)
|
||||
}
|
||||
baseType := pval.Type().Elem()
|
||||
|
||||
if isIntType(baseType) && isStringValue(param) {
|
||||
var z int64String
|
||||
if err := json.Unmarshal(param, &z); err != nil {
|
||||
return nil, fmt.Errorf("decoding string %q: %w", fn.argNames[i], err)
|
||||
}
|
||||
pval.Elem().Set(reflect.ValueOf(z).Convert(baseType))
|
||||
} else if err := json.Unmarshal(param, pval.Interface()); err != nil {
|
||||
return nil, fmt.Errorf("decoding %q: %w", fn.argNames[i], err)
|
||||
}
|
||||
|
||||
if isPtr {
|
||||
args[i+1] = pval
|
||||
} else {
|
||||
args[i+1] = pval.Elem()
|
||||
}
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// parseJSONParams parses data and returns a slice of JSON values matching the
|
||||
// positional parameters of fn. It reports an error if data is not "null" and
|
||||
// does not encode an object or an array, or if the number of array parameters
|
||||
// does not match the argument list of fn (excluding the context).
|
||||
func parseJSONParams(fn *RPCFunc, data []byte) ([]json.RawMessage, error) {
|
||||
base := bytes.TrimSpace(data)
|
||||
if bytes.HasPrefix(base, []byte("{")) {
|
||||
var m map[string]json.RawMessage
|
||||
if err := json.Unmarshal(base, &m); err != nil {
|
||||
return nil, fmt.Errorf("decoding parameter object: %w", err)
|
||||
}
|
||||
out := make([]json.RawMessage, len(fn.argNames))
|
||||
for i, name := range fn.argNames {
|
||||
if p, ok := m[name]; ok {
|
||||
out[i] = p
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
|
||||
} else if bytes.HasPrefix(base, []byte("[")) {
|
||||
var m []json.RawMessage
|
||||
if err := json.Unmarshal(base, &m); err != nil {
|
||||
return nil, fmt.Errorf("decoding parameter array: %w", err)
|
||||
}
|
||||
if len(m) != len(fn.argNames) {
|
||||
return nil, fmt.Errorf("got %d parameters, want %d", len(m), len(fn.argNames))
|
||||
}
|
||||
return m, nil
|
||||
|
||||
} else if bytes.Equal(base, []byte("null")) {
|
||||
return make([]json.RawMessage, len(fn.argNames)), nil
|
||||
}
|
||||
|
||||
return nil, errors.New("parameters must be an object or an array")
|
||||
}
|
||||
|
||||
// isStringValue reports whether data is a JSON string value.
|
||||
func isStringValue(data json.RawMessage) bool {
|
||||
return len(data) != 0 && data[0] == '"'
|
||||
}
|
||||
|
||||
type int64String int64
|
||||
|
||||
func (z *int64String) UnmarshalText(data []byte) error {
|
||||
v, err := strconv.ParseInt(string(data), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*z = int64String(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// writes a list of available rpc endpoints as an html page
|
||||
func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[string]*RPCFunc) {
|
||||
hasArgs := make(map[string]string)
|
||||
|
||||
@@ -17,9 +17,16 @@ import (
|
||||
)
|
||||
|
||||
func testMux() *http.ServeMux {
|
||||
type testArgs struct {
|
||||
S string `json:"s"`
|
||||
I json.Number `json:"i"`
|
||||
}
|
||||
type blockArgs struct {
|
||||
H json.Number `json:"h"`
|
||||
}
|
||||
funcMap := map[string]*RPCFunc{
|
||||
"c": NewRPCFunc(func(ctx context.Context, s string, i int) (string, error) { return "foo", nil }, "s", "i"),
|
||||
"block": NewRPCFunc(func(ctx context.Context, h int) (string, error) { return "block", nil }, "height"),
|
||||
"c": NewRPCFunc(func(ctx context.Context, arg *testArgs) (string, error) { return "foo", nil }),
|
||||
"block": NewRPCFunc(func(ctx context.Context, arg *blockArgs) (string, error) { return "block", nil }),
|
||||
}
|
||||
mux := http.NewServeMux()
|
||||
logger := log.NewNopLogger()
|
||||
@@ -46,7 +53,7 @@ func TestRPCParams(t *testing.T) {
|
||||
// id not captured in JSON parsing failures
|
||||
{`{"method": "c", "id": "0", "params": a}`, "invalid character", ""},
|
||||
{`{"method": "c", "id": "0", "params": ["a"]}`, "got 1", `"0"`},
|
||||
{`{"method": "c", "id": "0", "params": ["a", "b"]}`, "invalid syntax", `"0"`},
|
||||
{`{"method": "c", "id": "0", "params": ["a", "b"]}`, "invalid number", `"0"`},
|
||||
{`{"method": "c", "id": "0", "params": [1, 1]}`, "of type string", `"0"`},
|
||||
|
||||
// no ID - notification
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -25,7 +23,7 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
|
||||
ctx := rpctypes.WithCallInfo(req.Context(), &rpctypes.CallInfo{
|
||||
HTTPRequest: req,
|
||||
})
|
||||
args, err := parseURLParams(ctx, rpcFunc, req)
|
||||
args, err := parseURLParams(rpcFunc.argNames, req)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
@@ -33,10 +31,7 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
|
||||
return
|
||||
}
|
||||
jreq := rpctypes.NewRequest(uriReqID)
|
||||
outs := rpcFunc.f.Call(args)
|
||||
|
||||
logger.Debug("HTTPRestRPC", "method", req.URL.Path, "args", args, "returns", outs)
|
||||
result, err := unreflectResult(outs)
|
||||
result, err := rpcFunc.Call(ctx, args)
|
||||
if err == nil {
|
||||
writeHTTPResponse(w, logger, jreq.MakeResponse(result))
|
||||
} else {
|
||||
@@ -45,7 +40,7 @@ func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWrit
|
||||
}
|
||||
}
|
||||
|
||||
func parseURLParams(ctx context.Context, rf *RPCFunc, req *http.Request) ([]reflect.Value, error) {
|
||||
func parseURLParams(argNames []string, req *http.Request) ([]byte, error) {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, fmt.Errorf("invalid HTTP request: %w", err)
|
||||
}
|
||||
@@ -56,112 +51,35 @@ func parseURLParams(ctx context.Context, rf *RPCFunc, req *http.Request) ([]refl
|
||||
return "", false
|
||||
}
|
||||
|
||||
vals := make([]reflect.Value, len(rf.argNames)+1)
|
||||
vals[0] = reflect.ValueOf(ctx)
|
||||
for i, name := range rf.argNames {
|
||||
atype := rf.args[i+1]
|
||||
|
||||
text, ok := getArg(name)
|
||||
params := make(map[string]interface{})
|
||||
for _, name := range argNames {
|
||||
v, ok := getArg(name)
|
||||
if !ok {
|
||||
vals[i+1] = reflect.Zero(atype)
|
||||
continue
|
||||
}
|
||||
|
||||
val, err := parseArgValue(atype, text)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decoding parameter %q: %w", name, err)
|
||||
if z, err := decodeInteger(v); err == nil {
|
||||
params[name] = z
|
||||
} else if b, err := strconv.ParseBool(v); err == nil {
|
||||
params[name] = b
|
||||
} else if lc := strings.ToLower(v); strings.HasPrefix(lc, "0x") {
|
||||
dec, err := hex.DecodeString(lc[2:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid hex string: %w", err)
|
||||
} else if len(dec) == 0 {
|
||||
return nil, errors.New("invalid empty hex string")
|
||||
}
|
||||
params[name] = dec
|
||||
} else if isQuotedString(v) {
|
||||
var dec string
|
||||
if err := json.Unmarshal([]byte(v), &dec); err != nil {
|
||||
return nil, fmt.Errorf("invalid quoted string: %w", err)
|
||||
}
|
||||
params[name] = dec
|
||||
} else {
|
||||
params[name] = v
|
||||
}
|
||||
vals[i+1] = val
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
func parseArgValue(atype reflect.Type, text string) (reflect.Value, error) {
|
||||
// Regardless whether the argument is a pointer type, allocate a pointer so
|
||||
// we can set the computed value.
|
||||
var out reflect.Value
|
||||
isPtr := atype.Kind() == reflect.Ptr
|
||||
if isPtr {
|
||||
out = reflect.New(atype.Elem())
|
||||
} else {
|
||||
out = reflect.New(atype)
|
||||
}
|
||||
|
||||
baseType := out.Type().Elem()
|
||||
if isIntType(baseType) {
|
||||
// Integral type: Require a base-10 digit string. For compatibility with
|
||||
// existing use allow quotation marks.
|
||||
v, err := decodeInteger(text)
|
||||
if err != nil {
|
||||
return reflect.Value{}, fmt.Errorf("invalid integer: %w", err)
|
||||
}
|
||||
out.Elem().Set(reflect.ValueOf(v).Convert(baseType))
|
||||
|
||||
} else if isStringOrBytes(baseType) {
|
||||
// String or byte slice: Check for quotes, hex encoding.
|
||||
dec, err := decodeString(text)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
out.Elem().Set(reflect.ValueOf(dec).Convert(baseType))
|
||||
|
||||
} else if baseType.Kind() == reflect.Bool {
|
||||
b, err := strconv.ParseBool(text)
|
||||
if err != nil {
|
||||
return reflect.Value{}, fmt.Errorf("invalid boolean: %w", err)
|
||||
}
|
||||
out.Elem().Set(reflect.ValueOf(b))
|
||||
|
||||
} else if out.Type().Implements(textUnmarshalerType) {
|
||||
s, err := decodeString(text)
|
||||
if err != nil {
|
||||
return reflect.Value{}, err
|
||||
}
|
||||
v := reflect.New(baseType)
|
||||
dec := v.Interface().(encoding.TextUnmarshaler)
|
||||
if err := dec.UnmarshalText(s); err != nil {
|
||||
return reflect.Value{}, fmt.Errorf("invalid text: %w", err)
|
||||
}
|
||||
out.Elem().Set(v.Elem())
|
||||
|
||||
} else {
|
||||
// We don't know how to represent other types.
|
||||
return reflect.Value{}, fmt.Errorf("unsupported argument type %v", baseType)
|
||||
}
|
||||
|
||||
// If the argument wants a pointer, return the value as-is, otherwise
|
||||
// indirect the pointer back off.
|
||||
if isPtr {
|
||||
return out, nil
|
||||
}
|
||||
return out.Elem(), nil
|
||||
}
|
||||
|
||||
var (
|
||||
uint64Type = reflect.TypeOf(uint64(0))
|
||||
textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
|
||||
)
|
||||
|
||||
// isIntType reports whether atype is an integer-shaped type.
|
||||
func isIntType(atype reflect.Type) bool {
|
||||
switch atype.Kind() {
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return false
|
||||
default:
|
||||
return atype.ConvertibleTo(uint64Type)
|
||||
}
|
||||
}
|
||||
|
||||
// isStringOrBytes reports whether atype is a string or []byte.
|
||||
func isStringOrBytes(atype reflect.Type) bool {
|
||||
switch atype.Kind() {
|
||||
case reflect.String:
|
||||
return true
|
||||
case reflect.Slice:
|
||||
return atype.Elem().Kind() == reflect.Uint8
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return json.Marshal(params)
|
||||
}
|
||||
|
||||
// isQuotedString reports whether s is enclosed in double quotes.
|
||||
@@ -177,19 +95,3 @@ func decodeInteger(s string) (int64, error) {
|
||||
}
|
||||
return strconv.ParseInt(s, 10, 64)
|
||||
}
|
||||
|
||||
// decodeString decodes s into a byte slice. If s has an 0x prefix, it is
|
||||
// treated as a hex-encoded string. If it is "double quoted" it is treated as a
|
||||
// JSON string value. Otherwise, s is converted to bytes directly.
|
||||
func decodeString(s string) ([]byte, error) {
|
||||
if lc := strings.ToLower(s); strings.HasPrefix(lc, "0x") {
|
||||
return hex.DecodeString(lc[2:])
|
||||
} else if isQuotedString(s) {
|
||||
var dec string
|
||||
if err := json.Unmarshal([]byte(s), &dec); err != nil {
|
||||
return nil, fmt.Errorf("invalid quoted string: %w", err)
|
||||
}
|
||||
return []byte(dec), nil
|
||||
}
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
@@ -134,8 +133,12 @@ func TestParseJSONArray(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseJSONRPC(t *testing.T) {
|
||||
demo := func(ctx context.Context, height int, name string) error { return nil }
|
||||
call := NewRPCFunc(demo, "height", "name")
|
||||
type demoArgs struct {
|
||||
Height int `json:"height,string"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
demo := func(ctx context.Context, _ *demoArgs) error { return nil }
|
||||
rfunc := NewRPCFunc(demo)
|
||||
|
||||
cases := []struct {
|
||||
raw string
|
||||
@@ -156,14 +159,16 @@ func TestParseJSONRPC(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
for idx, tc := range cases {
|
||||
i := strconv.Itoa(idx)
|
||||
vals, err := parseParams(ctx, call, []byte(tc.raw))
|
||||
vals, err := rfunc.parseParams(ctx, []byte(tc.raw))
|
||||
if tc.fail {
|
||||
assert.Error(t, err, i)
|
||||
} else {
|
||||
assert.NoError(t, err, "%s: %+v", i, err)
|
||||
if assert.Equal(t, 3, len(vals), i) { // ctx, height, name
|
||||
assert.Equal(t, tc.height, vals[1].Int(), i)
|
||||
assert.Equal(t, tc.name, vals[2].String(), i)
|
||||
assert.Equal(t, 2, len(vals), i)
|
||||
p, ok := vals[1].Interface().(*demoArgs)
|
||||
if assert.True(t, ok) {
|
||||
assert.Equal(t, tc.height, int64(p.Height), i)
|
||||
assert.Equal(t, tc.name, p.Name, i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,50 +176,147 @@ func TestParseJSONRPC(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParseURI(t *testing.T) {
|
||||
demo := func(ctx context.Context, height int, name string) error { return nil }
|
||||
call := NewRPCFunc(demo, "height", "name")
|
||||
// URI parameter parsing happens in two phases:
|
||||
//
|
||||
// Phase 1 swizzles the query parameters into JSON. The result of this
|
||||
// phase must be valid JSON, but may fail the second stage.
|
||||
//
|
||||
// Phase 2 decodes the JSON to obtain the actual arguments. A failure at
|
||||
// this stage means the JSON is not compatible with the target.
|
||||
|
||||
cases := []struct {
|
||||
raw []string
|
||||
height int64
|
||||
name string
|
||||
fail bool
|
||||
}{
|
||||
// can parse numbers unquoted and strings quoted
|
||||
{[]string{"7", `"flew"`}, 7, "flew", false},
|
||||
{[]string{"22", `"john"`}, 22, "john", false},
|
||||
{[]string{"-10", `"bob"`}, -10, "bob", false},
|
||||
// can parse numbers quoted, too
|
||||
{[]string{`"7"`, `"flew"`}, 7, "flew", false},
|
||||
{[]string{`"-10"`, `"bob"`}, -10, "bob", false},
|
||||
// can parse strings hex-escaped, in either case
|
||||
{[]string{`-9`, `0x626f62`}, -9, "bob", false},
|
||||
{[]string{`-9`, `0X646F7567`}, -9, "doug", false},
|
||||
// can parse strings unquoted (as per OpenAPI docs)
|
||||
{[]string{`0`, `hey you`}, 0, "hey you", false},
|
||||
// fail for invalid numbers, strings, hex
|
||||
{[]string{`"-xx"`, `bob`}, 0, "", true}, // bad number
|
||||
{[]string{`"95""`, `"bob`}, 0, "", true}, // bad string
|
||||
{[]string{`15`, `0xa`}, 0, "", true}, // bad hex
|
||||
}
|
||||
for idx, tc := range cases {
|
||||
i := strconv.Itoa(idx)
|
||||
// data := []byte(tc.raw)
|
||||
url := fmt.Sprintf(
|
||||
"test.com/method?height=%v&name=%v",
|
||||
tc.raw[0], tc.raw[1])
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
assert.NoError(t, err)
|
||||
vals, err := parseURLParams(context.Background(), call, req)
|
||||
if tc.fail {
|
||||
assert.Error(t, err, i)
|
||||
} else {
|
||||
assert.NoError(t, err, "%s: %+v", i, err)
|
||||
if assert.Equal(t, 3, len(vals), i) {
|
||||
assert.Equal(t, tc.height, vals[1].Int(), i)
|
||||
assert.Equal(t, tc.name, vals[2].String(), i)
|
||||
}
|
||||
t.Run("Swizzle", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
args []string
|
||||
want string
|
||||
fail bool
|
||||
}{
|
||||
{
|
||||
name: "quoted numbers and strings",
|
||||
url: `http://localhost?num="7"&str="flew"&neg="-10"`,
|
||||
args: []string{"neg", "num", "str", "other"},
|
||||
want: `{"neg":-10,"num":7,"str":"flew"}`,
|
||||
},
|
||||
{
|
||||
name: "unquoted numbers and strings",
|
||||
url: `http://localhost?num1=7&str1=cabbage&num2=-199&str2=hey+you`,
|
||||
args: []string{"num1", "num2", "str1", "str2", "other"},
|
||||
want: `{"num1":7,"num2":-199,"str1":"cabbage","str2":"hey you"}`,
|
||||
},
|
||||
{
|
||||
name: "byte strings in hex",
|
||||
url: `http://localhost?lower=0x626f62&upper=0X646F7567`,
|
||||
args: []string{"upper", "lower", "other"},
|
||||
want: `{"lower":"Ym9i","upper":"ZG91Zw=="}`,
|
||||
},
|
||||
{
|
||||
name: "invalid hex odd length",
|
||||
url: `http://localhost?bad=0xa`,
|
||||
args: []string{"bad", "superbad"},
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid hex empty",
|
||||
url: `http://localhost?bad=0x`,
|
||||
args: []string{"bad"},
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
name: "invalid quoted string",
|
||||
url: `http://localhost?bad="double""`,
|
||||
args: []string{"bad"},
|
||||
fail: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
hreq, err := http.NewRequest("GET", test.url, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewRequest for %q: %v", test.url, err)
|
||||
}
|
||||
|
||||
bits, err := parseURLParams(test.args, hreq)
|
||||
if err != nil && !test.fail {
|
||||
t.Fatalf("Parse %q: unexpected error: %v", test.url, err)
|
||||
} else if err == nil && test.fail {
|
||||
t.Fatalf("Parse %q: got %#q, wanted error", test.url, string(bits))
|
||||
}
|
||||
if got := string(bits); got != test.want {
|
||||
t.Errorf("Parse %q: got %#q, want %#q", test.url, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Decode", func(t *testing.T) {
|
||||
type argValue struct {
|
||||
Height json.Number `json:"height"`
|
||||
Name string `json:"name"`
|
||||
Flag bool `json:"flag"`
|
||||
}
|
||||
|
||||
}
|
||||
echo := NewRPCFunc(func(_ context.Context, arg *argValue) (*argValue, error) {
|
||||
return arg, nil
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
fail string
|
||||
want interface{}
|
||||
}{
|
||||
{
|
||||
name: "valid all args",
|
||||
url: `http://localhost?height=235&flag=true&name="bogart"`,
|
||||
want: &argValue{
|
||||
Height: "235",
|
||||
Flag: true,
|
||||
Name: "bogart",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid partial args",
|
||||
url: `http://localhost?height="1987"&name=free+willy`,
|
||||
want: &argValue{
|
||||
Height: "1987",
|
||||
Name: "free willy",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid quoted number",
|
||||
url: `http://localhost?height="-xx"`,
|
||||
fail: "invalid number literal",
|
||||
},
|
||||
{
|
||||
name: "invalid unquoted number",
|
||||
url: `http://localhost?height=25*q`,
|
||||
fail: "invalid number literal",
|
||||
},
|
||||
{
|
||||
name: "invalid boolean",
|
||||
url: `http://localhost?flag="garbage"`,
|
||||
fail: "flag of type bool",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
hreq, err := http.NewRequest("GET", test.url, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewRequest for %q: %v", test.url, err)
|
||||
}
|
||||
bits, err := parseURLParams(echo.argNames, hreq)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse %#q: unexpected error: %v", test.url, err)
|
||||
}
|
||||
rsp, err := echo.Call(context.Background(), bits)
|
||||
if test.want != nil {
|
||||
assert.Equal(t, test.want, rsp)
|
||||
}
|
||||
if test.fail != "" {
|
||||
assert.ErrorContains(t, err, test.fail)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
|
||||
)
|
||||
|
||||
// RegisterRPCFuncs adds a route to mux for each non-websocket function in the
|
||||
@@ -28,27 +32,97 @@ func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger lo
|
||||
|
||||
// RPCFunc contains the introspected type information for a function.
|
||||
type RPCFunc struct {
|
||||
f reflect.Value // underlying rpc function
|
||||
args []reflect.Type // type of each function arg
|
||||
returns []reflect.Type // type of each return arg
|
||||
argNames []string // name of each argument
|
||||
ws bool // websocket only
|
||||
f reflect.Value // underlying rpc function
|
||||
param reflect.Type // the parameter struct, or nil
|
||||
result reflect.Type // the non-error result type, or nil
|
||||
argNames []string // name of each argument (for display)
|
||||
ws bool // websocket only
|
||||
}
|
||||
|
||||
// Call parses the given JSON parameters and calls the function wrapped by rf
|
||||
// with the resulting argument value. It reports an error if parameter parsing
|
||||
// fails, otherwise it returns the result from the wrapped function.
|
||||
func (rf *RPCFunc) Call(ctx context.Context, params json.RawMessage) (interface{}, error) {
|
||||
args, err := rf.parseParams(ctx, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
returns := rf.f.Call(args)
|
||||
|
||||
// Case 1: There is no non-error result type.
|
||||
if rf.result == nil {
|
||||
if oerr := returns[0].Interface(); oerr != nil {
|
||||
return nil, oerr.(error)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Case 2: There is a non-error result.
|
||||
if oerr := returns[1].Interface(); oerr != nil {
|
||||
// In case of error, report the error and ignore the result.
|
||||
return nil, oerr.(error)
|
||||
}
|
||||
return returns[0].Interface(), nil
|
||||
}
|
||||
|
||||
// parseParams parses the parameters of a JSON-RPC request and returns the
|
||||
// corresponding argument values. On success, the first argument value will be
|
||||
// the value of ctx.
|
||||
func (rf *RPCFunc) parseParams(ctx context.Context, params json.RawMessage) ([]reflect.Value, error) {
|
||||
// If rf does not accept parameters, there is no decoding to do, but verify
|
||||
// that no parameters were passed.
|
||||
if rf.param == nil {
|
||||
if !isNullOrEmpty(params) {
|
||||
return nil, invalidParamsError("no parameters accepted for this method")
|
||||
}
|
||||
return []reflect.Value{reflect.ValueOf(ctx)}, nil
|
||||
}
|
||||
bits, err := rf.adjustParams(params)
|
||||
if err != nil {
|
||||
return nil, invalidParamsError(err.Error())
|
||||
}
|
||||
arg := reflect.New(rf.param)
|
||||
if err := json.Unmarshal(bits, arg.Interface()); err != nil {
|
||||
return nil, invalidParamsError(err.Error())
|
||||
}
|
||||
return []reflect.Value{reflect.ValueOf(ctx), arg}, nil
|
||||
}
|
||||
|
||||
// adjustParams checks whether data is encoded as a JSON array, and if so
|
||||
// adjusts the values to match the corresponding parameter names.
|
||||
func (rf *RPCFunc) adjustParams(data []byte) (json.RawMessage, error) {
|
||||
base := bytes.TrimSpace(data)
|
||||
if bytes.HasPrefix(base, []byte("[")) {
|
||||
var args []json.RawMessage
|
||||
if err := json.Unmarshal(base, &args); err != nil {
|
||||
return nil, err
|
||||
} else if len(args) != len(rf.argNames) {
|
||||
return nil, fmt.Errorf("got %d arguments, want %d", len(args), len(rf.argNames))
|
||||
}
|
||||
m := make(map[string]json.RawMessage)
|
||||
for i, arg := range args {
|
||||
m[rf.argNames[i]] = arg
|
||||
}
|
||||
return json.Marshal(m)
|
||||
} else if bytes.HasPrefix(base, []byte("{")) || bytes.Equal(base, []byte("null")) {
|
||||
return base, nil
|
||||
}
|
||||
return nil, errors.New("parameters must be an object or an array")
|
||||
|
||||
}
|
||||
|
||||
// NewRPCFunc constructs an RPCFunc for f, which must be a function whose type
|
||||
// signature matches one of these schemes:
|
||||
//
|
||||
// func(context.Context, T1, T2, ...) error
|
||||
// func(context.Context, T1, T2, ...) (R, error)
|
||||
// func(context.Context) error
|
||||
// func(context.Context) (R, error)
|
||||
// func(context.Context, *T) error
|
||||
// func(context.Context, *T) (R, error)
|
||||
//
|
||||
// for arbitrary types T_i and R. The number of argNames must exactly match the
|
||||
// number of non-context arguments to f. Otherwise, NewRPCFunc panics.
|
||||
//
|
||||
// The parameter names given are used to map JSON object keys to the
|
||||
// corresonding parameter of the function. The names do not need to match the
|
||||
// declared names, but must match what the client sends in a request.
|
||||
func NewRPCFunc(f interface{}, argNames ...string) *RPCFunc {
|
||||
rf, err := newRPCFunc(f, argNames)
|
||||
// for an arbitrary struct type T and type R. NewRPCFunc will panic if f does
|
||||
// not have one of these forms.
|
||||
func NewRPCFunc(f interface{}) *RPCFunc {
|
||||
rf, err := newRPCFunc(f)
|
||||
if err != nil {
|
||||
panic("invalid RPC function: " + err.Error())
|
||||
}
|
||||
@@ -57,8 +131,8 @@ func NewRPCFunc(f interface{}, argNames ...string) *RPCFunc {
|
||||
|
||||
// NewWSRPCFunc behaves as NewRPCFunc, but marks the resulting function for use
|
||||
// via websocket.
|
||||
func NewWSRPCFunc(f interface{}, argNames ...string) *RPCFunc {
|
||||
rf := NewRPCFunc(f, argNames...)
|
||||
func NewWSRPCFunc(f interface{}) *RPCFunc {
|
||||
rf := NewRPCFunc(f)
|
||||
rf.ws = true
|
||||
return rf
|
||||
}
|
||||
@@ -69,7 +143,7 @@ var (
|
||||
)
|
||||
|
||||
// newRPCFunc constructs an RPCFunc for f. See the comment at NewRPCFunc.
|
||||
func newRPCFunc(f interface{}, argNames []string) (*RPCFunc, error) {
|
||||
func newRPCFunc(f interface{}) (*RPCFunc, error) {
|
||||
if f == nil {
|
||||
return nil, errors.New("nil function")
|
||||
}
|
||||
@@ -80,49 +154,74 @@ func newRPCFunc(f interface{}, argNames []string) (*RPCFunc, error) {
|
||||
return nil, errors.New("not a function")
|
||||
}
|
||||
|
||||
var ptype reflect.Type
|
||||
ft := fv.Type()
|
||||
if np := ft.NumIn(); np == 0 {
|
||||
if np := ft.NumIn(); np == 0 || np > 2 {
|
||||
return nil, errors.New("wrong number of parameters")
|
||||
} else if ft.In(0) != ctxType {
|
||||
return nil, errors.New("first parameter is not context.Context")
|
||||
} else if np-1 != len(argNames) {
|
||||
return nil, fmt.Errorf("have %d names for %d parameters", len(argNames), np-1)
|
||||
} else if np == 2 {
|
||||
ptype = ft.In(1)
|
||||
if ptype.Kind() != reflect.Ptr {
|
||||
return nil, errors.New("parameter type is not a pointer")
|
||||
}
|
||||
ptype = ptype.Elem()
|
||||
if ptype.Kind() != reflect.Struct {
|
||||
return nil, errors.New("parameter type is not a struct")
|
||||
}
|
||||
}
|
||||
|
||||
var rtype reflect.Type
|
||||
if no := ft.NumOut(); no < 1 || no > 2 {
|
||||
return nil, errors.New("wrong number of results")
|
||||
} else if ft.Out(no-1) != errType {
|
||||
return nil, errors.New("last result is not error")
|
||||
} else if no == 2 {
|
||||
rtype = ft.Out(0)
|
||||
}
|
||||
|
||||
args := make([]reflect.Type, ft.NumIn())
|
||||
for i := 0; i < ft.NumIn(); i++ {
|
||||
args[i] = ft.In(i)
|
||||
}
|
||||
outs := make([]reflect.Type, ft.NumOut())
|
||||
for i := 0; i < ft.NumOut(); i++ {
|
||||
outs[i] = ft.Out(i)
|
||||
var argNames []string
|
||||
if ptype != nil {
|
||||
for i := 0; i < ptype.NumField(); i++ {
|
||||
field := ptype.Field(i)
|
||||
if tag := strings.SplitN(field.Tag.Get("json"), ",", 2)[0]; tag != "" && tag != "-" {
|
||||
argNames = append(argNames, tag)
|
||||
} else if tag == "-" {
|
||||
// If the tag is "-" the field should explicitly be ignored, even
|
||||
// if it is otherwise eligible.
|
||||
} else if field.IsExported() && !field.Anonymous {
|
||||
// Examples: Name → name, MaxEffort → maxEffort.
|
||||
// Note that this is an aesthetic choice; the standard decoder will
|
||||
// match without regard to case anyway.
|
||||
name := strings.ToLower(field.Name[:1]) + field.Name[1:]
|
||||
argNames = append(argNames, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &RPCFunc{
|
||||
f: fv,
|
||||
args: args,
|
||||
returns: outs,
|
||||
param: ptype,
|
||||
result: rtype,
|
||||
argNames: argNames,
|
||||
}, nil
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------
|
||||
|
||||
// NOTE: assume returns is result struct and error. If error is not nil, return it
|
||||
func unreflectResult(returns []reflect.Value) (interface{}, error) {
|
||||
errV := returns[1]
|
||||
if err, ok := errV.Interface().(error); ok && err != nil {
|
||||
return nil, err
|
||||
// invalidParamsError returns an RPC invalid parameters error with the given
|
||||
// detail message.
|
||||
func invalidParamsError(msg string, args ...interface{}) error {
|
||||
return &rpctypes.RPCError{
|
||||
Code: int(rpctypes.CodeInvalidParams),
|
||||
Message: rpctypes.CodeInvalidParams.String(),
|
||||
Data: fmt.Sprintf(msg, args...),
|
||||
}
|
||||
rv := returns[0]
|
||||
// the result is a registered interface,
|
||||
// we need a pointer to it so we can marshal with type byte
|
||||
rvp := reflect.New(rv.Type())
|
||||
rvp.Elem().Set(rv)
|
||||
return rvp.Interface(), nil
|
||||
}
|
||||
|
||||
// isNullOrEmpty reports whether params is either itself empty or represents an
|
||||
// empty parameter (null, empty object, or empty array).
|
||||
func isNullOrEmpty(params json.RawMessage) bool {
|
||||
return len(params) == 0 ||
|
||||
bytes.Equal(params, []byte("null")) ||
|
||||
bytes.Equal(params, []byte("{}")) ||
|
||||
bytes.Equal(params, []byte("[]"))
|
||||
}
|
||||
|
||||
@@ -331,22 +331,8 @@ func (wsc *wsConnection) readRoutine(ctx context.Context) {
|
||||
RPCRequest: &request,
|
||||
WSConn: wsc,
|
||||
})
|
||||
args, err := parseParams(fctx, rpcFunc, request.Params)
|
||||
if err != nil {
|
||||
if err := wsc.WriteRPCResponse(writeCtx, request.MakeErrorf(rpctypes.CodeInvalidParams,
|
||||
"converting JSON parameters: %v", err)); err != nil {
|
||||
wsc.Logger.Error("error writing RPC response", "err", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
returns := rpcFunc.f.Call(args)
|
||||
|
||||
// TODO: Need to encode args/returns to string if we want to log them
|
||||
wsc.Logger.Info("WSJSONRPC", "method", request.Method)
|
||||
|
||||
var resp rpctypes.RPCResponse
|
||||
result, err := unreflectResult(returns)
|
||||
result, err := rpcFunc.Call(fctx, request.Params)
|
||||
if err == nil {
|
||||
resp = request.MakeResponse(result)
|
||||
} else {
|
||||
|
||||
@@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -44,8 +45,12 @@ func TestWebsocketManagerHandler(t *testing.T) {
|
||||
}
|
||||
|
||||
func newWSServer(t *testing.T, logger log.Logger) *httptest.Server {
|
||||
type args struct {
|
||||
S string `json:"s"`
|
||||
I json.Number `json:"i"`
|
||||
}
|
||||
funcMap := map[string]*RPCFunc{
|
||||
"c": NewWSRPCFunc(func(ctx context.Context, s string, i int) (string, error) { return "foo", nil }, "s", "i"),
|
||||
"c": NewWSRPCFunc(func(context.Context, *args) (string, error) { return "foo", nil }),
|
||||
}
|
||||
wm := NewWebsocketManager(logger, funcMap)
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
var routes = map[string]*rpcserver.RPCFunc{
|
||||
"hello_world": rpcserver.NewRPCFunc(HelloWorld, "name", "num"),
|
||||
"hello_world": rpcserver.NewRPCFunc(HelloWorld),
|
||||
}
|
||||
|
||||
func HelloWorld(ctx context.Context, name string, num int) (Result, error) {
|
||||
|
||||
@@ -13,10 +13,7 @@ import (
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
)
|
||||
|
||||
var mp *mempool.TxMempool
|
||||
var getMp func() mempool.Mempool
|
||||
|
||||
func init() {
|
||||
func FuzzMempool(f *testing.F) {
|
||||
app := kvstore.NewApplication()
|
||||
logger := log.NewNopLogger()
|
||||
conn := abciclient.NewLocalClient(logger, app)
|
||||
@@ -28,19 +25,9 @@ func init() {
|
||||
cfg := config.DefaultMempoolConfig()
|
||||
cfg.Broadcast = false
|
||||
|
||||
getMp = func() mempool.Mempool {
|
||||
if mp == nil {
|
||||
mp = mempool.NewTxMempool(logger, cfg, conn)
|
||||
}
|
||||
return mp
|
||||
}
|
||||
}
|
||||
mp := mempool.NewTxMempool(logger, cfg, conn)
|
||||
|
||||
func FuzzMempool(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
err := getMp().CheckTx(context.Background(), data, nil, mempool.TxInfo{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_ = mp.CheckTx(context.Background(), data, nil, mempool.TxInfo{})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,10 +17,14 @@ import (
|
||||
)
|
||||
|
||||
func FuzzRPCJSONRPCServer(f *testing.F) {
|
||||
type args struct {
|
||||
S string `json:"s"`
|
||||
I int `json:"i"`
|
||||
}
|
||||
var rpcFuncMap = map[string]*rpcserver.RPCFunc{
|
||||
"c": rpcserver.NewRPCFunc(func(ctx context.Context, s string, i int) (string, error) {
|
||||
"c": rpcserver.NewRPCFunc(func(context.Context, *args) (string, error) {
|
||||
return "foo", nil
|
||||
}, "s", "i"),
|
||||
}),
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
Reference in New Issue
Block a user