Merge branch 'cal/finalize-block' into cal/vote-extensions-1

This commit is contained in:
Callum Waters
2022-11-25 10:37:44 +01:00
56 changed files with 978 additions and 382 deletions
+5 -2
View File
@@ -125,6 +125,7 @@ func (cli *grpcClient) OnStop() {
if cli.conn != nil {
cli.conn.Close()
}
close(cli.chReqRes)
}
func (cli *grpcClient) StopForError(err error) {
@@ -147,7 +148,6 @@ func (cli *grpcClient) StopForError(err error) {
func (cli *grpcClient) Error() error {
cli.mtx.Lock()
defer cli.mtx.Unlock()
return cli.err
}
@@ -181,7 +181,10 @@ func (cli *grpcClient) finishAsyncCall(req *types.Request, res *types.Response)
//----------------------------------------
func (cli *grpcClient) Flush(ctx context.Context) error { return nil }
func (cli *grpcClient) Flush(ctx context.Context) error {
_, err := cli.client.Flush(ctx, types.ToRequestFlush().GetFlush(), grpc.WaitForReady(true))
return err
}
func (cli *grpcClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) {
return cli.client.Echo(ctx, types.ToRequestEcho(msg).GetEcho(), grpc.WaitForReady(true))
+80
View File
@@ -0,0 +1,80 @@
package abcicli_test
import (
"fmt"
"math/rand"
"net"
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"golang.org/x/net/context"
"github.com/tendermint/tendermint/libs/log"
tmnet "github.com/tendermint/tendermint/libs/net"
abciserver "github.com/tendermint/tendermint/abci/server"
"github.com/tendermint/tendermint/abci/types"
)
func TestGRPC(t *testing.T) {
app := types.NewBaseApplication()
numCheckTxs := 2000
socketFile := fmt.Sprintf("/tmp/test-%08x.sock", rand.Int31n(1<<30))
defer os.Remove(socketFile)
socket := fmt.Sprintf("unix://%v", socketFile)
// Start the listener
server := abciserver.NewGRPCServer(socket, app)
server.SetLogger(log.TestingLogger().With("module", "abci-server"))
err := server.Start()
require.NoError(t, err)
t.Cleanup(func() {
if err := server.Stop(); err != nil {
t.Error(err)
}
})
// Connect to the socket
//nolint:staticcheck // SA1019 Existing use of deprecated but supported dial option.
conn, err := grpc.Dial(socket, grpc.WithInsecure(), grpc.WithContextDialer(dialerFunc))
require.NoError(t, err)
t.Cleanup(func() {
if err := conn.Close(); err != nil {
t.Error(err)
}
})
client := types.NewABCIClient(conn)
// Write requests
for counter := 0; counter < numCheckTxs; counter++ {
// Send request
response, err := client.CheckTx(context.Background(), &types.RequestCheckTx{Tx: []byte("test")})
require.NoError(t, err)
counter++
if response.Code != 0 {
t.Error("CheckTx failed with ret_code", response.Code)
}
if counter > numCheckTxs {
t.Fatal("Too many CheckTx responses")
}
t.Log("response", counter)
if counter == numCheckTxs {
go func() {
time.Sleep(time.Second * 1) // Wait for a bit to allow counter overflow
}()
}
}
}
func dialerFunc(ctx context.Context, addr string) (net.Conn, error) {
return tmnet.Connect(addr)
}
+215 -161
View File
@@ -14,10 +14,12 @@ import (
"github.com/tendermint/tendermint/abci/types"
tmnet "github.com/tendermint/tendermint/libs/net"
"github.com/tendermint/tendermint/libs/service"
"github.com/tendermint/tendermint/libs/timer"
)
const (
reqQueueSize = 256 // TODO make configurable
reqQueueSize = 256 // TODO make configurable
flushThrottleMS = 20 // Don't wait longer than...
)
// socketClient is the client side implementation of the Tendermint
@@ -26,8 +28,6 @@ const (
//
// This is goroutine-safe. All calls are serialized to the server through an unbuffered queue. The socketClient
// tracks responses and expects them to respect the order of the requests sent.
//
// The buffer is flushed after every message sent.
type socketClient struct {
service.BaseService
@@ -35,7 +35,8 @@ type socketClient struct {
mustConnect bool
conn net.Conn
reqQueue chan *ReqRes
reqQueue chan *ReqRes
flushTimer *timer.ThrottleTimer
mtx sync.Mutex
err error
@@ -51,10 +52,12 @@ var _ Client = (*socketClient)(nil)
func NewSocketClient(addr string, mustConnect bool) Client {
cli := &socketClient{
reqQueue: make(chan *ReqRes, reqQueueSize),
flushTimer: timer.NewThrottleTimer("socketClient", flushThrottleMS),
mustConnect: mustConnect,
addr: addr,
reqSent: list.New(),
resCb: nil,
addr: addr,
reqSent: list.New(),
resCb: nil,
}
cli.BaseService = *service.NewBaseService(nil, "socketClient", cli)
return cli
@@ -95,6 +98,7 @@ func (cli *socketClient) OnStop() {
}
cli.flushQueue()
cli.flushTimer.Stop()
}
// Error returns an error if the client was stopped abruptly.
@@ -123,26 +127,37 @@ func (cli *socketClient) CheckTxAsync(ctx context.Context, req *types.RequestChe
//----------------------------------------
func (cli *socketClient) sendRequestsRoutine(conn io.Writer) {
bw := bufio.NewWriter(conn)
w := bufio.NewWriter(conn)
for {
select {
case <-cli.Quit():
return
case reqres := <-cli.reqQueue:
// N.B. We must enqueue before sending out the request, otherwise the
// server may reply before we do it, and the receiver will fail for an
// unsolicited reply.
cli.trackRequest(reqres)
if err := types.WriteMessage(reqres.Request, bw); err != nil {
err := types.WriteMessage(reqres.Request, w)
if err != nil {
cli.stopForError(fmt.Errorf("write to buffer: %w", err))
return
}
if err := bw.Flush(); err != nil {
cli.stopForError(fmt.Errorf("flush buffer: %w", err))
return
// If it's a flush request, flush the current buffer.
if _, ok := reqres.Request.Value.(*types.Request_Flush); ok {
err = w.Flush()
if err != nil {
cli.stopForError(fmt.Errorf("flush buffer: %w", err))
return
}
}
case <-cli.flushTimer.Ch: // flush queue
select {
case cli.reqQueue <- NewReqRes(types.ToRequestFlush()):
default:
// Probably will fill the buffer, or retry later.
}
case <-cli.Quit():
return
}
}
}
@@ -155,8 +170,8 @@ func (cli *socketClient) recvResponseRoutine(conn io.Reader) {
}
var res = &types.Response{}
if err := types.ReadMessage(r, res); err != nil {
err := types.ReadMessage(r, res)
if err != nil {
cli.stopForError(fmt.Errorf("read message: %w", err))
return
}
@@ -221,6 +236,182 @@ func (cli *socketClient) didRecvResponse(res *types.Response) error {
return nil
}
//----------------------------------------
func (cli *socketClient) Flush(ctx context.Context) error {
reqRes, err := cli.queueRequest(ctx, types.ToRequestFlush())
if err != nil {
return err
}
reqRes.Wait()
return nil
}
func (cli *socketClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestEcho(msg))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetEcho(), cli.Error()
}
func (cli *socketClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestInfo(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetInfo(), cli.Error()
}
func (cli *socketClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestCheckTx(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetCheckTx(), cli.Error()
}
func (cli *socketClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestQuery(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetQuery(), cli.Error()
}
func (cli *socketClient) Commit(ctx context.Context, req *types.RequestCommit) (*types.ResponseCommit, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestCommit())
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetCommit(), cli.Error()
}
func (cli *socketClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestInitChain(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetInitChain(), cli.Error()
}
func (cli *socketClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestListSnapshots(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetListSnapshots(), cli.Error()
}
func (cli *socketClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestOfferSnapshot(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetOfferSnapshot(), cli.Error()
}
func (cli *socketClient) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestLoadSnapshotChunk(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetLoadSnapshotChunk(), cli.Error()
}
func (cli *socketClient) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestApplySnapshotChunk(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetApplySnapshotChunk(), cli.Error()
}
func (cli *socketClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestPrepareProposal(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetPrepareProposal(), cli.Error()
}
func (cli *socketClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestProcessProposal(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetProcessProposal(), cli.Error()
}
func (cli *socketClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestExtendVote(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetExtendVote(), nil
}
func (cli *socketClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestVerifyVoteExtension(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetVerifyVoteExtension(), nil
}
func (cli *socketClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestFinalizeBlock(req))
if err != nil {
return nil, err
}
if err := cli.Flush(ctx); err != nil {
return nil, err
}
return reqRes.Response.GetFinalizeBlock(), cli.Error()
}
func (cli *socketClient) queueRequest(ctx context.Context, req *types.Request) (*ReqRes, error) {
reqres := NewReqRes(req)
@@ -231,6 +422,14 @@ func (cli *socketClient) queueRequest(ctx context.Context, req *types.Request) (
return nil, ctx.Err()
}
// Maybe auto-flush, or unset auto-flush
switch req.Value.(type) {
case *types.Request_Flush:
cli.flushTimer.Unset()
default:
cli.flushTimer.Set()
}
return reqres, nil
}
@@ -260,151 +459,6 @@ LOOP:
//----------------------------------------
func (cli *socketClient) Flush(ctx context.Context) error {
_, err := cli.queueRequest(ctx, types.ToRequestFlush())
if err != nil {
return err
}
return nil
}
func (cli *socketClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestEcho(msg))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetEcho(), cli.Error()
}
func (cli *socketClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestInfo(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetInfo(), cli.Error()
}
func (cli *socketClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestCheckTx(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetCheckTx(), cli.Error()
}
func (cli *socketClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestQuery(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetQuery(), cli.Error()
}
func (cli *socketClient) Commit(ctx context.Context, req *types.RequestCommit) (*types.ResponseCommit, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestCommit())
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetCommit(), cli.Error()
}
func (cli *socketClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestInitChain(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetInitChain(), cli.Error()
}
func (cli *socketClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestListSnapshots(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetListSnapshots(), cli.Error()
}
func (cli *socketClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestOfferSnapshot(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetOfferSnapshot(), cli.Error()
}
func (cli *socketClient) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestLoadSnapshotChunk(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetLoadSnapshotChunk(), cli.Error()
}
func (cli *socketClient) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestApplySnapshotChunk(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetApplySnapshotChunk(), cli.Error()
}
func (cli *socketClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestPrepareProposal(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetPrepareProposal(), cli.Error()
}
func (cli *socketClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestProcessProposal(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetProcessProposal(), cli.Error()
}
func (cli *socketClient) ExtendVote(ctx context.Context, req *types.RequestExtendVote) (*types.ResponseExtendVote, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestExtendVote(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetExtendVote(), nil
}
func (cli *socketClient) VerifyVoteExtension(ctx context.Context, req *types.RequestVerifyVoteExtension) (*types.ResponseVerifyVoteExtension, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestVerifyVoteExtension(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetVerifyVoteExtension(), nil
}
func (cli *socketClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
reqRes, err := cli.queueRequest(ctx, types.ToRequestFinalizeBlock(req))
if err != nil {
return nil, err
}
reqRes.Wait()
return reqRes.Response.GetFinalizeBlock(), cli.Error()
}
//----------------------------------------
func resMatchesReq(req *types.Request, res *types.Response) (ok bool) {
switch req.Value.(type) {
case *types.Request_Echo:
+92 -3
View File
@@ -3,6 +3,8 @@ package abcicli_test
import (
"context"
"fmt"
"math/rand"
"os"
"sync"
"testing"
"time"
@@ -33,7 +35,7 @@ func TestCalls(t *testing.T) {
}()
select {
case <-time.After(1 * time.Second):
case <-time.After(time.Second):
require.Fail(t, "No response arrived")
case err, ok := <-resp:
require.True(t, ok, "Must not close channel")
@@ -41,6 +43,84 @@ func TestCalls(t *testing.T) {
}
}
func TestHangingAsyncCalls(t *testing.T) {
app := slowApp{}
s, c := setupClientServer(t, app)
resp := make(chan error, 1)
go func() {
// Call CheckTx
reqres, err := c.CheckTxAsync(context.Background(), &types.RequestCheckTx{})
require.NoError(t, err)
// wait 50 ms for all events to travel socket, but
// no response yet from server
time.Sleep(50 * time.Millisecond)
// kill the server, so the connections break
err = s.Stop()
require.NoError(t, err)
// wait for the response from CheckTx
reqres.Wait()
resp <- c.Error()
}()
select {
case <-time.After(time.Second):
require.Fail(t, "No response arrived")
case err, ok := <-resp:
require.True(t, ok, "Must not close channel")
assert.Error(t, err, "We should get EOF error")
}
}
func TestBulk(t *testing.T) {
const numTxs = 700000
// use a socket instead of a port
socketFile := fmt.Sprintf("test-%08x.sock", rand.Int31n(1<<30))
defer os.Remove(socketFile)
socket := fmt.Sprintf("unix://%v", socketFile)
app := types.NewBaseApplication()
// Start the listener
server := server.NewSocketServer(socket, app)
t.Cleanup(func() {
if err := server.Stop(); err != nil {
t.Log(err)
}
})
err := server.Start()
require.NoError(t, err)
// Connect to the socket
client := abcicli.NewSocketClient(socket, false)
t.Cleanup(func() {
if err := client.Stop(); err != nil {
t.Log(err)
}
})
err = client.Start()
require.NoError(t, err)
// Construct request
rfb := &types.RequestFinalizeBlock{Txs: make([][]byte, numTxs)}
for counter := 0; counter < numTxs; counter++ {
rfb.Txs[counter] = []byte("test")
}
// Send bulk request
res, err := client.FinalizeBlock(context.Background(), rfb)
require.NoError(t, err)
require.Equal(t, numTxs, len(res.TxResults), "Number of txs doesn't match")
for _, tx := range res.TxResults {
require.Equal(t, uint32(0), tx.Code, "Tx failed")
}
// Send final flush message
err = client.Flush(context.Background())
require.NoError(t, err)
}
func setupClientServer(t *testing.T, app types.Application) (
service.Service, abcicli.Client) {
t.Helper()
@@ -55,7 +135,7 @@ func setupClientServer(t *testing.T, app types.Application) (
t.Cleanup(func() {
if err := s.Stop(); err != nil {
t.Error(err)
t.Log(err)
}
})
@@ -65,13 +145,22 @@ func setupClientServer(t *testing.T, app types.Application) (
t.Cleanup(func() {
if err := c.Stop(); err != nil {
t.Error(err)
t.Log(err)
}
})
return s, c
}
type slowApp struct {
types.BaseApplication
}
func (slowApp) CheckTx(_ context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
time.Sleep(time.Second)
return &types.ResponseCheckTx{}, nil
}
// TestCallbackInvokedWhenSetLaet ensures that the callback is invoked when
// set after the client completes the call into the app. Currently this
// test relies on the callback being allowed to be invoked twice if set multiple
+3 -4
View File
@@ -167,10 +167,9 @@ where example.file looks something like:
check_tx 0x00
check_tx 0xff
deliver_tx 0x00
finalize_block 0x00
check_tx 0x00
deliver_tx 0x01
deliver_tx 0x04
finalize_block 0x01 0x04 0xff
info
`,
Args: cobra.ExactArgs(0),
@@ -186,7 +185,7 @@ This command opens an interactive console for running any of the other commands
without opening a new connection each time
`,
Args: cobra.ExactArgs(0),
ValidArgs: []string{"echo", "info", "deliver_tx", "check_tx", "prepare_proposal", "process_proposal", "commit", "query"},
ValidArgs: []string{"echo", "info", "finalize_block", "check_tx", "prepare_proposal", "process_proposal", "commit", "query"},
RunE: cmdConsole,
}
+2 -5
View File
@@ -61,7 +61,7 @@ func NewPersistentApplication(dbDir string) *Application {
name := "kvstore"
db, err := dbm.NewGoLevelDB(name, dbDir)
if err != nil {
panic(err)
panic(fmt.Errorf("failed to create persistent app at %s: %w", dbDir, err))
}
return NewApplication(db)
}
@@ -77,9 +77,6 @@ func NewInMemoryApplication() *Application {
// Tendermint will ensure it is in sync with the application by potentially replaying the blocks it has. If the
// Application returns a 0 appBlockHeight, Tendermint will call InitChain to initialize the application with consensus related data
func (app *Application) Info(_ context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
appHash := make([]byte, 8)
binary.PutVarint(appHash, app.state.Size)
// Tendermint expects the application to persist validators, on start-up we need to reload them to memory if they exist
if len(app.valAddrToPubKeyMap) == 0 && app.state.Height > 0 {
validators := app.getValidators()
@@ -97,7 +94,7 @@ func (app *Application) Info(_ context.Context, req *types.RequestInfo) (*types.
Version: version.ABCIVersion,
AppVersion: AppVersion,
LastBlockHeight: app.state.Height,
LastBlockAppHash: appHash,
LastBlockAppHash: app.state.Hash(),
}, nil
}
+7 -5
View File
@@ -183,6 +183,7 @@ func (s *SocketServer) handleRequests(closeConn chan error, conn io.Reader, resp
}()
for {
var req = &types.Request{}
err := types.ReadMessage(bufReader, req)
if err != nil {
@@ -315,11 +316,12 @@ func (s *SocketServer) handleResponses(closeConn chan error, conn io.Writer, res
closeConn <- fmt.Errorf("error writing message: %w", err)
return
}
err = bufWriter.Flush()
if err != nil {
closeConn <- fmt.Errorf("error flushing write buffer: %w", err)
return
if _, ok := res.Value.(*types.Response_Flush); ok {
err = bufWriter.Flush()
if err != nil {
closeConn <- fmt.Errorf("error flushing write buffer: %w", err)
return
}
}
// If the application has responded with an exception, the server returns the error