implement finalize block

This commit is contained in:
Callum Waters
2022-09-20 14:14:28 +02:00
parent dfefd837e7
commit 344efa2e15
128 changed files with 5543 additions and 7989 deletions
+9 -117
View File
@@ -1,12 +1,11 @@
package abcicli
import (
"context"
"fmt"
"sync"
"github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/service"
tmsync "github.com/tendermint/tendermint/libs/sync"
)
const (
@@ -16,50 +15,18 @@ const (
//go:generate ../../scripts/mockery_generate.sh Client
// Client defines an interface for an ABCI client.
// All `Async` methods return a `ReqRes` object.
// All `Sync` methods return the appropriate protobuf ResponseXxx struct and an error.
// Note these are client errors, eg. ABCI socket connectivity issues.
// Application-related errors are reflected in response via ABCI error codes and logs.
// Client defines the interface for an ABCI client.
//
// NOTE these are client errors, eg. ABCI socket connectivity issues.
// Application-related errors are reflected in response via ABCI error codes
// and (potentially) error response.
type Client interface {
service.Service
types.Application
SetResponseCallback(Callback)
Error() error
FlushAsync() *ReqRes
EchoAsync(msg string) *ReqRes
InfoAsync(types.RequestInfo) *ReqRes
DeliverTxAsync(types.RequestDeliverTx) *ReqRes
CheckTxAsync(types.RequestCheckTx) *ReqRes
QueryAsync(types.RequestQuery) *ReqRes
CommitAsync() *ReqRes
InitChainAsync(types.RequestInitChain) *ReqRes
PrepareProposalAsync(types.RequestPrepareProposal) *ReqRes
BeginBlockAsync(types.RequestBeginBlock) *ReqRes
EndBlockAsync(types.RequestEndBlock) *ReqRes
ListSnapshotsAsync(types.RequestListSnapshots) *ReqRes
OfferSnapshotAsync(types.RequestOfferSnapshot) *ReqRes
LoadSnapshotChunkAsync(types.RequestLoadSnapshotChunk) *ReqRes
ApplySnapshotChunkAsync(types.RequestApplySnapshotChunk) *ReqRes
ProcessProposalAsync(types.RequestProcessProposal) *ReqRes
FlushSync() error
EchoSync(msg string) (*types.ResponseEcho, error)
InfoSync(types.RequestInfo) (*types.ResponseInfo, error)
DeliverTxSync(types.RequestDeliverTx) (*types.ResponseDeliverTx, error)
CheckTxSync(types.RequestCheckTx) (*types.ResponseCheckTx, error)
QuerySync(types.RequestQuery) (*types.ResponseQuery, error)
CommitSync() (*types.ResponseCommit, error)
InitChainSync(types.RequestInitChain) (*types.ResponseInitChain, error)
PrepareProposalSync(types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error)
BeginBlockSync(types.RequestBeginBlock) (*types.ResponseBeginBlock, error)
EndBlockSync(types.RequestEndBlock) (*types.ResponseEndBlock, error)
ListSnapshotsSync(types.RequestListSnapshots) (*types.ResponseListSnapshots, error)
OfferSnapshotSync(types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error)
LoadSnapshotChunkSync(types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error)
ApplySnapshotChunkSync(types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error)
ProcessProposalSync(types.RequestProcessProposal) (*types.ResponseProcessProposal, error)
Flush(context.Context) error
Echo(context.Context, string) (*types.ResponseEcho, error)
}
//----------------------------------------
@@ -77,78 +44,3 @@ func NewClient(addr, transport string, mustConnect bool) (client Client, err err
}
return
}
type Callback func(*types.Request, *types.Response)
type ReqRes struct {
*types.Request
*sync.WaitGroup
*types.Response // Not set atomically, so be sure to use WaitGroup.
mtx tmsync.Mutex
// callbackInvoked as a variable to track if the callback was already
// invoked during the regular execution of the request. This variable
// allows clients to set the callback simultaneously without potentially
// invoking the callback twice by accident, once when 'SetCallback' is
// called and once during the normal request.
callbackInvoked bool
cb func(*types.Response) // A single callback that may be set.
}
func NewReqRes(req *types.Request) *ReqRes {
return &ReqRes{
Request: req,
WaitGroup: waitGroup1(),
Response: nil,
callbackInvoked: false,
cb: nil,
}
}
// Sets sets the callback. If reqRes is already done, it will call the cb
// immediately. Note, reqRes.cb should not change if reqRes.done and only one
// callback is supported.
func (r *ReqRes) SetCallback(cb func(res *types.Response)) {
r.mtx.Lock()
if r.callbackInvoked {
r.mtx.Unlock()
cb(r.Response)
return
}
r.cb = cb
r.mtx.Unlock()
}
// InvokeCallback invokes a thread-safe execution of the configured callback
// if non-nil.
func (r *ReqRes) InvokeCallback() {
r.mtx.Lock()
defer r.mtx.Unlock()
if r.cb != nil {
r.cb(r.Response)
}
r.callbackInvoked = true
}
// GetCallback returns the configured callback of the ReqRes object which may be
// nil. Note, it is not safe to concurrently call this in cases where it is
// marked done and SetCallback is called before calling GetCallback as that
// will invoke the callback twice and create a potential race condition.
//
// ref: https://github.com/tendermint/tendermint/issues/5439
func (r *ReqRes) GetCallback() func(*types.Response) {
r.mtx.Lock()
defer r.mtx.Unlock()
return r.cb
}
func waitGroup1() (wg *sync.WaitGroup) {
wg = &sync.WaitGroup{}
wg.Add(1)
return
}
+41 -304
View File
@@ -1,18 +1,18 @@
package abcicli
import (
"context"
"fmt"
"net"
"sync"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"github.com/tendermint/tendermint/abci/types"
tmnet "github.com/tendermint/tendermint/libs/net"
"github.com/tendermint/tendermint/libs/service"
tmsync "github.com/tendermint/tendermint/libs/sync"
)
var _ Client = (*grpcClient)(nil)
@@ -23,27 +23,18 @@ type grpcClient struct {
service.BaseService
mustConnect bool
client types.ABCIApplicationClient
conn *grpc.ClientConn
chReqRes chan *ReqRes // dispatches "async" responses to callbacks *in order*, needed by mempool
client types.ABCIClient
conn *grpc.ClientConn
mtx tmsync.Mutex
addr string
err error
resCb func(*types.Request, *types.Response) // listens to all callbacks
mtx sync.Mutex
addr string
err error
}
func NewGRPCClient(addr string, mustConnect bool) Client {
cli := &grpcClient{
addr: addr,
mustConnect: mustConnect,
// Buffering the channel is needed to make calls appear asynchronous,
// which is required when the caller makes multiple async calls before
// processing callbacks (e.g. due to holding locks). 64 means that a
// caller can make up to 64 async calls before a callback must be
// processed (otherwise it deadlocks). It also means that we can make 64
// gRPC calls while processing a slow callback at the channel head.
chReqRes: make(chan *ReqRes, 64),
}
cli.BaseService = *service.NewBaseService(nil, "grpcClient", cli)
return cli
@@ -58,37 +49,12 @@ func (cli *grpcClient) OnStart() error {
return err
}
// This processes asynchronous request/response messages and dispatches
// them to callbacks.
go func() {
// Use a separate function to use defer for mutex unlocks (this handles panics)
callCb := func(reqres *ReqRes) {
cli.mtx.Lock()
defer cli.mtx.Unlock()
reqres.Done()
// Notify client listener if set
if cli.resCb != nil {
cli.resCb(reqres.Request, reqres.Response)
}
// Notify reqRes listener if set
reqres.InvokeCallback()
}
for reqres := range cli.chReqRes {
if reqres != nil {
callCb(reqres)
} else {
cli.Logger.Error("Received nil reqres")
}
}
}()
RETRY_LOOP:
for {
//nolint:staticcheck // SA1019 Existing use of deprecated but supported dial option.
conn, err := grpc.Dial(cli.addr, grpc.WithInsecure(), grpc.WithContextDialer(dialerFunc))
conn, err := grpc.Dial(cli.addr,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(dialerFunc),
)
if err != nil {
if cli.mustConnect {
return err
@@ -99,7 +65,7 @@ RETRY_LOOP:
}
cli.Logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr)
client := types.NewABCIApplicationClient(conn)
client := types.NewABCIClient(conn)
cli.conn = conn
ENSURE_CONNECTED:
@@ -123,7 +89,6 @@ func (cli *grpcClient) OnStop() {
if cli.conn != nil {
cli.conn.Close()
}
close(cli.chReqRes)
}
func (cli *grpcClient) StopForError(err error) {
@@ -146,290 +111,62 @@ func (cli *grpcClient) StopForError(err error) {
func (cli *grpcClient) Error() error {
cli.mtx.Lock()
defer cli.mtx.Unlock()
return cli.err
}
// Set listener for all responses
// NOTE: callback may get internally generated flush responses.
func (cli *grpcClient) SetResponseCallback(resCb Callback) {
cli.mtx.Lock()
cli.resCb = resCb
cli.mtx.Unlock()
}
//----------------------------------------
// GRPC calls are synchronous, but some callbacks expect to be called asynchronously
// (eg. the mempool expects to be able to lock to remove bad txs from cache).
// To accommodate, we finish each call in its own go-routine,
// which is expensive, but easy - if you want something better, use the socket protocol!
// maybe one day, if people really want it, we use grpc streams,
// but hopefully not :D
func (cli *grpcClient) EchoAsync(msg string) *ReqRes {
req := types.ToRequestEcho(msg)
res, err := cli.client.Echo(context.Background(), req.GetEcho(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Echo{Echo: res}})
}
func (cli *grpcClient) FlushAsync() *ReqRes {
req := types.ToRequestFlush()
res, err := cli.client.Flush(context.Background(), req.GetFlush(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Flush{Flush: res}})
}
func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes {
req := types.ToRequestInfo(params)
res, err := cli.client.Info(context.Background(), req.GetInfo(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Info{Info: res}})
}
func (cli *grpcClient) DeliverTxAsync(params types.RequestDeliverTx) *ReqRes {
req := types.ToRequestDeliverTx(params)
res, err := cli.client.DeliverTx(context.Background(), req.GetDeliverTx(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_DeliverTx{DeliverTx: res}})
}
func (cli *grpcClient) CheckTxAsync(params types.RequestCheckTx) *ReqRes {
req := types.ToRequestCheckTx(params)
res, err := cli.client.CheckTx(context.Background(), req.GetCheckTx(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_CheckTx{CheckTx: res}})
}
func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes {
req := types.ToRequestQuery(params)
res, err := cli.client.Query(context.Background(), req.GetQuery(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Query{Query: res}})
}
func (cli *grpcClient) CommitAsync() *ReqRes {
req := types.ToRequestCommit()
res, err := cli.client.Commit(context.Background(), req.GetCommit(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Commit{Commit: res}})
}
func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes {
req := types.ToRequestInitChain(params)
res, err := cli.client.InitChain(context.Background(), req.GetInitChain(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_InitChain{InitChain: res}})
}
func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes {
req := types.ToRequestBeginBlock(params)
res, err := cli.client.BeginBlock(context.Background(), req.GetBeginBlock(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_BeginBlock{BeginBlock: res}})
}
func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock) *ReqRes {
req := types.ToRequestEndBlock(params)
res, err := cli.client.EndBlock(context.Background(), req.GetEndBlock(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_EndBlock{EndBlock: res}})
}
func (cli *grpcClient) ListSnapshotsAsync(params types.RequestListSnapshots) *ReqRes {
req := types.ToRequestListSnapshots(params)
res, err := cli.client.ListSnapshots(context.Background(), req.GetListSnapshots(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_ListSnapshots{ListSnapshots: res}})
}
func (cli *grpcClient) OfferSnapshotAsync(params types.RequestOfferSnapshot) *ReqRes {
req := types.ToRequestOfferSnapshot(params)
res, err := cli.client.OfferSnapshot(context.Background(), req.GetOfferSnapshot(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_OfferSnapshot{OfferSnapshot: res}})
}
func (cli *grpcClient) LoadSnapshotChunkAsync(params types.RequestLoadSnapshotChunk) *ReqRes {
req := types.ToRequestLoadSnapshotChunk(params)
res, err := cli.client.LoadSnapshotChunk(context.Background(), req.GetLoadSnapshotChunk(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_LoadSnapshotChunk{LoadSnapshotChunk: res}})
}
func (cli *grpcClient) ApplySnapshotChunkAsync(params types.RequestApplySnapshotChunk) *ReqRes {
req := types.ToRequestApplySnapshotChunk(params)
res, err := cli.client.ApplySnapshotChunk(context.Background(), req.GetApplySnapshotChunk(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_ApplySnapshotChunk{ApplySnapshotChunk: res}})
}
func (cli *grpcClient) PrepareProposalAsync(params types.RequestPrepareProposal) *ReqRes {
req := types.ToRequestPrepareProposal(params)
res, err := cli.client.PrepareProposal(context.Background(), req.GetPrepareProposal(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_PrepareProposal{PrepareProposal: res}})
}
func (cli *grpcClient) ProcessProposalAsync(params types.RequestProcessProposal) *ReqRes {
req := types.ToRequestProcessProposal(params)
res, err := cli.client.ProcessProposal(context.Background(), req.GetProcessProposal(), grpc.WaitForReady(true))
if err != nil {
cli.StopForError(err)
}
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_ProcessProposal{ProcessProposal: res}})
}
// finishAsyncCall creates a ReqRes for an async call, and immediately populates it
// with the response. We don't complete it until it's been ordered via the channel.
func (cli *grpcClient) finishAsyncCall(req *types.Request, res *types.Response) *ReqRes {
reqres := NewReqRes(req)
reqres.Response = res
cli.chReqRes <- reqres // use channel for async responses, since they must be ordered
return reqres
}
// finishSyncCall waits for an async call to complete. It is necessary to call all
// sync calls asynchronously as well, to maintain call and response ordering via
// the channel, and this method will wait until the async call completes.
func (cli *grpcClient) finishSyncCall(reqres *ReqRes) *types.Response {
// It's possible that the callback is called twice, since the callback can
// be called immediately on SetCallback() in addition to after it has been
// set. This is because completing the ReqRes happens in a separate critical
// section from the one where the callback is called: there is a race where
// SetCallback() is called between completing the ReqRes and dispatching the
// callback.
//
// We also buffer the channel with 1 response, since SetCallback() will be
// called synchronously if the reqres is already completed, in which case
// it will block on sending to the channel since it hasn't gotten around to
// receiving from it yet.
//
// ReqRes should really handle callback dispatch internally, to guarantee
// that it's only called once and avoid the above race conditions.
var once sync.Once
ch := make(chan *types.Response, 1)
reqres.SetCallback(func(res *types.Response) {
once.Do(func() {
ch <- res
})
})
return <-ch
}
//----------------------------------------
func (cli *grpcClient) FlushSync() error {
reqres := cli.FlushAsync()
cli.finishSyncCall(reqres).GetFlush()
return cli.Error()
func (cli *grpcClient) Flush(ctx context.Context) error { return nil }
func (cli *grpcClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) {
return cli.client.Echo(ctx, types.ToRequestEcho(msg).GetEcho(), grpc.WaitForReady(true))
}
func (cli *grpcClient) EchoSync(msg string) (*types.ResponseEcho, error) {
reqres := cli.EchoAsync(msg)
// StopForError should already have been called if error is set
return cli.finishSyncCall(reqres).GetEcho(), cli.Error()
func (cli *grpcClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
return cli.client.Info(ctx, types.ToRequestInfo(req).GetInfo(), grpc.WaitForReady(true))
}
func (cli *grpcClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
reqres := cli.InfoAsync(req)
return cli.finishSyncCall(reqres).GetInfo(), cli.Error()
func (cli *grpcClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
return cli.client.CheckTx(ctx, types.ToRequestCheckTx(req).GetCheckTx(), grpc.WaitForReady(true))
}
func (cli *grpcClient) DeliverTxSync(params types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
reqres := cli.DeliverTxAsync(params)
return cli.finishSyncCall(reqres).GetDeliverTx(), cli.Error()
func (cli *grpcClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) {
return cli.client.Query(ctx, types.ToRequestQuery(req).GetQuery(), grpc.WaitForReady(true))
}
func (cli *grpcClient) CheckTxSync(params types.RequestCheckTx) (*types.ResponseCheckTx, error) {
reqres := cli.CheckTxAsync(params)
return cli.finishSyncCall(reqres).GetCheckTx(), cli.Error()
func (cli *grpcClient) Commit(ctx context.Context, req *types.RequestCommit) (*types.ResponseCommit, error) {
return cli.client.Commit(ctx, types.ToRequestCommit().GetCommit(), grpc.WaitForReady(true))
}
func (cli *grpcClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) {
reqres := cli.QueryAsync(req)
return cli.finishSyncCall(reqres).GetQuery(), cli.Error()
func (cli *grpcClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) {
return cli.client.InitChain(ctx, types.ToRequestInitChain(req).GetInitChain(), grpc.WaitForReady(true))
}
func (cli *grpcClient) CommitSync() (*types.ResponseCommit, error) {
reqres := cli.CommitAsync()
return cli.finishSyncCall(reqres).GetCommit(), cli.Error()
func (cli *grpcClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
return cli.client.ListSnapshots(ctx, types.ToRequestListSnapshots(req).GetListSnapshots(), grpc.WaitForReady(true))
}
func (cli *grpcClient) InitChainSync(params types.RequestInitChain) (*types.ResponseInitChain, error) {
reqres := cli.InitChainAsync(params)
return cli.finishSyncCall(reqres).GetInitChain(), cli.Error()
func (cli *grpcClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
return cli.client.OfferSnapshot(ctx, types.ToRequestOfferSnapshot(req).GetOfferSnapshot(), grpc.WaitForReady(true))
}
func (cli *grpcClient) BeginBlockSync(params types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
reqres := cli.BeginBlockAsync(params)
return cli.finishSyncCall(reqres).GetBeginBlock(), cli.Error()
func (cli *grpcClient) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
return cli.client.LoadSnapshotChunk(ctx, types.ToRequestLoadSnapshotChunk(req).GetLoadSnapshotChunk(), grpc.WaitForReady(true))
}
func (cli *grpcClient) EndBlockSync(params types.RequestEndBlock) (*types.ResponseEndBlock, error) {
reqres := cli.EndBlockAsync(params)
return cli.finishSyncCall(reqres).GetEndBlock(), cli.Error()
func (cli *grpcClient) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
return cli.client.ApplySnapshotChunk(ctx, types.ToRequestApplySnapshotChunk(req).GetApplySnapshotChunk(), grpc.WaitForReady(true))
}
func (cli *grpcClient) ListSnapshotsSync(params types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
reqres := cli.ListSnapshotsAsync(params)
return cli.finishSyncCall(reqres).GetListSnapshots(), cli.Error()
func (cli *grpcClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
return cli.client.PrepareProposal(ctx, types.ToRequestPrepareProposal(req).GetPrepareProposal(), grpc.WaitForReady(true))
}
func (cli *grpcClient) OfferSnapshotSync(params types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
reqres := cli.OfferSnapshotAsync(params)
return cli.finishSyncCall(reqres).GetOfferSnapshot(), cli.Error()
func (cli *grpcClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
return cli.client.ProcessProposal(ctx, types.ToRequestProcessProposal(req).GetProcessProposal(), grpc.WaitForReady(true))
}
func (cli *grpcClient) LoadSnapshotChunkSync(
params types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
reqres := cli.LoadSnapshotChunkAsync(params)
return cli.finishSyncCall(reqres).GetLoadSnapshotChunk(), cli.Error()
}
func (cli *grpcClient) ApplySnapshotChunkSync(
params types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
reqres := cli.ApplySnapshotChunkAsync(params)
return cli.finishSyncCall(reqres).GetApplySnapshotChunk(), cli.Error()
}
func (cli *grpcClient) PrepareProposalSync(
params types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
reqres := cli.PrepareProposalAsync(params)
return cli.finishSyncCall(reqres).GetPrepareProposal(), cli.Error()
}
func (cli *grpcClient) ProcessProposalSync(params types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
reqres := cli.ProcessProposalAsync(params)
return cli.finishSyncCall(reqres).GetProcessProposal(), cli.Error()
func (cli *grpcClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
return cli.client.FinalizeBlock(ctx, types.ToRequestFinalizeBlock(req).GetFinalizeBlock(), grpc.WaitForReady(true))
}
+34 -260
View File
@@ -1,31 +1,26 @@
package abcicli
import (
"context"
types "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/service"
tmsync "github.com/tendermint/tendermint/libs/sync"
)
var _ Client = (*localClient)(nil)
// NOTE: use defer to unlock mutex because Application might panic (e.g., in
// case of malicious tx or query). It only makes sense for publicly exposed
// methods like CheckTx (/broadcast_tx_* RPC endpoint) or Query (/abci_query
// RPC endpoint), but defers are used everywhere for the sake of consistency.
type localClient struct {
service.BaseService
mtx *tmsync.Mutex
types.Application
Callback
}
var _ Client = (*localClient)(nil)
// NewLocalClient creates a local client, which will be directly calling the
// methods of the given app.
//
// Both Async and Sync methods ignore the given context.Context parameter.
// NewLocalClient creates a local client, which wraps the application interface that
// Tendermint as the client will call to the application as the server. The only
// difference, is that the local client has a global mutex which enforces serialization
// of all the ABCI calls from Tendermint to the Application.
func NewLocalClient(mtx *tmsync.Mutex, app types.Application) Client {
if mtx == nil {
mtx = new(tmsync.Mutex)
@@ -38,321 +33,100 @@ func NewLocalClient(mtx *tmsync.Mutex, app types.Application) Client {
return cli
}
func (app *localClient) SetResponseCallback(cb Callback) {
app.mtx.Lock()
app.Callback = cb
app.mtx.Unlock()
}
// TODO: change types.Application to include Error()?
func (app *localClient) Error() error {
return nil
}
func (app *localClient) FlushAsync() *ReqRes {
// Do nothing
return newLocalReqRes(types.ToRequestFlush(), nil)
}
func (app *localClient) EchoAsync(msg string) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.callback(
types.ToRequestEcho(msg),
types.ToResponseEcho(msg),
)
}
func (app *localClient) InfoAsync(req types.RequestInfo) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.Info(req)
return app.callback(
types.ToRequestInfo(req),
types.ToResponseInfo(res),
)
}
func (app *localClient) DeliverTxAsync(params types.RequestDeliverTx) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.DeliverTx(params)
return app.callback(
types.ToRequestDeliverTx(params),
types.ToResponseDeliverTx(res),
)
}
func (app *localClient) CheckTxAsync(req types.RequestCheckTx) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.CheckTx(req)
return app.callback(
types.ToRequestCheckTx(req),
types.ToResponseCheckTx(res),
)
}
func (app *localClient) QueryAsync(req types.RequestQuery) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.Query(req)
return app.callback(
types.ToRequestQuery(req),
types.ToResponseQuery(res),
)
}
func (app *localClient) CommitAsync() *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.Commit()
return app.callback(
types.ToRequestCommit(),
types.ToResponseCommit(res),
)
}
func (app *localClient) InitChainAsync(req types.RequestInitChain) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.InitChain(req)
return app.callback(
types.ToRequestInitChain(req),
types.ToResponseInitChain(res),
)
}
func (app *localClient) BeginBlockAsync(req types.RequestBeginBlock) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.BeginBlock(req)
return app.callback(
types.ToRequestBeginBlock(req),
types.ToResponseBeginBlock(res),
)
}
func (app *localClient) EndBlockAsync(req types.RequestEndBlock) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.EndBlock(req)
return app.callback(
types.ToRequestEndBlock(req),
types.ToResponseEndBlock(res),
)
}
func (app *localClient) ListSnapshotsAsync(req types.RequestListSnapshots) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.ListSnapshots(req)
return app.callback(
types.ToRequestListSnapshots(req),
types.ToResponseListSnapshots(res),
)
}
func (app *localClient) OfferSnapshotAsync(req types.RequestOfferSnapshot) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.OfferSnapshot(req)
return app.callback(
types.ToRequestOfferSnapshot(req),
types.ToResponseOfferSnapshot(res),
)
}
func (app *localClient) LoadSnapshotChunkAsync(req types.RequestLoadSnapshotChunk) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.LoadSnapshotChunk(req)
return app.callback(
types.ToRequestLoadSnapshotChunk(req),
types.ToResponseLoadSnapshotChunk(res),
)
}
func (app *localClient) ApplySnapshotChunkAsync(req types.RequestApplySnapshotChunk) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.ApplySnapshotChunk(req)
return app.callback(
types.ToRequestApplySnapshotChunk(req),
types.ToResponseApplySnapshotChunk(res),
)
}
func (app *localClient) PrepareProposalAsync(req types.RequestPrepareProposal) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.PrepareProposal(req)
return app.callback(
types.ToRequestPrepareProposal(req),
types.ToResponsePrepareProposal(res),
)
}
func (app *localClient) ProcessProposalAsync(req types.RequestProcessProposal) *ReqRes {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.ProcessProposal(req)
return app.callback(
types.ToRequestProcessProposal(req),
types.ToResponseProcessProposal(res),
)
}
//-------------------------------------------------------
func (app *localClient) FlushSync() error {
func (app *localClient) Flush(context.Context) error {
return nil
}
func (app *localClient) EchoSync(msg string) (*types.ResponseEcho, error) {
func (app *localClient) Echo(_ context.Context, msg string) (*types.ResponseEcho, error) {
return &types.ResponseEcho{Message: msg}, nil
}
func (app *localClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
func (app *localClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.Info(req)
return &res, nil
return app.Application.Info(ctx, req)
}
func (app *localClient) DeliverTxSync(req types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
func (app *localClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.DeliverTx(req)
return &res, nil
return app.Application.CheckTx(ctx, req)
}
func (app *localClient) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCheckTx, error) {
func (app *localClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.CheckTx(req)
return &res, nil
return app.Application.Query(ctx, req)
}
func (app *localClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) {
func (app *localClient) Commit(ctx context.Context, req *types.RequestCommit) (*types.ResponseCommit, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.Query(req)
return &res, nil
return app.Application.Commit(ctx, req)
}
func (app *localClient) CommitSync() (*types.ResponseCommit, error) {
func (app *localClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.Commit()
return &res, nil
return app.Application.InitChain(ctx, req)
}
func (app *localClient) InitChainSync(req types.RequestInitChain) (*types.ResponseInitChain, error) {
func (app *localClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.InitChain(req)
return &res, nil
return app.Application.ListSnapshots(ctx, req)
}
func (app *localClient) BeginBlockSync(req types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
func (app *localClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.BeginBlock(req)
return &res, nil
return app.Application.OfferSnapshot(ctx, req)
}
func (app *localClient) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) {
func (app *localClient) LoadSnapshotChunk(ctx context.Context,
req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.EndBlock(req)
return &res, nil
return app.Application.LoadSnapshotChunk(ctx, req)
}
func (app *localClient) ListSnapshotsSync(req types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
func (app *localClient) ApplySnapshotChunk(ctx context.Context,
req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.ListSnapshots(req)
return &res, nil
return app.Application.ApplySnapshotChunk(ctx, req)
}
func (app *localClient) OfferSnapshotSync(req types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
func (app *localClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.OfferSnapshot(req)
return &res, nil
return app.Application.PrepareProposal(ctx, req)
}
func (app *localClient) LoadSnapshotChunkSync(
req types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
func (app *localClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.LoadSnapshotChunk(req)
return &res, nil
return app.Application.ProcessProposal(ctx, req)
}
func (app *localClient) ApplySnapshotChunkSync(
req types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
func (app *localClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.ApplySnapshotChunk(req)
return &res, nil
}
func (app *localClient) PrepareProposalSync(req types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.PrepareProposal(req)
return &res, nil
}
func (app *localClient) ProcessProposalSync(req types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
res := app.Application.ProcessProposal(req)
return &res, nil
}
//-------------------------------------------------------
func (app *localClient) callback(req *types.Request, res *types.Response) *ReqRes {
app.Callback(req, res)
rr := newLocalReqRes(req, res)
rr.callbackInvoked = true
return rr
}
func newLocalReqRes(req *types.Request, res *types.Response) *ReqRes {
reqRes := NewReqRes(req)
reqRes.Response = res
return reqRes
return app.Application.FinalizeBlock(ctx, req)
}
+116 -423
View File
@@ -3,10 +3,10 @@
package mocks
import (
abcicli "github.com/tendermint/tendermint/abci/client"
log "github.com/tendermint/tendermint/libs/log"
context "context"
mock "github.com/stretchr/testify/mock"
log "github.com/tendermint/tendermint/libs/log"
types "github.com/tendermint/tendermint/abci/types"
)
@@ -16,29 +16,13 @@ type Client struct {
mock.Mock
}
// ApplySnapshotChunkAsync provides a mock function with given fields: _a0
func (_m *Client) ApplySnapshotChunkAsync(_a0 types.RequestApplySnapshotChunk) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestApplySnapshotChunk) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// ApplySnapshotChunkSync provides a mock function with given fields: _a0
func (_m *Client) ApplySnapshotChunkSync(_a0 types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
ret := _m.Called(_a0)
// ApplySnapshotChunk provides a mock function with given fields: _a0, _a1
func (_m *Client) ApplySnapshotChunk(_a0 context.Context, _a1 *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseApplySnapshotChunk
if rf, ok := ret.Get(0).(func(types.RequestApplySnapshotChunk) *types.ResponseApplySnapshotChunk); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestApplySnapshotChunk) *types.ResponseApplySnapshotChunk); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseApplySnapshotChunk)
@@ -46,8 +30,8 @@ func (_m *Client) ApplySnapshotChunkSync(_a0 types.RequestApplySnapshotChunk) (*
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestApplySnapshotChunk) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestApplySnapshotChunk) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -55,68 +39,13 @@ func (_m *Client) ApplySnapshotChunkSync(_a0 types.RequestApplySnapshotChunk) (*
return r0, r1
}
// BeginBlockAsync provides a mock function with given fields: _a0
func (_m *Client) BeginBlockAsync(_a0 types.RequestBeginBlock) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestBeginBlock) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// BeginBlockSync provides a mock function with given fields: _a0
func (_m *Client) BeginBlockSync(_a0 types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
ret := _m.Called(_a0)
var r0 *types.ResponseBeginBlock
if rf, ok := ret.Get(0).(func(types.RequestBeginBlock) *types.ResponseBeginBlock); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseBeginBlock)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestBeginBlock) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CheckTxAsync provides a mock function with given fields: _a0
func (_m *Client) CheckTxAsync(_a0 types.RequestCheckTx) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestCheckTx) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// CheckTxSync provides a mock function with given fields: _a0
func (_m *Client) CheckTxSync(_a0 types.RequestCheckTx) (*types.ResponseCheckTx, error) {
ret := _m.Called(_a0)
// CheckTx provides a mock function with given fields: _a0, _a1
func (_m *Client) CheckTx(_a0 context.Context, _a1 *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseCheckTx
if rf, ok := ret.Get(0).(func(types.RequestCheckTx) *types.ResponseCheckTx); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestCheckTx) *types.ResponseCheckTx); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseCheckTx)
@@ -124,8 +53,8 @@ func (_m *Client) CheckTxSync(_a0 types.RequestCheckTx) (*types.ResponseCheckTx,
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestCheckTx) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestCheckTx) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -133,29 +62,13 @@ func (_m *Client) CheckTxSync(_a0 types.RequestCheckTx) (*types.ResponseCheckTx,
return r0, r1
}
// CommitAsync provides a mock function with given fields:
func (_m *Client) CommitAsync() *abcicli.ReqRes {
ret := _m.Called()
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func() *abcicli.ReqRes); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// CommitSync provides a mock function with given fields:
func (_m *Client) CommitSync() (*types.ResponseCommit, error) {
ret := _m.Called()
// Commit provides a mock function with given fields: _a0, _a1
func (_m *Client) Commit(_a0 context.Context, _a1 *types.RequestCommit) (*types.ResponseCommit, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseCommit
if rf, ok := ret.Get(0).(func() *types.ResponseCommit); ok {
r0 = rf()
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestCommit) *types.ResponseCommit); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseCommit)
@@ -163,8 +76,8 @@ func (_m *Client) CommitSync() (*types.ResponseCommit, error) {
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestCommit) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -172,68 +85,13 @@ func (_m *Client) CommitSync() (*types.ResponseCommit, error) {
return r0, r1
}
// DeliverTxAsync provides a mock function with given fields: _a0
func (_m *Client) DeliverTxAsync(_a0 types.RequestDeliverTx) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestDeliverTx) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// DeliverTxSync provides a mock function with given fields: _a0
func (_m *Client) DeliverTxSync(_a0 types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
ret := _m.Called(_a0)
var r0 *types.ResponseDeliverTx
if rf, ok := ret.Get(0).(func(types.RequestDeliverTx) *types.ResponseDeliverTx); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseDeliverTx)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestDeliverTx) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// EchoAsync provides a mock function with given fields: msg
func (_m *Client) EchoAsync(msg string) *abcicli.ReqRes {
ret := _m.Called(msg)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(string) *abcicli.ReqRes); ok {
r0 = rf(msg)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// EchoSync provides a mock function with given fields: msg
func (_m *Client) EchoSync(msg string) (*types.ResponseEcho, error) {
ret := _m.Called(msg)
// Echo provides a mock function with given fields: _a0, _a1
func (_m *Client) Echo(_a0 context.Context, _a1 string) (*types.ResponseEcho, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseEcho
if rf, ok := ret.Get(0).(func(string) *types.ResponseEcho); ok {
r0 = rf(msg)
if rf, ok := ret.Get(0).(func(context.Context, string) *types.ResponseEcho); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseEcho)
@@ -241,47 +99,8 @@ func (_m *Client) EchoSync(msg string) (*types.ResponseEcho, error) {
}
var r1 error
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(msg)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// EndBlockAsync provides a mock function with given fields: _a0
func (_m *Client) EndBlockAsync(_a0 types.RequestEndBlock) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestEndBlock) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// EndBlockSync provides a mock function with given fields: _a0
func (_m *Client) EndBlockSync(_a0 types.RequestEndBlock) (*types.ResponseEndBlock, error) {
ret := _m.Called(_a0)
var r0 *types.ResponseEndBlock
if rf, ok := ret.Get(0).(func(types.RequestEndBlock) *types.ResponseEndBlock); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseEndBlock)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestEndBlock) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -303,68 +122,22 @@ func (_m *Client) Error() error {
return r0
}
// FlushAsync provides a mock function with given fields:
func (_m *Client) FlushAsync() *abcicli.ReqRes {
ret := _m.Called()
// FinalizeBlock provides a mock function with given fields: _a0, _a1
func (_m *Client) FinalizeBlock(_a0 context.Context, _a1 *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
ret := _m.Called(_a0, _a1)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func() *abcicli.ReqRes); ok {
r0 = rf()
var r0 *types.ResponseFinalizeBlock
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestFinalizeBlock) *types.ResponseFinalizeBlock); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// FlushSync provides a mock function with given fields:
func (_m *Client) FlushSync() error {
ret := _m.Called()
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
} else {
r0 = ret.Error(0)
}
return r0
}
// InfoAsync provides a mock function with given fields: _a0
func (_m *Client) InfoAsync(_a0 types.RequestInfo) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestInfo) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// InfoSync provides a mock function with given fields: _a0
func (_m *Client) InfoSync(_a0 types.RequestInfo) (*types.ResponseInfo, error) {
ret := _m.Called(_a0)
var r0 *types.ResponseInfo
if rf, ok := ret.Get(0).(func(types.RequestInfo) *types.ResponseInfo); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseInfo)
r0 = ret.Get(0).(*types.ResponseFinalizeBlock)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestInfo) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestFinalizeBlock) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -372,29 +145,50 @@ func (_m *Client) InfoSync(_a0 types.RequestInfo) (*types.ResponseInfo, error) {
return r0, r1
}
// InitChainAsync provides a mock function with given fields: _a0
func (_m *Client) InitChainAsync(_a0 types.RequestInitChain) *abcicli.ReqRes {
// Flush provides a mock function with given fields: _a0
func (_m *Client) Flush(_a0 context.Context) error {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestInitChain) *abcicli.ReqRes); ok {
var r0 error
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
r0 = ret.Error(0)
}
return r0
}
// InitChainSync provides a mock function with given fields: _a0
func (_m *Client) InitChainSync(_a0 types.RequestInitChain) (*types.ResponseInitChain, error) {
ret := _m.Called(_a0)
// Info provides a mock function with given fields: _a0, _a1
func (_m *Client) Info(_a0 context.Context, _a1 *types.RequestInfo) (*types.ResponseInfo, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseInfo
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestInfo) *types.ResponseInfo); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseInfo)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestInfo) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// InitChain provides a mock function with given fields: _a0, _a1
func (_m *Client) InitChain(_a0 context.Context, _a1 *types.RequestInitChain) (*types.ResponseInitChain, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseInitChain
if rf, ok := ret.Get(0).(func(types.RequestInitChain) *types.ResponseInitChain); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestInitChain) *types.ResponseInitChain); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseInitChain)
@@ -402,8 +196,8 @@ func (_m *Client) InitChainSync(_a0 types.RequestInitChain) (*types.ResponseInit
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestInitChain) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestInitChain) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -425,29 +219,13 @@ func (_m *Client) IsRunning() bool {
return r0
}
// ListSnapshotsAsync provides a mock function with given fields: _a0
func (_m *Client) ListSnapshotsAsync(_a0 types.RequestListSnapshots) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestListSnapshots) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// ListSnapshotsSync provides a mock function with given fields: _a0
func (_m *Client) ListSnapshotsSync(_a0 types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
ret := _m.Called(_a0)
// ListSnapshots provides a mock function with given fields: _a0, _a1
func (_m *Client) ListSnapshots(_a0 context.Context, _a1 *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseListSnapshots
if rf, ok := ret.Get(0).(func(types.RequestListSnapshots) *types.ResponseListSnapshots); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestListSnapshots) *types.ResponseListSnapshots); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseListSnapshots)
@@ -455,8 +233,8 @@ func (_m *Client) ListSnapshotsSync(_a0 types.RequestListSnapshots) (*types.Resp
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestListSnapshots) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestListSnapshots) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -464,29 +242,13 @@ func (_m *Client) ListSnapshotsSync(_a0 types.RequestListSnapshots) (*types.Resp
return r0, r1
}
// LoadSnapshotChunkAsync provides a mock function with given fields: _a0
func (_m *Client) LoadSnapshotChunkAsync(_a0 types.RequestLoadSnapshotChunk) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestLoadSnapshotChunk) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// LoadSnapshotChunkSync provides a mock function with given fields: _a0
func (_m *Client) LoadSnapshotChunkSync(_a0 types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
ret := _m.Called(_a0)
// LoadSnapshotChunk provides a mock function with given fields: _a0, _a1
func (_m *Client) LoadSnapshotChunk(_a0 context.Context, _a1 *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseLoadSnapshotChunk
if rf, ok := ret.Get(0).(func(types.RequestLoadSnapshotChunk) *types.ResponseLoadSnapshotChunk); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestLoadSnapshotChunk) *types.ResponseLoadSnapshotChunk); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseLoadSnapshotChunk)
@@ -494,8 +256,8 @@ func (_m *Client) LoadSnapshotChunkSync(_a0 types.RequestLoadSnapshotChunk) (*ty
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestLoadSnapshotChunk) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestLoadSnapshotChunk) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -503,29 +265,13 @@ func (_m *Client) LoadSnapshotChunkSync(_a0 types.RequestLoadSnapshotChunk) (*ty
return r0, r1
}
// OfferSnapshotAsync provides a mock function with given fields: _a0
func (_m *Client) OfferSnapshotAsync(_a0 types.RequestOfferSnapshot) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestOfferSnapshot) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// OfferSnapshotSync provides a mock function with given fields: _a0
func (_m *Client) OfferSnapshotSync(_a0 types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
ret := _m.Called(_a0)
// OfferSnapshot provides a mock function with given fields: _a0, _a1
func (_m *Client) OfferSnapshot(_a0 context.Context, _a1 *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseOfferSnapshot
if rf, ok := ret.Get(0).(func(types.RequestOfferSnapshot) *types.ResponseOfferSnapshot); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestOfferSnapshot) *types.ResponseOfferSnapshot); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseOfferSnapshot)
@@ -533,8 +279,8 @@ func (_m *Client) OfferSnapshotSync(_a0 types.RequestOfferSnapshot) (*types.Resp
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestOfferSnapshot) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestOfferSnapshot) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -575,29 +321,13 @@ func (_m *Client) OnStop() {
_m.Called()
}
// PrepareProposalAsync provides a mock function with given fields: _a0
func (_m *Client) PrepareProposalAsync(_a0 types.RequestPrepareProposal) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestPrepareProposal) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// PrepareProposalSync provides a mock function with given fields: _a0
func (_m *Client) PrepareProposalSync(_a0 types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
ret := _m.Called(_a0)
// PrepareProposal provides a mock function with given fields: _a0, _a1
func (_m *Client) PrepareProposal(_a0 context.Context, _a1 *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponsePrepareProposal
if rf, ok := ret.Get(0).(func(types.RequestPrepareProposal) *types.ResponsePrepareProposal); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestPrepareProposal) *types.ResponsePrepareProposal); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponsePrepareProposal)
@@ -605,8 +335,8 @@ func (_m *Client) PrepareProposalSync(_a0 types.RequestPrepareProposal) (*types.
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestPrepareProposal) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestPrepareProposal) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -614,29 +344,13 @@ func (_m *Client) PrepareProposalSync(_a0 types.RequestPrepareProposal) (*types.
return r0, r1
}
// ProcessProposalAsync provides a mock function with given fields: _a0
func (_m *Client) ProcessProposalAsync(_a0 types.RequestProcessProposal) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestProcessProposal) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// ProcessProposalSync provides a mock function with given fields: _a0
func (_m *Client) ProcessProposalSync(_a0 types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
ret := _m.Called(_a0)
// ProcessProposal provides a mock function with given fields: _a0, _a1
func (_m *Client) ProcessProposal(_a0 context.Context, _a1 *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseProcessProposal
if rf, ok := ret.Get(0).(func(types.RequestProcessProposal) *types.ResponseProcessProposal); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestProcessProposal) *types.ResponseProcessProposal); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseProcessProposal)
@@ -644,8 +358,8 @@ func (_m *Client) ProcessProposalSync(_a0 types.RequestProcessProposal) (*types.
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestProcessProposal) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestProcessProposal) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -653,29 +367,13 @@ func (_m *Client) ProcessProposalSync(_a0 types.RequestProcessProposal) (*types.
return r0, r1
}
// QueryAsync provides a mock function with given fields: _a0
func (_m *Client) QueryAsync(_a0 types.RequestQuery) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestQuery) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// QuerySync provides a mock function with given fields: _a0
func (_m *Client) QuerySync(_a0 types.RequestQuery) (*types.ResponseQuery, error) {
ret := _m.Called(_a0)
// Query provides a mock function with given fields: _a0, _a1
func (_m *Client) Query(_a0 context.Context, _a1 *types.RequestQuery) (*types.ResponseQuery, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseQuery
if rf, ok := ret.Get(0).(func(types.RequestQuery) *types.ResponseQuery); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestQuery) *types.ResponseQuery); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseQuery)
@@ -683,8 +381,8 @@ func (_m *Client) QuerySync(_a0 types.RequestQuery) (*types.ResponseQuery, error
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestQuery) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestQuery) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -727,11 +425,6 @@ func (_m *Client) SetLogger(_a0 log.Logger) {
_m.Called(_a0)
}
// SetResponseCallback provides a mock function with given fields: _a0
func (_m *Client) SetResponseCallback(_a0 abcicli.Callback) {
_m.Called(_a0)
}
// Start provides a mock function with given fields:
func (_m *Client) Start() error {
ret := _m.Called()
+215 -318
View File
@@ -3,27 +3,27 @@ package abcicli
import (
"bufio"
"container/list"
"context"
"errors"
"fmt"
"io"
"net"
"reflect"
"sync"
"time"
"github.com/tendermint/tendermint/abci/types"
tmnet "github.com/tendermint/tendermint/libs/net"
"github.com/tendermint/tendermint/libs/service"
tmsync "github.com/tendermint/tendermint/libs/sync"
"github.com/tendermint/tendermint/libs/timer"
)
const (
reqQueueSize = 256 // TODO make configurable
flushThrottleMS = 20 // Don't wait longer than...
)
// This is goroutine-safe, but users should beware that the application in
// general is not meant to be interfaced with concurrent callers.
// socketClient is the client side implementation of the Tendermint
// Socket Protocol (TSP). It is used by an instance of Tendermint to pass
// ABCI requests to an out of process application running the socketServer.
//
// 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
@@ -31,29 +31,24 @@ type socketClient struct {
mustConnect bool
conn net.Conn
reqQueue chan *ReqRes
flushTimer *timer.ThrottleTimer
reqQueue chan *requestAndResponse
mtx tmsync.Mutex
mtx sync.Mutex
err error
reqSent *list.List // list of requests sent, waiting for response
resCb func(*types.Request, *types.Response) // called on all requests, if set.
reqSent *list.List // list of requests sent, waiting for response
}
var _ Client = (*socketClient)(nil)
// NewSocketClient creates a new socket client, which connects to a given
// address. If mustConnect is true, the client will return an error upon start
// if it fails to connect.
// if it fails to connect else it will continue to retry.
func NewSocketClient(addr string, mustConnect bool) Client {
cli := &socketClient{
reqQueue: make(chan *ReqRes, reqQueueSize),
flushTimer: timer.NewThrottleTimer("socketClient", flushThrottleMS),
reqQueue: make(chan *requestAndResponse),
mustConnect: mustConnect,
addr: addr,
reqSent: list.New(),
resCb: nil,
addr: addr,
reqSent: list.New(),
}
cli.BaseService = *service.NewBaseService(nil, "socketClient", cli)
return cli
@@ -67,6 +62,8 @@ func (cli *socketClient) OnStart() error {
conn net.Conn
)
fmt.Println("starting socket client")
for {
conn, err = tmnet.Connect(cli.addr)
if err != nil {
@@ -89,12 +86,11 @@ func (cli *socketClient) OnStart() error {
// OnStop implements Service by closing connection and flushing all queues.
func (cli *socketClient) OnStop() {
fmt.Println("stopping socket client")
if cli.conn != nil {
cli.conn.Close()
}
cli.flushQueue()
cli.flushTimer.Stop()
cli.drainQueue()
}
// Error returns an error if the client was stopped abruptly.
@@ -104,48 +100,31 @@ func (cli *socketClient) Error() error {
return cli.err
}
// SetResponseCallback sets a callback, which will be executed for each
// non-error & non-empty response from the server.
//
// NOTE: callback may get internally generated flush responses.
func (cli *socketClient) SetResponseCallback(resCb Callback) {
cli.mtx.Lock()
cli.resCb = resCb
cli.mtx.Unlock()
}
//----------------------------------------
func (cli *socketClient) sendRequestsRoutine(conn io.Writer) {
w := bufio.NewWriter(conn)
bw := bufio.NewWriter(conn)
for {
select {
case <-cli.Quit():
return
case reqres := <-cli.reqQueue:
// cli.Logger.Debug("Sent request", "requestType", reflect.TypeOf(reqres.Request), "request", reqres.Request)
// 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)
cli.willSendReq(reqres)
err := types.WriteMessage(reqres.Request, w)
if err != nil {
fmt.Println("writing message")
if err := types.WriteMessage(reqres.Request, bw); err != nil {
fmt.Println(err)
cli.stopForError(fmt.Errorf("write to 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
}
if err := bw.Flush(); 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
}
}
}
@@ -153,14 +132,19 @@ func (cli *socketClient) sendRequestsRoutine(conn io.Writer) {
func (cli *socketClient) recvResponseRoutine(conn io.Reader) {
r := bufio.NewReader(conn)
for {
var res = &types.Response{}
err := types.ReadMessage(r, res)
if err != nil {
cli.stopForError(fmt.Errorf("read message: %w", err))
if !cli.IsRunning() {
return
}
// cli.Logger.Debug("Received response", "responseType", reflect.TypeOf(res), "response", res)
var res = &types.Response{}
fmt.Println("client waiting to read a message")
if err := types.ReadMessage(r, res); err != nil {
fmt.Println(err)
cli.stopForError(fmt.Errorf("read message: %w", err))
return
}
fmt.Println("client finished reading message")
switch r := res.Value.(type) {
case *types.Response_Exception: // app responded with error
@@ -177,7 +161,13 @@ func (cli *socketClient) recvResponseRoutine(conn io.Reader) {
}
}
func (cli *socketClient) willSendReq(reqres *ReqRes) {
func (cli *socketClient) trackRequest(reqres *requestAndResponse) {
// N.B. We must NOT hold the client state lock while checking this, or we
// may deadlock with shutdown.
if !cli.IsRunning() {
return
}
cli.mtx.Lock()
defer cli.mtx.Unlock()
cli.reqSent.PushBack(reqres)
@@ -190,285 +180,171 @@ func (cli *socketClient) didRecvResponse(res *types.Response) error {
// Get the first ReqRes.
next := cli.reqSent.Front()
if next == nil {
return fmt.Errorf("unexpected %v when nothing expected", reflect.TypeOf(res.Value))
return fmt.Errorf("unexpected response %T when no call was made", res.Value)
}
reqres := next.Value.(*ReqRes)
reqres := next.Value.(*requestAndResponse)
if !resMatchesReq(reqres.Request, res) {
return fmt.Errorf("unexpected %v when response to %v expected",
reflect.TypeOf(res.Value), reflect.TypeOf(reqres.Request.Value))
return fmt.Errorf("unexpected response %T to the request %T", res.Value, reqres.Request.Value)
}
reqres.Response = res
reqres.Done() // release waiters
reqres.markDone() // release waiters
cli.reqSent.Remove(next) // pop first item from linked list
// Notify client listener if set (global callback).
if cli.resCb != nil {
cli.resCb(reqres.Request, res)
}
// Notify reqRes listener if set (request specific callback).
//
// NOTE: It is possible this callback isn't set on the reqres object. At this
// point, in which case it will be called after, when it is set.
reqres.InvokeCallback()
return nil
}
//----------------------------------------
func (cli *socketClient) EchoAsync(msg string) *ReqRes {
return cli.queueRequest(types.ToRequestEcho(msg))
}
func (cli *socketClient) FlushAsync() *ReqRes {
return cli.queueRequest(types.ToRequestFlush())
}
func (cli *socketClient) InfoAsync(req types.RequestInfo) *ReqRes {
return cli.queueRequest(types.ToRequestInfo(req))
}
func (cli *socketClient) DeliverTxAsync(req types.RequestDeliverTx) *ReqRes {
return cli.queueRequest(types.ToRequestDeliverTx(req))
}
func (cli *socketClient) CheckTxAsync(req types.RequestCheckTx) *ReqRes {
return cli.queueRequest(types.ToRequestCheckTx(req))
}
func (cli *socketClient) QueryAsync(req types.RequestQuery) *ReqRes {
return cli.queueRequest(types.ToRequestQuery(req))
}
func (cli *socketClient) CommitAsync() *ReqRes {
return cli.queueRequest(types.ToRequestCommit())
}
func (cli *socketClient) InitChainAsync(req types.RequestInitChain) *ReqRes {
return cli.queueRequest(types.ToRequestInitChain(req))
}
func (cli *socketClient) BeginBlockAsync(req types.RequestBeginBlock) *ReqRes {
return cli.queueRequest(types.ToRequestBeginBlock(req))
}
func (cli *socketClient) EndBlockAsync(req types.RequestEndBlock) *ReqRes {
return cli.queueRequest(types.ToRequestEndBlock(req))
}
func (cli *socketClient) ListSnapshotsAsync(req types.RequestListSnapshots) *ReqRes {
return cli.queueRequest(types.ToRequestListSnapshots(req))
}
func (cli *socketClient) OfferSnapshotAsync(req types.RequestOfferSnapshot) *ReqRes {
return cli.queueRequest(types.ToRequestOfferSnapshot(req))
}
func (cli *socketClient) LoadSnapshotChunkAsync(req types.RequestLoadSnapshotChunk) *ReqRes {
return cli.queueRequest(types.ToRequestLoadSnapshotChunk(req))
}
func (cli *socketClient) ApplySnapshotChunkAsync(req types.RequestApplySnapshotChunk) *ReqRes {
return cli.queueRequest(types.ToRequestApplySnapshotChunk(req))
}
func (cli *socketClient) PrepareProposalAsync(req types.RequestPrepareProposal) *ReqRes {
return cli.queueRequest(types.ToRequestPrepareProposal(req))
}
func (cli *socketClient) ProcessProposalAsync(req types.RequestProcessProposal) *ReqRes {
return cli.queueRequest(types.ToRequestProcessProposal(req))
}
//----------------------------------------
func (cli *socketClient) FlushSync() error {
reqRes := cli.queueRequest(types.ToRequestFlush())
if err := cli.Error(); err != nil {
return err
}
reqRes.Wait() // NOTE: if we don't flush the queue, its possible to get stuck here
return cli.Error()
}
func (cli *socketClient) EchoSync(msg string) (*types.ResponseEcho, error) {
reqres := cli.queueRequest(types.ToRequestEcho(msg))
if err := cli.FlushSync(); err != nil {
return nil, err
func (cli *socketClient) doRequest(ctx context.Context, req *types.Request) (*types.Response, error) {
if !cli.IsRunning() {
return nil, errors.New("client has stopped")
}
return reqres.Response.GetEcho(), cli.Error()
}
reqres := makeReqRes(req)
func (cli *socketClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
reqres := cli.queueRequest(types.ToRequestInfo(req))
if err := cli.FlushSync(); err != nil {
return nil, err
select {
case cli.reqQueue <- reqres:
case <-ctx.Done():
return nil, fmt.Errorf("can't queue req: %w", ctx.Err())
}
return reqres.Response.GetInfo(), cli.Error()
}
select {
case <-reqres.signal:
if err := cli.Error(); err != nil {
return nil, err
}
func (cli *socketClient) DeliverTxSync(req types.RequestDeliverTx) (*types.ResponseDeliverTx, error) {
reqres := cli.queueRequest(types.ToRequestDeliverTx(req))
if err := cli.FlushSync(); err != nil {
return nil, err
return reqres.Response, nil
case <-ctx.Done():
return nil, ctx.Err()
}
return reqres.Response.GetDeliverTx(), cli.Error()
}
func (cli *socketClient) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCheckTx, error) {
reqres := cli.queueRequest(types.ToRequestCheckTx(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetCheckTx(), cli.Error()
}
func (cli *socketClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) {
reqres := cli.queueRequest(types.ToRequestQuery(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetQuery(), cli.Error()
}
func (cli *socketClient) CommitSync() (*types.ResponseCommit, error) {
reqres := cli.queueRequest(types.ToRequestCommit())
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetCommit(), cli.Error()
}
func (cli *socketClient) InitChainSync(req types.RequestInitChain) (*types.ResponseInitChain, error) {
reqres := cli.queueRequest(types.ToRequestInitChain(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetInitChain(), cli.Error()
}
func (cli *socketClient) BeginBlockSync(req types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
reqres := cli.queueRequest(types.ToRequestBeginBlock(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetBeginBlock(), cli.Error()
}
func (cli *socketClient) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) {
reqres := cli.queueRequest(types.ToRequestEndBlock(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetEndBlock(), cli.Error()
}
func (cli *socketClient) ListSnapshotsSync(req types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
reqres := cli.queueRequest(types.ToRequestListSnapshots(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetListSnapshots(), cli.Error()
}
func (cli *socketClient) OfferSnapshotSync(req types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
reqres := cli.queueRequest(types.ToRequestOfferSnapshot(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetOfferSnapshot(), cli.Error()
}
func (cli *socketClient) LoadSnapshotChunkSync(
req types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
reqres := cli.queueRequest(types.ToRequestLoadSnapshotChunk(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetLoadSnapshotChunk(), cli.Error()
}
func (cli *socketClient) ApplySnapshotChunkSync(
req types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
reqres := cli.queueRequest(types.ToRequestApplySnapshotChunk(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetApplySnapshotChunk(), cli.Error()
}
func (cli *socketClient) PrepareProposalSync(req types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
reqres := cli.queueRequest(types.ToRequestPrepareProposal(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetPrepareProposal(), cli.Error()
}
func (cli *socketClient) ProcessProposalSync(req types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
reqres := cli.queueRequest(types.ToRequestProcessProposal(req))
if err := cli.FlushSync(); err != nil {
return nil, err
}
return reqres.Response.GetProcessProposal(), cli.Error()
}
//----------------------------------------
func (cli *socketClient) queueRequest(req *types.Request) *ReqRes {
reqres := NewReqRes(req)
// TODO: set cli.err if reqQueue times out
cli.reqQueue <- reqres
// Maybe auto-flush, or unset auto-flush
switch req.Value.(type) {
case *types.Request_Flush:
cli.flushTimer.Unset()
default:
cli.flushTimer.Set()
}
return reqres
}
func (cli *socketClient) flushQueue() {
// drainQueue marks as complete and discards all remaining pending requests
// from the queue.
func (cli *socketClient) drainQueue() {
cli.mtx.Lock()
defer cli.mtx.Unlock()
// mark all in-flight messages as resolved (they will get cli.Error())
for req := cli.reqSent.Front(); req != nil; req = req.Next() {
reqres := req.Value.(*ReqRes)
reqres.Done()
reqres := req.Value.(*requestAndResponse)
reqres.markDone()
}
}
// mark all queued messages as resolved
LOOP:
for {
select {
case reqres := <-cli.reqQueue:
reqres.Done()
default:
break LOOP
}
//----------------------------------------
func (cli *socketClient) Flush(ctx context.Context) error {
_, err := cli.doRequest(ctx, types.ToRequestFlush())
if err != nil {
return err
}
return nil
}
func (cli *socketClient) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) {
res, err := cli.doRequest(ctx, types.ToRequestEcho(msg))
if err != nil {
return nil, err
}
return res.GetEcho(), nil
}
func (cli *socketClient) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
res, err := cli.doRequest(ctx, types.ToRequestInfo(req))
if err != nil {
return nil, err
}
return res.GetInfo(), nil
}
func (cli *socketClient) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
res, err := cli.doRequest(ctx, types.ToRequestCheckTx(req))
if err != nil {
return nil, err
}
return res.GetCheckTx(), nil
}
func (cli *socketClient) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) {
res, err := cli.doRequest(ctx, types.ToRequestQuery(req))
if err != nil {
return nil, err
}
return res.GetQuery(), nil
}
func (cli *socketClient) Commit(ctx context.Context, req *types.RequestCommit) (*types.ResponseCommit, error) {
res, err := cli.doRequest(ctx, types.ToRequestCommit())
if err != nil {
return nil, err
}
return res.GetCommit(), nil
}
func (cli *socketClient) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) {
res, err := cli.doRequest(ctx, types.ToRequestInitChain(req))
if err != nil {
return nil, err
}
return res.GetInitChain(), nil
}
func (cli *socketClient) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
res, err := cli.doRequest(ctx, types.ToRequestListSnapshots(req))
if err != nil {
return nil, err
}
return res.GetListSnapshots(), nil
}
func (cli *socketClient) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
res, err := cli.doRequest(ctx, types.ToRequestOfferSnapshot(req))
if err != nil {
return nil, err
}
return res.GetOfferSnapshot(), nil
}
func (cli *socketClient) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
res, err := cli.doRequest(ctx, types.ToRequestLoadSnapshotChunk(req))
if err != nil {
return nil, err
}
return res.GetLoadSnapshotChunk(), nil
}
func (cli *socketClient) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
res, err := cli.doRequest(ctx, types.ToRequestApplySnapshotChunk(req))
if err != nil {
return nil, err
}
return res.GetApplySnapshotChunk(), nil
}
func (cli *socketClient) PrepareProposal(ctx context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
res, err := cli.doRequest(ctx, types.ToRequestPrepareProposal(req))
if err != nil {
return nil, err
}
return res.GetPrepareProposal(), nil
}
func (cli *socketClient) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
res, err := cli.doRequest(ctx, types.ToRequestProcessProposal(req))
if err != nil {
return nil, err
}
return res.GetProcessProposal(), nil
}
func (cli *socketClient) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
res, err := cli.doRequest(ctx, types.ToRequestFinalizeBlock(req))
if err != nil {
return nil, err
}
return res.GetFinalizeBlock(), nil
}
//----------------------------------------
@@ -481,8 +357,6 @@ func resMatchesReq(req *types.Request, res *types.Response) (ok bool) {
_, ok = res.Value.(*types.Response_Flush)
case *types.Request_Info:
_, ok = res.Value.(*types.Response_Info)
case *types.Request_DeliverTx:
_, ok = res.Value.(*types.Response_DeliverTx)
case *types.Request_CheckTx:
_, ok = res.Value.(*types.Response_CheckTx)
case *types.Request_Commit:
@@ -491,10 +365,6 @@ func resMatchesReq(req *types.Request, res *types.Response) (ok bool) {
_, ok = res.Value.(*types.Response_Query)
case *types.Request_InitChain:
_, ok = res.Value.(*types.Response_InitChain)
case *types.Request_BeginBlock:
_, ok = res.Value.(*types.Response_BeginBlock)
case *types.Request_EndBlock:
_, ok = res.Value.(*types.Response_EndBlock)
case *types.Request_ApplySnapshotChunk:
_, ok = res.Value.(*types.Response_ApplySnapshotChunk)
case *types.Request_LoadSnapshotChunk:
@@ -507,11 +377,14 @@ func resMatchesReq(req *types.Request, res *types.Response) (ok bool) {
_, ok = res.Value.(*types.Response_PrepareProposal)
case *types.Request_ProcessProposal:
_, ok = res.Value.(*types.Response_ProcessProposal)
case *types.Request_FinalizeBlock:
_, ok = res.Value.(*types.Response_FinalizeBlock)
}
return ok
}
func (cli *socketClient) stopForError(err error) {
fmt.Printf("stopping for error %v\n", err)
if !cli.IsRunning() {
return
}
@@ -527,3 +400,27 @@ func (cli *socketClient) stopForError(err error) {
cli.Logger.Error("Error stopping abci.socketClient", "err", err)
}
}
type requestAndResponse struct {
*types.Request
*types.Response
mtx sync.Mutex
signal chan struct{}
}
func makeReqRes(req *types.Request) *requestAndResponse {
return &requestAndResponse{
Request: req,
Response: nil,
signal: make(chan struct{}),
}
}
// markDone marks the ReqRes object as done.
func (r *requestAndResponse) markDone() {
r.mtx.Lock()
defer r.mtx.Unlock()
close(r.signal)
}
+25 -141
View File
@@ -1,8 +1,8 @@
package abcicli_test
import (
"context"
"fmt"
"sync"
"testing"
"time"
@@ -16,34 +16,24 @@ import (
"github.com/tendermint/tendermint/libs/service"
)
func TestProperSyncCalls(t *testing.T) {
app := slowApp{}
func TestCalls(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
app := types.BaseApplication{}
s, c := setupClientServer(t, app)
t.Cleanup(func() {
if err := s.Stop(); err != nil {
t.Error(err)
}
})
t.Cleanup(func() {
if err := c.Stop(); err != nil {
t.Error(err)
}
})
_, c := setupClientServer(t, app)
resp := make(chan error, 1)
go func() {
// This is BeginBlockSync unrolled....
reqres := c.BeginBlockAsync(types.RequestBeginBlock{})
err := c.FlushSync()
res, err := c.Echo(ctx, "hello")
require.NoError(t, err)
res := reqres.Response.GetBeginBlock()
require.NotNil(t, res)
resp <- c.Error()
}()
select {
case <-time.After(time.Second):
case <-time.After(3 * time.Second):
fmt.Println("no response arrived")
require.Fail(t, "No response arrived")
case err, ok := <-resp:
require.True(t, ok, "Must not close channel")
@@ -51,139 +41,33 @@ func TestProperSyncCalls(t *testing.T) {
}
}
func TestHangingSyncCalls(t *testing.T) {
app := slowApp{}
s, c := setupClientServer(t, app)
t.Cleanup(func() {
if err := s.Stop(); err != nil {
t.Log(err)
}
})
t.Cleanup(func() {
if err := c.Stop(); err != nil {
t.Log(err)
}
})
resp := make(chan error, 1)
go func() {
// Start BeginBlock and flush it
reqres := c.BeginBlockAsync(types.RequestBeginBlock{})
flush := c.FlushAsync()
// wait 20 ms for all events to travel socket, but
// no response yet from server
time.Sleep(20 * time.Millisecond)
// kill the server, so the connections break
err := s.Stop()
require.NoError(t, err)
// wait for the response from BeginBlock
reqres.Wait()
flush.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 setupClientServer(t *testing.T, app types.Application) (
service.Service, abcicli.Client) {
t.Helper()
// some port between 20k and 30k
port := 20000 + tmrand.Int32()%10000
addr := fmt.Sprintf("localhost:%d", port)
s, err := server.NewServer(addr, "socket", app)
require.NoError(t, err)
err = s.Start()
s := server.NewSocketServer(addr, app)
err := s.Start()
require.NoError(t, err)
c := abcicli.NewSocketClient(addr, true)
err = c.Start()
require.NoError(t, err)
t.Cleanup(func() {
if err := s.Stop(); err != nil {
t.Error(err)
}
})
t.Cleanup(func() {
fmt.Println("stopping client")
if err := c.Stop(); err != nil {
t.Error(err)
}
})
return s, c
}
type slowApp struct {
types.BaseApplication
}
func (slowApp) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
time.Sleep(200 * time.Millisecond)
return types.ResponseBeginBlock{}
}
// 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
// times, once when set early and once when set late.
func TestCallbackInvokedWhenSetLate(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
app := blockedABCIApplication{
wg: wg,
}
_, c := setupClientServer(t, app)
reqRes := c.CheckTxAsync(types.RequestCheckTx{})
done := make(chan struct{})
cb := func(_ *types.Response) {
close(done)
}
reqRes.SetCallback(cb)
app.wg.Done()
<-done
var called bool
cb = func(_ *types.Response) {
called = true
}
reqRes.SetCallback(cb)
require.True(t, called)
}
type blockedABCIApplication struct {
wg *sync.WaitGroup
types.BaseApplication
}
func (b blockedABCIApplication) CheckTx(r types.RequestCheckTx) types.ResponseCheckTx {
b.wg.Wait()
return b.BaseApplication.CheckTx(r)
}
// TestCallbackInvokedWhenSetEarly ensures that the callback is invoked when
// set before the client completes the call into the app.
func TestCallbackInvokedWhenSetEarly(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(1)
app := blockedABCIApplication{
wg: wg,
}
_, c := setupClientServer(t, app)
reqRes := c.CheckTxAsync(types.RequestCheckTx{})
done := make(chan struct{})
cb := func(_ *types.Response) {
close(done)
}
reqRes.SetCallback(cb)
app.wg.Done()
called := func() bool {
select {
case <-done:
return true
default:
return false
}
}
require.Eventually(t, called, time.Second, time.Millisecond*25)
}
+84 -53
View File
@@ -15,7 +15,6 @@ import (
tmos "github.com/tendermint/tendermint/libs/os"
abcicli "github.com/tendermint/tendermint/abci/client"
"github.com/tendermint/tendermint/abci/example/code"
"github.com/tendermint/tendermint/abci/example/kvstore"
"github.com/tendermint/tendermint/abci/server"
servertest "github.com/tendermint/tendermint/abci/tests/server"
@@ -139,7 +138,6 @@ func addCommands() {
RootCmd.AddCommand(consoleCmd)
RootCmd.AddCommand(echoCmd)
RootCmd.AddCommand(infoCmd)
RootCmd.AddCommand(deliverTxCmd)
RootCmd.AddCommand(checkTxCmd)
RootCmd.AddCommand(commitCmd)
RootCmd.AddCommand(versionCmd)
@@ -148,6 +146,7 @@ func addCommands() {
RootCmd.AddCommand(processProposalCmd)
addQueryFlags()
RootCmd.AddCommand(queryCmd)
RootCmd.AddCommand(finalizeBlockCmd)
// examples
addKVStoreFlags()
@@ -206,12 +205,12 @@ var infoCmd = &cobra.Command{
RunE: cmdInfo,
}
var deliverTxCmd = &cobra.Command{
Use: "deliver_tx",
Short: "deliver a new transaction to the application",
Long: "deliver a new transaction to the application",
Args: cobra.ExactArgs(1),
RunE: cmdDeliverTx,
var finalizeBlockCmd = &cobra.Command{
Use: "finalize_block",
Short: "deliver a block of transactions to the application",
Long: "deliver a block of transactions to the application",
Args: cobra.MinimumNArgs(1),
RunE: cmdFinalizeBlock,
}
var checkTxCmd = &cobra.Command{
@@ -311,30 +310,52 @@ func compose(fs []func() error) error {
}
func cmdTest(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
return compose(
[]func() error{
func() error { return servertest.InitChain(client) },
func() error { return servertest.Commit(client, nil) },
func() error { return servertest.DeliverTx(client, []byte("abc"), code.CodeTypeBadNonce, nil) },
func() error { return servertest.Commit(client, nil) },
func() error { return servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeOK, nil) },
func() error { return servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 1}) },
func() error { return servertest.DeliverTx(client, []byte{0x00}, code.CodeTypeBadNonce, nil) },
func() error { return servertest.DeliverTx(client, []byte{0x01}, code.CodeTypeOK, nil) },
func() error { return servertest.DeliverTx(client, []byte{0x00, 0x02}, code.CodeTypeOK, nil) },
func() error { return servertest.DeliverTx(client, []byte{0x00, 0x03}, code.CodeTypeOK, nil) },
func() error { return servertest.DeliverTx(client, []byte{0x00, 0x00, 0x04}, code.CodeTypeOK, nil) },
func() error { return servertest.InitChain(ctx, client) },
func() error { return servertest.Commit(ctx, client) },
func() error {
return servertest.DeliverTx(client, []byte{0x00, 0x00, 0x06}, code.CodeTypeBadNonce, nil)
return servertest.FinalizeBlock(ctx, client, [][]byte{
[]byte("abc"),
}, []uint32{
kvstore.CodeTypeBadNonce,
}, nil, nil)
},
func() error { return servertest.Commit(client, []byte{0, 0, 0, 0, 0, 0, 0, 5}) },
func() error { return servertest.Commit(ctx, client) },
func() error {
return servertest.PrepareProposal(client, [][]byte{
return servertest.FinalizeBlock(ctx, client, [][]byte{
{0x00},
}, []uint32{
kvstore.CodeTypeOK,
}, nil, []byte{0, 0, 0, 0, 0, 0, 0, 1})
},
func() error { return servertest.Commit(ctx, client) },
func() error {
return servertest.FinalizeBlock(ctx, client, [][]byte{
{0x00},
{0x01},
{0x00, 0x02},
{0x00, 0x03},
{0x00, 0x00, 0x04},
{0x00, 0x00, 0x06},
}, []uint32{
kvstore.CodeTypeBadNonce,
kvstore.CodeTypeOK,
kvstore.CodeTypeOK,
kvstore.CodeTypeOK,
kvstore.CodeTypeOK,
kvstore.CodeTypeBadNonce,
}, nil, []byte{0, 0, 0, 0, 0, 0, 0, 5})
},
func() error { return servertest.Commit(ctx, client) },
func() error {
return servertest.PrepareProposal(ctx, client, [][]byte{
{0x01},
}, [][]byte{{0x01}}, nil)
},
func() error {
return servertest.ProcessProposal(client, [][]byte{
return servertest.ProcessProposal(ctx, client, [][]byte{
{0x01},
}, types.ResponseProcessProposal_ACCEPT)
},
@@ -430,8 +451,8 @@ func muxOnCommands(cmd *cobra.Command, pArgs []string) error {
return cmdCheckTx(cmd, actualArgs)
case "commit":
return cmdCommit(cmd, actualArgs)
case "deliver_tx":
return cmdDeliverTx(cmd, actualArgs)
case "finalize_block":
return cmdFinalizeBlock(cmd, actualArgs)
case "echo":
return cmdEcho(cmd, actualArgs)
case "info":
@@ -462,7 +483,7 @@ func cmdUnimplemented(cmd *cobra.Command, args []string) error {
fmt.Printf("%s: %s\n", echoCmd.Use, echoCmd.Short)
fmt.Printf("%s: %s\n", infoCmd.Use, infoCmd.Short)
fmt.Printf("%s: %s\n", checkTxCmd.Use, checkTxCmd.Short)
fmt.Printf("%s: %s\n", deliverTxCmd.Use, deliverTxCmd.Short)
fmt.Printf("%s: %s\n", finalizeBlockCmd.Use, finalizeBlockCmd.Short)
fmt.Printf("%s: %s\n", queryCmd.Use, queryCmd.Short)
fmt.Printf("%s: %s\n", commitCmd.Use, commitCmd.Short)
fmt.Println("Use \"[command] --help\" for more information about a command.")
@@ -476,13 +497,15 @@ func cmdEcho(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
msg = args[0]
}
res, err := client.EchoSync(msg)
res, err := client.Echo(cmd.Context(), msg)
if err != nil {
return err
}
printResponse(cmd, args, response{
Data: []byte(res.Message),
})
return nil
}
@@ -492,7 +515,7 @@ func cmdInfo(cmd *cobra.Command, args []string) error {
if len(args) == 1 {
version = args[0]
}
res, err := client.InfoSync(types.RequestInfo{Version: version})
res, err := client.Info(cmd.Context(), &types.RequestInfo{Version: version})
if err != nil {
return err
}
@@ -504,29 +527,40 @@ func cmdInfo(cmd *cobra.Command, args []string) error {
const codeBad uint32 = 10
// Append a new tx to application
func cmdDeliverTx(cmd *cobra.Command, args []string) error {
// Append new txs to application
func cmdFinalizeBlock(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
printResponse(cmd, args, response{
Code: codeBad,
Log: "want the tx",
Log: "Must provide at least one transaction",
})
return nil
}
txBytes, err := stringOrHexToBytes(args[0])
txs := make([][]byte, len(args))
for i, arg := range args {
txBytes, err := stringOrHexToBytes(arg)
if err != nil {
return err
}
txs[i] = txBytes
}
res, err := client.FinalizeBlock(cmd.Context(), &types.RequestFinalizeBlock{Txs: txs})
if err != nil {
return err
}
res, err := client.DeliverTxSync(types.RequestDeliverTx{Tx: txBytes})
if err != nil {
return err
resps := make([]response, 0, len(res.TxResults)+1)
for _, tx := range res.TxResults {
resps = append(resps, response{
Code: tx.Code,
Data: tx.Data,
Info: tx.Info,
Log: tx.Log,
})
}
printResponse(cmd, args, response{
Code: res.Code,
Data: res.Data,
Info: res.Info,
Log: res.Log,
resps = append(resps, response{
Data: res.AgreedAppData,
})
printResponse(cmd, args, resps...)
return nil
}
@@ -543,7 +577,7 @@ func cmdCheckTx(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
res, err := client.CheckTxSync(types.RequestCheckTx{Tx: txBytes})
res, err := client.CheckTx(cmd.Context(), &types.RequestCheckTx{Tx: txBytes})
if err != nil {
return err
}
@@ -558,13 +592,11 @@ func cmdCheckTx(cmd *cobra.Command, args []string) error {
// Get application Merkle root hash
func cmdCommit(cmd *cobra.Command, args []string) error {
res, err := client.CommitSync()
_, err := client.Commit(cmd.Context(), &types.RequestCommit{})
if err != nil {
return err
}
printResponse(cmd, args, response{
Data: res.Data,
})
printResponse(cmd, args, response{})
return nil
}
@@ -583,7 +615,7 @@ func cmdQuery(cmd *cobra.Command, args []string) error {
return err
}
resQuery, err := client.QuerySync(types.RequestQuery{
resQuery, err := client.Query(cmd.Context(), &types.RequestQuery{
Data: queryBytes,
Path: flagPath,
Height: int64(flagHeight),
@@ -617,7 +649,7 @@ func cmdPrepareProposal(cmd *cobra.Command, args []string) error {
txsBytesArray[i] = txBytes
}
res, err := client.PrepareProposalSync(types.RequestPrepareProposal{
res, err := client.PrepareProposal(cmd.Context(), &types.RequestPrepareProposal{
Txs: txsBytesArray,
// kvstore has to have this parameter in order not to reject a tx as the default value is 0
MaxTxBytes: 65536,
@@ -628,7 +660,7 @@ func cmdPrepareProposal(cmd *cobra.Command, args []string) error {
resps := make([]response, 0, len(res.Txs))
for _, tx := range res.Txs {
resps = append(resps, response{
Code: code.CodeTypeOK,
Code: 0, // CodeOK
Log: "Succeeded. Tx: " + string(tx),
})
}
@@ -648,7 +680,7 @@ func cmdProcessProposal(cmd *cobra.Command, args []string) error {
txsBytesArray[i] = txBytes
}
res, err := client.ProcessProposalSync(types.RequestProcessProposal{
res, err := client.ProcessProposal(cmd.Context(), &types.RequestProcessProposal{
Txs: txsBytesArray,
})
if err != nil {
@@ -673,8 +705,7 @@ func cmdKVStore(cmd *cobra.Command, args []string) error {
return err
}
}
app = kvstore.NewPersistentKVStoreApplication(flagPersist)
app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
app = kvstore.NewPersistentApplication(flagPersist)
// Start the listener
srv, err := server.NewServer(flagAddress, flagAbci, app)
@@ -716,9 +747,9 @@ func printResponse(cmd *cobra.Command, args []string, rsps ...response) {
}
if len(rsp.Data) != 0 {
// Do no print this line when using the commit command
// Do no print this line when using the finalize_block command
// because the string comes out as gibberish
if cmd.Use != "commit" {
if cmd.Use != "finalize_block" {
fmt.Printf("-> data: %s\n", rsp.Data)
}
fmt.Printf("-> data.hex: 0x%X\n", rsp.Data)
-3
View File
@@ -1,3 +0,0 @@
package example
// so the go tool doesn't return errors about no buildable go files ...
-187
View File
@@ -1,187 +0,0 @@
package example
import (
"fmt"
"math/rand"
"net"
"os"
"reflect"
"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"
abcicli "github.com/tendermint/tendermint/abci/client"
"github.com/tendermint/tendermint/abci/example/code"
"github.com/tendermint/tendermint/abci/example/kvstore"
abciserver "github.com/tendermint/tendermint/abci/server"
"github.com/tendermint/tendermint/abci/types"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
func TestKVStore(t *testing.T) {
fmt.Println("### Testing KVStore")
testStream(t, kvstore.NewApplication())
}
func TestBaseApp(t *testing.T) {
fmt.Println("### Testing BaseApp")
testStream(t, types.NewBaseApplication())
}
func TestGRPC(t *testing.T) {
fmt.Println("### Testing GRPC")
testGRPCSync(t, types.NewGRPCApplication(types.NewBaseApplication()))
}
func testStream(t *testing.T, app types.Application) {
numDeliverTxs := 20000
socketFile := fmt.Sprintf("test-%08x.sock", rand.Int31n(1<<30))
defer os.Remove(socketFile)
socket := fmt.Sprintf("unix://%v", socketFile)
// Start the listener
server := abciserver.NewSocketServer(socket, app)
server.SetLogger(log.TestingLogger().With("module", "abci-server"))
if err := server.Start(); err != nil {
require.NoError(t, err, "Error starting socket server")
}
t.Cleanup(func() {
if err := server.Stop(); err != nil {
t.Error(err)
}
})
// Connect to the socket
client := abcicli.NewSocketClient(socket, false)
client.SetLogger(log.TestingLogger().With("module", "abci-client"))
if err := client.Start(); err != nil {
t.Fatalf("Error starting socket client: %v", err.Error())
}
t.Cleanup(func() {
if err := client.Stop(); err != nil {
t.Error(err)
}
})
done := make(chan struct{})
counter := 0
client.SetResponseCallback(func(req *types.Request, res *types.Response) {
// Process response
switch r := res.Value.(type) {
case *types.Response_DeliverTx:
counter++
if r.DeliverTx.Code != code.CodeTypeOK {
t.Error("DeliverTx failed with ret_code", r.DeliverTx.Code)
}
if counter > numDeliverTxs {
t.Fatalf("Too many DeliverTx responses. Got %d, expected %d", counter, numDeliverTxs)
}
if counter == numDeliverTxs {
go func() {
time.Sleep(time.Second * 1) // Wait for a bit to allow counter overflow
close(done)
}()
return
}
case *types.Response_Flush:
// ignore
default:
t.Error("Unexpected response type", reflect.TypeOf(res.Value))
}
})
// Write requests
for counter := 0; counter < numDeliverTxs; counter++ {
// Send request
reqRes := client.DeliverTxAsync(types.RequestDeliverTx{Tx: []byte("test")})
_ = reqRes
// check err ?
// Sometimes send flush messages
if counter%123 == 0 {
client.FlushAsync()
// check err ?
}
}
// Send final flush message
client.FlushAsync()
<-done
}
//-------------------------
// test grpc
func dialerFunc(ctx context.Context, addr string) (net.Conn, error) {
return tmnet.Connect(addr)
}
func testGRPCSync(t *testing.T, app types.ABCIApplicationServer) {
numDeliverTxs := 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"))
if err := server.Start(); err != nil {
t.Fatalf("Error starting GRPC server: %v", err.Error())
}
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))
if err != nil {
t.Fatalf("Error dialing GRPC server: %v", err.Error())
}
t.Cleanup(func() {
if err := conn.Close(); err != nil {
t.Error(err)
}
})
client := types.NewABCIApplicationClient(conn)
// Write requests
for counter := 0; counter < numDeliverTxs; counter++ {
// Send request
response, err := client.DeliverTx(context.Background(), &types.RequestDeliverTx{Tx: []byte("test")})
if err != nil {
t.Fatalf("Error in GRPC DeliverTx: %v", err.Error())
}
counter++
if response.Code != code.CodeTypeOK {
t.Error("DeliverTx failed with ret_code", response.Code)
}
if counter > numDeliverTxs {
t.Fatal("Too many DeliverTx responses")
}
t.Log("response", counter)
if counter == numDeliverTxs {
go func() {
time.Sleep(time.Second * 1) // Wait for a bit to allow counter overflow
}()
}
}
}
+1 -15
View File
@@ -1,24 +1,10 @@
# KVStore
There are two app's here: the KVStoreApplication and the PersistentKVStoreApplication.
## KVStoreApplication
The KVStoreApplication is a simple merkle key-value store.
Transactions of the form `key=value` are stored as key-value pairs in the tree.
Transactions without an `=` sign set the value to the key.
The app has no replay protection (other than what the mempool provides).
## PersistentKVStoreApplication
The PersistentKVStoreApplication wraps the KVStoreApplication
and provides two additional features:
1) persistence of state across app restarts (using Tendermint's ABCI-Handshake mechanism)
2) validator set changes
The state is persisted in leveldb along with the last block committed,
and the Handshake allows any necessary blocks to be replayed.
Validator set changes are effected using the following transaction format:
```md
@@ -27,4 +13,4 @@ Validator set changes are effected using the following transaction format:
where `pubkeyN` is a base64-encoded 32-byte ed25519 key and `powerN` is a new voting power for the validator with `pubkeyN` (possibly a new one).
To remove a validator from the validator set, set power to `0`.
There is no sybil protection against new validators joining.
There is no sybil protection against new validators joining.
@@ -1,4 +1,4 @@
package code
package kvstore
// Return codes for the examples
const (
+5 -2
View File
@@ -1,6 +1,8 @@
package kvstore
import (
"context"
"github.com/tendermint/tendermint/abci/types"
tmrand "github.com/tendermint/tendermint/libs/rand"
)
@@ -29,8 +31,9 @@ func RandVals(cnt int) []types.ValidatorUpdate {
// InitKVStore initializes the kvstore app with some data,
// which allows tests to pass and is fine as long as you
// don't make any tx that modify the validator state
func InitKVStore(app *PersistentKVStoreApplication) {
app.InitChain(types.RequestInitChain{
func InitKVStore(ctx context.Context, app *Application) error {
_, err := app.InitChain(ctx, &types.RequestInitChain{
Validators: RandVals(1),
})
return err
}
+282 -37
View File
@@ -2,14 +2,20 @@ package kvstore
import (
"bytes"
"context"
"encoding/base64"
"encoding/binary"
"encoding/json"
"fmt"
"strconv"
"strings"
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/abci/example/code"
"github.com/tendermint/tendermint/abci/types"
cryptoencoding "github.com/tendermint/tendermint/crypto/encoding"
"github.com/tendermint/tendermint/libs/log"
cryptoproto "github.com/tendermint/tendermint/proto/tendermint/crypto"
"github.com/tendermint/tendermint/version"
)
@@ -17,7 +23,7 @@ var (
stateKey = []byte("stateKey")
kvPairPrefixKey = []byte("kvPairKey:")
ProtocolVersion uint64 = 0x1
AppVersion uint64 = 0x1
)
type State struct {
@@ -69,35 +75,75 @@ type Application struct {
state State
RetainBlocks int64 // blocks to retain after commit (via ResponseCommit.RetainHeight)
txToRemove map[string]struct{}
logger log.Logger
// validator set
ValUpdates []types.ValidatorUpdate
valAddrToPubKeyMap map[string]cryptoproto.PublicKey
}
func NewApplication() *Application {
state := loadState(dbm.NewMemDB())
return &Application{state: state}
func NewApplication(db dbm.DB) *Application {
return &Application{
logger: log.NewNopLogger(),
state: loadState(db),
valAddrToPubKeyMap: make(map[string]cryptoproto.PublicKey),
}
}
func (app *Application) Info(req types.RequestInfo) (resInfo types.ResponseInfo) {
return types.ResponseInfo{
func NewPersistentApplication(dbDir string) *Application {
name := "kvstore"
db, err := dbm.NewGoLevelDB(name, dbDir)
if err != nil {
panic(err)
}
return NewApplication(db)
}
func NewInMemoryApplication() *Application {
return NewApplication(dbm.NewMemDB())
}
func (app *Application) InitChain(_ context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) {
for _, v := range req.Validators {
r := app.updateValidator(v)
if r.IsErr() {
app.logger.Error("error updating validators", "r", r)
panic("problem updating validators")
}
}
return &types.ResponseInitChain{}, nil
}
func (app *Application) Info(_ context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
return &types.ResponseInfo{
Data: fmt.Sprintf("{\"size\":%v}", app.state.Size),
Version: version.ABCIVersion,
AppVersion: ProtocolVersion,
AppVersion: AppVersion,
LastBlockHeight: app.state.Height,
LastBlockAppHash: app.state.AppHash,
}
}, nil
}
// tx is either "key=value" or just arbitrary bytes
func (app *Application) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
if isReplacedTx(req.Tx) {
app.txToRemove[string(req.Tx)] = struct{}{}
// tx is either "val:pubkey!power" or "key=value" or just arbitrary bytes
func (app *Application) handleTx(tx []byte) *types.ExecTxResult {
// if it starts with "val:", update the validator set
// format is "val:pubkey!power"
if isValidatorTx(tx) {
// update validators in the merkle tree
// and in app.ValUpdates
return app.execValidatorTx(tx)
}
var key, value string
parts := bytes.Split(req.Tx, []byte("="))
if isPrepareTx(tx) {
return app.execPrepareTx(tx)
}
var key, value string
parts := bytes.Split(tx, []byte("="))
if len(parts) == 2 {
key, value = string(parts[0]), string(parts[1])
} else {
key, value = string(req.Tx), string(req.Tx)
key, value = string(tx), string(tx)
}
err := app.state.db.Set(prefixKey([]byte(key)), []byte(value))
@@ -118,42 +164,91 @@ func (app *Application) DeliverTx(req types.RequestDeliverTx) types.ResponseDeli
},
}
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Events: events}
return &types.ExecTxResult{Code: CodeTypeOK, Events: events}
}
func (app *Application) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx {
if req.Type == types.CheckTxType_Recheck {
if _, ok := app.txToRemove[string(req.Tx)]; ok {
return types.ResponseCheckTx{Code: code.CodeTypeExecuted, GasWanted: 1}
func (app *Application) Close() error {
return app.state.db.Close()
}
func (app *Application) FinalizeBlock(_ context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
// reset valset changes
app.ValUpdates = make([]types.ValidatorUpdate, 0)
// Punish validators who committed equivocation.
for _, ev := range req.Misbehavior {
if ev.Type == types.MisbehaviorType_DUPLICATE_VOTE {
addr := string(ev.Validator.Address)
if pubKey, ok := app.valAddrToPubKeyMap[addr]; ok {
app.updateValidator(types.ValidatorUpdate{
PubKey: pubKey,
Power: ev.Validator.Power - 1,
})
app.logger.Info("Decreased val power by 1 because of the equivocation",
"val", addr)
} else {
panic(fmt.Errorf("wanted to punish val %q but can't find it", addr))
}
}
}
return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
}
func (app *Application) Commit() types.ResponseCommit {
respTxs := make([]*types.ExecTxResult, len(req.Txs))
for i, tx := range req.Txs {
respTxs[i] = app.handleTx(tx)
}
// Using a memdb - just return the big endian size of the db
appHash := make([]byte, 8)
binary.PutVarint(appHash, app.state.Size)
app.state.AppHash = appHash
app.state.Height++
return &types.ResponseFinalizeBlock{TxResults: respTxs, ValidatorUpdates: app.ValUpdates, AgreedAppData: appHash}, nil
}
func (app *Application) CheckTx(_ context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
if req.Type == types.CheckTxType_Recheck {
if _, ok := app.txToRemove[string(req.Tx)]; ok {
return &types.ResponseCheckTx{Code: CodeTypeExecuted, GasWanted: 1}, nil
}
}
return &types.ResponseCheckTx{Code: CodeTypeOK, GasWanted: 1}, nil
}
func (app *Application) Commit(_ context.Context, _ *types.RequestCommit) (*types.ResponseCommit, error) {
// empty out the set of transactions to remove via rechecktx
saveState(app.state)
resp := types.ResponseCommit{Data: appHash}
resp := &types.ResponseCommit{}
if app.RetainBlocks > 0 && app.state.Height >= app.RetainBlocks {
resp.RetainHeight = app.state.Height - app.RetainBlocks + 1
}
return resp
return resp, nil
}
// Returns an associated value or nil if missing.
func (app *Application) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
func (app *Application) Query(_ context.Context, reqQuery *types.RequestQuery) (*types.ResponseQuery, error) {
resQuery := &types.ResponseQuery{}
if reqQuery.Path == "/val" {
key := []byte("val:" + string(reqQuery.Data))
value, err := app.state.db.Get(key)
if err != nil {
panic(err)
}
return &types.ResponseQuery{
Key: reqQuery.Data,
Value: value,
}, nil
}
if reqQuery.Prove {
value, err := app.state.db.Get(prefixKey(reqQuery.Data))
if err != nil {
panic(err)
}
if value == nil {
resQuery.Log = "does not exist"
} else {
@@ -164,7 +259,7 @@ func (app *Application) Query(reqQuery types.RequestQuery) (resQuery types.Respo
resQuery.Value = value
resQuery.Height = app.state.Height
return
return resQuery, nil
}
resQuery.Key = reqQuery.Data
@@ -180,20 +275,170 @@ func (app *Application) Query(reqQuery types.RequestQuery) (resQuery types.Respo
resQuery.Value = value
resQuery.Height = app.state.Height
return resQuery
return resQuery, nil
}
func (app *Application) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
app.txToRemove = map[string]struct{}{}
return types.ResponseBeginBlock{}
func (app *Application) PrepareProposal(_ context.Context, req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
return &types.ResponsePrepareProposal{Txs: app.substPrepareTx(req.Txs, req.MaxTxBytes)}, nil
}
func (app *Application) ProcessProposal(
req types.RequestProcessProposal) types.ResponseProcessProposal {
func (app *Application) ProcessProposal(_ context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
for _, tx := range req.Txs {
if len(tx) == 0 {
return types.ResponseProcessProposal{Status: types.ResponseProcessProposal_REJECT}
if len(tx) == 0 || isPrepareTx(tx) {
return &types.ResponseProcessProposal{Status: types.ResponseProcessProposal_REJECT}, nil
}
}
return types.ResponseProcessProposal{Status: types.ResponseProcessProposal_ACCEPT}
return &types.ResponseProcessProposal{Status: types.ResponseProcessProposal_ACCEPT}, nil
}
//---------------------------------------------
// update validators
func (app *Application) Validators() (validators []types.ValidatorUpdate) {
itr, err := app.state.db.Iterator(nil, nil)
if err != nil {
panic(err)
}
for ; itr.Valid(); itr.Next() {
if isValidatorTx(itr.Key()) {
validator := new(types.ValidatorUpdate)
err := types.ReadMessage(bytes.NewBuffer(itr.Value()), validator)
if err != nil {
panic(err)
}
validators = append(validators, *validator)
}
}
if err = itr.Error(); err != nil {
panic(err)
}
return
}
func MakeValSetChangeTx(pubkey cryptoproto.PublicKey, power int64) []byte {
pk, err := cryptoencoding.PubKeyFromProto(pubkey)
if err != nil {
panic(err)
}
pubStr := base64.StdEncoding.EncodeToString(pk.Bytes())
return []byte(fmt.Sprintf("val:%s!%d", pubStr, power))
}
func isValidatorTx(tx []byte) bool {
return strings.HasPrefix(string(tx), ValidatorSetChangePrefix)
}
// format is "val:pubkey!power"
// pubkey is a base64-encoded 32-byte ed25519 key
func (app *Application) execValidatorTx(tx []byte) *types.ExecTxResult {
tx = tx[len(ValidatorSetChangePrefix):]
// get the pubkey and power
pubKeyAndPower := strings.Split(string(tx), "!")
if len(pubKeyAndPower) != 2 {
return &types.ExecTxResult{
Code: CodeTypeEncodingError,
Log: fmt.Sprintf("Expected 'pubkey!power'. Got %v", pubKeyAndPower)}
}
pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1]
// decode the pubkey
pubkey, err := base64.StdEncoding.DecodeString(pubkeyS)
if err != nil {
return &types.ExecTxResult{
Code: CodeTypeEncodingError,
Log: fmt.Sprintf("Pubkey (%s) is invalid base64", pubkeyS)}
}
// decode the power
power, err := strconv.ParseInt(powerS, 10, 64)
if err != nil {
return &types.ExecTxResult{
Code: CodeTypeEncodingError,
Log: fmt.Sprintf("Power (%s) is not an int", powerS)}
}
// update
return app.updateValidator(types.UpdateValidator(pubkey, power, ""))
}
// add, update, or remove a validator
func (app *Application) updateValidator(v types.ValidatorUpdate) *types.ExecTxResult {
pubkey, err := cryptoencoding.PubKeyFromProto(v.PubKey)
if err != nil {
panic(fmt.Errorf("can't decode public key: %w", err))
}
key := []byte("val:" + string(pubkey.Bytes()))
if v.Power == 0 {
// remove validator
hasKey, err := app.state.db.Has(key)
if err != nil {
panic(err)
}
if !hasKey {
pubStr := base64.StdEncoding.EncodeToString(pubkey.Bytes())
return &types.ExecTxResult{
Code: CodeTypeUnauthorized,
Log: fmt.Sprintf("Cannot remove non-existent validator %s", pubStr)}
}
if err = app.state.db.Delete(key); err != nil {
panic(err)
}
delete(app.valAddrToPubKeyMap, string(pubkey.Address()))
} else {
// add or update validator
value := bytes.NewBuffer(make([]byte, 0))
if err := types.WriteMessage(&v, value); err != nil {
return &types.ExecTxResult{
Code: CodeTypeEncodingError,
Log: fmt.Sprintf("error encoding validator: %v", err)}
}
if err = app.state.db.Set(key, value.Bytes()); err != nil {
panic(err)
}
app.valAddrToPubKeyMap[string(pubkey.Address())] = v.PubKey
}
// we only update the changes array if we successfully updated the tree
app.ValUpdates = append(app.ValUpdates, v)
return &types.ExecTxResult{Code: CodeTypeOK}
}
// -----------------------------
const (
PreparePrefix = "prepare"
ReplacePrefix = "replace"
ValidatorSetChangePrefix string = "val:"
)
func isPrepareTx(tx []byte) bool { return bytes.HasPrefix(tx, []byte(PreparePrefix)) }
// execPrepareTx is noop. tx data is considered as placeholder
// and is substitute at the PrepareProposal.
func (app *Application) execPrepareTx(tx []byte) *types.ExecTxResult {
// noop
return &types.ExecTxResult{}
}
// substPrepareTx substitutes all the transactions prefixed with 'prepare' in the
// proposal for transactions with the prefix stripped.
func (app *Application) substPrepareTx(blockData [][]byte, maxTxBytes int64) [][]byte {
txs := make([][]byte, 0, len(blockData))
var totalBytes int64
for _, tx := range blockData {
txMod := tx
if isPrepareTx(tx) {
txMod = bytes.Replace(tx, []byte(PreparePrefix), []byte(ReplacePrefix), 1)
}
totalBytes += int64(len(txMod))
if totalBytes > maxTxBytes {
break
}
txs = append(txs, txMod)
}
return txs
}
+139 -128
View File
@@ -1,8 +1,8 @@
package kvstore
import (
"context"
"fmt"
"os"
"sort"
"testing"
@@ -12,10 +12,8 @@ import (
"github.com/tendermint/tendermint/libs/service"
abcicli "github.com/tendermint/tendermint/abci/client"
"github.com/tendermint/tendermint/abci/example/code"
abciserver "github.com/tendermint/tendermint/abci/server"
"github.com/tendermint/tendermint/abci/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)
const (
@@ -23,79 +21,89 @@ const (
testValue = "def"
)
func testKVStore(t *testing.T, app types.Application, tx []byte, key, value string) {
req := types.RequestDeliverTx{Tx: tx}
ar := app.DeliverTx(req)
require.False(t, ar.IsErr(), ar)
// repeating tx doesn't raise error
ar = app.DeliverTx(req)
require.False(t, ar.IsErr(), ar)
// commit
app.Commit()
func TestKVStoreKV(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
info := app.Info(types.RequestInfo{})
kvstore := NewInMemoryApplication()
key := testKey
value := key
tx := []byte(key)
testKVStore(ctx, t, kvstore, tx, key, value)
value = testValue
tx = []byte(key + "=" + value)
testKVStore(ctx, t, kvstore, tx, key, value)
}
func testKVStore(ctx context.Context, t *testing.T, app types.Application, tx []byte, key, value string) {
req := &types.RequestFinalizeBlock{Txs: [][]byte{tx}}
ar, err := app.FinalizeBlock(ctx, req)
require.NoError(t, err)
require.Equal(t, 1, len(ar.TxResults))
require.False(t, ar.TxResults[0].IsErr())
// repeating tx doesn't raise error
ar, err = app.FinalizeBlock(ctx, req)
require.NoError(t, err)
require.Equal(t, 1, len(ar.TxResults))
require.False(t, ar.TxResults[0].IsErr())
// commit
_, err = app.Commit(ctx, &types.RequestCommit{})
require.NoError(t, err)
info, err := app.Info(ctx, &types.RequestInfo{})
require.NoError(t, err)
require.NotZero(t, info.LastBlockHeight)
// make sure query is fine
resQuery := app.Query(types.RequestQuery{
resQuery, err := app.Query(ctx, &types.RequestQuery{
Path: "/store",
Data: []byte(key),
})
require.Equal(t, code.CodeTypeOK, resQuery.Code)
require.NoError(t, err)
require.Equal(t, CodeTypeOK, resQuery.Code)
require.Equal(t, key, string(resQuery.Key))
require.Equal(t, value, string(resQuery.Value))
require.EqualValues(t, info.LastBlockHeight, resQuery.Height)
// make sure proof is fine
resQuery = app.Query(types.RequestQuery{
resQuery, err = app.Query(ctx, &types.RequestQuery{
Path: "/store",
Data: []byte(key),
Prove: true,
})
require.EqualValues(t, code.CodeTypeOK, resQuery.Code)
require.NoError(t, err)
require.EqualValues(t, CodeTypeOK, resQuery.Code)
require.Equal(t, key, string(resQuery.Key))
require.Equal(t, value, string(resQuery.Value))
require.EqualValues(t, info.LastBlockHeight, resQuery.Height)
}
func TestKVStoreKV(t *testing.T) {
kvstore := NewApplication()
key := testKey
value := key
tx := []byte(key)
testKVStore(t, kvstore, tx, key, value)
value = testValue
tx = []byte(key + "=" + value)
testKVStore(t, kvstore, tx, key, value)
}
func TestPersistentKVStoreKV(t *testing.T) {
dir, err := os.MkdirTemp("/tmp", "abci-kvstore-test") // TODO
if err != nil {
t.Fatal(err)
}
kvstore := NewPersistentKVStoreApplication(dir)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
kvstore := NewPersistentApplication(t.TempDir())
key := testKey
value := key
tx := []byte(key)
testKVStore(t, kvstore, tx, key, value)
testKVStore(ctx, t, kvstore, tx, key, value)
value = testValue
tx = []byte(key + "=" + value)
testKVStore(t, kvstore, tx, key, value)
testKVStore(ctx, t, kvstore, tx, key, value)
}
func TestPersistentKVStoreInfo(t *testing.T) {
dir, err := os.MkdirTemp("/tmp", "abci-kvstore-test") // TODO
if err != nil {
t.Fatal(err)
}
kvstore := NewPersistentKVStoreApplication(dir)
InitKVStore(kvstore)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
kvstore := NewPersistentApplication(t.TempDir())
require.NoError(t, InitKVStore(ctx, kvstore))
height := int64(0)
resInfo := kvstore.Info(types.RequestInfo{})
resInfo, err := kvstore.Info(ctx, &types.RequestInfo{})
require.NoError(t, err)
if resInfo.LastBlockHeight != height {
t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight)
}
@@ -103,36 +111,35 @@ func TestPersistentKVStoreInfo(t *testing.T) {
// make and apply block
height = int64(1)
hash := []byte("foo")
header := tmproto.Header{
Height: height,
if _, err := kvstore.FinalizeBlock(ctx, &types.RequestFinalizeBlock{Hash: hash, Height: height}); err != nil {
t.Fatal(err)
}
kvstore.BeginBlock(types.RequestBeginBlock{Hash: hash, Header: header})
kvstore.EndBlock(types.RequestEndBlock{Height: header.Height})
kvstore.Commit()
resInfo = kvstore.Info(types.RequestInfo{})
if resInfo.LastBlockHeight != height {
t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight)
}
_, err = kvstore.Commit(ctx, &types.RequestCommit{})
require.NoError(t, err)
resInfo, err = kvstore.Info(ctx, &types.RequestInfo{})
require.NoError(t, err)
require.Equal(t, height, resInfo.LastBlockHeight)
}
// add a validator, remove a validator, update a validator
func TestValUpdates(t *testing.T) {
dir, err := os.MkdirTemp("/tmp", "abci-kvstore-test") // TODO
if err != nil {
t.Fatal(err)
}
kvstore := NewPersistentKVStoreApplication(dir)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
kvstore := NewInMemoryApplication()
// init with some validators
total := 10
nInit := 5
vals := RandVals(total)
// initialize with the first nInit
kvstore.InitChain(types.RequestInitChain{
_, err := kvstore.InitChain(ctx, &types.RequestInitChain{
Validators: vals[:nInit],
})
require.NoError(t, err)
vals1, vals2 := vals[:nInit], kvstore.Validators()
valsEqual(t, vals1, vals2)
@@ -145,7 +152,7 @@ func TestValUpdates(t *testing.T) {
tx1 := MakeValSetChangeTx(v1.PubKey, v1.Power)
tx2 := MakeValSetChangeTx(v2.PubKey, v2.Power)
makeApplyBlock(t, kvstore, 1, diff, tx1, tx2)
makeApplyBlock(ctx, t, kvstore, 1, diff, tx1, tx2)
vals1, vals2 = vals[:nInit+2], kvstore.Validators()
valsEqual(t, vals1, vals2)
@@ -160,7 +167,7 @@ func TestValUpdates(t *testing.T) {
tx2 = MakeValSetChangeTx(v2.PubKey, v2.Power)
tx3 := MakeValSetChangeTx(v3.PubKey, v3.Power)
makeApplyBlock(t, kvstore, 2, diff, tx1, tx2, tx3)
makeApplyBlock(ctx, t, kvstore, 2, diff, tx1, tx2, tx3)
vals1 = append(vals[:nInit-2], vals[nInit+1]) //nolint: gocritic
vals2 = kvstore.Validators()
@@ -176,7 +183,7 @@ func TestValUpdates(t *testing.T) {
diff = []types.ValidatorUpdate{v1}
tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power)
makeApplyBlock(t, kvstore, 3, diff, tx1)
makeApplyBlock(ctx, t, kvstore, 3, diff, tx1)
vals1 = append([]types.ValidatorUpdate{v1}, vals1[1:]...)
vals2 = kvstore.Validators()
@@ -184,7 +191,49 @@ func TestValUpdates(t *testing.T) {
}
func TestClientServer(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// set up socket app
kvstore := NewInMemoryApplication()
client, server, err := makeSocketClientServer(kvstore, "kvstore-socket")
require.NoError(t, err)
t.Cleanup(func() {
if err := server.Stop(); err != nil {
t.Error(err)
}
})
t.Cleanup(func() {
if err := client.Stop(); err != nil {
t.Error(err)
}
})
runClientTests(ctx, t, client)
// set up grpc app
kvstore = NewInMemoryApplication()
gclient, gserver, err := makeGRPCClientServer(kvstore, "/tmp/kvstore-grpc")
require.NoError(t, err)
t.Cleanup(func() {
cancel()
if err := gserver.Stop(); err != nil {
t.Error(err)
}
})
t.Cleanup(func() {
cancel()
if err := gclient.Stop(); err != nil {
t.Error(err)
}
})
runClientTests(ctx, t, gclient)
}
func makeApplyBlock(
ctx context.Context,
t *testing.T,
kvstore types.Application,
heightInt int,
@@ -193,25 +242,23 @@ func makeApplyBlock(
// make and apply block
height := int64(heightInt)
hash := []byte("foo")
header := tmproto.Header{
resFinalizeBlock, err := kvstore.FinalizeBlock(ctx, &types.RequestFinalizeBlock{
Hash: hash,
Height: height,
}
Txs: txs,
})
require.NoError(t, err)
kvstore.BeginBlock(types.RequestBeginBlock{Hash: hash, Header: header})
for _, tx := range txs {
if r := kvstore.DeliverTx(types.RequestDeliverTx{Tx: tx}); r.IsErr() {
t.Fatal(r)
}
}
resEndBlock := kvstore.EndBlock(types.RequestEndBlock{Height: header.Height})
kvstore.Commit()
_, err = kvstore.Commit(ctx, &types.RequestCommit{})
require.NoError(t, err)
valsEqual(t, diff, resEndBlock.ValidatorUpdates)
valsEqual(t, diff, resFinalizeBlock.ValidatorUpdates)
}
// order doesn't matter
func valsEqual(t *testing.T, vals1, vals2 []types.ValidatorUpdate) {
t.Helper()
if len(vals1) != len(vals2) {
t.Fatalf("vals dont match in len. got %d, expected %d", len(vals2), len(vals1))
}
@@ -255,8 +302,7 @@ func makeGRPCClientServer(app types.Application, name string) (abcicli.Client, s
socket := fmt.Sprintf("unix://%s.sock", name)
logger := log.TestingLogger()
gapp := types.NewGRPCApplication(app)
server := abciserver.NewGRPCServer(socket, gapp)
server := abciserver.NewGRPCServer(socket, app)
server.SetLogger(logger.With("module", "abci-server"))
if err := server.Start(); err != nil {
return nil, nil, err
@@ -273,90 +319,55 @@ func makeGRPCClientServer(app types.Application, name string) (abcicli.Client, s
return client, server, nil
}
func TestClientServer(t *testing.T) {
// set up socket app
kvstore := NewApplication()
client, server, err := makeSocketClientServer(kvstore, "kvstore-socket")
require.NoError(t, err)
t.Cleanup(func() {
if err := server.Stop(); err != nil {
t.Error(err)
}
})
t.Cleanup(func() {
if err := client.Stop(); err != nil {
t.Error(err)
}
})
runClientTests(t, client)
// set up grpc app
kvstore = NewApplication()
gclient, gserver, err := makeGRPCClientServer(kvstore, "/tmp/kvstore-grpc")
require.NoError(t, err)
t.Cleanup(func() {
if err := gserver.Stop(); err != nil {
t.Error(err)
}
})
t.Cleanup(func() {
if err := gclient.Stop(); err != nil {
t.Error(err)
}
})
runClientTests(t, gclient)
}
func runClientTests(t *testing.T, client abcicli.Client) {
func runClientTests(ctx context.Context, t *testing.T, client abcicli.Client) {
// run some tests....
key := testKey
value := key
tx := []byte(key)
testClient(t, client, tx, key, value)
testClient(ctx, t, client, tx, key, value)
value = testValue
tx = []byte(key + "=" + value)
testClient(t, client, tx, key, value)
testClient(ctx, t, client, tx, key, value)
}
func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) {
ar, err := app.DeliverTxSync(types.RequestDeliverTx{Tx: tx})
func testClient(ctx context.Context, t *testing.T, app abcicli.Client, tx []byte, key, value string) {
ar, err := app.FinalizeBlock(ctx, &types.RequestFinalizeBlock{Txs: [][]byte{tx}})
require.NoError(t, err)
require.False(t, ar.IsErr(), ar)
// repeating tx doesn't raise error
ar, err = app.DeliverTxSync(types.RequestDeliverTx{Tx: tx})
require.Equal(t, 1, len(ar.TxResults))
require.False(t, ar.TxResults[0].IsErr())
// repeating FinalizeBlock doesn't raise error
ar, err = app.FinalizeBlock(ctx, &types.RequestFinalizeBlock{Txs: [][]byte{tx}})
require.NoError(t, err)
require.False(t, ar.IsErr(), ar)
require.Equal(t, 1, len(ar.TxResults))
require.False(t, ar.TxResults[0].IsErr())
// commit
_, err = app.CommitSync()
_, err = app.Commit(ctx, &types.RequestCommit{})
require.NoError(t, err)
info, err := app.InfoSync(types.RequestInfo{})
info, err := app.Info(ctx, &types.RequestInfo{})
require.NoError(t, err)
require.NotZero(t, info.LastBlockHeight)
// make sure query is fine
resQuery, err := app.QuerySync(types.RequestQuery{
resQuery, err := app.Query(ctx, &types.RequestQuery{
Path: "/store",
Data: []byte(key),
})
require.Nil(t, err)
require.Equal(t, code.CodeTypeOK, resQuery.Code)
require.NoError(t, err)
require.Equal(t, CodeTypeOK, resQuery.Code)
require.Equal(t, key, string(resQuery.Key))
require.Equal(t, value, string(resQuery.Value))
require.EqualValues(t, info.LastBlockHeight, resQuery.Height)
// make sure proof is fine
resQuery, err = app.QuerySync(types.RequestQuery{
resQuery, err = app.Query(ctx, &types.RequestQuery{
Path: "/store",
Data: []byte(key),
Prove: true,
})
require.Nil(t, err)
require.Equal(t, code.CodeTypeOK, resQuery.Code)
require.NoError(t, err)
require.Equal(t, CodeTypeOK, resQuery.Code)
require.Equal(t, key, string(resQuery.Key))
require.Equal(t, value, string(resQuery.Value))
require.EqualValues(t, info.LastBlockHeight, resQuery.Height)
-343
View File
@@ -1,343 +0,0 @@
package kvstore
import (
"bytes"
"encoding/base64"
"fmt"
"strconv"
"strings"
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/abci/example/code"
"github.com/tendermint/tendermint/abci/types"
cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
"github.com/tendermint/tendermint/libs/log"
pc "github.com/tendermint/tendermint/proto/tendermint/crypto"
)
const (
ValidatorSetChangePrefix string = "val:"
)
//-----------------------------------------
var _ types.Application = (*PersistentKVStoreApplication)(nil)
type PersistentKVStoreApplication struct {
app *Application
// validator set
ValUpdates []types.ValidatorUpdate
valAddrToPubKeyMap map[string]pc.PublicKey
logger log.Logger
}
func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication {
name := "kvstore"
db, err := dbm.NewGoLevelDB(name, dbDir)
if err != nil {
panic(err)
}
state := loadState(db)
return &PersistentKVStoreApplication{
app: &Application{
state: state,
txToRemove: map[string]struct{}{},
},
valAddrToPubKeyMap: make(map[string]pc.PublicKey),
logger: log.NewNopLogger(),
}
}
func (app *PersistentKVStoreApplication) SetLogger(l log.Logger) {
app.logger = l
}
func (app *PersistentKVStoreApplication) Info(req types.RequestInfo) types.ResponseInfo {
res := app.app.Info(req)
res.LastBlockHeight = app.app.state.Height
res.LastBlockAppHash = app.app.state.AppHash
return res
}
// tx is either "val:pubkey!power" or "key=value" or just arbitrary bytes
func (app *PersistentKVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
// if it starts with "val:", update the validator set
// format is "val:pubkey!power"
if isValidatorTx(req.Tx) {
// update validators in the merkle tree
// and in app.ValUpdates
return app.execValidatorTx(req.Tx)
}
if isPrepareTx(req.Tx) {
return app.execPrepareTx(req.Tx)
}
// otherwise, update the key-value store
return app.app.DeliverTx(req)
}
func (app *PersistentKVStoreApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx {
return app.app.CheckTx(req)
}
// Commit will panic if InitChain was not called
func (app *PersistentKVStoreApplication) Commit() types.ResponseCommit {
return app.app.Commit()
}
// When path=/val and data={validator address}, returns the validator update (types.ValidatorUpdate) varint encoded.
// For any other path, returns an associated value or nil if missing.
func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) {
switch reqQuery.Path {
case "/val":
key := []byte("val:" + string(reqQuery.Data))
value, err := app.app.state.db.Get(key)
if err != nil {
panic(err)
}
resQuery.Key = reqQuery.Data
resQuery.Value = value
return
default:
return app.app.Query(reqQuery)
}
}
// Save the validators in the merkle tree
func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) types.ResponseInitChain {
for _, v := range req.Validators {
r := app.updateValidator(v)
if r.IsErr() {
app.logger.Error("Error updating validators", "r", r)
}
}
return types.ResponseInitChain{}
}
// Track the block hash and header information
func (app *PersistentKVStoreApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
// reset valset changes
app.ValUpdates = make([]types.ValidatorUpdate, 0)
// Punish validators who committed equivocation.
for _, ev := range req.ByzantineValidators {
if ev.Type == types.MisbehaviorType_DUPLICATE_VOTE {
addr := string(ev.Validator.Address)
if pubKey, ok := app.valAddrToPubKeyMap[addr]; ok {
app.updateValidator(types.ValidatorUpdate{
PubKey: pubKey,
Power: ev.Validator.Power - 1,
})
app.logger.Info("Decreased val power by 1 because of the equivocation",
"val", addr)
} else {
app.logger.Error("Wanted to punish val, but can't find it",
"val", addr)
}
}
}
return app.app.BeginBlock(req)
}
// Update the validator set
func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock {
return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates}
}
func (app *PersistentKVStoreApplication) ListSnapshots(
req types.RequestListSnapshots) types.ResponseListSnapshots {
return types.ResponseListSnapshots{}
}
func (app *PersistentKVStoreApplication) LoadSnapshotChunk(
req types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk {
return types.ResponseLoadSnapshotChunk{}
}
func (app *PersistentKVStoreApplication) OfferSnapshot(
req types.RequestOfferSnapshot) types.ResponseOfferSnapshot {
return types.ResponseOfferSnapshot{Result: types.ResponseOfferSnapshot_ABORT}
}
func (app *PersistentKVStoreApplication) ApplySnapshotChunk(
req types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk {
return types.ResponseApplySnapshotChunk{Result: types.ResponseApplySnapshotChunk_ABORT}
}
func (app *PersistentKVStoreApplication) PrepareProposal(
req types.RequestPrepareProposal) types.ResponsePrepareProposal {
return types.ResponsePrepareProposal{Txs: app.substPrepareTx(req.Txs, req.MaxTxBytes)}
}
func (app *PersistentKVStoreApplication) ProcessProposal(
req types.RequestProcessProposal) types.ResponseProcessProposal {
for _, tx := range req.Txs {
if len(tx) == 0 || isPrepareTx(tx) {
return types.ResponseProcessProposal{Status: types.ResponseProcessProposal_REJECT}
}
}
return types.ResponseProcessProposal{Status: types.ResponseProcessProposal_ACCEPT}
}
//---------------------------------------------
// update validators
func (app *PersistentKVStoreApplication) Validators() (validators []types.ValidatorUpdate) {
itr, err := app.app.state.db.Iterator(nil, nil)
if err != nil {
panic(err)
}
for ; itr.Valid(); itr.Next() {
if isValidatorTx(itr.Key()) {
validator := new(types.ValidatorUpdate)
err := types.ReadMessage(bytes.NewBuffer(itr.Value()), validator)
if err != nil {
panic(err)
}
validators = append(validators, *validator)
}
}
if err = itr.Error(); err != nil {
panic(err)
}
return
}
func MakeValSetChangeTx(pubkey pc.PublicKey, power int64) []byte {
pk, err := cryptoenc.PubKeyFromProto(pubkey)
if err != nil {
panic(err)
}
pubStr := base64.StdEncoding.EncodeToString(pk.Bytes())
return []byte(fmt.Sprintf("val:%s!%d", pubStr, power))
}
func isValidatorTx(tx []byte) bool {
return strings.HasPrefix(string(tx), ValidatorSetChangePrefix)
}
// format is "val:pubkey!power"
// pubkey is a base64-encoded 32-byte ed25519 key
func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx {
tx = tx[len(ValidatorSetChangePrefix):]
// get the pubkey and power
pubKeyAndPower := strings.Split(string(tx), "!")
if len(pubKeyAndPower) != 2 {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Expected 'pubkey!power'. Got %v", pubKeyAndPower)}
}
pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1]
// decode the pubkey
pubkey, err := base64.StdEncoding.DecodeString(pubkeyS)
if err != nil {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Pubkey (%s) is invalid base64", pubkeyS)}
}
// decode the power
power, err := strconv.ParseInt(powerS, 10, 64)
if err != nil {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Power (%s) is not an int", powerS)}
}
// update
return app.updateValidator(types.UpdateValidator(pubkey, power, ""))
}
// add, update, or remove a validator
func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx {
pubkey, err := cryptoenc.PubKeyFromProto(v.PubKey)
if err != nil {
panic(fmt.Errorf("can't decode public key: %w", err))
}
key := []byte("val:" + string(pubkey.Bytes()))
if v.Power == 0 {
// remove validator
hasKey, err := app.app.state.db.Has(key)
if err != nil {
panic(err)
}
if !hasKey {
pubStr := base64.StdEncoding.EncodeToString(pubkey.Bytes())
return types.ResponseDeliverTx{
Code: code.CodeTypeUnauthorized,
Log: fmt.Sprintf("Cannot remove non-existent validator %s", pubStr)}
}
if err = app.app.state.db.Delete(key); err != nil {
panic(err)
}
delete(app.valAddrToPubKeyMap, string(pubkey.Address()))
} else {
// add or update validator
value := bytes.NewBuffer(make([]byte, 0))
if err := types.WriteMessage(&v, value); err != nil {
return types.ResponseDeliverTx{
Code: code.CodeTypeEncodingError,
Log: fmt.Sprintf("Error encoding validator: %v", err)}
}
if err = app.app.state.db.Set(key, value.Bytes()); err != nil {
panic(err)
}
app.valAddrToPubKeyMap[string(pubkey.Address())] = v.PubKey
}
// we only update the changes array if we successfully updated the tree
app.ValUpdates = append(app.ValUpdates, v)
return types.ResponseDeliverTx{Code: code.CodeTypeOK}
}
// -----------------------------
const (
PreparePrefix = "prepare"
ReplacePrefix = "replace"
)
func isPrepareTx(tx []byte) bool { return bytes.HasPrefix(tx, []byte(PreparePrefix)) }
func isReplacedTx(tx []byte) bool {
return bytes.HasPrefix(tx, []byte(ReplacePrefix))
}
// execPrepareTx is noop. tx data is considered as placeholder
// and is substitute at the PrepareProposal.
func (app *PersistentKVStoreApplication) execPrepareTx(tx []byte) types.ResponseDeliverTx {
// noop
return types.ResponseDeliverTx{}
}
// substPrepareTx substitutes all the transactions prefixed with 'prepare' in the
// proposal for transactions with the prefix stripped.
func (app *PersistentKVStoreApplication) substPrepareTx(blockData [][]byte, maxTxBytes int64) [][]byte {
txs := make([][]byte, 0, len(blockData))
var totalBytes int64
for _, tx := range blockData {
txMod := tx
if isPrepareTx(tx) {
txMod = bytes.Replace(tx, []byte(PreparePrefix), []byte(ReplacePrefix), 1)
}
totalBytes += int64(len(txMod))
if totalBytes > maxTxBytes {
break
}
txs = append(txs, txMod)
}
return txs
}
+19 -3
View File
@@ -1,6 +1,7 @@
package server
import (
"context"
"net"
"google.golang.org/grpc"
@@ -18,11 +19,11 @@ type GRPCServer struct {
listener net.Listener
server *grpc.Server
app types.ABCIApplicationServer
app types.Application
}
// NewGRPCServer returns a new gRPC ABCI server
func NewGRPCServer(protoAddr string, app types.ABCIApplicationServer) service.Service {
func NewGRPCServer(protoAddr string, app types.Application) service.Service {
proto, addr := tmnet.ProtocolAndAddress(protoAddr)
s := &GRPCServer{
proto: proto,
@@ -44,7 +45,7 @@ func (s *GRPCServer) OnStart() error {
s.listener = ln
s.server = grpc.NewServer()
types.RegisterABCIApplicationServer(s.server, s.app)
types.RegisterABCIServer(s.server, &gRPCApplication{s.app})
s.Logger.Info("Listening", "proto", s.proto, "addr", s.addr)
go func() {
@@ -59,3 +60,18 @@ func (s *GRPCServer) OnStart() error {
func (s *GRPCServer) OnStop() {
s.server.Stop()
}
//-------------------------------------------------------
// gRPCApplication is a gRPC shim for Application
type gRPCApplication struct {
types.Application
}
func (app *gRPCApplication) Echo(_ context.Context, req *types.RequestEcho) (*types.ResponseEcho, error) {
return &types.ResponseEcho{Message: req.Message}, nil
}
func (app *gRPCApplication) Flush(_ context.Context, req *types.RequestFlush) (*types.ResponseFlush, error) {
return &types.ResponseFlush{}, nil
}
+3 -1
View File
@@ -14,6 +14,8 @@ import (
"github.com/tendermint/tendermint/libs/service"
)
// NewServer is a utility function for out of process applications to set up either a socket or
// grpc server that can listen to requests from the equivalent Tendermint client
func NewServer(protoAddr, transport string, app types.Application) (service.Service, error) {
var s service.Service
var err error
@@ -21,7 +23,7 @@ func NewServer(protoAddr, transport string, app types.Application) (service.Serv
case "socket":
s = NewSocketServer(protoAddr, app)
case "grpc":
s = NewGRPCServer(protoAddr, types.NewGRPCApplication(app))
s = NewGRPCServer(protoAddr, app)
default:
err = fmt.Errorf("unknown server type %s", transport)
}
+102 -42
View File
@@ -2,6 +2,8 @@ package server
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"net"
@@ -15,8 +17,11 @@ import (
tmsync "github.com/tendermint/tendermint/libs/sync"
)
// var maxNumberConnections = 2
// SocketServer is the server-side implementation of the TSP (Tendermint Socket Protocol)
// for out-of-process go applications. Note, in the case of an application written in golang,
// the developer may also run both Tendermint and the application within the same process.
//
// The socket server deliver
type SocketServer struct {
service.BaseService
isLoggerSet bool
@@ -33,6 +38,9 @@ type SocketServer struct {
app types.Application
}
const responseBufferSize = 1000
// NewSocketServer creates a server from a golang-based out-of-process application.
func NewSocketServer(protoAddr string, app types.Application) service.Service {
proto, addr := tmnet.ProtocolAndAddress(protoAddr)
s := &SocketServer{
@@ -120,8 +128,8 @@ func (s *SocketServer) acceptConnectionsRoutine() {
connID := s.addConn(conn)
closeConn := make(chan error, 2) // Push to signal connection closed
responses := make(chan *types.Response, 1000) // A channel to buffer responses
closeConn := make(chan error, 2) // Push to signal connection closed
responses := make(chan *types.Response, responseBufferSize) // A channel to buffer responses
// Read requests from conn and deal with them
go s.handleRequests(closeConn, conn, responses)
@@ -157,7 +165,9 @@ func (s *SocketServer) handleRequests(closeConn chan error, conn io.Reader, resp
var bufReader = bufio.NewReader(conn)
defer func() {
// make sure to recover from any app-related panics to allow proper socket cleanup
// make sure to recover from any app-related panics to allow proper socket cleanup.
// In the case of a panic, we do not notify the client by passing an exception so
// presume that the client is still running and retying to connect
r := recover()
if r != nil {
const size = 64 << 10
@@ -173,7 +183,6 @@ func (s *SocketServer) handleRequests(closeConn chan error, conn io.Reader, resp
}()
for {
var req = &types.Request{}
err := types.ReadMessage(bufReader, req)
if err != nil {
@@ -184,63 +193,104 @@ func (s *SocketServer) handleRequests(closeConn chan error, conn io.Reader, resp
}
return
}
fmt.Println("server has read a message")
s.appMtx.Lock()
count++
s.handleRequest(req, responses)
resp, err := s.handleRequest(context.TODO(), req)
if err != nil {
// any error either from the application or because of an unknown request
// throws an exception back to the client. This will stop the server and
// should also halt the client.
responses <- types.ToResponseException(err.Error())
} else {
responses <- resp
}
s.appMtx.Unlock()
}
}
func (s *SocketServer) handleRequest(req *types.Request, responses chan<- *types.Response) {
// handleRequests takes a request and calls the application passing the returned
func (s *SocketServer) handleRequest(ctx context.Context, req *types.Request) (*types.Response, error) {
switch r := req.Value.(type) {
case *types.Request_Echo:
responses <- types.ToResponseEcho(r.Echo.Message)
fmt.Println("server handling request")
return types.ToResponseEcho(r.Echo.Message), nil
case *types.Request_Flush:
responses <- types.ToResponseFlush()
return types.ToResponseFlush(), nil
case *types.Request_Info:
res := s.app.Info(*r.Info)
responses <- types.ToResponseInfo(res)
case *types.Request_DeliverTx:
res := s.app.DeliverTx(*r.DeliverTx)
responses <- types.ToResponseDeliverTx(res)
res, err := s.app.Info(ctx, r.Info)
if err != nil {
return nil, err
}
return types.ToResponseInfo(res), nil
case *types.Request_CheckTx:
res := s.app.CheckTx(*r.CheckTx)
responses <- types.ToResponseCheckTx(res)
res, err := s.app.CheckTx(ctx, r.CheckTx)
if err != nil {
return nil, err
}
return types.ToResponseCheckTx(res), nil
case *types.Request_Commit:
res := s.app.Commit()
responses <- types.ToResponseCommit(res)
res, err := s.app.Commit(ctx, r.Commit)
if err != nil {
return nil, err
}
return types.ToResponseCommit(res), nil
case *types.Request_Query:
res := s.app.Query(*r.Query)
responses <- types.ToResponseQuery(res)
res, err := s.app.Query(ctx, r.Query)
if err != nil {
return nil, err
}
return types.ToResponseQuery(res), nil
case *types.Request_InitChain:
res := s.app.InitChain(*r.InitChain)
responses <- types.ToResponseInitChain(res)
case *types.Request_BeginBlock:
res := s.app.BeginBlock(*r.BeginBlock)
responses <- types.ToResponseBeginBlock(res)
case *types.Request_EndBlock:
res := s.app.EndBlock(*r.EndBlock)
responses <- types.ToResponseEndBlock(res)
res, err := s.app.InitChain(ctx, r.InitChain)
if err != nil {
return nil, err
}
return types.ToResponseInitChain(res), nil
case *types.Request_FinalizeBlock:
res, err := s.app.FinalizeBlock(ctx, r.FinalizeBlock)
if err != nil {
return nil, err
}
return types.ToResponseFinalizeBlock(res), nil
case *types.Request_ListSnapshots:
res := s.app.ListSnapshots(*r.ListSnapshots)
responses <- types.ToResponseListSnapshots(res)
res, err := s.app.ListSnapshots(ctx, r.ListSnapshots)
if err != nil {
return nil, err
}
return types.ToResponseListSnapshots(res), nil
case *types.Request_OfferSnapshot:
res := s.app.OfferSnapshot(*r.OfferSnapshot)
responses <- types.ToResponseOfferSnapshot(res)
res, err := s.app.OfferSnapshot(ctx, r.OfferSnapshot)
if err != nil {
return nil, err
}
return types.ToResponseOfferSnapshot(res), nil
case *types.Request_PrepareProposal:
res := s.app.PrepareProposal(*r.PrepareProposal)
responses <- types.ToResponsePrepareProposal(res)
res, err := s.app.PrepareProposal(ctx, r.PrepareProposal)
if err != nil {
return nil, err
}
return types.ToResponsePrepareProposal(res), nil
case *types.Request_ProcessProposal:
res := s.app.ProcessProposal(*r.ProcessProposal)
responses <- types.ToResponseProcessProposal(res)
res, err := s.app.ProcessProposal(ctx, r.ProcessProposal)
if err != nil {
return nil, err
}
return types.ToResponseProcessProposal(res), nil
case *types.Request_LoadSnapshotChunk:
res := s.app.LoadSnapshotChunk(*r.LoadSnapshotChunk)
responses <- types.ToResponseLoadSnapshotChunk(res)
res, err := s.app.LoadSnapshotChunk(ctx, r.LoadSnapshotChunk)
if err != nil {
return nil, err
}
return types.ToResponseLoadSnapshotChunk(res), nil
case *types.Request_ApplySnapshotChunk:
res := s.app.ApplySnapshotChunk(*r.ApplySnapshotChunk)
responses <- types.ToResponseApplySnapshotChunk(res)
res, err := s.app.ApplySnapshotChunk(ctx, r.ApplySnapshotChunk)
if err != nil {
return nil, err
}
return types.ToResponseApplySnapshotChunk(res), nil
default:
responses <- types.ToResponseException("Unknown request")
return nil, fmt.Errorf("unknown request from client: %T", req)
}
}
@@ -250,8 +300,11 @@ func (s *SocketServer) handleResponses(closeConn chan error, conn io.Writer, res
var bufWriter = bufio.NewWriter(conn)
for {
var res = <-responses
fmt.Println("server writing response")
fmt.Println(res)
err := types.WriteMessage(res, bufWriter)
if err != nil {
fmt.Println(err)
closeConn <- fmt.Errorf("error writing message: %w", err)
return
}
@@ -262,6 +315,13 @@ func (s *SocketServer) handleResponses(closeConn chan error, conn io.Writer, res
return
}
}
// If the application has responded with an exception, the server returns the error
// back to the client and closes the connection. The receiving Tendermint client should
// log the error and gracefully terminate
if e, ok := res.Value.(*types.Response_Exception); ok {
closeConn <- errors.New(e.Exception.Error)
}
count++
fmt.Println("server finished writing response")
}
}
+1 -1
View File
@@ -13,7 +13,7 @@ import (
func TestClientServerNoAddrPrefix(t *testing.T) {
addr := "localhost:26658"
transport := "socket"
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
server, err := abciserver.NewServer(addr, transport, app)
assert.NoError(t, err, "expected no error on NewServer")
+33 -30
View File
@@ -2,6 +2,7 @@ package testsuite
import (
"bytes"
"context"
"errors"
"fmt"
@@ -10,7 +11,7 @@ import (
tmrand "github.com/tendermint/tendermint/libs/rand"
)
func InitChain(client abcicli.Client) error {
func InitChain(ctx context.Context, client abcicli.Client) error {
total := 10
vals := make([]types.ValidatorUpdate, total)
for i := 0; i < total; i++ {
@@ -18,7 +19,7 @@ func InitChain(client abcicli.Client) error {
power := tmrand.Int()
vals[i] = types.UpdateValidator(pubkey, int64(power), "")
}
_, err := client.InitChainSync(types.RequestInitChain{
_, err := client.InitChain(ctx, &types.RequestInitChain{
Validators: vals,
})
if err != nil {
@@ -29,44 +30,46 @@ func InitChain(client abcicli.Client) error {
return nil
}
func Commit(client abcicli.Client, hashExp []byte) error {
res, err := client.CommitSync()
data := res.Data
func Commit(ctx context.Context, client abcicli.Client) error {
_, err := client.Commit(ctx, &types.RequestCommit{})
if err != nil {
fmt.Println("Failed test: Commit")
fmt.Printf("error while committing: %v\n", err)
return err
}
if !bytes.Equal(data, hashExp) {
fmt.Println("Failed test: Commit")
fmt.Printf("Commit hash was unexpected. Got %X expected %X\n", data, hashExp)
return errors.New("commitTx failed")
}
fmt.Println("Passed test: Commit")
return nil
}
func DeliverTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) error {
res, _ := client.DeliverTxSync(types.RequestDeliverTx{Tx: txBytes})
code, data, log := res.Code, res.Data, res.Log
if code != codeExp {
fmt.Println("Failed test: DeliverTx")
fmt.Printf("DeliverTx response code was unexpected. Got %v expected %v. Log: %v\n",
code, codeExp, log)
return errors.New("deliverTx error")
func FinalizeBlock(ctx context.Context, client abcicli.Client, txBytes [][]byte, codeExp []uint32, dataExp []byte, hashExp []byte) error {
res, _ := client.FinalizeBlock(ctx, &types.RequestFinalizeBlock{Txs: txBytes})
appHash := res.AgreedAppData
for i, tx := range res.TxResults {
code, data, log := tx.Code, tx.Data, tx.Log
if code != codeExp[i] {
fmt.Println("Failed test: FinalizeBlock")
fmt.Printf("FinalizeBlock response code was unexpected. Got %v expected %v. Log: %v\n",
code, codeExp, log)
return errors.New("FinalizeBlock error")
}
if !bytes.Equal(data, dataExp) {
fmt.Println("Failed test: FinalizeBlock")
fmt.Printf("FinalizeBlock response data was unexpected. Got %X expected %X\n",
data, dataExp)
return errors.New("FinalizeBlock error")
}
}
if !bytes.Equal(data, dataExp) {
fmt.Println("Failed test: DeliverTx")
fmt.Printf("DeliverTx response data was unexpected. Got %X expected %X\n",
data, dataExp)
return errors.New("deliverTx error")
if !bytes.Equal(appHash, hashExp) {
fmt.Println("Failed test: FinalizeBlock")
fmt.Printf("Application hash was unexpected. Got %X expected %X\n", appHash, hashExp)
return errors.New("FinalizeBlock error")
}
fmt.Println("Passed test: DeliverTx")
fmt.Println("Passed test: FinalizeBlock")
return nil
}
func PrepareProposal(client abcicli.Client, txBytes [][]byte, txExpected [][]byte, dataExp []byte) error {
res, _ := client.PrepareProposalSync(types.RequestPrepareProposal{Txs: txBytes})
func PrepareProposal(ctx context.Context, client abcicli.Client, txBytes [][]byte, txExpected [][]byte, dataExp []byte) error {
res, _ := client.PrepareProposal(ctx, &types.RequestPrepareProposal{Txs: txBytes})
for i, tx := range res.Txs {
if !bytes.Equal(tx, txExpected[i]) {
fmt.Println("Failed test: PrepareProposal")
@@ -79,8 +82,8 @@ func PrepareProposal(client abcicli.Client, txBytes [][]byte, txExpected [][]byt
return nil
}
func ProcessProposal(client abcicli.Client, txBytes [][]byte, statusExp types.ResponseProcessProposal_ProposalStatus) error {
res, _ := client.ProcessProposalSync(types.RequestProcessProposal{Txs: txBytes})
func ProcessProposal(ctx context.Context, client abcicli.Client, txBytes [][]byte, statusExp types.ResponseProcessProposal_ProposalStatus) error {
res, _ := client.ProcessProposal(ctx, &types.RequestProcessProposal{Txs: txBytes})
if res.Status != statusExp {
fmt.Println("Failed test: ProcessProposal")
fmt.Printf("ProcessProposal response status was unexpected. Got %v expected %v.",
@@ -91,8 +94,8 @@ func ProcessProposal(client abcicli.Client, txBytes [][]byte, statusExp types.Re
return nil
}
func CheckTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) error {
res, _ := client.CheckTxSync(types.RequestCheckTx{Tx: txBytes})
func CheckTx(ctx context.Context, client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) error {
res, _ := client.CheckTx(ctx, &types.RequestCheckTx{Tx: txBytes})
code, data, log := res.Code, res.Data, res.Log
if code != codeExp {
fmt.Println("Failed test: CheckTx")
+46 -149
View File
@@ -1,37 +1,33 @@
package types
import (
context "golang.org/x/net/context"
)
import "context"
//go:generate ../../scripts/mockery_generate.sh Application
// Application is an interface that enables any finite, deterministic state machine
// to be driven by a blockchain-based replication engine via the ABCI.
// All methods take a RequestXxx argument and return a ResponseXxx argument,
// except CheckTx/DeliverTx, which take `tx []byte`, and `Commit`, which takes nothing.
type Application interface {
// Info/Query Connection
Info(RequestInfo) ResponseInfo // Return application info
Query(RequestQuery) ResponseQuery // Query for state
Info(context.Context, *RequestInfo) (*ResponseInfo, error) // Return application info
Query(context.Context, *RequestQuery) (*ResponseQuery, error) // Query for state
// Mempool Connection
CheckTx(RequestCheckTx) ResponseCheckTx // Validate a tx for the mempool
CheckTx(context.Context, *RequestCheckTx) (*ResponseCheckTx, error) // Validate a tx for the mempool
// Consensus Connection
InitChain(RequestInitChain) ResponseInitChain // Initialize blockchain w validators/other info from TendermintCore
PrepareProposal(RequestPrepareProposal) ResponsePrepareProposal
ProcessProposal(RequestProcessProposal) ResponseProcessProposal
BeginBlock(RequestBeginBlock) ResponseBeginBlock // Signals the beginning of a block
DeliverTx(RequestDeliverTx) ResponseDeliverTx // Deliver a tx for full processing
EndBlock(RequestEndBlock) ResponseEndBlock // Signals the end of a block, returns changes to the validator set
Commit() ResponseCommit // Commit the state and return the application Merkle root hash
InitChain(context.Context, *RequestInitChain) (*ResponseInitChain, error) // Initialize blockchain w validators/other info from TendermintCore
PrepareProposal(context.Context, *RequestPrepareProposal) (*ResponsePrepareProposal, error)
ProcessProposal(context.Context, *RequestProcessProposal) (*ResponseProcessProposal, error)
// Deliver the decided block with its txs to the Application
FinalizeBlock(context.Context, *RequestFinalizeBlock) (*ResponseFinalizeBlock, error)
// Commit the state and return the application Merkle root hash
Commit(context.Context, *RequestCommit) (*ResponseCommit, error)
// State Sync Connection
ListSnapshots(RequestListSnapshots) ResponseListSnapshots // List available snapshots
OfferSnapshot(RequestOfferSnapshot) ResponseOfferSnapshot // Offer a snapshot to the application
LoadSnapshotChunk(RequestLoadSnapshotChunk) ResponseLoadSnapshotChunk // Load a snapshot chunk
ApplySnapshotChunk(RequestApplySnapshotChunk) ResponseApplySnapshotChunk // Apply a shapshot chunk
ListSnapshots(context.Context, *RequestListSnapshots) (*ResponseListSnapshots, error) // List available snapshots
OfferSnapshot(context.Context, *RequestOfferSnapshot) (*ResponseOfferSnapshot, error) // Offer a snapshot to the application
LoadSnapshotChunk(context.Context, *RequestLoadSnapshotChunk) (*ResponseLoadSnapshotChunk, error) // Load a snapshot chunk
ApplySnapshotChunk(context.Context, *RequestApplySnapshotChunk) (*ResponseApplySnapshotChunk, error) // Apply a shapshot chunk
}
//-------------------------------------------------------
@@ -39,62 +35,49 @@ type Application interface {
var _ Application = (*BaseApplication)(nil)
type BaseApplication struct {
}
type BaseApplication struct{}
func NewBaseApplication() *BaseApplication {
return &BaseApplication{}
}
func (BaseApplication) Info(req RequestInfo) ResponseInfo {
return ResponseInfo{}
func (BaseApplication) Info(_ context.Context, req *RequestInfo) (*ResponseInfo, error) {
return &ResponseInfo{}, nil
}
func (BaseApplication) DeliverTx(req RequestDeliverTx) ResponseDeliverTx {
return ResponseDeliverTx{Code: CodeTypeOK}
func (BaseApplication) CheckTx(_ context.Context, req *RequestCheckTx) (*ResponseCheckTx, error) {
return &ResponseCheckTx{Code: CodeTypeOK}, nil
}
func (BaseApplication) CheckTx(req RequestCheckTx) ResponseCheckTx {
return ResponseCheckTx{Code: CodeTypeOK}
func (BaseApplication) Commit(_ context.Context, req *RequestCommit) (*ResponseCommit, error) {
return &ResponseCommit{}, nil
}
func (BaseApplication) Commit() ResponseCommit {
return ResponseCommit{}
func (BaseApplication) Query(_ context.Context, req *RequestQuery) (*ResponseQuery, error) {
return &ResponseQuery{Code: CodeTypeOK}, nil
}
func (BaseApplication) Query(req RequestQuery) ResponseQuery {
return ResponseQuery{Code: CodeTypeOK}
func (BaseApplication) InitChain(_ context.Context, req *RequestInitChain) (*ResponseInitChain, error) {
return &ResponseInitChain{}, nil
}
func (BaseApplication) InitChain(req RequestInitChain) ResponseInitChain {
return ResponseInitChain{}
func (BaseApplication) ListSnapshots(_ context.Context, req *RequestListSnapshots) (*ResponseListSnapshots, error) {
return &ResponseListSnapshots{}, nil
}
func (BaseApplication) BeginBlock(req RequestBeginBlock) ResponseBeginBlock {
return ResponseBeginBlock{}
func (BaseApplication) OfferSnapshot(_ context.Context, req *RequestOfferSnapshot) (*ResponseOfferSnapshot, error) {
return &ResponseOfferSnapshot{}, nil
}
func (BaseApplication) EndBlock(req RequestEndBlock) ResponseEndBlock {
return ResponseEndBlock{}
func (BaseApplication) LoadSnapshotChunk(_ context.Context, _ *RequestLoadSnapshotChunk) (*ResponseLoadSnapshotChunk, error) {
return &ResponseLoadSnapshotChunk{}, nil
}
func (BaseApplication) ListSnapshots(req RequestListSnapshots) ResponseListSnapshots {
return ResponseListSnapshots{}
func (BaseApplication) ApplySnapshotChunk(_ context.Context, req *RequestApplySnapshotChunk) (*ResponseApplySnapshotChunk, error) {
return &ResponseApplySnapshotChunk{}, nil
}
func (BaseApplication) OfferSnapshot(req RequestOfferSnapshot) ResponseOfferSnapshot {
return ResponseOfferSnapshot{}
}
func (BaseApplication) LoadSnapshotChunk(req RequestLoadSnapshotChunk) ResponseLoadSnapshotChunk {
return ResponseLoadSnapshotChunk{}
}
func (BaseApplication) ApplySnapshotChunk(req RequestApplySnapshotChunk) ResponseApplySnapshotChunk {
return ResponseApplySnapshotChunk{}
}
func (BaseApplication) PrepareProposal(req RequestPrepareProposal) ResponsePrepareProposal {
func (BaseApplication) PrepareProposal(_ context.Context, req *RequestPrepareProposal) (*ResponsePrepareProposal, error) {
txs := make([][]byte, 0, len(req.Txs))
var totalBytes int64
for _, tx := range req.Txs {
@@ -104,105 +87,19 @@ func (BaseApplication) PrepareProposal(req RequestPrepareProposal) ResponsePrepa
}
txs = append(txs, tx)
}
return ResponsePrepareProposal{Txs: txs}
return &ResponsePrepareProposal{Txs: txs}, nil
}
func (BaseApplication) ProcessProposal(req RequestProcessProposal) ResponseProcessProposal {
return ResponseProcessProposal{
Status: ResponseProcessProposal_ACCEPT}
func (BaseApplication) ProcessProposal(_ context.Context, req *RequestProcessProposal) (*ResponseProcessProposal, error) {
return &ResponseProcessProposal{Status: ResponseProcessProposal_ACCEPT}, nil
}
//-------------------------------------------------------
// GRPCApplication is a GRPC wrapper for Application
type GRPCApplication struct {
app Application
}
func NewGRPCApplication(app Application) *GRPCApplication {
return &GRPCApplication{app}
}
func (app *GRPCApplication) Echo(ctx context.Context, req *RequestEcho) (*ResponseEcho, error) {
return &ResponseEcho{Message: req.Message}, nil
}
func (app *GRPCApplication) Flush(ctx context.Context, req *RequestFlush) (*ResponseFlush, error) {
return &ResponseFlush{}, nil
}
func (app *GRPCApplication) Info(ctx context.Context, req *RequestInfo) (*ResponseInfo, error) {
res := app.app.Info(*req)
return &res, nil
}
func (app *GRPCApplication) DeliverTx(ctx context.Context, req *RequestDeliverTx) (*ResponseDeliverTx, error) {
res := app.app.DeliverTx(*req)
return &res, nil
}
func (app *GRPCApplication) CheckTx(ctx context.Context, req *RequestCheckTx) (*ResponseCheckTx, error) {
res := app.app.CheckTx(*req)
return &res, nil
}
func (app *GRPCApplication) Query(ctx context.Context, req *RequestQuery) (*ResponseQuery, error) {
res := app.app.Query(*req)
return &res, nil
}
func (app *GRPCApplication) Commit(ctx context.Context, req *RequestCommit) (*ResponseCommit, error) {
res := app.app.Commit()
return &res, nil
}
func (app *GRPCApplication) InitChain(ctx context.Context, req *RequestInitChain) (*ResponseInitChain, error) {
res := app.app.InitChain(*req)
return &res, nil
}
func (app *GRPCApplication) BeginBlock(ctx context.Context, req *RequestBeginBlock) (*ResponseBeginBlock, error) {
res := app.app.BeginBlock(*req)
return &res, nil
}
func (app *GRPCApplication) EndBlock(ctx context.Context, req *RequestEndBlock) (*ResponseEndBlock, error) {
res := app.app.EndBlock(*req)
return &res, nil
}
func (app *GRPCApplication) ListSnapshots(
ctx context.Context, req *RequestListSnapshots) (*ResponseListSnapshots, error) {
res := app.app.ListSnapshots(*req)
return &res, nil
}
func (app *GRPCApplication) OfferSnapshot(
ctx context.Context, req *RequestOfferSnapshot) (*ResponseOfferSnapshot, error) {
res := app.app.OfferSnapshot(*req)
return &res, nil
}
func (app *GRPCApplication) LoadSnapshotChunk(
ctx context.Context, req *RequestLoadSnapshotChunk) (*ResponseLoadSnapshotChunk, error) {
res := app.app.LoadSnapshotChunk(*req)
return &res, nil
}
func (app *GRPCApplication) ApplySnapshotChunk(
ctx context.Context, req *RequestApplySnapshotChunk) (*ResponseApplySnapshotChunk, error) {
res := app.app.ApplySnapshotChunk(*req)
return &res, nil
}
func (app *GRPCApplication) PrepareProposal(
ctx context.Context, req *RequestPrepareProposal) (*ResponsePrepareProposal, error) {
res := app.app.PrepareProposal(*req)
return &res, nil
}
func (app *GRPCApplication) ProcessProposal(
ctx context.Context, req *RequestProcessProposal) (*ResponseProcessProposal, error) {
res := app.app.ProcessProposal(*req)
return &res, nil
func (BaseApplication) FinalizeBlock(_ context.Context, req *RequestFinalizeBlock) (*ResponseFinalizeBlock, error) {
txs := make([]*ExecTxResult, len(req.Txs))
for i := range req.Txs {
txs[i] = &ExecTxResult{Code: CodeTypeOK}
}
return &ResponseFinalizeBlock{
TxResults: txs,
}, nil
}
+47 -75
View File
@@ -15,11 +15,7 @@ const (
func WriteMessage(msg proto.Message, w io.Writer) error {
protoWriter := protoio.NewDelimitedWriter(w)
_, err := protoWriter.WriteMsg(msg)
if err != nil {
return err
}
return nil
return err
}
// ReadMessage reads a varint length-delimited protobuf message.
@@ -42,21 +38,15 @@ func ToRequestFlush() *Request {
}
}
func ToRequestInfo(req RequestInfo) *Request {
func ToRequestInfo(req *RequestInfo) *Request {
return &Request{
Value: &Request_Info{&req},
Value: &Request_Info{req},
}
}
func ToRequestDeliverTx(req RequestDeliverTx) *Request {
func ToRequestCheckTx(req *RequestCheckTx) *Request {
return &Request{
Value: &Request_DeliverTx{&req},
}
}
func ToRequestCheckTx(req RequestCheckTx) *Request {
return &Request{
Value: &Request_CheckTx{&req},
Value: &Request_CheckTx{req},
}
}
@@ -66,63 +56,57 @@ func ToRequestCommit() *Request {
}
}
func ToRequestQuery(req RequestQuery) *Request {
func ToRequestQuery(req *RequestQuery) *Request {
return &Request{
Value: &Request_Query{&req},
Value: &Request_Query{req},
}
}
func ToRequestInitChain(req RequestInitChain) *Request {
func ToRequestInitChain(req *RequestInitChain) *Request {
return &Request{
Value: &Request_InitChain{&req},
Value: &Request_InitChain{req},
}
}
func ToRequestBeginBlock(req RequestBeginBlock) *Request {
func ToRequestListSnapshots(req *RequestListSnapshots) *Request {
return &Request{
Value: &Request_BeginBlock{&req},
Value: &Request_ListSnapshots{req},
}
}
func ToRequestEndBlock(req RequestEndBlock) *Request {
func ToRequestOfferSnapshot(req *RequestOfferSnapshot) *Request {
return &Request{
Value: &Request_EndBlock{&req},
Value: &Request_OfferSnapshot{req},
}
}
func ToRequestListSnapshots(req RequestListSnapshots) *Request {
func ToRequestLoadSnapshotChunk(req *RequestLoadSnapshotChunk) *Request {
return &Request{
Value: &Request_ListSnapshots{&req},
Value: &Request_LoadSnapshotChunk{req},
}
}
func ToRequestOfferSnapshot(req RequestOfferSnapshot) *Request {
func ToRequestApplySnapshotChunk(req *RequestApplySnapshotChunk) *Request {
return &Request{
Value: &Request_OfferSnapshot{&req},
Value: &Request_ApplySnapshotChunk{req},
}
}
func ToRequestLoadSnapshotChunk(req RequestLoadSnapshotChunk) *Request {
func ToRequestPrepareProposal(req *RequestPrepareProposal) *Request {
return &Request{
Value: &Request_LoadSnapshotChunk{&req},
Value: &Request_PrepareProposal{req},
}
}
func ToRequestApplySnapshotChunk(req RequestApplySnapshotChunk) *Request {
func ToRequestProcessProposal(req *RequestProcessProposal) *Request {
return &Request{
Value: &Request_ApplySnapshotChunk{&req},
Value: &Request_ProcessProposal{req},
}
}
func ToRequestPrepareProposal(req RequestPrepareProposal) *Request {
func ToRequestFinalizeBlock(req *RequestFinalizeBlock) *Request {
return &Request{
Value: &Request_PrepareProposal{&req},
}
}
func ToRequestProcessProposal(req RequestProcessProposal) *Request {
return &Request{
Value: &Request_ProcessProposal{&req},
Value: &Request_FinalizeBlock{req},
}
}
@@ -146,86 +130,74 @@ func ToResponseFlush() *Response {
}
}
func ToResponseInfo(res ResponseInfo) *Response {
func ToResponseInfo(res *ResponseInfo) *Response {
return &Response{
Value: &Response_Info{&res},
Value: &Response_Info{res},
}
}
func ToResponseDeliverTx(res ResponseDeliverTx) *Response {
func ToResponseCheckTx(res *ResponseCheckTx) *Response {
return &Response{
Value: &Response_DeliverTx{&res},
Value: &Response_CheckTx{res},
}
}
func ToResponseCheckTx(res ResponseCheckTx) *Response {
func ToResponseCommit(res *ResponseCommit) *Response {
return &Response{
Value: &Response_CheckTx{&res},
Value: &Response_Commit{res},
}
}
func ToResponseCommit(res ResponseCommit) *Response {
func ToResponseQuery(res *ResponseQuery) *Response {
return &Response{
Value: &Response_Commit{&res},
Value: &Response_Query{res},
}
}
func ToResponseQuery(res ResponseQuery) *Response {
func ToResponseInitChain(res *ResponseInitChain) *Response {
return &Response{
Value: &Response_Query{&res},
Value: &Response_InitChain{res},
}
}
func ToResponseInitChain(res ResponseInitChain) *Response {
func ToResponseListSnapshots(res *ResponseListSnapshots) *Response {
return &Response{
Value: &Response_InitChain{&res},
Value: &Response_ListSnapshots{res},
}
}
func ToResponseBeginBlock(res ResponseBeginBlock) *Response {
func ToResponseOfferSnapshot(res *ResponseOfferSnapshot) *Response {
return &Response{
Value: &Response_BeginBlock{&res},
Value: &Response_OfferSnapshot{res},
}
}
func ToResponseEndBlock(res ResponseEndBlock) *Response {
func ToResponseLoadSnapshotChunk(res *ResponseLoadSnapshotChunk) *Response {
return &Response{
Value: &Response_EndBlock{&res},
Value: &Response_LoadSnapshotChunk{res},
}
}
func ToResponseListSnapshots(res ResponseListSnapshots) *Response {
func ToResponseApplySnapshotChunk(res *ResponseApplySnapshotChunk) *Response {
return &Response{
Value: &Response_ListSnapshots{&res},
Value: &Response_ApplySnapshotChunk{res},
}
}
func ToResponseOfferSnapshot(res ResponseOfferSnapshot) *Response {
func ToResponsePrepareProposal(res *ResponsePrepareProposal) *Response {
return &Response{
Value: &Response_OfferSnapshot{&res},
Value: &Response_PrepareProposal{res},
}
}
func ToResponseLoadSnapshotChunk(res ResponseLoadSnapshotChunk) *Response {
func ToResponseProcessProposal(res *ResponseProcessProposal) *Response {
return &Response{
Value: &Response_LoadSnapshotChunk{&res},
Value: &Response_ProcessProposal{res},
}
}
func ToResponseApplySnapshotChunk(res ResponseApplySnapshotChunk) *Response {
func ToResponseFinalizeBlock(res *ResponseFinalizeBlock) *Response {
return &Response{
Value: &Response_ApplySnapshotChunk{&res},
}
}
func ToResponsePrepareProposal(res ResponsePrepareProposal) *Response {
return &Response{
Value: &Response_PrepareProposal{&res},
}
}
func ToResponseProcessProposal(res ResponseProcessProposal) *Response {
return &Response{
Value: &Response_ProcessProposal{&res},
Value: &Response_FinalizeBlock{res},
}
}
+2 -2
View File
@@ -13,8 +13,8 @@ import (
)
func TestMarshalJSON(t *testing.T) {
b, err := json.Marshal(&ResponseDeliverTx{})
assert.Nil(t, err)
b, err := json.Marshal(&ExecTxResult{Code: 1})
assert.NoError(t, err)
// include empty fields.
assert.True(t, strings.Contains(string(b), "code"))
r1 := ResponseCheckTx{
+203 -121
View File
@@ -3,6 +3,8 @@
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
types "github.com/tendermint/tendermint/abci/types"
)
@@ -12,200 +14,280 @@ type Application struct {
mock.Mock
}
// ApplySnapshotChunk provides a mock function with given fields: _a0
func (_m *Application) ApplySnapshotChunk(_a0 types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk {
ret := _m.Called(_a0)
// ApplySnapshotChunk provides a mock function with given fields: _a0, _a1
func (_m *Application) ApplySnapshotChunk(_a0 context.Context, _a1 *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseApplySnapshotChunk
if rf, ok := ret.Get(0).(func(types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk); ok {
r0 = rf(_a0)
var r0 *types.ResponseApplySnapshotChunk
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestApplySnapshotChunk) *types.ResponseApplySnapshotChunk); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseApplySnapshotChunk)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseApplySnapshotChunk)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestApplySnapshotChunk) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// BeginBlock provides a mock function with given fields: _a0
func (_m *Application) BeginBlock(_a0 types.RequestBeginBlock) types.ResponseBeginBlock {
ret := _m.Called(_a0)
// CheckTx provides a mock function with given fields: _a0, _a1
func (_m *Application) CheckTx(_a0 context.Context, _a1 *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseBeginBlock
if rf, ok := ret.Get(0).(func(types.RequestBeginBlock) types.ResponseBeginBlock); ok {
r0 = rf(_a0)
var r0 *types.ResponseCheckTx
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestCheckTx) *types.ResponseCheckTx); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseBeginBlock)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseCheckTx)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestCheckTx) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CheckTx provides a mock function with given fields: _a0
func (_m *Application) CheckTx(_a0 types.RequestCheckTx) types.ResponseCheckTx {
ret := _m.Called(_a0)
// Commit provides a mock function with given fields: _a0, _a1
func (_m *Application) Commit(_a0 context.Context, _a1 *types.RequestCommit) (*types.ResponseCommit, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseCheckTx
if rf, ok := ret.Get(0).(func(types.RequestCheckTx) types.ResponseCheckTx); ok {
r0 = rf(_a0)
var r0 *types.ResponseCommit
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestCommit) *types.ResponseCommit); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseCheckTx)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseCommit)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestCommit) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Commit provides a mock function with given fields:
func (_m *Application) Commit() types.ResponseCommit {
ret := _m.Called()
// FinalizeBlock provides a mock function with given fields: _a0, _a1
func (_m *Application) FinalizeBlock(_a0 context.Context, _a1 *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseCommit
if rf, ok := ret.Get(0).(func() types.ResponseCommit); ok {
r0 = rf()
var r0 *types.ResponseFinalizeBlock
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestFinalizeBlock) *types.ResponseFinalizeBlock); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseCommit)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseFinalizeBlock)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestFinalizeBlock) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeliverTx provides a mock function with given fields: _a0
func (_m *Application) DeliverTx(_a0 types.RequestDeliverTx) types.ResponseDeliverTx {
ret := _m.Called(_a0)
// Info provides a mock function with given fields: _a0, _a1
func (_m *Application) Info(_a0 context.Context, _a1 *types.RequestInfo) (*types.ResponseInfo, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseDeliverTx
if rf, ok := ret.Get(0).(func(types.RequestDeliverTx) types.ResponseDeliverTx); ok {
r0 = rf(_a0)
var r0 *types.ResponseInfo
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestInfo) *types.ResponseInfo); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseDeliverTx)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseInfo)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestInfo) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// EndBlock provides a mock function with given fields: _a0
func (_m *Application) EndBlock(_a0 types.RequestEndBlock) types.ResponseEndBlock {
ret := _m.Called(_a0)
// InitChain provides a mock function with given fields: _a0, _a1
func (_m *Application) InitChain(_a0 context.Context, _a1 *types.RequestInitChain) (*types.ResponseInitChain, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseEndBlock
if rf, ok := ret.Get(0).(func(types.RequestEndBlock) types.ResponseEndBlock); ok {
r0 = rf(_a0)
var r0 *types.ResponseInitChain
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestInitChain) *types.ResponseInitChain); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseEndBlock)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseInitChain)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestInitChain) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Info provides a mock function with given fields: _a0
func (_m *Application) Info(_a0 types.RequestInfo) types.ResponseInfo {
ret := _m.Called(_a0)
// ListSnapshots provides a mock function with given fields: _a0, _a1
func (_m *Application) ListSnapshots(_a0 context.Context, _a1 *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseInfo
if rf, ok := ret.Get(0).(func(types.RequestInfo) types.ResponseInfo); ok {
r0 = rf(_a0)
var r0 *types.ResponseListSnapshots
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestListSnapshots) *types.ResponseListSnapshots); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseInfo)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseListSnapshots)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestListSnapshots) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// InitChain provides a mock function with given fields: _a0
func (_m *Application) InitChain(_a0 types.RequestInitChain) types.ResponseInitChain {
ret := _m.Called(_a0)
// LoadSnapshotChunk provides a mock function with given fields: _a0, _a1
func (_m *Application) LoadSnapshotChunk(_a0 context.Context, _a1 *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseInitChain
if rf, ok := ret.Get(0).(func(types.RequestInitChain) types.ResponseInitChain); ok {
r0 = rf(_a0)
var r0 *types.ResponseLoadSnapshotChunk
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestLoadSnapshotChunk) *types.ResponseLoadSnapshotChunk); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseInitChain)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseLoadSnapshotChunk)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestLoadSnapshotChunk) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListSnapshots provides a mock function with given fields: _a0
func (_m *Application) ListSnapshots(_a0 types.RequestListSnapshots) types.ResponseListSnapshots {
ret := _m.Called(_a0)
// OfferSnapshot provides a mock function with given fields: _a0, _a1
func (_m *Application) OfferSnapshot(_a0 context.Context, _a1 *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseListSnapshots
if rf, ok := ret.Get(0).(func(types.RequestListSnapshots) types.ResponseListSnapshots); ok {
r0 = rf(_a0)
var r0 *types.ResponseOfferSnapshot
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestOfferSnapshot) *types.ResponseOfferSnapshot); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseListSnapshots)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseOfferSnapshot)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestOfferSnapshot) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// LoadSnapshotChunk provides a mock function with given fields: _a0
func (_m *Application) LoadSnapshotChunk(_a0 types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk {
ret := _m.Called(_a0)
// PrepareProposal provides a mock function with given fields: _a0, _a1
func (_m *Application) PrepareProposal(_a0 context.Context, _a1 *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseLoadSnapshotChunk
if rf, ok := ret.Get(0).(func(types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk); ok {
r0 = rf(_a0)
var r0 *types.ResponsePrepareProposal
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestPrepareProposal) *types.ResponsePrepareProposal); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseLoadSnapshotChunk)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponsePrepareProposal)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestPrepareProposal) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// OfferSnapshot provides a mock function with given fields: _a0
func (_m *Application) OfferSnapshot(_a0 types.RequestOfferSnapshot) types.ResponseOfferSnapshot {
ret := _m.Called(_a0)
// ProcessProposal provides a mock function with given fields: _a0, _a1
func (_m *Application) ProcessProposal(_a0 context.Context, _a1 *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponseOfferSnapshot
if rf, ok := ret.Get(0).(func(types.RequestOfferSnapshot) types.ResponseOfferSnapshot); ok {
r0 = rf(_a0)
var r0 *types.ResponseProcessProposal
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestProcessProposal) *types.ResponseProcessProposal); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseOfferSnapshot)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseProcessProposal)
}
}
return r0
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestProcessProposal) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// PrepareProposal provides a mock function with given fields: _a0
func (_m *Application) PrepareProposal(_a0 types.RequestPrepareProposal) types.ResponsePrepareProposal {
ret := _m.Called(_a0)
// Query provides a mock function with given fields: _a0, _a1
func (_m *Application) Query(_a0 context.Context, _a1 *types.RequestQuery) (*types.ResponseQuery, error) {
ret := _m.Called(_a0, _a1)
var r0 types.ResponsePrepareProposal
if rf, ok := ret.Get(0).(func(types.RequestPrepareProposal) types.ResponsePrepareProposal); ok {
r0 = rf(_a0)
var r0 *types.ResponseQuery
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestQuery) *types.ResponseQuery); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponsePrepareProposal)
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseQuery)
}
}
return r0
}
// ProcessProposal provides a mock function with given fields: _a0
func (_m *Application) ProcessProposal(_a0 types.RequestProcessProposal) types.ResponseProcessProposal {
ret := _m.Called(_a0)
var r0 types.ResponseProcessProposal
if rf, ok := ret.Get(0).(func(types.RequestProcessProposal) types.ResponseProcessProposal); ok {
r0 = rf(_a0)
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestQuery) error); ok {
r1 = rf(_a0, _a1)
} else {
r0 = ret.Get(0).(types.ResponseProcessProposal)
r1 = ret.Error(1)
}
return r0
}
// Query provides a mock function with given fields: _a0
func (_m *Application) Query(_a0 types.RequestQuery) types.ResponseQuery {
ret := _m.Called(_a0)
var r0 types.ResponseQuery
if rf, ok := ret.Get(0).(func(types.RequestQuery) types.ResponseQuery); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(types.ResponseQuery)
}
return r0
return r0, r1
}
type mockConstructorTestingTNewApplication interface {
-187
View File
@@ -1,187 +0,0 @@
package mocks
import (
types "github.com/tendermint/tendermint/abci/types"
)
// BaseMock provides a wrapper around the generated Application mock and a BaseApplication.
// BaseMock first tries to use the mock's implementation of the method.
// If no functionality was provided for the mock by the user, BaseMock dispatches
// to the BaseApplication and uses its functionality.
// BaseMock allows users to provide mocked functionality for only the methods that matter
// for their test while avoiding a panic if the code calls Application methods that are
// not relevant to the test.
type BaseMock struct {
base *types.BaseApplication
*Application
}
func NewBaseMock() BaseMock {
return BaseMock{
base: types.NewBaseApplication(),
Application: new(Application),
}
}
// Info/Query Connection
// Return application info
func (m BaseMock) Info(input types.RequestInfo) types.ResponseInfo {
var ret types.ResponseInfo
defer func() {
if r := recover(); r != nil {
ret = m.base.Info(input)
}
}()
ret = m.Application.Info(input)
return ret
}
func (m BaseMock) Query(input types.RequestQuery) types.ResponseQuery {
var ret types.ResponseQuery
defer func() {
if r := recover(); r != nil {
ret = m.base.Query(input)
}
}()
ret = m.Application.Query(input)
return ret
}
// Mempool Connection
// Validate a tx for the mempool
func (m BaseMock) CheckTx(input types.RequestCheckTx) types.ResponseCheckTx {
var ret types.ResponseCheckTx
defer func() {
if r := recover(); r != nil {
ret = m.base.CheckTx(input)
}
}()
ret = m.Application.CheckTx(input)
return ret
}
// Consensus Connection
// Initialize blockchain w validators/other info from TendermintCore
func (m BaseMock) InitChain(input types.RequestInitChain) types.ResponseInitChain {
var ret types.ResponseInitChain
defer func() {
if r := recover(); r != nil {
ret = m.base.InitChain(input)
}
}()
ret = m.Application.InitChain(input)
return ret
}
func (m BaseMock) PrepareProposal(input types.RequestPrepareProposal) types.ResponsePrepareProposal {
var ret types.ResponsePrepareProposal
defer func() {
if r := recover(); r != nil {
ret = m.base.PrepareProposal(input)
}
}()
ret = m.Application.PrepareProposal(input)
return ret
}
func (m BaseMock) ProcessProposal(input types.RequestProcessProposal) types.ResponseProcessProposal {
var ret types.ResponseProcessProposal
defer func() {
if r := recover(); r != nil {
ret = m.base.ProcessProposal(input)
}
}()
ret = m.Application.ProcessProposal(input)
return ret
}
// Commit the state and return the application Merkle root hash
func (m BaseMock) Commit() types.ResponseCommit {
var ret types.ResponseCommit
defer func() {
if r := recover(); r != nil {
ret = m.base.Commit()
}
}()
ret = m.Application.Commit()
return ret
}
// State Sync Connection
// List available snapshots
func (m BaseMock) ListSnapshots(input types.RequestListSnapshots) types.ResponseListSnapshots {
var ret types.ResponseListSnapshots
defer func() {
if r := recover(); r != nil {
ret = m.base.ListSnapshots(input)
}
}()
ret = m.Application.ListSnapshots(input)
return ret
}
func (m BaseMock) OfferSnapshot(input types.RequestOfferSnapshot) types.ResponseOfferSnapshot {
var ret types.ResponseOfferSnapshot
defer func() {
if r := recover(); r != nil {
ret = m.base.OfferSnapshot(input)
}
}()
ret = m.Application.OfferSnapshot(input)
return ret
}
func (m BaseMock) LoadSnapshotChunk(input types.RequestLoadSnapshotChunk) types.ResponseLoadSnapshotChunk {
var ret types.ResponseLoadSnapshotChunk
defer func() {
if r := recover(); r != nil {
ret = m.base.LoadSnapshotChunk(input)
}
}()
ret = m.Application.LoadSnapshotChunk(input)
return ret
}
func (m BaseMock) ApplySnapshotChunk(input types.RequestApplySnapshotChunk) types.ResponseApplySnapshotChunk {
var ret types.ResponseApplySnapshotChunk
defer func() {
if r := recover(); r != nil {
ret = m.base.ApplySnapshotChunk(input)
}
}()
ret = m.Application.ApplySnapshotChunk(input)
return ret
}
func (m BaseMock) BeginBlock(input types.RequestBeginBlock) types.ResponseBeginBlock {
var ret types.ResponseBeginBlock
defer func() {
if r := recover(); r != nil {
ret = m.base.BeginBlock(input)
}
}()
ret = m.Application.BeginBlock(input)
return ret
}
func (m BaseMock) DeliverTx(input types.RequestDeliverTx) types.ResponseDeliverTx {
var ret types.ResponseDeliverTx
defer func() {
if r := recover(); r != nil {
ret = m.base.DeliverTx(input)
}
}()
ret = m.Application.DeliverTx(input)
return ret
}
func (m BaseMock) EndBlock(input types.RequestEndBlock) types.ResponseEndBlock {
var ret types.ResponseEndBlock
defer func() {
if r := recover(); r != nil {
ret = m.base.EndBlock(input)
}
}()
ret = m.Application.EndBlock(input)
return ret
}
+33 -5
View File
@@ -22,12 +22,12 @@ func (r ResponseCheckTx) IsErr() bool {
}
// IsOK returns true if Code is OK.
func (r ResponseDeliverTx) IsOK() bool {
func (r ExecTxResult) IsOK() bool {
return r.Code == CodeTypeOK
}
// IsErr returns true if Code is something other than OK.
func (r ResponseDeliverTx) IsErr() bool {
func (r ExecTxResult) IsErr() bool {
return r.Code != CodeTypeOK
}
@@ -72,12 +72,12 @@ func (r *ResponseCheckTx) UnmarshalJSON(b []byte) error {
return jsonpbUnmarshaller.Unmarshal(reader, r)
}
func (r *ResponseDeliverTx) MarshalJSON() ([]byte, error) {
func (r *ExecTxResult) MarshalJSON() ([]byte, error) {
s, err := jsonpbMarshaller.MarshalToString(r)
return []byte(s), err
}
func (r *ResponseDeliverTx) UnmarshalJSON(b []byte) error {
func (r *ExecTxResult) UnmarshalJSON(b []byte) error {
reader := bytes.NewBuffer(b)
return jsonpbUnmarshaller.Unmarshal(reader, r)
}
@@ -124,7 +124,35 @@ type jsonRoundTripper interface {
var _ jsonRoundTripper = (*ResponseCommit)(nil)
var _ jsonRoundTripper = (*ResponseQuery)(nil)
var _ jsonRoundTripper = (*ResponseDeliverTx)(nil)
var _ jsonRoundTripper = (*ExecTxResult)(nil)
var _ jsonRoundTripper = (*ResponseCheckTx)(nil)
var _ jsonRoundTripper = (*EventAttribute)(nil)
// deterministicExecTxResult constructs a copy of response that omits
// non-deterministic fields. The input response is not modified.
func deterministicExecTxResult(response *ExecTxResult) *ExecTxResult {
return &ExecTxResult{
Code: response.Code,
Data: response.Data,
GasWanted: response.GasWanted,
GasUsed: response.GasUsed,
}
}
// MarshalTxResults encodes the the TxResults as a list of byte
// slices. It strips off the non-deterministic pieces of the TxResults
// so that the resulting data can be used for hash comparisons and used
// in Merkle proofs.
func MarshalTxResults(r []*ExecTxResult) ([][]byte, error) {
s := make([][]byte, len(r))
for i, e := range r {
d := deterministicExecTxResult(e)
b, err := d.Marshal()
if err != nil {
return nil, err
}
s[i] = b
}
return s, nil
}
+2047 -2683
View File
File diff suppressed because it is too large Load Diff
+74
View File
@@ -0,0 +1,74 @@
package types_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle"
)
func TestHashAndProveResults(t *testing.T) {
trs := []*abci.ExecTxResult{
// Note, these tests rely on the first two entries being in this order.
{Code: 0, Data: nil},
{Code: 0, Data: []byte{}},
{Code: 0, Data: []byte("one")},
{Code: 14, Data: nil},
{Code: 14, Data: []byte("foo")},
{Code: 14, Data: []byte("bar")},
}
// Nil and []byte{} should produce the same bytes
bz0, err := trs[0].Marshal()
require.NoError(t, err)
bz1, err := trs[1].Marshal()
require.NoError(t, err)
require.Equal(t, bz0, bz1)
// Make sure that we can get a root hash from results and verify proofs.
rs, err := abci.MarshalTxResults(trs)
require.NoError(t, err)
root := merkle.HashFromByteSlices(rs)
assert.NotEmpty(t, root)
_, proofs := merkle.ProofsFromByteSlices(rs)
for i, tr := range trs {
bz, err := tr.Marshal()
require.NoError(t, err)
valid := proofs[i].Verify(root, bz)
assert.NoError(t, valid, "%d", i)
}
}
func TestHashDeterministicFieldsOnly(t *testing.T) {
tr1 := abci.ExecTxResult{
Code: 1,
Data: []byte("transaction"),
Log: "nondeterministic data: abc",
Info: "nondeterministic data: abc",
GasWanted: 1000,
GasUsed: 1000,
Events: []abci.Event{},
Codespace: "nondeterministic.data.abc",
}
tr2 := abci.ExecTxResult{
Code: 1,
Data: []byte("transaction"),
Log: "nondeterministic data: def",
Info: "nondeterministic data: def",
GasWanted: 1000,
GasUsed: 1000,
Events: []abci.Event{},
Codespace: "nondeterministic.data.def",
}
r1, err := abci.MarshalTxResults([]*abci.ExecTxResult{&tr1})
require.NoError(t, err)
r2, err := abci.MarshalTxResults([]*abci.ExecTxResult{&tr2})
require.NoError(t, err)
require.Equal(t, merkle.HashFromByteSlices(r1), merkle.HashFromByteSlices(r2))
}
+3 -37
View File
@@ -62,7 +62,7 @@ func newReactor(
panic("only support one validator")
}
app := &testApp{}
app := abci.NewBaseApplication()
cc := proxy.NewLocalClientCreator(app)
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
err := proxyApp.Start()
@@ -73,7 +73,7 @@ func newReactor(
blockDB := dbm.NewMemDB()
stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
blockStore := store.NewBlockStore(blockDB)
@@ -100,7 +100,7 @@ func newReactor(
fastSync := true
db := dbm.NewMemDB()
stateStore = sm.NewStore(db, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
blockExec := sm.NewBlockExecutor(stateStore, log.TestingLogger(), proxyApp.Consensus(),
mp, sm.EmptyEvidencePool{})
@@ -296,37 +296,3 @@ func TestBadBlockStopsPeer(t *testing.T) {
assert.True(t, lastReactorPair.reactor.Switch.Peers().Size() < len(reactorPairs)-1)
}
type testApp struct {
abci.BaseApplication
}
var _ abci.Application = (*testApp)(nil)
func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
return abci.ResponseInfo{}
}
func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
return abci.ResponseBeginBlock{}
}
func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
return abci.ResponseEndBlock{}
}
func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
return abci.ResponseDeliverTx{Events: []abci.Event{}}
}
func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
return abci.ResponseCheckTx{}
}
func (app *testApp) Commit() abci.ResponseCommit {
return abci.ResponseCommit{}
}
func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
return
}
+10 -10
View File
@@ -41,7 +41,7 @@ reindex from the base block height(inclusive); and the default end-height is 0,
the tooling will reindex until the latest block height(inclusive). User can omit
either or both arguments.
Note: This operation requires ABCI Responses. Do not set DiscardABCIResponses to true if you
Note: This operation requires ABCI Responses. Do not set DiscardFinalizeBlockResponses to true if you
want to use this command.
`,
Example: `
@@ -147,28 +147,28 @@ func eventReIndex(cmd *cobra.Command, args eventReIndexArgs) error {
return fmt.Errorf("not able to load block at height %d from the blockstore", i)
}
r, err := args.stateStore.LoadABCIResponses(i)
r, err := args.stateStore.LoadFinalizeBlockResponse(i)
if err != nil {
return fmt.Errorf("not able to load ABCI Response at height %d from the statestore", i)
}
e := types.EventDataNewBlockHeader{
Header: b.Header,
NumTxs: int64(len(b.Txs)),
ResultBeginBlock: *r.BeginBlock,
ResultEndBlock: *r.EndBlock,
e := types.EventDataNewBlock{
Block: b,
ResultFinalizeBlock: *r,
}
numTxs := len(b.Data.Txs)
var batch *txindex.Batch
if e.NumTxs > 0 {
batch = txindex.NewBatch(e.NumTxs)
if numTxs > 0 {
batch = txindex.NewBatch(int64(numTxs))
for i := range b.Data.Txs {
tr := abcitypes.TxResult{
Height: b.Height,
Index: uint32(i),
Tx: b.Data.Txs[i],
Result: *(r.DeliverTxs[i]),
Result: *(r.TxResults[i]),
}
if err = batch.Add(&tr); err != nil {
+10 -12
View File
@@ -13,7 +13,6 @@ import (
abcitypes "github.com/tendermint/tendermint/abci/types"
tmcfg "github.com/tendermint/tendermint/config"
prototmstate "github.com/tendermint/tendermint/proto/tendermint/state"
blockmocks "github.com/tendermint/tendermint/state/indexer/mocks"
"github.com/tendermint/tendermint/state/mocks"
txmocks "github.com/tendermint/tendermint/state/txindex/mocks"
@@ -140,25 +139,24 @@ func TestReIndexEvent(t *testing.T) {
On("LoadBlock", base).Return(&types.Block{Data: types.Data{Txs: types.Txs{make(types.Tx, 1)}}}).
On("LoadBlock", height).Return(&types.Block{Data: types.Data{Txs: types.Txs{make(types.Tx, 1)}}})
dtx := abcitypes.ResponseDeliverTx{}
abciResp := &prototmstate.ABCIResponses{
DeliverTxs: []*abcitypes.ResponseDeliverTx{&dtx},
EndBlock: &abcitypes.ResponseEndBlock{},
BeginBlock: &abcitypes.ResponseBeginBlock{},
abciResp := &abcitypes.ResponseFinalizeBlock{
TxResults: []*abcitypes.ExecTxResult{
{Code: 1},
},
}
mockBlockIndexer.
On("Index", mock.AnythingOfType("types.EventDataNewBlockHeader")).Return(errors.New("")).Once().
On("Index", mock.AnythingOfType("types.EventDataNewBlockHeader")).Return(nil)
On("Index", mock.AnythingOfType("types.EventDataNewBlock")).Return(errors.New("")).Once().
On("Index", mock.AnythingOfType("types.EventDataNewBlock")).Return(nil)
mockTxIndexer.
On("AddBatch", mock.AnythingOfType("*txindex.Batch")).Return(errors.New("")).Once().
On("AddBatch", mock.AnythingOfType("*txindex.Batch")).Return(nil)
mockStateStore.
On("LoadABCIResponses", base).Return(nil, errors.New("")).Once().
On("LoadABCIResponses", base).Return(abciResp, nil).
On("LoadABCIResponses", height).Return(abciResp, nil)
On("LoadFinalizeBlockResponse", base).Return(nil, errors.New("")).Once().
On("LoadFinalizeBlockResponse", base).Return(abciResp, nil).
On("LoadFinalizeBlockResponse", height).Return(abciResp, nil)
testCases := []struct {
startHeight int64
@@ -166,7 +164,7 @@ func TestReIndexEvent(t *testing.T) {
reIndexErr bool
}{
{base, height, true}, // LoadBlock error
{base, height, true}, // LoadABCIResponses error
{base, height, true}, // LoadFinalizeBlockResponse error
{base, height, true}, // index block event error
{base, height, true}, // index tx event error
{base, base, false},
+1 -1
View File
@@ -78,7 +78,7 @@ func loadStateAndBlockStore(config *cfg.Config) (*store.BlockStore, state.Store,
return nil, nil, err
}
stateStore := state.NewStore(stateDB, state.StoreOptions{
DiscardABCIResponses: config.Storage.DiscardABCIResponses,
DiscardFinalizeBlockResponses: config.Storage.DiscardFinalizeBlockResponses,
})
return blockStore, stateStore, nil
+3 -3
View File
@@ -1098,14 +1098,14 @@ type StorageConfig struct {
// Set to false to ensure ABCI responses are persisted. ABCI responses are
// required for `/block_results` RPC queries, and to reindex events in the
// command-line tool.
DiscardABCIResponses bool `mapstructure:"discard_abci_responses"`
DiscardFinalizeBlockResponses bool `mapstructure:"discard_abci_responses"`
}
// DefaultStorageConfig returns the default configuration options relating to
// Tendermint storage optimization.
func DefaultStorageConfig() *StorageConfig {
return &StorageConfig{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
}
}
@@ -1113,7 +1113,7 @@ func DefaultStorageConfig() *StorageConfig {
// testing.
func TestStorageConfig() *StorageConfig {
return &StorageConfig{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
}
}
+1 -1
View File
@@ -490,7 +490,7 @@ peer_query_maj23_sleep_duration = "{{ .Consensus.PeerQueryMaj23SleepDuration }}"
# considerable amount of disk space. Set to false to ensure ABCI responses are
# persisted. ABCI responses are required for /block_results RPC queries, and to
# reindex events in the command-line tool.
discard_abci_responses = {{ .Storage.DiscardABCIResponses}}
discard_abci_responses = {{ .Storage.DiscardFinalizeBlockResponses}}
#######################################################
### Transaction Indexer Configuration Options ###
+9 -7
View File
@@ -21,6 +21,7 @@ import (
"github.com/tendermint/tendermint/libs/service"
tmsync "github.com/tendermint/tendermint/libs/sync"
mempl "github.com/tendermint/tendermint/mempool"
"github.com/tendermint/tendermint/proxy"
cfg "github.com/tendermint/tendermint/config"
mempoolv0 "github.com/tendermint/tendermint/mempool/v0"
@@ -51,7 +52,7 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
logger := consensusLogger().With("test", "byzantine", "validator", i)
stateDB := dbm.NewMemDB() // each state needs its own db
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc)
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
@@ -59,15 +60,16 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
app := appFunc()
vals := types.TM2PB.ValidatorUpdates(state.Validators)
app.InitChain(abci.RequestInitChain{Validators: vals})
_, err := app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals})
require.NoError(t, err)
blockDB := dbm.NewMemDB()
blockStore := store.NewBlockStore(blockDB)
mtx := new(tmsync.Mutex)
// one for mempool, one for consensus
proxyAppConnCon := abcicli.NewLocalClient(mtx, app)
proxyAppConnConMem := abcicli.NewLocalClient(mtx, app)
proxyAppConnCon := proxy.NewAppConnConsensus(abcicli.NewLocalClient(mtx, app), nil)
proxyAppConnMem := proxy.NewAppConnMempool(abcicli.NewLocalClient(mtx, app), nil)
// Make Mempool
var mempool mempl.Mempool
@@ -75,14 +77,14 @@ func TestByzantinePrevoteEquivocation(t *testing.T) {
switch thisConfig.Mempool.Version {
case cfg.MempoolV0:
mempool = mempoolv0.NewCListMempool(config.Mempool,
proxyAppConnConMem,
proxyAppConnMem,
state.LastBlockHeight,
mempoolv0.WithPreCheck(sm.TxPreCheck(state)),
mempoolv0.WithPostCheck(sm.TxPostCheck(state)))
case cfg.MempoolV1:
mempool = mempoolv1.NewTxMempool(logger,
config.Mempool,
proxyAppConnConMem,
proxyAppConnMem,
state.LastBlockHeight,
mempoolv1.WithPreCheck(sm.TxPreCheck(state)),
mempoolv1.WithPostCheck(sm.TxPostCheck(state)),
@@ -307,7 +309,7 @@ func TestByzantineConflictingProposalsWithPartition(t *testing.T) {
N := 4
logger := consensusLogger().With("test", "byzantine")
app := newKVStore
css, cleanup := randConsensusNet(N, "consensus_byzantine_test", newMockTickerFunc(false), app)
css, cleanup := randConsensusNet(t, N, "consensus_byzantine_test", newMockTickerFunc(false), app)
defer cleanup()
// give the byzantine validator a normal ticker
+24 -27
View File
@@ -35,6 +35,7 @@ import (
"github.com/tendermint/tendermint/p2p"
"github.com/tendermint/tendermint/privval"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/store"
"github.com/tendermint/tendermint/types"
@@ -396,8 +397,8 @@ func newStateWithConfigAndBlockStore(
// one for mempool, one for consensus
mtx := new(tmsync.Mutex)
proxyAppConnCon := abcicli.NewLocalClient(mtx, app)
proxyAppConnConMem := abcicli.NewLocalClient(mtx, app)
proxyAppConnCon := proxy.NewAppConnConsensus(abcicli.NewLocalClient(mtx, app), nil)
proxyAppConnMem := proxy.NewAppConnMempool(abcicli.NewLocalClient(mtx, app), nil)
// Make Mempool
memplMetrics := mempl.NopMetrics()
@@ -407,7 +408,7 @@ func newStateWithConfigAndBlockStore(
switch config.Mempool.Version {
case cfg.MempoolV0:
mempool = mempoolv0.NewCListMempool(config.Mempool,
proxyAppConnConMem,
proxyAppConnMem,
state.LastBlockHeight,
mempoolv0.WithMetrics(memplMetrics),
mempoolv0.WithPreCheck(sm.TxPreCheck(state)),
@@ -416,7 +417,7 @@ func newStateWithConfigAndBlockStore(
logger := consensusLogger()
mempool = mempoolv1.NewTxMempool(logger,
config.Mempool,
proxyAppConnConMem,
proxyAppConnMem,
state.LastBlockHeight,
mempoolv1.WithMetrics(memplMetrics),
mempoolv1.WithPreCheck(sm.TxPreCheck(state)),
@@ -432,7 +433,7 @@ func newStateWithConfigAndBlockStore(
// Make State
stateDB := blockDB
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
if err := stateStore.Save(state); err != nil { // for save height 1's validators info
panic(err)
@@ -463,7 +464,7 @@ func loadPrivValidator(config *cfg.Config) *privval.FilePV {
}
func randState(nValidators int) (*State, []*validatorStub) {
return randStateWithApp(nValidators, kvstore.NewApplication())
return randStateWithApp(nValidators, kvstore.NewInMemoryApplication())
}
func randStateWithApp(nValidators int, app abci.Application) (*State, []*validatorStub) {
@@ -746,8 +747,9 @@ func consensusLogger() log.Logger {
}).With("module", "consensus")
}
func randConsensusNet(nValidators int, testName string, tickerFunc func() TimeoutTicker,
func randConsensusNet(t *testing.T, nValidators int, testName string, tickerFunc func() TimeoutTicker,
appFunc func() abci.Application, configOpts ...func(*cfg.Config)) ([]*State, cleanupFunc) {
t.Helper()
genDoc, privVals := randGenesisDoc(nValidators, false, 30)
css := make([]*State, nValidators)
logger := consensusLogger()
@@ -755,7 +757,7 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou
for i := 0; i < nValidators; i++ {
stateDB := dbm.NewMemDB() // each state needs its own db
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc)
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
@@ -766,7 +768,8 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou
ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
app := appFunc()
vals := types.TM2PB.ValidatorUpdates(state.Validators)
app.InitChain(abci.RequestInitChain{Validators: vals})
_, err := app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals})
require.NoError(t, err)
css[i] = newStateWithConfigAndBlockStore(thisConfig, state, privVals[i], app, stateDB)
css[i].SetTimeoutTicker(tickerFunc())
@@ -781,6 +784,7 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou
// nPeers = nValidators + nNotValidator
func randConsensusNetWithPeers(
t *testing.T,
nValidators,
nPeers int,
testName string,
@@ -795,7 +799,7 @@ func randConsensusNetWithPeers(
for i := 0; i < nPeers; i++ {
stateDB := dbm.NewMemDB() // each state needs its own db
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc)
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
@@ -808,26 +812,19 @@ func randConsensusNetWithPeers(
if i < nValidators {
privVal = privVals[i]
} else {
tempKeyFile, err := os.CreateTemp("", "priv_validator_key_")
if err != nil {
panic(err)
}
tempStateFile, err := os.CreateTemp("", "priv_validator_state_")
if err != nil {
panic(err)
}
privVal = privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
tempKeyFile := t.TempDir()
tempStateFile := t.TempDir()
privVal = privval.GenFilePV(tempKeyFile, tempStateFile)
}
app := appFunc(path.Join(config.DBDir(), fmt.Sprintf("%s_%d", testName, i)))
vals := types.TM2PB.ValidatorUpdates(state.Validators)
if _, ok := app.(*kvstore.PersistentKVStoreApplication); ok {
if _, ok := app.(*kvstore.Application); ok {
// simulate handshake, receive app version. If don't do this, replay test will fail
state.Version.Consensus.App = kvstore.ProtocolVersion
state.Version.Consensus.App = kvstore.AppVersion
}
app.InitChain(abci.RequestInitChain{Validators: vals})
// sm.SaveState(stateDB,state) //height 1's validatorsInfo already saved in LoadStateFromDBOrGenesisDoc above
_, err := app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals})
require.NoError(t, err)
css[i] = newStateWithConfig(thisConfig, state, privVal, app)
css[i].SetTimeoutTicker(tickerFunc())
@@ -932,15 +929,15 @@ func newPersistentKVStore() abci.Application {
if err != nil {
panic(err)
}
return kvstore.NewPersistentKVStoreApplication(dir)
return kvstore.NewPersistentApplication(dir)
}
func newKVStore() abci.Application {
return kvstore.NewApplication()
return kvstore.NewInMemoryApplication()
}
func newPersistentKVStoreWithPath(dbDir string) abci.Application {
return kvstore.NewPersistentKVStoreApplication(dbDir)
return kvstore.NewPersistentApplication(dbDir)
}
func signDataIsEqual(v1 *types.Vote, v2 *tmproto.Vote) bool {
+1 -1
View File
@@ -18,7 +18,7 @@ import (
// Ensure a testnet makes blocks
func TestReactorInvalidPrecommit(t *testing.T) {
N := 4
css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
css, cleanup := randConsensusNet(t, N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
defer cleanup()
for i := 0; i < 4; i++ {
+15 -88
View File
@@ -1,6 +1,7 @@
package consensus
import (
"context"
"encoding/binary"
"fmt"
"os"
@@ -12,7 +13,7 @@ import (
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/abci/example/code"
"github.com/tendermint/tendermint/abci/example/kvstore"
abci "github.com/tendermint/tendermint/abci/types"
mempl "github.com/tendermint/tendermint/mempool"
sm "github.com/tendermint/tendermint/state"
@@ -29,7 +30,7 @@ func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) {
defer os.RemoveAll(config.RootDir)
config.Consensus.CreateEmptyBlocks = false
state, privVals := randGenesisState(1, false, 10)
cs := newStateWithConfig(config, state, privVals[0], NewCounterApplication())
cs := newStateWithConfig(config, state, privVals[0], kvstore.NewInMemoryApplication())
assertMempool(cs.txNotifier).EnableTxsAvailable()
height, round := cs.Height, cs.Round
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
@@ -49,7 +50,7 @@ func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) {
config.Consensus.CreateEmptyBlocksInterval = ensureTimeout
state, privVals := randGenesisState(1, false, 10)
cs := newStateWithConfig(config, state, privVals[0], NewCounterApplication())
cs := newStateWithConfig(config, state, privVals[0], kvstore.NewInMemoryApplication())
assertMempool(cs.txNotifier).EnableTxsAvailable()
@@ -66,7 +67,7 @@ func TestMempoolProgressInHigherRound(t *testing.T) {
defer os.RemoveAll(config.RootDir)
config.Consensus.CreateEmptyBlocks = false
state, privVals := randGenesisState(1, false, 10)
cs := newStateWithConfig(config, state, privVals[0], NewCounterApplication())
cs := newStateWithConfig(config, state, privVals[0], kvstore.NewInMemoryApplication())
assertMempool(cs.txNotifier).EnableTxsAvailable()
height, round := cs.Height, cs.Round
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
@@ -113,8 +114,8 @@ func deliverTxsRange(cs *State, start, end int) {
func TestMempoolTxConcurrentWithCommit(t *testing.T) {
state, privVals := randGenesisState(1, false, 10)
blockDB := dbm.NewMemDB()
stateStore := sm.NewStore(blockDB, sm.StoreOptions{DiscardABCIResponses: false})
cs := newStateWithConfigAndBlockStore(config, state, privVals[0], NewCounterApplication(), blockDB)
stateStore := sm.NewStore(blockDB, sm.StoreOptions{DiscardFinalizeBlockResponses: false})
cs := newStateWithConfigAndBlockStore(config, state, privVals[0], kvstore.NewInMemoryApplication(), blockDB)
err := stateStore.Save(state)
require.NoError(t, err)
newBlockHeaderCh := subscribe(cs.eventBus, types.EventQueryNewBlockHeader)
@@ -136,9 +137,9 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) {
func TestMempoolRmBadTx(t *testing.T) {
state, privVals := randGenesisState(1, false, 10)
app := NewCounterApplication()
app := kvstore.NewInMemoryApplication()
blockDB := dbm.NewMemDB()
stateStore := sm.NewStore(blockDB, sm.StoreOptions{DiscardABCIResponses: false})
stateStore := sm.NewStore(blockDB, sm.StoreOptions{DiscardFinalizeBlockResponses: false})
cs := newStateWithConfigAndBlockStore(config, state, privVals[0], app, blockDB)
err := stateStore.Save(state)
require.NoError(t, err)
@@ -147,11 +148,10 @@ func TestMempoolRmBadTx(t *testing.T) {
txBytes := make([]byte, 8)
binary.BigEndian.PutUint64(txBytes, uint64(0))
resDeliver := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes})
assert.False(t, resDeliver.IsErr(), fmt.Sprintf("expected no error. got %v", resDeliver))
resCommit := app.Commit()
assert.True(t, len(resCommit.Data) > 0)
res, err := app.FinalizeBlock(context.Background(), &abci.RequestFinalizeBlock{Txs: [][]byte{txBytes}})
require.NoError(t, err)
assert.False(t, res.TxResults[0].IsErr())
assert.True(t, len(res.AgreedAppData) > 0)
emptyMempoolCh := make(chan struct{})
checkTxRespCh := make(chan struct{})
@@ -159,8 +159,8 @@ func TestMempoolRmBadTx(t *testing.T) {
// Try to send the tx through the mempool.
// CheckTx should not err, but the app should return a bad abci code
// and the tx should get removed from the pool
err := assertMempool(cs.txNotifier).CheckTx(txBytes, func(r *abci.Response) {
if r.GetCheckTx().Code != code.CodeTypeBadNonce {
err := assertMempool(cs.txNotifier).CheckTx(txBytes, func(r *abci.ResponseCheckTx) {
if r.Code != kvstore.CodeTypeBadNonce {
t.Errorf("expected checktx to return bad nonce, got %v", r)
return
}
@@ -202,76 +202,3 @@ func TestMempoolRmBadTx(t *testing.T) {
return
}
}
// CounterApplication that maintains a mempool state and resets it upon commit
type CounterApplication struct {
abci.BaseApplication
txCount int
mempoolTxCount int
}
func NewCounterApplication() *CounterApplication {
return &CounterApplication{}
}
func (app *CounterApplication) Info(req abci.RequestInfo) abci.ResponseInfo {
return abci.ResponseInfo{Data: fmt.Sprintf("txs:%v", app.txCount)}
}
func (app *CounterApplication) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
txValue := txAsUint64(req.Tx)
if txValue != uint64(app.txCount) {
return abci.ResponseDeliverTx{
Code: code.CodeTypeBadNonce,
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.txCount, txValue)}
}
app.txCount++
return abci.ResponseDeliverTx{Code: code.CodeTypeOK}
}
func (app *CounterApplication) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
txValue := txAsUint64(req.Tx)
if txValue != uint64(app.mempoolTxCount) {
return abci.ResponseCheckTx{
Code: code.CodeTypeBadNonce,
Log: fmt.Sprintf("Invalid nonce. Expected %v, got %v", app.mempoolTxCount, txValue)}
}
app.mempoolTxCount++
return abci.ResponseCheckTx{Code: code.CodeTypeOK}
}
func txAsUint64(tx []byte) uint64 {
tx8 := make([]byte, 8)
copy(tx8[len(tx8)-len(tx):], tx)
return binary.BigEndian.Uint64(tx8)
}
func (app *CounterApplication) Commit() abci.ResponseCommit {
app.mempoolTxCount = app.txCount
if app.txCount == 0 {
return abci.ResponseCommit{}
}
hash := make([]byte, 8)
binary.BigEndian.PutUint64(hash, uint64(app.txCount))
return abci.ResponseCommit{Data: hash}
}
func (app *CounterApplication) PrepareProposal(
req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
txs := make([][]byte, 0, len(req.Txs))
var totalBytes int64
for _, tx := range req.Txs {
totalBytes += int64(len(tx))
if totalBytes > req.MaxTxBytes {
break
}
txs = append(txs, tx)
}
return abci.ResponsePrepareProposal{Txs: txs}
}
func (app *CounterApplication) ProcessProposal(
req abci.RequestProcessProposal) abci.ResponseProcessProposal {
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
}
+16 -12
View File
@@ -34,6 +34,7 @@ import (
"github.com/tendermint/tendermint/p2p"
p2pmock "github.com/tendermint/tendermint/p2p/mock"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
statemocks "github.com/tendermint/tendermint/state/mocks"
"github.com/tendermint/tendermint/store"
@@ -112,7 +113,7 @@ func stopConsensusNet(logger log.Logger, reactors []*Reactor, eventBuses []*type
// Ensure a testnet makes blocks
func TestReactorBasic(t *testing.T) {
N := 4
css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
css, cleanup := randConsensusNet(t, N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
defer cleanup()
reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N)
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
@@ -139,7 +140,7 @@ func TestReactorWithEvidence(t *testing.T) {
for i := 0; i < nValidators; i++ {
stateDB := dbm.NewMemDB() // each state needs its own db
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
state, _ := stateStore.LoadFromDBOrGenesisDoc(genDoc)
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
@@ -147,7 +148,8 @@ func TestReactorWithEvidence(t *testing.T) {
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
app := appFunc()
vals := types.TM2PB.ValidatorUpdates(state.Validators)
app.InitChain(abci.RequestInitChain{Validators: vals})
_, err := app.InitChain(context.Background(), &abci.RequestInitChain{Validators: vals})
require.NoError(t, err)
pv := privVals[i]
// duplicate code from:
@@ -159,8 +161,8 @@ func TestReactorWithEvidence(t *testing.T) {
mtx := new(tmsync.Mutex)
memplMetrics := mempl.NopMetrics()
// one for mempool, one for consensus
proxyAppConnCon := abcicli.NewLocalClient(mtx, app)
proxyAppConnConMem := abcicli.NewLocalClient(mtx, app)
proxyAppConnCon := proxy.NewAppConnConsensus(abcicli.NewLocalClient(mtx, app), nil)
proxyAppConnMem := proxy.NewAppConnMempool(abcicli.NewLocalClient(mtx, app), nil)
// Make Mempool
var mempool mempl.Mempool
@@ -168,7 +170,7 @@ func TestReactorWithEvidence(t *testing.T) {
switch config.Mempool.Version {
case cfg.MempoolV0:
mempool = mempoolv0.NewCListMempool(config.Mempool,
proxyAppConnConMem,
proxyAppConnMem,
state.LastBlockHeight,
mempoolv0.WithMetrics(memplMetrics),
mempoolv0.WithPreCheck(sm.TxPreCheck(state)),
@@ -176,7 +178,7 @@ func TestReactorWithEvidence(t *testing.T) {
case cfg.MempoolV1:
mempool = mempoolv1.NewTxMempool(logger,
config.Mempool,
proxyAppConnConMem,
proxyAppConnMem,
state.LastBlockHeight,
mempoolv1.WithMetrics(memplMetrics),
mempoolv1.WithPreCheck(sm.TxPreCheck(state)),
@@ -236,7 +238,7 @@ func TestReactorWithEvidence(t *testing.T) {
// Ensure a testnet makes blocks when there are txs
func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) {
N := 4
css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore,
css, cleanup := randConsensusNet(t, N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore,
func(c *cfg.Config) {
c.Consensus.CreateEmptyBlocks = false
})
@@ -257,7 +259,7 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) {
func TestReactorReceiveDoesNotPanicIfAddPeerHasntBeenCalledYet(t *testing.T) {
N := 1
css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
css, cleanup := randConsensusNet(t, N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
defer cleanup()
reactors, _, eventBuses := startConsensusNet(t, css, N)
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
@@ -280,7 +282,7 @@ func TestReactorReceiveDoesNotPanicIfAddPeerHasntBeenCalledYet(t *testing.T) {
func TestReactorReceivePanicsIfInitPeerHasntBeenCalledYet(t *testing.T) {
N := 1
css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
css, cleanup := randConsensusNet(t, N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
defer cleanup()
reactors, _, eventBuses := startConsensusNet(t, css, N)
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
@@ -303,7 +305,7 @@ func TestReactorReceivePanicsIfInitPeerHasntBeenCalledYet(t *testing.T) {
// Test we record stats about votes and block parts from other peers.
func TestReactorRecordsVotesAndBlockParts(t *testing.T) {
N := 4
css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
css, cleanup := randConsensusNet(t, N, "consensus_reactor_test", newMockTickerFunc(true), newKVStore)
defer cleanup()
reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N)
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
@@ -329,6 +331,7 @@ func TestReactorVotingPowerChange(t *testing.T) {
nVals := 4
logger := log.TestingLogger()
css, cleanup := randConsensusNet(
t,
nVals,
"consensus_voting_power_changes_test",
newMockTickerFunc(true),
@@ -409,6 +412,7 @@ func TestReactorValidatorSetChanges(t *testing.T) {
nPeers := 7
nVals := 4
css, _, _, cleanup := randConsensusNetWithPeers(
t,
nVals,
nPeers,
"consensus_val_set_changes_test",
@@ -524,7 +528,7 @@ func TestReactorValidatorSetChanges(t *testing.T) {
// Check we can make blocks with skip_timeout_commit=false
func TestReactorWithTimeoutCommit(t *testing.T) {
N := 4
css, cleanup := randConsensusNet(N, "consensus_reactor_with_timeout_commit_test", newMockTickerFunc(false), newKVStore)
css, cleanup := randConsensusNet(t, N, "consensus_reactor_with_timeout_commit_test", newMockTickerFunc(false), newKVStore)
defer cleanup()
// override default SkipTimeoutCommit == true for tests
for i := 0; i < N; i++ {
+7 -6
View File
@@ -2,6 +2,7 @@ package consensus
import (
"bytes"
"context"
"fmt"
"hash/crc32"
"io"
@@ -241,7 +242,7 @@ func (h *Handshaker) NBlocks() int {
func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
// Handshake is done via ABCI Info on the query conn.
res, err := proxyApp.Query().InfoSync(proxy.RequestInfo)
res, err := proxyApp.Query().Info(context.TODO(), proxy.RequestInfo)
if err != nil {
return fmt.Errorf("error calling Info: %v", err)
}
@@ -308,7 +309,7 @@ func (h *Handshaker) ReplayBlocks(
validatorSet := types.NewValidatorSet(validators)
nextVals := types.TM2PB.ValidatorUpdates(validatorSet)
pbparams := h.genDoc.ConsensusParams.ToProto()
req := abci.RequestInitChain{
req := &abci.RequestInitChain{
Time: h.genDoc.GenesisTime,
ChainId: h.genDoc.ChainID,
InitialHeight: h.genDoc.InitialHeight,
@@ -316,7 +317,7 @@ func (h *Handshaker) ReplayBlocks(
Validators: nextVals,
AppStateBytes: h.genDoc.AppState,
}
res, err := proxyApp.Consensus().InitChainSync(req)
res, err := proxyApp.Consensus().InitChain(context.TODO(), req)
if err != nil {
return nil, err
}
@@ -418,11 +419,11 @@ func (h *Handshaker) ReplayBlocks(
case appBlockHeight == storeBlockHeight:
// We ran Commit, but didn't save the state, so replayBlock with mock app.
abciResponses, err := h.stateStore.LoadLastABCIResponse(storeBlockHeight)
finalizeBlockResponse, err := h.stateStore.LoadLastFinalizeBlockResponse(storeBlockHeight)
if err != nil {
return nil, err
}
mockApp := newMockProxyApp(appHash, abciResponses)
mockApp := newMockProxyApp(finalizeBlockResponse)
h.logger.Info("Replay last block using mock app")
state, err = h.replayBlock(state, storeBlockHeight, mockApp)
return state.AppHash, err
@@ -468,7 +469,7 @@ func (h *Handshaker) replayBlocks(
assertAppHashEqualsOneFromBlock(appHash, block)
}
appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, h.stateStore, h.genDoc.InitialHeight)
err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, h.stateStore, h.genDoc.InitialHeight)
if err != nil {
return nil, err
}
+1 -1
View File
@@ -298,7 +298,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
tmos.Exit(err.Error())
}
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
gdoc, err := sm.MakeGenesisDocFromFile(config.GenesisFile())
if err != nil {
+9 -26
View File
@@ -1,10 +1,11 @@
package consensus
import (
"context"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/clist"
mempl "github.com/tendermint/tendermint/mempool"
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
"github.com/tendermint/tendermint/proxy"
"github.com/tendermint/tendermint/types"
)
@@ -19,7 +20,7 @@ func (emptyMempool) Lock() {}
func (emptyMempool) Unlock() {}
func (emptyMempool) Size() int { return 0 }
func (emptyMempool) SizeBytes() int64 { return 0 }
func (emptyMempool) CheckTx(_ types.Tx, _ func(*abci.Response), _ mempl.TxInfo) error {
func (emptyMempool) CheckTx(_ types.Tx, _ func(*abci.ResponseCheckTx), _ mempl.TxInfo) error {
return nil
}
@@ -32,7 +33,7 @@ func (emptyMempool) ReapMaxTxs(n int) types.Txs { return types.Txs{
func (emptyMempool) Update(
_ int64,
_ types.Txs,
_ []*abci.ResponseDeliverTx,
_ []*abci.ExecTxResult,
_ mempl.PreCheckFunc,
_ mempl.PostCheckFunc,
) error {
@@ -56,10 +57,9 @@ func (emptyMempool) CloseWAL() {}
// Useful because we don't want to call Commit() twice for the same block on
// the real app.
func newMockProxyApp(appHash []byte, abciResponses *tmstate.ABCIResponses) proxy.AppConnConsensus {
func newMockProxyApp(finalizeBlockResponse *abci.ResponseFinalizeBlock) proxy.AppConnConsensus {
clientCreator := proxy.NewLocalClientCreator(&mockProxyApp{
appHash: appHash,
abciResponses: abciResponses,
finalizeBlockResponse: finalizeBlockResponse,
})
cli, _ := clientCreator.NewABCIClient()
err := cli.Start()
@@ -71,26 +71,9 @@ func newMockProxyApp(appHash []byte, abciResponses *tmstate.ABCIResponses) proxy
type mockProxyApp struct {
abci.BaseApplication
appHash []byte
txCount int
abciResponses *tmstate.ABCIResponses
finalizeBlockResponse *abci.ResponseFinalizeBlock
}
func (mock *mockProxyApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
r := mock.abciResponses.DeliverTxs[mock.txCount]
mock.txCount++
if r == nil {
return abci.ResponseDeliverTx{}
}
return *r
}
func (mock *mockProxyApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
mock.txCount = 0
return *mock.abciResponses.EndBlock
}
func (mock *mockProxyApp) Commit() abci.ResponseCommit {
return abci.ResponseCommit{Data: mock.appHash}
func (mock *mockProxyApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
return mock.finalizeBlockResponse, nil
}
+37 -66
View File
@@ -14,11 +14,13 @@ import (
"github.com/cosmos/gogoproto/proto"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tm-db"
"github.com/tendermint/tendermint/abci/example/kvstore"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/abci/types/mocks"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
cryptoenc "github.com/tendermint/tendermint/crypto/encoding"
@@ -27,7 +29,6 @@ import (
tmrand "github.com/tendermint/tendermint/libs/rand"
mempl "github.com/tendermint/tendermint/mempool"
"github.com/tendermint/tendermint/privval"
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
@@ -73,7 +74,7 @@ func startNewStateAndWaitForBlock(t *testing.T, consensusReplayConfig *cfg.Confi
consensusReplayConfig,
state,
privValidator,
kvstore.NewApplication(),
kvstore.NewInMemoryApplication(),
blockDB,
)
cs.SetLogger(logger)
@@ -160,7 +161,7 @@ LOOP:
blockDB := dbm.NewMemDB()
stateDB := blockDB
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
state, err := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile())
require.NoError(t, err)
@@ -169,7 +170,7 @@ LOOP:
consensusReplayConfig,
state,
privValidator,
kvstore.NewApplication(),
kvstore.NewInMemoryApplication(),
blockDB,
)
cs.SetLogger(logger)
@@ -326,6 +327,7 @@ func TestSimulateValidatorsChange(t *testing.T) {
nPeers := 7
nVals := 4
css, genDoc, config, cleanup := randConsensusNetWithPeers(
t,
nVals,
nPeers,
"replay_test",
@@ -595,49 +597,27 @@ func TestHandshakeReplayNone(t *testing.T) {
// Test mockProxyApp should not panic when app return ABCIResponses with some empty ResponseDeliverTx
func TestMockProxyApp(t *testing.T) {
sim.CleanupFunc() // clean the test env created in TestSimulateValidatorsChange
logger := log.TestingLogger()
var validTxs, invalidTxs = 0, 0
txIndex := 0
assert.NotPanics(t, func() {
abciResWithEmptyDeliverTx := new(tmstate.ABCIResponses)
abciResWithEmptyDeliverTx.DeliverTxs = make([]*abci.ResponseDeliverTx, 0)
abciResWithEmptyDeliverTx.DeliverTxs = append(abciResWithEmptyDeliverTx.DeliverTxs, &abci.ResponseDeliverTx{})
abciResWithEmptyDeliverTx := &abci.ResponseFinalizeBlock{
TxResults: []*abci.ExecTxResult{},
}
// called when saveABCIResponses:
// called when SaveFinalizeBlockResponse:
bytes, err := proto.Marshal(abciResWithEmptyDeliverTx)
require.NoError(t, err)
loadedAbciRes := new(tmstate.ABCIResponses)
loadedBlockResp := new(abci.ResponseFinalizeBlock)
// this also happens sm.LoadABCIResponses
err = proto.Unmarshal(bytes, loadedAbciRes)
// this also happens sm.LoadFinalizeBlockResponse
err = proto.Unmarshal(bytes, loadedBlockResp)
require.NoError(t, err)
mock := newMockProxyApp([]byte("mock_hash"), loadedAbciRes)
abciRes := new(tmstate.ABCIResponses)
abciRes.DeliverTxs = make([]*abci.ResponseDeliverTx, len(loadedAbciRes.DeliverTxs))
// Execute transactions and get hash.
proxyCb := func(req *abci.Request, res *abci.Response) {
if r, ok := res.Value.(*abci.Response_DeliverTx); ok {
// TODO: make use of res.Log
// TODO: make use of this info
// Blocks may include invalid txs.
txRes := r.DeliverTx
if txRes.Code == abci.CodeTypeOK {
validTxs++
} else {
logger.Debug("Invalid tx", "code", txRes.Code, "log", txRes.Log)
invalidTxs++
}
abciRes.DeliverTxs[txIndex] = txRes
txIndex++
}
}
mock.SetResponseCallback(proxyCb)
mock := newMockProxyApp(loadedBlockResp)
someTx := []byte("tx")
mock.DeliverTxAsync(abci.RequestDeliverTx{Tx: someTx})
_, err = mock.FinalizeBlock(context.Background(), &abci.RequestFinalizeBlock{Txs: [][]byte{someTx}})
require.NoError(t, err)
})
assert.True(t, validTxs == 1)
assert.True(t, invalidTxs == 0)
@@ -700,11 +680,11 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
require.NoError(t, err)
pubKey, err := privVal.GetPubKey()
require.NoError(t, err)
stateDB, genesisState, store = stateAndStore(t, config, pubKey, kvstore.ProtocolVersion)
stateDB, genesisState, store = stateAndStore(t, config, pubKey, kvstore.AppVersion)
}
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
store.chain = chain
store.commits = commits
@@ -715,7 +695,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
latestAppHash := state.AppHash
// make a new client creator
kvstoreApp := kvstore.NewPersistentKVStoreApplication(
kvstoreApp := kvstore.NewPersistentApplication(
filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_a", nBlocks, mode)))
clientCreator2 := proxy.NewLocalClientCreator(kvstoreApp)
@@ -725,7 +705,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
proxyApp := proxy.NewAppConns(clientCreator2, proxy.NopMetrics())
stateDB1 := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB1, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
err := stateStore.Save(genesisState)
require.NoError(t, err)
@@ -764,7 +744,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin
}
// get the latest app hash from the app
res, err := proxyApp.Query().InfoSync(abci.RequestInfo{Version: ""})
res, err := proxyApp.Query().Info(context.Background(), &abci.RequestInfo{Version: ""})
if err != nil {
t.Fatal(err)
}
@@ -809,9 +789,9 @@ func buildAppStateFromChain(t *testing.T, proxyApp proxy.AppConns, stateStore sm
}
defer proxyApp.Stop() //nolint:errcheck // ignore
state.Version.Consensus.App = kvstore.ProtocolVersion // simulate handshake, receive app version
state.Version.Consensus.App = kvstore.AppVersion // simulate handshake, receive app version
validators := types.TM2PB.ValidatorUpdates(state.Validators)
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{
if _, err := proxyApp.Consensus().InitChain(context.Background(), &abci.RequestInitChain{
Validators: validators,
}); err != nil {
panic(err)
@@ -852,7 +832,7 @@ func buildTMStateFromChain(
mode uint) sm.State {
// run the whole chain against this client to build up the tendermint state
clientCreator := proxy.NewLocalClientCreator(
kvstore.NewPersistentKVStoreApplication(
kvstore.NewPersistentApplication(
filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_t", nBlocks, mode))))
proxyApp := proxy.NewAppConns(clientCreator, proxy.NopMetrics())
if err := proxyApp.Start(); err != nil {
@@ -860,9 +840,9 @@ func buildTMStateFromChain(
}
defer proxyApp.Stop() //nolint:errcheck
state.Version.Consensus.App = kvstore.ProtocolVersion // simulate handshake, receive app version
state.Version.Consensus.App = kvstore.AppVersion // simulate handshake, receive app version
validators := types.TM2PB.ValidatorUpdates(state.Validators)
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{
if _, err := proxyApp.Consensus().InitChain(context.Background(), &abci.RequestInitChain{
Validators: validators,
}); err != nil {
panic(err)
@@ -907,7 +887,7 @@ func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) {
require.NoError(t, err)
stateDB, state, store := stateAndStore(t, config, pubKey, appVersion)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
state.LastValidators = state.Validators.Copy()
@@ -974,15 +954,15 @@ type badApp struct {
onlyLastHashIsWrong bool
}
func (app *badApp) Commit() abci.ResponseCommit {
func (app *badApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
app.height++
if app.onlyLastHashIsWrong {
if app.height == app.numBlocks {
return abci.ResponseCommit{Data: tmrand.Bytes(8)}
return &abci.ResponseFinalizeBlock{AgreedAppData: tmrand.Bytes(8)}, nil
}
return abci.ResponseCommit{Data: []byte{app.height}}
return &abci.ResponseFinalizeBlock{AgreedAppData: []byte{app.height}}, nil
} else if app.allHashesAreWrong {
return abci.ResponseCommit{Data: tmrand.Bytes(8)}
return &abci.ResponseFinalizeBlock{AgreedAppData: tmrand.Bytes(8)}, nil
}
panic("either allHashesAreWrong or onlyLastHashIsWrong must be set")
@@ -1124,7 +1104,7 @@ func stateAndStore(
) (dbm.DB, sm.State, *mockBlockStore) {
stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
state, err := sm.MakeGenesisStateFromFile(config.GenesisFile())
require.NoError(t, err)
@@ -1201,7 +1181,10 @@ func (bs *mockBlockStore) PruneBlocks(height int64) (uint64, error) {
func TestHandshakeUpdatesValidators(t *testing.T) {
val, _ := types.RandValidator(true, 10)
vals := types.NewValidatorSet([]*types.Validator{val})
app := &initChainApp{vals: types.TM2PB.ValidatorUpdates(vals)}
app := &mocks.Application{}
app.On("InitChain", mock.Anything, mock.Anything).Return(&abci.ResponseInitChain{
Validators: types.TM2PB.ValidatorUpdates(vals),
}, nil)
clientCreator := proxy.NewLocalClientCreator(app)
config := ResetConfig("handshake_test_")
@@ -1211,7 +1194,7 @@ func TestHandshakeUpdatesValidators(t *testing.T) {
require.NoError(t, err)
stateDB, state, store := stateAndStore(t, config, pubKey, 0x0)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
oldValAddr := state.Validators.Validators[0].Address
@@ -1240,15 +1223,3 @@ func TestHandshakeUpdatesValidators(t *testing.T) {
assert.NotEqual(t, oldValAddr, newValAddr)
assert.Equal(t, newValAddr, expectValAddr)
}
// returns the vals on InitChain
type initChainApp struct {
abci.BaseApplication
vals []abci.ValidatorUpdate
}
func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
return abci.ResponseInitChain{
Validators: ica.vals,
}
}
+3 -3
View File
@@ -665,7 +665,7 @@ func TestStateLockPOLRelock(t *testing.T) {
signAddVotes(cs1, tmproto.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
// before we timeout to the new round set the new proposal
cs2 := newState(cs1.state, vs2, kvstore.NewApplication())
cs2 := newState(cs1.state, vs2, kvstore.NewInMemoryApplication())
prop, propBlock := decideProposal(t, cs2, vs2, vs2.Height, vs2.Round+1)
if prop == nil || propBlock == nil {
t.Fatal("Failed to create proposal block with vs2")
@@ -853,7 +853,7 @@ func TestStateLockPOLUnlockOnUnknownBlock(t *testing.T) {
signAddVotes(cs1, tmproto.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
// before we timeout to the new round set the new proposal
cs2 := newState(cs1.state, vs2, kvstore.NewApplication())
cs2 := newState(cs1.state, vs2, kvstore.NewInMemoryApplication())
prop, propBlock := decideProposal(t, cs2, vs2, vs2.Height, vs2.Round+1)
if prop == nil || propBlock == nil {
t.Fatal("Failed to create proposal block with vs2")
@@ -899,7 +899,7 @@ func TestStateLockPOLUnlockOnUnknownBlock(t *testing.T) {
signAddVotes(cs1, tmproto.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4)
// before we timeout to the new round set the new proposal
cs3 := newState(cs1.state, vs3, kvstore.NewApplication())
cs3 := newState(cs1.state, vs3, kvstore.NewInMemoryApplication())
prop, propBlock = decideProposal(t, cs3, vs3, vs3.Height, vs3.Round+1)
if prop == nil || propBlock == nil {
t.Fatal("Failed to create proposal block with vs2")
+3 -3
View File
@@ -30,7 +30,7 @@ import (
func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) {
config := getConfig(t)
app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator"))
app := kvstore.NewPersistentApplication(filepath.Join(config.DBDir(), "wal_generator"))
logger := log.TestingLogger().With("wal_generator", "wal_generator")
logger.Info("generating WAL (last height msg excluded)", "numBlocks", numBlocks)
@@ -48,13 +48,13 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) {
blockStoreDB := db.NewMemDB()
stateDB := blockStoreDB
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
state, err := sm.MakeGenesisState(genDoc)
if err != nil {
return fmt.Errorf("failed to make genesis state: %w", err)
}
state.Version.Consensus.App = kvstore.ProtocolVersion
state.Version.Consensus.App = kvstore.AppVersion
if err = stateStore.Save(state); err != nil {
t.Error(err)
}
+1 -1
View File
@@ -359,7 +359,7 @@ func TestRecoverPendingEvidence(t *testing.T) {
func initializeStateFromValidatorSet(valSet *types.ValidatorSet, height int64) sm.Store {
stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
state := sm.State{
ChainID: evidenceChainID,
+1 -1
View File
@@ -2,7 +2,7 @@ package test
import "github.com/tendermint/tendermint/types"
func MakeNTxs(height, n int64) []types.Tx {
func MakeNTxs(height, n int64) types.Txs {
txs := make([]types.Tx, n)
for i := range txs {
txs[i] = types.Tx([]byte{byte(height), byte(i / 256), byte(i % 256)})
+1 -1
View File
@@ -156,7 +156,7 @@ func ExampleClient_VerifyLightBlockAtHeight() {
func TestMain(m *testing.M) {
// start a tendermint node (and kvstore) in the background to test against
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
node := rpctest.StartTendermint(app, rpctest.SuppressStdout)
code := m.Run()
+1 -1
View File
@@ -34,7 +34,7 @@ func TestNewProvider(t *testing.T) {
}
func TestProvider(t *testing.T) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
app.RetainBlocks = 10
node := rpctest.StartTendermint(app)
+3 -23
View File
@@ -8,9 +8,6 @@ import (
"regexp"
"time"
"github.com/cosmos/gogoproto/proto"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
tmmath "github.com/tendermint/tendermint/libs/math"
@@ -18,6 +15,7 @@ import (
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
"github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
@@ -379,6 +377,7 @@ func (c *Client) BlockByHash(ctx context.Context, hash []byte) (*ctypes.ResultBl
// BlockResults returns the block results for the given height. If no height is
// provided, the results of the block preceding the latest are returned.
// NOTE: Light client only verifies the tx results
func (c *Client) BlockResults(ctx context.Context, height *int64) (*ctypes.ResultBlockResults, error) {
var h int64
if height == nil {
@@ -410,27 +409,8 @@ func (c *Client) BlockResults(ctx context.Context, height *int64) (*ctypes.Resul
return nil, err
}
// proto-encode BeginBlock events
bbeBytes, err := proto.Marshal(&abci.ResponseBeginBlock{
Events: res.BeginBlockEvents,
})
if err != nil {
return nil, err
}
// Build a Merkle tree of proto-encoded DeliverTx results and get a hash.
results := types.NewResults(res.TxsResults)
// proto-encode EndBlock events.
ebeBytes, err := proto.Marshal(&abci.ResponseEndBlock{
Events: res.EndBlockEvents,
})
if err != nil {
return nil, err
}
// Build a Merkle tree out of the above 3 binary slices.
rH := merkle.HashFromByteSlices([][]byte{bbeBytes, results.Hash(), ebeBytes})
rH := state.TxResultsHash(res.TxsResults)
// Verify block results.
if !bytes.Equal(rH, trustedBlock.LastResultsHash) {
+2 -2
View File
@@ -32,7 +32,7 @@ const (
type Mempool interface {
// CheckTx executes a new transaction against the application to determine
// its validity and whether it should be added to the mempool.
CheckTx(tx types.Tx, callback func(*abci.Response), txInfo TxInfo) error
CheckTx(tx types.Tx, callback func(*abci.ResponseCheckTx), txInfo TxInfo) error
// RemoveTxByKey removes a transaction, identified by its key,
// from the mempool.
@@ -67,7 +67,7 @@ type Mempool interface {
Update(
blockHeight int64,
blockTxs types.Txs,
deliverTxResponses []*abci.ResponseDeliverTx,
deliverTxResponses []*abci.ExecTxResult,
newPreFn PreCheckFunc,
newPostFn PostCheckFunc,
) error
+4 -4
View File
@@ -17,11 +17,11 @@ type Mempool struct {
}
// CheckTx provides a mock function with given fields: tx, callback, txInfo
func (_m *Mempool) CheckTx(tx types.Tx, callback func(*abcitypes.Response), txInfo mempool.TxInfo) error {
func (_m *Mempool) CheckTx(tx types.Tx, callback func(*abcitypes.ResponseCheckTx), txInfo mempool.TxInfo) error {
ret := _m.Called(tx, callback, txInfo)
var r0 error
if rf, ok := ret.Get(0).(func(types.Tx, func(*abcitypes.Response), mempool.TxInfo) error); ok {
if rf, ok := ret.Get(0).(func(types.Tx, func(*abcitypes.ResponseCheckTx), mempool.TxInfo) error); ok {
r0 = rf(tx, callback, txInfo)
} else {
r0 = ret.Error(0)
@@ -155,11 +155,11 @@ func (_m *Mempool) Unlock() {
}
// Update provides a mock function with given fields: blockHeight, blockTxs, deliverTxResponses, newPreFn, newPostFn
func (_m *Mempool) Update(blockHeight int64, blockTxs types.Txs, deliverTxResponses []*abcitypes.ResponseDeliverTx, newPreFn mempool.PreCheckFunc, newPostFn mempool.PostCheckFunc) error {
func (_m *Mempool) Update(blockHeight int64, blockTxs types.Txs, deliverTxResponses []*abcitypes.ExecTxResult, newPreFn mempool.PreCheckFunc, newPostFn mempool.PostCheckFunc) error {
ret := _m.Called(blockHeight, blockTxs, deliverTxResponses, newPreFn, newPostFn)
var r0 error
if rf, ok := ret.Get(0).(func(int64, types.Txs, []*abcitypes.ResponseDeliverTx, mempool.PreCheckFunc, mempool.PostCheckFunc) error); ok {
if rf, ok := ret.Get(0).(func(int64, types.Txs, []*abcitypes.ExecTxResult, mempool.PreCheckFunc, mempool.PostCheckFunc) error); ok {
r0 = rf(blockHeight, blockTxs, deliverTxResponses, newPreFn, newPostFn)
} else {
r0 = ret.Error(0)
+4 -4
View File
@@ -11,7 +11,7 @@ import (
)
func BenchmarkReap(b *testing.B) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup := newMempoolWithApp(cc)
defer cleanup()
@@ -33,7 +33,7 @@ func BenchmarkReap(b *testing.B) {
}
func BenchmarkCheckTx(b *testing.B) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup := newMempoolWithApp(cc)
defer cleanup()
@@ -55,7 +55,7 @@ func BenchmarkCheckTx(b *testing.B) {
}
func BenchmarkParallelCheckTx(b *testing.B) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup := newMempoolWithApp(cc)
defer cleanup()
@@ -80,7 +80,7 @@ func BenchmarkParallelCheckTx(b *testing.B) {
}
func BenchmarkCheckDuplicateTx(b *testing.B) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup := newMempoolWithApp(cc)
defer cleanup()
+1 -1
View File
@@ -14,7 +14,7 @@ import (
)
func TestCacheAfterUpdate(t *testing.T) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup := newMempoolWithApp(cc)
defer cleanup()
+133 -163
View File
@@ -2,6 +2,7 @@ package v0
import (
"bytes"
"context"
"errors"
"sync"
"sync/atomic"
@@ -92,8 +93,6 @@ func NewCListMempool(
mp.cache = mempool.NopTxCache{}
}
proxyAppConn.SetResponseCallback(mp.globalCb)
for _, option := range options {
option(mp)
}
@@ -152,7 +151,7 @@ func (mem *CListMempool) SizeBytes() int64 {
// Lock() must be help by the caller during execution.
func (mem *CListMempool) FlushAppConn() error {
return mem.proxyAppConn.FlushSync()
return mem.proxyAppConn.Flush(context.TODO())
}
// XXX: Unsafe! Calling Flush may leave mempool in inconsistent state.
@@ -202,7 +201,7 @@ func (mem *CListMempool) TxsWaitChan() <-chan struct{} {
// Safe for concurrent use by multiple goroutines.
func (mem *CListMempool) CheckTx(
tx types.Tx,
cb func(*abci.Response),
cb func(*abci.ResponseCheckTx),
txInfo mempool.TxInfo,
) error {
@@ -251,31 +250,16 @@ func (mem *CListMempool) CheckTx(
return mempool.ErrTxInCache
}
reqRes := mem.proxyAppConn.CheckTxAsync(abci.RequestCheckTx{Tx: tx})
reqRes.SetCallback(mem.reqResCb(tx, txInfo.SenderID, txInfo.SenderP2PID, cb))
return nil
}
// Global callback that will be called after every ABCI response.
// Having a single global callback avoids needing to set a callback for each request.
// However, processing the checkTx response requires the peerID (so we can track which txs we heard from who),
// and peerID is not included in the ABCI request, so we have to set request-specific callbacks that
// include this information. If we're not in the midst of a recheck, this function will just return,
// so the request specific callback can do the work.
//
// When rechecking, we don't need the peerID, so the recheck callback happens
// here.
func (mem *CListMempool) globalCb(req *abci.Request, res *abci.Response) {
if mem.recheckCursor == nil {
return
resp, err := mem.proxyAppConn.CheckTx(context.TODO(), &abci.RequestCheckTx{Tx: tx})
if err != nil {
mem.cache.Remove(tx)
mem.logger.Error("error from check tx", "err", err)
return err
}
mem.metrics.RecheckTimes.Add(1)
mem.resCbRecheck(req, res)
mem.addNewTransaction(tx, txInfo.SenderID, txInfo.SenderP2PID, cb, resp)
// update metrics
mem.metrics.Size.Set(float64(mem.Size()))
return nil
}
// Request specific callback that should be set on individual reqRes objects
@@ -287,27 +271,70 @@ func (mem *CListMempool) globalCb(req *abci.Request, res *abci.Response) {
// when all other response processing is complete.
//
// Used in CheckTx to record PeerID who sent us the tx.
func (mem *CListMempool) reqResCb(
func (mem *CListMempool) addNewTransaction(
tx []byte,
peerID uint16,
peerP2PID p2p.ID,
externalCb func(*abci.Response),
) func(res *abci.Response) {
return func(res *abci.Response) {
if mem.recheckCursor != nil {
// this should never happen
panic("recheck cursor is not nil in reqResCb")
externalCb func(*abci.ResponseCheckTx),
resp *abci.ResponseCheckTx,
) {
if mem.recheckCursor != nil {
// this should never happen
panic("recheck cursor is not nil in reqResCb")
}
var postCheckErr error
if mem.postCheck != nil {
postCheckErr = mem.postCheck(tx, resp)
}
if (resp.Code == abci.CodeTypeOK) && postCheckErr == nil {
// Check mempool isn't full again to reduce the chance of exceeding the
// limits.
if err := mem.isFull(len(tx)); err != nil {
// remove from cache (mempool might have a space later)
mem.cache.Remove(tx)
mem.logger.Error(err.Error())
return
}
mem.resCbFirstTime(tx, peerID, peerP2PID, res)
// update metrics
mem.metrics.Size.Set(float64(mem.Size()))
// passed in by the caller of CheckTx, eg. the RPC
if externalCb != nil {
externalCb(res)
memTx := &mempoolTx{
height: mem.height,
gasWanted: resp.GasWanted,
tx: tx,
}
memTx.senders.Store(peerID, true)
mem.addTx(memTx)
mem.logger.Debug(
"added good transaction",
"tx", types.Tx(tx).Hash(),
"resp", resp,
"height", memTx.height,
"total", mem.Size(),
)
mem.notifyTxsAvailable()
} else {
// ignore bad transaction
mem.logger.Debug(
"rejected bad transaction",
"tx", types.Tx(tx).Hash(),
"peerID", peerP2PID,
"resp", resp,
"err", postCheckErr,
)
mem.metrics.FailedTxs.Add(1)
if !mem.config.KeepInvalidTxsInCache {
// remove from cache (it might be good later)
mem.cache.Remove(tx)
}
}
// update metrics
mem.metrics.Size.Set(float64(mem.Size()))
// passed in by the caller of CheckTx, eg. the RPC
if externalCb != nil {
externalCb(resp)
}
}
@@ -365,136 +392,68 @@ func (mem *CListMempool) isFull(txSize int) error {
return nil
}
// callback, which is called after the app checked the tx for the first time.
//
// The case where the app checks the tx for the second and subsequent times is
// handled by the resCbRecheck callback.
func (mem *CListMempool) resCbFirstTime(
tx []byte,
peerID uint16,
peerP2PID p2p.ID,
res *abci.Response,
) {
switch r := res.Value.(type) {
case *abci.Response_CheckTx:
var postCheckErr error
if mem.postCheck != nil {
postCheckErr = mem.postCheck(tx, r.CheckTx)
}
if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil {
// Check mempool isn't full again to reduce the chance of exceeding the
// limits.
if err := mem.isFull(len(tx)); err != nil {
// remove from cache (mempool might have a space later)
mem.cache.Remove(tx)
mem.logger.Error(err.Error())
return
}
memTx := &mempoolTx{
height: mem.height,
gasWanted: r.CheckTx.GasWanted,
tx: tx,
}
memTx.senders.Store(peerID, true)
mem.addTx(memTx)
mem.logger.Debug(
"added good transaction",
"tx", types.Tx(tx).Hash(),
"res", r,
"height", memTx.height,
"total", mem.Size(),
)
mem.notifyTxsAvailable()
} else {
// ignore bad transaction
mem.logger.Debug(
"rejected bad transaction",
"tx", types.Tx(tx).Hash(),
"peerID", peerP2PID,
"res", r,
"err", postCheckErr,
)
mem.metrics.FailedTxs.Add(1)
if !mem.config.KeepInvalidTxsInCache {
// remove from cache (it might be good later)
mem.cache.Remove(tx)
}
}
default:
// ignore other messages
}
}
// callback, which is called after the app rechecked the tx.
//
// The case where the app checks the tx for the first time is handled by the
// resCbFirstTime callback.
func (mem *CListMempool) resCbRecheck(req *abci.Request, res *abci.Response) {
switch r := res.Value.(type) {
case *abci.Response_CheckTx:
tx := req.GetCheckTx().Tx
memTx := mem.recheckCursor.Value.(*mempoolTx)
func (mem *CListMempool) resCbRecheck(req *abci.RequestCheckTx, res *abci.ResponseCheckTx) {
tx := req.Tx
memTx := mem.recheckCursor.Value.(*mempoolTx)
// Search through the remaining list of tx to recheck for a transaction that matches
// the one we received from the ABCI application.
for {
if bytes.Equal(tx, memTx.tx) {
// We've found a tx in the recheck list that matches the tx that we
// received from the ABCI application.
// Break, and use this transaction for further checks.
break
}
mem.logger.Error(
"re-CheckTx transaction mismatch",
"got", types.Tx(tx),
"expected", memTx.tx,
)
if mem.recheckCursor == mem.recheckEnd {
// we reached the end of the recheckTx list without finding a tx
// matching the one we received from the ABCI application.
// Return without processing any tx.
mem.recheckCursor = nil
return
}
mem.recheckCursor = mem.recheckCursor.Next()
memTx = mem.recheckCursor.Value.(*mempoolTx)
// Search through the remaining list of tx to recheck for a transaction that matches
// the one we received from the ABCI application.
for {
if bytes.Equal(tx, memTx.tx) {
// We've found a tx in the recheck list that matches the tx that we
// received from the ABCI application.
// Break, and use this transaction for further checks.
break
}
var postCheckErr error
if mem.postCheck != nil {
postCheckErr = mem.postCheck(tx, r.CheckTx)
}
mem.logger.Error(
"re-CheckTx transaction mismatch",
"got", types.Tx(tx),
"expected", memTx.tx,
)
if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil {
// Good, nothing to do.
} else {
// Tx became invalidated due to newly committed block.
mem.logger.Debug("tx is no longer valid", "tx", types.Tx(tx).Hash(), "res", r, "err", postCheckErr)
// NOTE: we remove tx from the cache because it might be good later
mem.removeTx(tx, mem.recheckCursor, !mem.config.KeepInvalidTxsInCache)
}
if mem.recheckCursor == mem.recheckEnd {
// we reached the end of the recheckTx list without finding a tx
// matching the one we received from the ABCI application.
// Return without processing any tx.
mem.recheckCursor = nil
} else {
mem.recheckCursor = mem.recheckCursor.Next()
return
}
if mem.recheckCursor == nil {
// Done!
mem.logger.Debug("done rechecking txs")
// incase the recheck removed all txs
if mem.Size() > 0 {
mem.notifyTxsAvailable()
}
mem.recheckCursor = mem.recheckCursor.Next()
memTx = mem.recheckCursor.Value.(*mempoolTx)
}
var postCheckErr error
if mem.postCheck != nil {
postCheckErr = mem.postCheck(tx, res)
}
if (res.Code == abci.CodeTypeOK) && postCheckErr == nil {
// Good, nothing to do.
} else {
// Tx became invalidated due to newly committed block.
mem.logger.Debug("tx is no longer valid", "tx", types.Tx(tx).Hash(), "res", res, "err", postCheckErr)
// NOTE: we remove tx from the cache because it might be good later
mem.removeTx(tx, mem.recheckCursor, !mem.config.KeepInvalidTxsInCache)
}
if mem.recheckCursor == mem.recheckEnd {
mem.recheckCursor = nil
} else {
mem.recheckCursor = mem.recheckCursor.Next()
}
if mem.recheckCursor == nil {
// Done!
mem.logger.Debug("done rechecking txs")
// incase the recheck removed all txs
if mem.Size() > 0 {
mem.notifyTxsAvailable()
}
default:
// ignore other messages
}
}
@@ -579,7 +538,7 @@ func (mem *CListMempool) ReapMaxTxs(max int) types.Txs {
func (mem *CListMempool) Update(
height int64,
txs types.Txs,
deliverTxResponses []*abci.ResponseDeliverTx,
txResults []*abci.ExecTxResult,
preCheck mempool.PreCheckFunc,
postCheck mempool.PostCheckFunc,
) error {
@@ -595,7 +554,7 @@ func (mem *CListMempool) Update(
}
for i, tx := range txs {
if deliverTxResponses[i].Code == abci.CodeTypeOK {
if txResults[i].Code == abci.CodeTypeOK {
// Add valid committed tx to the cache (if missing).
_ = mem.cache.Push(tx)
} else if !mem.config.KeepInvalidTxsInCache {
@@ -650,13 +609,24 @@ func (mem *CListMempool) recheckTxs() {
// NOTE: globalCb may be called concurrently.
for e := mem.txs.Front(); e != nil; e = e.Next() {
memTx := e.Value.(*mempoolTx)
mem.proxyAppConn.CheckTxAsync(abci.RequestCheckTx{
req := &abci.RequestCheckTx{
Tx: memTx.tx,
Type: abci.CheckTxType_Recheck,
})
}
res, err := mem.proxyAppConn.CheckTx(context.TODO(), req)
if err != nil {
mem.logger.Error("recheckTx app error", "err", err)
}
mem.resCbRecheck(req, res)
}
mem.proxyAppConn.FlushAsync()
mem.metrics.RecheckTimes.Add(1)
mem.metrics.Size.Set(float64(mem.Size()))
if err := mem.proxyAppConn.Flush(context.TODO()); err != nil {
mem.logger.Error("recheckTx flush", err, "err")
}
}
//--------------------------------------------------------------------------------
+45 -60
View File
@@ -1,6 +1,7 @@
package v0
import (
"context"
"crypto/rand"
"encoding/binary"
"fmt"
@@ -21,6 +22,7 @@ import (
abciserver "github.com/tendermint/tendermint/abci/server"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/internal/test"
"github.com/tendermint/tendermint/libs/log"
tmrand "github.com/tendermint/tendermint/libs/rand"
"github.com/tendermint/tendermint/libs/service"
@@ -119,7 +121,7 @@ func checkTxs(t *testing.T, mp mempool.Mempool, count int, peerID uint16) types.
}
func TestReapMaxBytesMaxGas(t *testing.T) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup := newMempoolWithApp(cc)
defer cleanup()
@@ -127,8 +129,6 @@ func TestReapMaxBytesMaxGas(t *testing.T) {
// Ensure gas calculation behaves as expected
checkTxs(t, mp, 1, mempool.UnknownPeerID)
tx0 := mp.TxsFront().Value.(*mempoolTx)
// assert that kv store has gas wanted = 1.
require.Equal(t, app.CheckTx(abci.RequestCheckTx{Tx: tx0.tx}).GasWanted, int64(1), "KVStore had a gas value neq to 1")
require.Equal(t, tx0.gasWanted, int64(1), "transactions gas was set incorrectly")
// ensure each tx is 20 bytes long
require.Equal(t, len(tx0.tx), 20, "Tx is longer than 20 bytes")
@@ -168,7 +168,7 @@ func TestReapMaxBytesMaxGas(t *testing.T) {
}
func TestMempoolFilters(t *testing.T) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup := newMempoolWithApp(cc)
defer cleanup()
@@ -207,7 +207,7 @@ func TestMempoolFilters(t *testing.T) {
}
func TestMempoolUpdate(t *testing.T) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup := newMempoolWithApp(cc)
defer cleanup()
@@ -245,16 +245,14 @@ func TestMempoolUpdate(t *testing.T) {
}
func TestMempoolUpdateDoesNotPanicWhenApplicationMissedTx(t *testing.T) {
var callback abciclient.Callback
mockClient := new(abciclimocks.Client)
mockClient.On("Start").Return(nil)
mockClient.On("SetLogger", mock.Anything)
mockClient.On("Error").Return(nil).Times(4)
mockClient.On("FlushAsync", mock.Anything).Return(abciclient.NewReqRes(abci.ToRequestFlush()), nil)
mockClient.On("SetResponseCallback", mock.MatchedBy(func(cb abciclient.Callback) bool { callback = cb; return true }))
mockClient.On("Flush", mock.Anything).Return(nil)
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup, err := newMempoolWithAppMock(cc, mockClient)
require.NoError(t, err)
@@ -262,16 +260,14 @@ func TestMempoolUpdateDoesNotPanicWhenApplicationMissedTx(t *testing.T) {
// Add 4 transactions to the mempool by calling the mempool's `CheckTx` on each of them.
txs := []types.Tx{[]byte{0x01}, []byte{0x02}, []byte{0x03}, []byte{0x04}}
for _, tx := range txs {
reqRes := abciclient.NewReqRes(abci.ToRequestCheckTx(abci.RequestCheckTx{Tx: tx}))
reqRes.Response = abci.ToResponseCheckTx(abci.ResponseCheckTx{Code: abci.CodeTypeOK})
mockClient.On("CheckTxAsync", mock.Anything, mock.Anything).Return(reqRes, nil)
for idx, tx := range txs {
mockClient.On("CheckTx", mock.Anything, &abci.RequestCheckTx{Tx: tx}).Return(&abci.ResponseCheckTx{Code: abci.CodeTypeOK}, nil)
if idx != 0 {
// for all other txs we expect them to be rechecked
mockClient.On("CheckTx", mock.Anything, &abci.RequestCheckTx{Tx: tx, Type: 1}).Return(&abci.ResponseCheckTx{Code: abci.CodeTypeOK}, nil)
}
err := mp.CheckTx(tx, nil, mempool.TxInfo{})
require.NoError(t, err)
// ensure that the callback that the mempool sets on the ReqRes is run.
reqRes.InvokeCallback()
}
// Calling update to remove the first transaction from the mempool.
@@ -279,23 +275,11 @@ func TestMempoolUpdateDoesNotPanicWhenApplicationMissedTx(t *testing.T) {
err = mp.Update(0, []types.Tx{txs[0]}, abciResponses(1, abci.CodeTypeOK), nil, nil)
require.Nil(t, err)
// The mempool has now sent its requests off to the client to be rechecked
// and is waiting for the corresponding callbacks to be called.
// We now call the mempool-supplied callback on the first and third transaction.
// This simulates the client dropping the second request.
// Previous versions of this code panicked when the ABCI application missed
// a recheck-tx request.
resp := abci.ResponseCheckTx{Code: abci.CodeTypeOK}
req := abci.RequestCheckTx{Tx: txs[1]}
callback(abci.ToRequestCheckTx(req), abci.ToResponseCheckTx(resp))
req = abci.RequestCheckTx{Tx: txs[3]}
callback(abci.ToRequestCheckTx(req), abci.ToResponseCheckTx(resp))
mockClient.AssertExpectations(t)
}
func TestMempool_KeepInvalidTxsInCache(t *testing.T) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
wcfg := config.DefaultConfig()
wcfg.Mempool.KeepInvalidTxsInCache = true
@@ -314,10 +298,12 @@ func TestMempool_KeepInvalidTxsInCache(t *testing.T) {
require.NoError(t, err)
// simulate new block
_ = app.DeliverTx(abci.RequestDeliverTx{Tx: a})
_ = app.DeliverTx(abci.RequestDeliverTx{Tx: b})
_, err = app.FinalizeBlock(context.Background(), &abci.RequestFinalizeBlock{
Txs: [][]byte{a, b},
})
require.NoError(t, err)
err = mp.Update(1, []types.Tx{a, b},
[]*abci.ResponseDeliverTx{{Code: abci.CodeTypeOK}, {Code: 2}}, nil, nil)
[]*abci.ExecTxResult{{Code: abci.CodeTypeOK}, {Code: 2}}, nil, nil)
require.NoError(t, err)
// a must be added to the cache
@@ -347,7 +333,7 @@ func TestMempool_KeepInvalidTxsInCache(t *testing.T) {
}
func TestTxsAvailable(t *testing.T) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup := newMempoolWithApp(cc)
defer cleanup()
@@ -391,7 +377,7 @@ func TestTxsAvailable(t *testing.T) {
}
func TestSerialReap(t *testing.T) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mp, cleanup := newMempoolWithApp(cc)
@@ -443,26 +429,25 @@ func TestSerialReap(t *testing.T) {
}
commitRange := func(start, end int) {
// Deliver some txs.
for i := start; i < end; i++ {
txBytes := make([]byte, 8)
binary.BigEndian.PutUint64(txBytes, uint64(i))
res, err := appConnCon.DeliverTxSync(abci.RequestDeliverTx{Tx: txBytes})
if err != nil {
t.Errorf("client error committing tx: %v", err)
}
if res.IsErr() {
// Deliver some txs in a block
res, err := appConnCon.FinalizeBlock(context.Background(), &abci.RequestFinalizeBlock{Txs: test.MakeNTxs(10, 8).ToSliceOfBytes()})
if err != nil {
t.Errorf("client error committing tx: %v", err)
}
for _, txResult := range res.TxResults {
if txResult.IsErr() {
t.Errorf("error committing tx. Code:%v result:%X log:%v",
res.Code, res.Data, res.Log)
txResult.Code, txResult.Data, txResult.Log)
}
}
res, err := appConnCon.CommitSync()
if len(res.AgreedAppData) != 8 {
t.Errorf("error committing. Hash:%X", res.AgreedAppData)
}
_, err = appConnCon.Commit(context.Background(), &abci.RequestCommit{})
if err != nil {
t.Errorf("client error committing: %v", err)
}
if len(res.Data) != 8 {
t.Errorf("error committing. Hash:%X", res.Data)
}
}
//----------------------------------------
@@ -501,7 +486,7 @@ func TestSerialReap(t *testing.T) {
}
func TestMempool_CheckTxChecksTxSize(t *testing.T) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mempl, cleanup := newMempoolWithApp(cc)
@@ -547,7 +532,7 @@ func TestMempool_CheckTxChecksTxSize(t *testing.T) {
}
func TestMempoolTxsBytes(t *testing.T) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
cfg := config.ResetTestRoot("mempool_test")
@@ -591,7 +576,7 @@ func TestMempoolTxsBytes(t *testing.T) {
}
// 6. zero after tx is rechecked and removed due to not being valid anymore
app2 := kvstore.NewApplication()
app2 := kvstore.NewInMemoryApplication()
cc = proxy.NewLocalClientCreator(app2)
mp, cleanup = newMempoolWithApp(cc)
@@ -614,13 +599,13 @@ func TestMempoolTxsBytes(t *testing.T) {
}
})
res, err := appConnCon.DeliverTxSync(abci.RequestDeliverTx{Tx: txBytes})
res, err := appConnCon.FinalizeBlock(context.Background(), &abci.RequestFinalizeBlock{Txs: [][]byte{txBytes}})
require.NoError(t, err)
require.EqualValues(t, 0, res.Code)
require.EqualValues(t, 0, res.TxResults[0].Code)
require.NotEmpty(t, res.AgreedAppData)
res2, err := appConnCon.CommitSync()
_, err = appConnCon.Commit(context.Background(), &abci.RequestCommit{})
require.NoError(t, err)
require.NotEmpty(t, res2.Data)
// Pretend like we committed nothing so txBytes gets rechecked and removed.
err = mp.Update(1, []types.Tx{}, abciResponses(0, abci.CodeTypeOK), nil, nil)
@@ -644,7 +629,7 @@ func TestMempoolTxsBytes(t *testing.T) {
// since otherwise we're not actually testing the concurrency of the mempool here!
func TestMempoolRemoteAppConcurrency(t *testing.T) {
sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", tmrand.Str(6))
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
_, server := newRemoteApp(t, sockPath, app)
t.Cleanup(func() {
if err := server.Stop(); err != nil {
@@ -695,10 +680,10 @@ func newRemoteApp(t *testing.T, addr string, app abci.Application) (abciclient.C
return clientCreator, server
}
func abciResponses(n int, code uint32) []*abci.ResponseDeliverTx {
responses := make([]*abci.ResponseDeliverTx, 0, n)
func abciResponses(n int, code uint32) []*abci.ExecTxResult {
responses := make([]*abci.ExecTxResult, 0, n)
for i := 0; i < n; i++ {
responses = append(responses, &abci.ResponseDeliverTx{Code: code})
responses = append(responses, &abci.ExecTxResult{Code: code})
}
return responses
}
+4 -4
View File
@@ -99,9 +99,9 @@ func TestReactorConcurrency(t *testing.T) {
reactors[0].mempool.Lock()
defer reactors[0].mempool.Unlock()
deliverTxResponses := make([]*abci.ResponseDeliverTx, len(txs))
deliverTxResponses := make([]*abci.ExecTxResult, len(txs))
for i := range txs {
deliverTxResponses[i] = &abci.ResponseDeliverTx{Code: 0}
deliverTxResponses[i] = &abci.ExecTxResult{Code: 0}
}
err := reactors[0].mempool.Update(1, txs, deliverTxResponses, nil, nil)
assert.NoError(t, err)
@@ -115,7 +115,7 @@ func TestReactorConcurrency(t *testing.T) {
reactors[1].mempool.Lock()
defer reactors[1].mempool.Unlock()
err := reactors[1].mempool.Update(1, []types.Tx{}, make([]*abci.ResponseDeliverTx, 0), nil, nil)
err := reactors[1].mempool.Update(1, []types.Tx{}, make([]*abci.ExecTxResult, 0), nil, nil)
assert.NoError(t, err)
}()
@@ -302,7 +302,7 @@ func makeAndConnectReactors(config *cfg.Config, n int) []*Reactor {
reactors := make([]*Reactor, n)
logger := mempoolLogger()
for i := 0; i < n; i++ {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mempool, cleanup := newMempoolWithApp(cc)
defer cleanup()
+12 -11
View File
@@ -1,6 +1,7 @@
package v1
import (
"context"
"fmt"
"runtime"
"sort"
@@ -138,7 +139,7 @@ func (txmp *TxMempool) FlushAppConn() error {
txmp.mtx.Unlock()
defer txmp.mtx.Lock()
return txmp.proxyAppConn.FlushSync()
return txmp.proxyAppConn.Flush(context.TODO())
}
// EnableTxsAvailable enables the mempool to trigger events when transactions
@@ -174,7 +175,7 @@ func (txmp *TxMempool) TxsAvailable() <-chan struct{} { return txmp.txsAvailable
// is (strictly) lower than the priority of tx and whose size together exceeds
// the size of tx, and adds tx instead. If no such transactions exist, tx is
// discarded.
func (txmp *TxMempool) CheckTx(tx types.Tx, cb func(*abci.Response), txInfo mempool.TxInfo) error {
func (txmp *TxMempool) CheckTx(tx types.Tx, cb func(*abci.ResponseCheckTx), txInfo mempool.TxInfo) error {
// During the initial phase of CheckTx, we do not need to modify any state.
// A transaction will not actually be added to the mempool until it survives
@@ -218,7 +219,7 @@ func (txmp *TxMempool) CheckTx(tx types.Tx, cb func(*abci.Response), txInfo memp
}
// Invoke an ABCI CheckTx for this transaction.
rsp, err := txmp.proxyAppConn.CheckTxSync(abci.RequestCheckTx{Tx: tx})
rsp, err := txmp.proxyAppConn.CheckTx(context.TODO(), &abci.RequestCheckTx{Tx: tx})
if err != nil {
txmp.cache.Remove(tx)
return err
@@ -232,7 +233,7 @@ func (txmp *TxMempool) CheckTx(tx types.Tx, cb func(*abci.Response), txInfo memp
wtx.SetPeer(txInfo.SenderID)
txmp.addNewTransaction(wtx, rsp)
if cb != nil {
cb(&abci.Response{Value: &abci.Response_CheckTx{CheckTx: rsp}})
cb(rsp)
}
return nil
}
@@ -367,7 +368,7 @@ func (txmp *TxMempool) ReapMaxTxs(max int) types.Txs {
}
// Update removes all the given transactions from the mempool and the cache,
// and updates the current block height. The blockTxs and deliverTxResponses
// and updates the current block height. The blockTxs and txResults
// must have the same length with each response corresponding to the tx at the
// same offset.
//
@@ -380,14 +381,14 @@ func (txmp *TxMempool) ReapMaxTxs(max int) types.Txs {
func (txmp *TxMempool) Update(
blockHeight int64,
blockTxs types.Txs,
deliverTxResponses []*abci.ResponseDeliverTx,
txResults []*abci.ExecTxResult,
newPreFn mempool.PreCheckFunc,
newPostFn mempool.PostCheckFunc,
) error {
// Safety check: Transactions and responses must match in number.
if len(blockTxs) != len(deliverTxResponses) {
if len(blockTxs) != len(txResults) {
panic(fmt.Sprintf("mempool: got %d transactions but %d DeliverTx responses",
len(blockTxs), len(deliverTxResponses)))
len(blockTxs), len(txResults)))
}
txmp.height = blockHeight
@@ -404,7 +405,7 @@ func (txmp *TxMempool) Update(
// Add successful committed transactions to the cache (if they are not
// already present). Transactions that failed to commit are removed from
// the cache unless the operator has explicitly requested we keep them.
if deliverTxResponses[i].Code == abci.CodeTypeOK {
if txResults[i].Code == abci.CodeTypeOK {
_ = txmp.cache.Push(tx)
} else if !txmp.config.KeepInvalidTxsInCache {
txmp.cache.Remove(tx)
@@ -679,7 +680,7 @@ func (txmp *TxMempool) recheckTransactions() {
wtx := wtx
start(func() error {
// The response for this CheckTx is handled by the default recheckTxCallback.
rsp, err := txmp.proxyAppConn.CheckTxSync(abci.RequestCheckTx{
rsp, err := txmp.proxyAppConn.CheckTx(context.TODO(), &abci.RequestCheckTx{
Tx: wtx.tx,
Type: abci.CheckTxType_Recheck,
})
@@ -692,7 +693,7 @@ func (txmp *TxMempool) recheckTransactions() {
return nil
})
}
_ = txmp.proxyAppConn.FlushAsync()
_ = txmp.proxyAppConn.Flush(context.TODO())
// When recheck is complete, trigger a notification for more transactions.
_ = g.Wait()
+25 -27
View File
@@ -2,6 +2,7 @@ package v1
import (
"bytes"
"context"
"errors"
"fmt"
"math/rand"
@@ -15,7 +16,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/abci/example/code"
"github.com/tendermint/tendermint/abci/example/kvstore"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/config"
@@ -36,7 +36,7 @@ type testTx struct {
priority int64
}
func (app *application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
func (app *application) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) {
var (
priority int64
sender string
@@ -47,35 +47,35 @@ func (app *application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
if len(parts) == 3 {
v, err := strconv.ParseInt(string(parts[2]), 10, 64)
if err != nil {
return abci.ResponseCheckTx{
return &abci.ResponseCheckTx{
Priority: priority,
Code: 100,
GasWanted: 1,
}
}, nil
}
priority = v
sender = string(parts[0])
} else {
return abci.ResponseCheckTx{
return &abci.ResponseCheckTx{
Priority: priority,
Code: 101,
GasWanted: 1,
}
}, nil
}
return abci.ResponseCheckTx{
return &abci.ResponseCheckTx{
Priority: priority,
Sender: sender,
Code: code.CodeTypeOK,
Code: kvstore.CodeTypeOK,
GasWanted: 1,
}
}, nil
}
func setup(t testing.TB, cacheSize int, options ...TxMempoolOption) *TxMempool {
t.Helper()
app := &application{kvstore.NewApplication()}
app := &application{kvstore.NewInMemoryApplication()}
cc := proxy.NewLocalClientCreator(app)
cfg := config.ResetTestRoot(strings.ReplaceAll(t.Name(), "/", "|"))
@@ -97,7 +97,7 @@ func setup(t testing.TB, cacheSize int, options ...TxMempoolOption) *TxMempool {
// its callback has finished executing. It fails t if CheckTx fails.
func mustCheckTx(t *testing.T, txmp *TxMempool, spec string) {
done := make(chan struct{})
if err := txmp.CheckTx([]byte(spec), func(*abci.Response) {
if err := txmp.CheckTx([]byte(spec), func(*abci.ResponseCheckTx) {
close(done)
}, mempool.TxInfo{}); err != nil {
t.Fatalf("CheckTx for %q failed: %v", spec, err)
@@ -164,9 +164,9 @@ func TestTxMempool_TxsAvailable(t *testing.T) {
rawTxs[i] = tx.tx
}
responses := make([]*abci.ResponseDeliverTx, len(rawTxs[:50]))
responses := make([]*abci.ExecTxResult, len(rawTxs[:50]))
for i := 0; i < len(responses); i++ {
responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
responses[i] = &abci.ExecTxResult{Code: abci.CodeTypeOK}
}
// commit half the transactions and ensure we fire an event
@@ -193,9 +193,9 @@ func TestTxMempool_Size(t *testing.T) {
rawTxs[i] = tx.tx
}
responses := make([]*abci.ResponseDeliverTx, len(rawTxs[:50]))
responses := make([]*abci.ExecTxResult, len(rawTxs[:50]))
for i := 0; i < len(responses); i++ {
responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
responses[i] = &abci.ExecTxResult{Code: abci.CodeTypeOK}
}
txmp.Lock()
@@ -288,9 +288,9 @@ func TestTxMempool_Flush(t *testing.T) {
rawTxs[i] = tx.tx
}
responses := make([]*abci.ResponseDeliverTx, len(rawTxs[:50]))
responses := make([]*abci.ExecTxResult, len(rawTxs[:50]))
for i := 0; i < len(responses); i++ {
responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
responses[i] = &abci.ExecTxResult{Code: abci.CodeTypeOK}
}
txmp.Lock()
@@ -485,7 +485,7 @@ func TestTxMempool_ConcurrentTxs(t *testing.T) {
for range ticker.C {
reapedTxs := txmp.ReapMaxTxs(200)
if len(reapedTxs) > 0 {
responses := make([]*abci.ResponseDeliverTx, len(reapedTxs))
responses := make([]*abci.ExecTxResult, len(reapedTxs))
for i := 0; i < len(responses); i++ {
var code uint32
@@ -495,7 +495,7 @@ func TestTxMempool_ConcurrentTxs(t *testing.T) {
code = abci.CodeTypeOK
}
responses[i] = &abci.ResponseDeliverTx{Code: code}
responses[i] = &abci.ExecTxResult{Code: code}
}
txmp.Lock()
@@ -577,9 +577,9 @@ func TestTxMempool_ExpiredTxs_NumBlocks(t *testing.T) {
// reap 5 txs at the next height -- no txs should expire
reapedTxs := txmp.ReapMaxTxs(5)
responses := make([]*abci.ResponseDeliverTx, len(reapedTxs))
responses := make([]*abci.ExecTxResult, len(reapedTxs))
for i := 0; i < len(responses); i++ {
responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
responses[i] = &abci.ExecTxResult{Code: abci.CodeTypeOK}
}
txmp.Lock()
@@ -601,9 +601,9 @@ func TestTxMempool_ExpiredTxs_NumBlocks(t *testing.T) {
// removed. However, we do know that that at most 95 txs can be expired and
// removed.
reapedTxs = txmp.ReapMaxTxs(5)
responses = make([]*abci.ResponseDeliverTx, len(reapedTxs))
responses = make([]*abci.ExecTxResult, len(reapedTxs))
for i := 0; i < len(responses); i++ {
responses[i] = &abci.ResponseDeliverTx{Code: abci.CodeTypeOK}
responses[i] = &abci.ExecTxResult{Code: abci.CodeTypeOK}
}
txmp.Lock()
@@ -639,14 +639,12 @@ func TestTxMempool_CheckTxPostCheckError(t *testing.T) {
_, err := rng.Read(tx)
require.NoError(t, err)
callback := func(res *abci.Response) {
checkTxRes, ok := res.Value.(*abci.Response_CheckTx)
require.True(t, ok)
callback := func(res *abci.ResponseCheckTx) {
expectedErrString := ""
if testCase.err != nil {
expectedErrString = testCase.err.Error()
}
require.Equal(t, expectedErrString, checkTxRes.CheckTx.MempoolError)
require.Equal(t, expectedErrString, res.MempoolError)
}
require.NoError(t, txmp.CheckTx(tx, callback, mempool.TxInfo{SenderID: 0}))
})
+1 -1
View File
@@ -97,7 +97,7 @@ func makeAndConnectReactors(config *cfg.Config, n int) []*Reactor {
reactors := make([]*Reactor, n)
logger := mempoolLogger()
for i := 0; i < n; i++ {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
mempool, cleanup := newMempoolWithApp(cc)
defer cleanup()
+5 -5
View File
@@ -429,7 +429,7 @@ func createEvidenceReactor(config *cfg.Config, dbProvider DBProvider,
}
evidenceLogger := logger.With("module", "evidence")
evidencePool, err := evidence.NewPool(evidenceDB, sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: config.Storage.DiscardABCIResponses,
DiscardFinalizeBlockResponses: config.Storage.DiscardFinalizeBlockResponses,
}), blockStore)
if err != nil {
return nil, nil, err
@@ -519,7 +519,7 @@ func createTransport(
connFilters,
// ABCI query for address filtering.
func(_ p2p.ConnSet, c net.Conn, _ []net.IP) error {
res, err := proxyApp.Query().QuerySync(abci.RequestQuery{
res, err := proxyApp.Query().Query(context.TODO(), &abci.RequestQuery{
Path: fmt.Sprintf("/p2p/filter/addr/%s", c.RemoteAddr().String()),
})
if err != nil {
@@ -537,7 +537,7 @@ func createTransport(
peerFilters,
// ABCI query for ID filtering.
func(_ p2p.IPeerSet, p p2p.Peer) error {
res, err := proxyApp.Query().QuerySync(abci.RequestQuery{
res, err := proxyApp.Query().Query(context.TODO(), &abci.RequestQuery{
Path: fmt.Sprintf("/p2p/filter/id/%s", p.ID()),
})
if err != nil {
@@ -716,7 +716,7 @@ func NewNode(config *cfg.Config,
}
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: config.Storage.DiscardABCIResponses,
DiscardFinalizeBlockResponses: config.Storage.DiscardFinalizeBlockResponses,
})
state, genDoc, err := LoadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider)
@@ -1391,7 +1391,7 @@ func LoadStateFromDBOrGenesisDocProvider(
}
}
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
state, err := stateStore.LoadFromDBOrGenesisDoc(genDoc)
if err != nil {
+6 -6
View File
@@ -123,7 +123,7 @@ func TestNodeSetAppVersion(t *testing.T) {
require.NoError(t, err)
// default config uses the kvstore app
var appVersion = kvstore.ProtocolVersion
var appVersion = kvstore.AppVersion
// check version is set in state
state, err := n.stateStore.Load()
@@ -225,7 +225,7 @@ func testFreeAddr(t *testing.T) string {
func TestCreateProposalBlock(t *testing.T) {
config := cfg.ResetTestRoot("node_create_proposal")
defer os.RemoveAll(config.RootDir)
cc := proxy.NewLocalClientCreator(kvstore.NewApplication())
cc := proxy.NewLocalClientCreator(kvstore.NewInMemoryApplication())
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
err := proxyApp.Start()
require.Nil(t, err)
@@ -236,7 +236,7 @@ func TestCreateProposalBlock(t *testing.T) {
var height int64 = 1
state, stateDB, privVals := state(1, height)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
maxBytes := 16384
var partSize uint32 = 256
@@ -336,7 +336,7 @@ func TestCreateProposalBlock(t *testing.T) {
func TestMaxProposalBlockSize(t *testing.T) {
config := cfg.ResetTestRoot("node_create_proposal")
defer os.RemoveAll(config.RootDir)
cc := proxy.NewLocalClientCreator(kvstore.NewApplication())
cc := proxy.NewLocalClientCreator(kvstore.NewInMemoryApplication())
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
err := proxyApp.Start()
require.Nil(t, err)
@@ -347,7 +347,7 @@ func TestMaxProposalBlockSize(t *testing.T) {
var height int64 = 1
state, stateDB, _ := state(1, height)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
var maxBytes int64 = 16384
var partSize uint32 = 256
@@ -476,7 +476,7 @@ func state(nVals int, height int64) (sm.State, dbm.DB, []types.PrivValidator) {
// save validators to db for 2 heights
stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
if err := stateStore.Save(s); err != nil {
panic(err)
+72 -81
View File
@@ -6,16 +6,33 @@ option go_package = "github.com/tendermint/tendermint/abci/types";
// For more information on gogo.proto, see:
// https://github.com/cosmos/gogoproto/blob/master/extensions.md
import "tendermint/crypto/proof.proto";
import "tendermint/types/types.proto";
import "tendermint/crypto/keys.proto";
import "tendermint/types/params.proto";
import "google/protobuf/timestamp.proto";
import "gogoproto/gogo.proto";
// This file is copied from http://github.com/tendermint/abci
// NOTE: When using custom types, mind the warnings.
// https://github.com/cosmos/gogoproto/blob/master/custom_types.md#warnings-and-issues
service ABCI {
rpc Echo(RequestEcho) returns (ResponseEcho);
rpc Flush(RequestFlush) returns (ResponseFlush);
rpc Info(RequestInfo) returns (ResponseInfo);
rpc CheckTx(RequestCheckTx) returns (ResponseCheckTx);
rpc Query(RequestQuery) returns (ResponseQuery);
rpc Commit(RequestCommit) returns (ResponseCommit);
rpc InitChain(RequestInitChain) returns (ResponseInitChain);
rpc ListSnapshots(RequestListSnapshots) returns (ResponseListSnapshots);
rpc OfferSnapshot(RequestOfferSnapshot) returns (ResponseOfferSnapshot);
rpc LoadSnapshotChunk(RequestLoadSnapshotChunk)
returns (ResponseLoadSnapshotChunk);
rpc ApplySnapshotChunk(RequestApplySnapshotChunk)
returns (ResponseApplySnapshotChunk);
rpc PrepareProposal(RequestPrepareProposal) returns (ResponsePrepareProposal);
rpc ProcessProposal(RequestProcessProposal) returns (ResponseProcessProposal);
rpc FinalizeBlock(RequestFinalizeBlock) returns (ResponseFinalizeBlock);
}
//----------------------------------------
// Request types
@@ -26,10 +43,7 @@ message Request {
RequestInfo info = 3;
RequestInitChain init_chain = 5;
RequestQuery query = 6;
RequestBeginBlock begin_block = 7;
RequestCheckTx check_tx = 8;
RequestDeliverTx deliver_tx = 9;
RequestEndBlock end_block = 10;
RequestCommit commit = 11;
RequestListSnapshots list_snapshots = 12;
RequestOfferSnapshot offer_snapshot = 13;
@@ -37,8 +51,9 @@ message Request {
RequestApplySnapshotChunk apply_snapshot_chunk = 15;
RequestPrepareProposal prepare_proposal = 16;
RequestProcessProposal process_proposal = 17;
RequestFinalizeBlock finalize_block = 20;
}
reserved 4;
reserved 4, 7, 9, 10; // SetOption, BeginBlock, DeliverTx, EndBlock
}
message RequestEcho {
@@ -71,13 +86,6 @@ message RequestQuery {
bool prove = 4;
}
message RequestBeginBlock {
bytes hash = 1;
tendermint.types.Header header = 2 [(gogoproto.nullable) = false];
CommitInfo last_commit_info = 3 [(gogoproto.nullable) = false];
repeated Misbehavior byzantine_validators = 4 [(gogoproto.nullable) = false];
}
enum CheckTxType {
NEW = 0 [(gogoproto.enumvalue_customname) = "New"];
RECHECK = 1 [(gogoproto.enumvalue_customname) = "Recheck"];
@@ -88,14 +96,6 @@ message RequestCheckTx {
CheckTxType type = 2;
}
message RequestDeliverTx {
bytes tx = 1;
}
message RequestEndBlock {
int64 height = 1;
}
message RequestCommit {}
// lists available snapshots
@@ -149,6 +149,19 @@ message RequestProcessProposal {
bytes proposer_address = 8;
}
message RequestFinalizeBlock {
repeated bytes txs = 1;
CommitInfo decided_last_commit = 2 [(gogoproto.nullable) = false];
repeated Misbehavior misbehavior = 3 [(gogoproto.nullable) = false];
// hash is the merkle root hash of the fields of the decided block.
bytes hash = 4;
int64 height = 5;
google.protobuf.Timestamp time = 6 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
bytes next_validators_hash = 7;
// proposer_address is the address of the public key of the original proposer of the block.
bytes proposer_address = 8;
}
//----------------------------------------
// Response types
@@ -160,10 +173,7 @@ message Response {
ResponseInfo info = 4;
ResponseInitChain init_chain = 6;
ResponseQuery query = 7;
ResponseBeginBlock begin_block = 8;
ResponseCheckTx check_tx = 9;
ResponseDeliverTx deliver_tx = 10;
ResponseEndBlock end_block = 11;
ResponseCommit commit = 12;
ResponseListSnapshots list_snapshots = 13;
ResponseOfferSnapshot offer_snapshot = 14;
@@ -171,8 +181,9 @@ message Response {
ResponseApplySnapshotChunk apply_snapshot_chunk = 16;
ResponsePrepareProposal prepare_proposal = 17;
ResponseProcessProposal process_proposal = 18;
ResponseFinalizeBlock finalize_block = 21;
}
reserved 5;
reserved 5, 8, 10, 11; // SetOption, BeginBlock, DeliverTx, EndBlock
}
// nondeterministic
@@ -215,11 +226,6 @@ message ResponseQuery {
string codespace = 10;
}
message ResponseBeginBlock {
repeated Event events = 1
[(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"];
}
message ResponseCheckTx {
uint32 code = 1;
bytes data = 2;
@@ -238,30 +244,8 @@ message ResponseCheckTx {
string mempool_error = 11;
}
message ResponseDeliverTx {
uint32 code = 1;
bytes data = 2;
string log = 3; // nondeterministic
string info = 4; // nondeterministic
int64 gas_wanted = 5 [json_name = "gas_wanted"];
int64 gas_used = 6 [json_name = "gas_used"];
repeated Event events = 7 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "events,omitempty"
]; // nondeterministic
string codespace = 8;
}
message ResponseEndBlock {
repeated ValidatorUpdate validator_updates = 1 [(gogoproto.nullable) = false];
tendermint.types.ConsensusParams consensus_param_updates = 2;
repeated Event events = 3
[(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"];
}
message ResponseCommit {
// reserve 1
bytes data = 2;
reserved 1, 2; // data was previously returned here
int64 retain_height = 3;
}
@@ -315,6 +299,22 @@ message ResponseProcessProposal {
}
}
message ResponseFinalizeBlock {
// set of block events emmitted as part of executing the block
repeated Event events = 1
[(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"];
// the result of executing each transaction including the events
// the particular transction emitted. This should match the order
// of the transactions delivered in the block itself
repeated ExecTxResult tx_results = 2;
// a list of updates to the validator set. These will reflect the validator set at current height + 2.
repeated ValidatorUpdate validator_updates = 3 [(gogoproto.nullable) = false];
// updates to the consensus params, if any.
tendermint.types.ConsensusParams consensus_param_updates = 4;
// agreed_app_data is the bytes that all nodes reach consensus upon in the following height. It is often the hash of the applications' state which can be used to confirm that execution of the transactions was deterministic.
bytes agreed_app_data = 5;
}
//----------------------------------------
// Misc.
@@ -349,14 +349,29 @@ message EventAttribute {
bool index = 3; // nondeterministic
}
// ExecTxResult contains results of executing one individual transaction.
//
// * Its structure is equivalent to #ResponseDeliverTx which will be deprecated/deleted
message ExecTxResult {
uint32 code = 1;
bytes data = 2;
string log = 3; // nondeterministic
string info = 4; // nondeterministic
int64 gas_wanted = 5;
int64 gas_used = 6;
repeated Event events = 7
[(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"]; // nondeterministic
string codespace = 8;
}
// TxResult contains results of executing the transaction.
//
// One usage is indexing transaction results.
message TxResult {
int64 height = 1;
uint32 index = 2;
bytes tx = 3;
ResponseDeliverTx result = 4 [(gogoproto.nullable) = false];
int64 height = 1;
uint32 index = 2;
bytes tx = 3;
ExecTxResult result = 4 [(gogoproto.nullable) = false];
}
//----------------------------------------
@@ -418,27 +433,3 @@ message Snapshot {
bytes hash = 4; // Arbitrary snapshot hash, equal only if identical
bytes metadata = 5; // Arbitrary application metadata
}
//----------------------------------------
// Service Definition
service ABCIApplication {
rpc Echo(RequestEcho) returns (ResponseEcho);
rpc Flush(RequestFlush) returns (ResponseFlush);
rpc Info(RequestInfo) returns (ResponseInfo);
rpc DeliverTx(RequestDeliverTx) returns (ResponseDeliverTx);
rpc CheckTx(RequestCheckTx) returns (ResponseCheckTx);
rpc Query(RequestQuery) returns (ResponseQuery);
rpc Commit(RequestCommit) returns (ResponseCommit);
rpc InitChain(RequestInitChain) returns (ResponseInitChain);
rpc BeginBlock(RequestBeginBlock) returns (ResponseBeginBlock);
rpc EndBlock(RequestEndBlock) returns (ResponseEndBlock);
rpc ListSnapshots(RequestListSnapshots) returns (ResponseListSnapshots);
rpc OfferSnapshot(RequestOfferSnapshot) returns (ResponseOfferSnapshot);
rpc LoadSnapshotChunk(RequestLoadSnapshotChunk)
returns (ResponseLoadSnapshotChunk);
rpc ApplySnapshotChunk(RequestApplySnapshotChunk)
returns (ResponseApplySnapshotChunk);
rpc PrepareProposal(RequestPrepareProposal) returns (ResponsePrepareProposal);
rpc ProcessProposal(RequestProcessProposal) returns (ResponseProcessProposal);
}
+29 -30
View File
@@ -6,7 +6,6 @@ package coregrpc
import (
context "context"
fmt "fmt"
grpc1 "github.com/cosmos/gogoproto/grpc"
proto "github.com/cosmos/gogoproto/proto"
types "github.com/tendermint/tendermint/abci/types"
grpc "google.golang.org/grpc"
@@ -145,8 +144,8 @@ func (m *ResponsePing) XXX_DiscardUnknown() {
var xxx_messageInfo_ResponsePing proto.InternalMessageInfo
type ResponseBroadcastTx struct {
CheckTx *types.ResponseCheckTx `protobuf:"bytes,1,opt,name=check_tx,json=checkTx,proto3" json:"check_tx,omitempty"`
DeliverTx *types.ResponseDeliverTx `protobuf:"bytes,2,opt,name=deliver_tx,json=deliverTx,proto3" json:"deliver_tx,omitempty"`
CheckTx *types.ResponseCheckTx `protobuf:"bytes,1,opt,name=check_tx,json=checkTx,proto3" json:"check_tx,omitempty"`
TxResult *types.ExecTxResult `protobuf:"bytes,2,opt,name=tx_result,json=txResult,proto3" json:"tx_result,omitempty"`
}
func (m *ResponseBroadcastTx) Reset() { *m = ResponseBroadcastTx{} }
@@ -189,9 +188,9 @@ func (m *ResponseBroadcastTx) GetCheckTx() *types.ResponseCheckTx {
return nil
}
func (m *ResponseBroadcastTx) GetDeliverTx() *types.ResponseDeliverTx {
func (m *ResponseBroadcastTx) GetTxResult() *types.ExecTxResult {
if m != nil {
return m.DeliverTx
return m.TxResult
}
return nil
}
@@ -206,7 +205,7 @@ func init() {
func init() { proto.RegisterFile("tendermint/rpc/grpc/types.proto", fileDescriptor_0ffff5682c662b95) }
var fileDescriptor_0ffff5682c662b95 = []byte{
// 316 bytes of a gzipped FileDescriptorProto
// 320 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2f, 0x49, 0xcd, 0x4b,
0x49, 0x2d, 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x2f, 0x2a, 0x48, 0xd6, 0x4f, 0x07, 0x11, 0x25, 0x95,
0x05, 0xa9, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xc2, 0x08, 0x05, 0x7a, 0x45, 0x05,
@@ -214,19 +213,19 @@ var fileDescriptor_0ffff5682c662b95 = []byte{
0x72, 0x71, 0x07, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x04, 0x64, 0xe6, 0xa5, 0x2b, 0xa9, 0x70,
0x09, 0x41, 0xb9, 0x4e, 0x45, 0xf9, 0x89, 0x29, 0xc9, 0x89, 0xc5, 0x25, 0x21, 0x15, 0x42, 0x7c,
0x5c, 0x4c, 0x25, 0x15, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x4c, 0x25, 0x15, 0x4a, 0x7c,
0x5c, 0x3c, 0x41, 0xa9, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x60, 0x5d, 0x53, 0x19, 0xb9, 0x84,
0x61, 0x02, 0xc8, 0xfa, 0xac, 0xb9, 0x38, 0x92, 0x33, 0x52, 0x93, 0xb3, 0xe3, 0xa1, 0xba, 0xb9,
0x8d, 0x14, 0xf4, 0x90, 0x5c, 0x08, 0x72, 0x8c, 0x1e, 0x4c, 0x9f, 0x33, 0x48, 0x61, 0x48, 0x45,
0x10, 0x7b, 0x32, 0x84, 0x21, 0xe4, 0xc8, 0xc5, 0x95, 0x92, 0x9a, 0x93, 0x59, 0x96, 0x5a, 0x04,
0xd2, 0xce, 0x04, 0xd6, 0xae, 0x84, 0x53, 0xbb, 0x0b, 0x44, 0x69, 0x48, 0x45, 0x10, 0x67, 0x0a,
0x8c, 0x69, 0xb4, 0x97, 0x91, 0x8b, 0x07, 0xee, 0x1e, 0xc7, 0x00, 0x4f, 0x21, 0x6f, 0x2e, 0x16,
0x90, 0x83, 0x85, 0x50, 0x9c, 0x01, 0x0b, 0x28, 0x3d, 0xa4, 0x80, 0x90, 0x52, 0xc4, 0xa1, 0x02,
0xe1, 0x6b, 0xa1, 0x04, 0x2e, 0x6e, 0x64, 0xcf, 0xaa, 0xe3, 0x33, 0x13, 0x49, 0xa1, 0x94, 0x06,
0x5e, 0xa3, 0x91, 0x54, 0x3a, 0xf9, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83,
0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43,
0x94, 0x51, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0x52, 0xf4, 0x62,
0x49, 0x1f, 0xd6, 0xc9, 0xf9, 0x45, 0xa9, 0x20, 0x46, 0x12, 0x1b, 0x38, 0xc6, 0x8d, 0x01, 0x01,
0x00, 0x00, 0xff, 0xff, 0xf6, 0x4b, 0x02, 0xd8, 0x46, 0x02, 0x00, 0x00,
0x5c, 0x3c, 0x41, 0xa9, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x60, 0x5d, 0x7d, 0x8c, 0x5c, 0xc2,
0x30, 0x01, 0x64, 0x7d, 0xd6, 0x5c, 0x1c, 0xc9, 0x19, 0xa9, 0xc9, 0xd9, 0xf1, 0x50, 0xdd, 0xdc,
0x46, 0x0a, 0x7a, 0x48, 0x2e, 0x04, 0x39, 0x46, 0x0f, 0xa6, 0xcf, 0x19, 0xa4, 0x30, 0xa4, 0x22,
0x88, 0x3d, 0x19, 0xc2, 0x10, 0xb2, 0xe2, 0xe2, 0x2c, 0xa9, 0x88, 0x2f, 0x4a, 0x2d, 0x2e, 0xcd,
0x29, 0x91, 0x60, 0x02, 0xeb, 0x96, 0xc5, 0xd0, 0xed, 0x5a, 0x91, 0x9a, 0x1c, 0x52, 0x11, 0x04,
0x56, 0x14, 0xc4, 0x51, 0x02, 0x65, 0x19, 0xed, 0x65, 0xe4, 0xe2, 0x81, 0x3b, 0xc4, 0x31, 0xc0,
0x53, 0xc8, 0x9b, 0x8b, 0x05, 0xe4, 0x52, 0x21, 0x14, 0xfb, 0x61, 0x21, 0xa4, 0x87, 0x14, 0x02,
0x52, 0x8a, 0x38, 0x54, 0x20, 0xbc, 0x2b, 0x94, 0xc0, 0xc5, 0x8d, 0xec, 0x4b, 0x75, 0x7c, 0x66,
0x22, 0x29, 0x94, 0xd2, 0xc0, 0x6b, 0x34, 0x92, 0x4a, 0x27, 0x9f, 0x13, 0x8f, 0xe4, 0x18, 0x2f,
0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18,
0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4a, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf,
0xd5, 0x47, 0x8a, 0x57, 0x2c, 0x09, 0xc3, 0x3a, 0x39, 0xbf, 0x28, 0x15, 0xc4, 0x48, 0x62, 0x03,
0x47, 0xb5, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x93, 0xa5, 0x24, 0x3f, 0x02, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -246,10 +245,10 @@ type BroadcastAPIClient interface {
}
type broadcastAPIClient struct {
cc grpc1.ClientConn
cc *grpc.ClientConn
}
func NewBroadcastAPIClient(cc grpc1.ClientConn) BroadcastAPIClient {
func NewBroadcastAPIClient(cc *grpc.ClientConn) BroadcastAPIClient {
return &broadcastAPIClient{cc}
}
@@ -288,7 +287,7 @@ func (*UnimplementedBroadcastAPIServer) BroadcastTx(ctx context.Context, req *Re
return nil, status.Errorf(codes.Unimplemented, "method BroadcastTx not implemented")
}
func RegisterBroadcastAPIServer(s grpc1.Server, srv BroadcastAPIServer) {
func RegisterBroadcastAPIServer(s *grpc.Server, srv BroadcastAPIServer) {
s.RegisterService(&_BroadcastAPI_serviceDesc, srv)
}
@@ -441,9 +440,9 @@ func (m *ResponseBroadcastTx) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.DeliverTx != nil {
if m.TxResult != nil {
{
size, err := m.DeliverTx.MarshalToSizedBuffer(dAtA[:i])
size, err := m.TxResult.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
@@ -520,8 +519,8 @@ func (m *ResponseBroadcastTx) Size() (n int) {
l = m.CheckTx.Size()
n += 1 + l + sovTypes(uint64(l))
}
if m.DeliverTx != nil {
l = m.DeliverTx.Size()
if m.TxResult != nil {
l = m.TxResult.Size()
n += 1 + l + sovTypes(uint64(l))
}
return n
@@ -784,7 +783,7 @@ func (m *ResponseBroadcastTx) Unmarshal(dAtA []byte) error {
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field DeliverTx", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field TxResult", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
@@ -811,10 +810,10 @@ func (m *ResponseBroadcastTx) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.DeliverTx == nil {
m.DeliverTx = &types.ResponseDeliverTx{}
if m.TxResult == nil {
m.TxResult = &types.ExecTxResult{}
}
if err := m.DeliverTx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
if err := m.TxResult.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
+2 -2
View File
@@ -19,8 +19,8 @@ message RequestBroadcastTx {
message ResponsePing {}
message ResponseBroadcastTx {
tendermint.abci.ResponseCheckTx check_tx = 1;
tendermint.abci.ResponseDeliverTx deliver_tx = 2;
tendermint.abci.ResponseCheckTx check_tx = 1;
tendermint.abci.ExecTxResult tx_result = 2;
}
//----------------------------------------
File diff suppressed because it is too large Load Diff
+25 -9
View File
@@ -11,13 +11,28 @@ import "tendermint/types/params.proto";
import "tendermint/version/types.proto";
import "google/protobuf/timestamp.proto";
// ABCIResponses retains the responses
// of the various ABCI calls during block processing.
// It is persisted to disk for each height before calling Commit.
message ABCIResponses {
repeated tendermint.abci.ResponseDeliverTx deliver_txs = 1;
tendermint.abci.ResponseEndBlock end_block = 2;
tendermint.abci.ResponseBeginBlock begin_block = 3;
// LegacyABCIResponses retains the responses
// of the legacy ABCI calls during block processing.
// Note ReponseDeliverTx is renamed to ExecTxResult but they are semantically the same
// Kept for backwards compatibility for versions prior to v0.38
message LegacyABCIResponses {
repeated tendermint.abci.ExecTxResult deliver_txs = 1;
ResponseEndBlock end_block = 2;
ResponseBeginBlock begin_block = 3;
}
// ResponseBeginBlock is kept for backwards compatibility for versions prior to v0.38
message ResponseBeginBlock {
repeated tendermint.abci.Event events = 1
[(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"];
}
// ResponseEndBlock is kept for backwards compatibility for versions prior to v0.38
message ResponseEndBlock {
repeated tendermint.abci.ValidatorUpdate validator_updates = 1 [(gogoproto.nullable) = false];
tendermint.types.ConsensusParams consensus_param_updates = 2;
repeated tendermint.abci.Event events = 3
[(gogoproto.nullable) = false, (gogoproto.jsontag) = "events,omitempty"];
}
// ValidatorsInfo represents the latest validator set, or the last height it changed
@@ -33,8 +48,9 @@ message ConsensusParamsInfo {
}
message ABCIResponsesInfo {
ABCIResponses abci_responses = 1;
int64 height = 2;
LegacyABCIResponses legaycy_abci_responses = 1;
int64 height = 2;
abci.ResponseFinalizeBlock response_finalize_block = 3;
}
message Version {
+56 -93
View File
@@ -1,6 +1,7 @@
package proxy
import (
"context"
"time"
"github.com/go-kit/kit/metrics"
@@ -15,44 +16,36 @@ import (
// Enforce which abci msgs can be sent on a connection at the type level
type AppConnConsensus interface {
SetResponseCallback(abcicli.Callback)
Error() error
InitChainSync(types.RequestInitChain) (*types.ResponseInitChain, error)
PrepareProposalSync(types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error)
ProcessProposalSync(types.RequestProcessProposal) (*types.ResponseProcessProposal, error)
BeginBlockSync(types.RequestBeginBlock) (*types.ResponseBeginBlock, error)
DeliverTxAsync(types.RequestDeliverTx) *abcicli.ReqRes
EndBlockSync(types.RequestEndBlock) (*types.ResponseEndBlock, error)
CommitSync() (*types.ResponseCommit, error)
InitChain(context.Context, *types.RequestInitChain) (*types.ResponseInitChain, error)
PrepareProposal(context.Context, *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error)
ProcessProposal(context.Context, *types.RequestProcessProposal) (*types.ResponseProcessProposal, error)
FinalizeBlock(context.Context, *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error)
Commit(context.Context) (*types.ResponseCommit, error)
}
type AppConnMempool interface {
SetResponseCallback(abcicli.Callback)
Error() error
CheckTxAsync(types.RequestCheckTx) *abcicli.ReqRes
CheckTxSync(types.RequestCheckTx) (*types.ResponseCheckTx, error)
FlushAsync() *abcicli.ReqRes
FlushSync() error
CheckTx(context.Context, *types.RequestCheckTx) (*types.ResponseCheckTx, error)
Flush(context.Context) error
}
type AppConnQuery interface {
Error() error
EchoSync(string) (*types.ResponseEcho, error)
InfoSync(types.RequestInfo) (*types.ResponseInfo, error)
QuerySync(types.RequestQuery) (*types.ResponseQuery, error)
Echo(context.Context, string) (*types.ResponseEcho, error)
Info(context.Context, *types.RequestInfo) (*types.ResponseInfo, error)
Query(context.Context, *types.RequestQuery) (*types.ResponseQuery, error)
}
type AppConnSnapshot interface {
Error() error
ListSnapshotsSync(types.RequestListSnapshots) (*types.ResponseListSnapshots, error)
OfferSnapshotSync(types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error)
LoadSnapshotChunkSync(types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error)
ApplySnapshotChunkSync(types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error)
ListSnapshots(context.Context, *types.RequestListSnapshots) (*types.ResponseListSnapshots, error)
OfferSnapshot(context.Context, *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error)
LoadSnapshotChunk(context.Context, *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error)
ApplySnapshotChunk(context.Context, *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error)
}
//-----------------------------------------------------------------------------------------
@@ -72,48 +65,34 @@ func NewAppConnConsensus(appConn abcicli.Client, metrics *Metrics) AppConnConsen
}
}
func (app *appConnConsensus) SetResponseCallback(cb abcicli.Callback) {
app.appConn.SetResponseCallback(cb)
}
func (app *appConnConsensus) Error() error {
return app.appConn.Error()
}
func (app *appConnConsensus) InitChainSync(req types.RequestInitChain) (*types.ResponseInitChain, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "init_chain", "type", "sync"))()
return app.appConn.InitChainSync(req)
func (app *appConnConsensus) InitChain(ctx context.Context, req *types.RequestInitChain) (*types.ResponseInitChain, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "init_chain"))()
return app.appConn.InitChain(ctx, req)
}
func (app *appConnConsensus) PrepareProposalSync(
req types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "prepare_proposal", "type", "sync"))()
return app.appConn.PrepareProposalSync(req)
func (app *appConnConsensus) PrepareProposal(ctx context.Context,
req *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "prepare_proposal"))()
return app.appConn.PrepareProposal(ctx, req)
}
func (app *appConnConsensus) ProcessProposalSync(req types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "process_proposal", "type", "sync"))()
return app.appConn.ProcessProposalSync(req)
func (app *appConnConsensus) ProcessProposal(ctx context.Context, req *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "process_proposal"))()
return app.appConn.ProcessProposal(ctx, req)
}
func (app *appConnConsensus) BeginBlockSync(req types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "begin_block", "type", "sync"))()
return app.appConn.BeginBlockSync(req)
}
func (app *appConnConsensus) DeliverTxAsync(req types.RequestDeliverTx) *abcicli.ReqRes {
func (app *appConnConsensus) FinalizeBlock(ctx context.Context, req *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "deliver_tx", "type", "async"))()
return app.appConn.DeliverTxAsync(req)
return app.appConn.FinalizeBlock(ctx, req)
}
func (app *appConnConsensus) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "end_block", "type", "sync"))()
return app.appConn.EndBlockSync(req)
}
func (app *appConnConsensus) CommitSync() (*types.ResponseCommit, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "commit", "type", "sync"))()
return app.appConn.CommitSync()
func (app *appConnConsensus) Commit(ctx context.Context) (*types.ResponseCommit, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "commit"))()
return app.appConn.Commit(ctx, &types.RequestCommit{})
}
//------------------------------------------------
@@ -131,32 +110,18 @@ func NewAppConnMempool(appConn abcicli.Client, metrics *Metrics) AppConnMempool
}
}
func (app *appConnMempool) SetResponseCallback(cb abcicli.Callback) {
app.appConn.SetResponseCallback(cb)
}
func (app *appConnMempool) Error() error {
return app.appConn.Error()
}
func (app *appConnMempool) FlushAsync() *abcicli.ReqRes {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "flush", "type", "async"))()
return app.appConn.FlushAsync()
func (app *appConnMempool) Flush(ctx context.Context) error {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "flush"))()
return app.appConn.Flush(ctx)
}
func (app *appConnMempool) FlushSync() error {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "flush", "type", "sync"))()
return app.appConn.FlushSync()
}
func (app *appConnMempool) CheckTxAsync(req types.RequestCheckTx) *abcicli.ReqRes {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "check_tx", "type", "async"))()
return app.appConn.CheckTxAsync(req)
}
func (app *appConnMempool) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCheckTx, error) {
func (app *appConnMempool) CheckTx(ctx context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "check_tx", "type", "sync"))()
return app.appConn.CheckTxSync(req)
return app.appConn.CheckTx(ctx, req)
}
//------------------------------------------------
@@ -178,19 +143,19 @@ func (app *appConnQuery) Error() error {
return app.appConn.Error()
}
func (app *appConnQuery) EchoSync(msg string) (*types.ResponseEcho, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "echo", "type", "sync"))()
return app.appConn.EchoSync(msg)
func (app *appConnQuery) Echo(ctx context.Context, msg string) (*types.ResponseEcho, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "echo"))()
return app.appConn.Echo(ctx, msg)
}
func (app *appConnQuery) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "info", "type", "sync"))()
return app.appConn.InfoSync(req)
func (app *appConnQuery) Info(ctx context.Context, req *types.RequestInfo) (*types.ResponseInfo, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "info"))()
return app.appConn.Info(ctx, req)
}
func (app *appConnQuery) QuerySync(reqQuery types.RequestQuery) (*types.ResponseQuery, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "query", "type", "sync"))()
return app.appConn.QuerySync(reqQuery)
func (app *appConnQuery) Query(ctx context.Context, req *types.RequestQuery) (*types.ResponseQuery, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "query"))()
return app.appConn.Query(ctx, req)
}
//------------------------------------------------
@@ -212,26 +177,24 @@ func (app *appConnSnapshot) Error() error {
return app.appConn.Error()
}
func (app *appConnSnapshot) ListSnapshotsSync(req types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "list_snapshots", "type", "sync"))()
return app.appConn.ListSnapshotsSync(req)
func (app *appConnSnapshot) ListSnapshots(ctx context.Context, req *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "list_snapshots"))()
return app.appConn.ListSnapshots(ctx, req)
}
func (app *appConnSnapshot) OfferSnapshotSync(req types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "offer_snapshot", "type", "sync"))()
return app.appConn.OfferSnapshotSync(req)
func (app *appConnSnapshot) OfferSnapshot(ctx context.Context, req *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "offer_snapshot"))()
return app.appConn.OfferSnapshot(ctx, req)
}
func (app *appConnSnapshot) LoadSnapshotChunkSync(
req types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "load_snapshot_chunk", "type", "sync"))()
return app.appConn.LoadSnapshotChunkSync(req)
func (app *appConnSnapshot) LoadSnapshotChunk(ctx context.Context, req *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "load_snapshot_chunk"))()
return app.appConn.LoadSnapshotChunk(ctx, req)
}
func (app *appConnSnapshot) ApplySnapshotChunkSync(
req types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "apply_snapshot_chunk", "type", "sync"))()
return app.appConn.ApplySnapshotChunkSync(req)
func (app *appConnSnapshot) ApplySnapshotChunk(ctx context.Context, req *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
defer addTimeSample(app.metrics.MethodTimingSeconds.With("method", "apply_snapshot_chunk"))()
return app.appConn.ApplySnapshotChunk(ctx, req)
}
// addTimeSample returns a function that, when called, adds an observation to m.
+16 -82
View File
@@ -1,48 +1,17 @@
package proxy
import (
"context"
"fmt"
"strings"
"testing"
abcicli "github.com/tendermint/tendermint/abci/client"
"github.com/tendermint/tendermint/abci/example/kvstore"
"github.com/tendermint/tendermint/abci/server"
"github.com/tendermint/tendermint/abci/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/log"
tmrand "github.com/tendermint/tendermint/libs/rand"
)
//----------------------------------------
type AppConnTest interface {
EchoAsync(string) *abcicli.ReqRes
FlushSync() error
InfoSync(types.RequestInfo) (*types.ResponseInfo, error)
}
type appConnTest struct {
appConn abcicli.Client
}
func NewAppConnTest(appConn abcicli.Client) AppConnTest {
return &appConnTest{appConn}
}
func (app *appConnTest) EchoAsync(msg string) *abcicli.ReqRes {
return app.appConn.EchoAsync(msg)
}
func (app *appConnTest) FlushSync() error {
return app.appConn.FlushSync()
}
func (app *appConnTest) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
return app.appConn.InfoSync(req)
}
//----------------------------------------
var SOCKET = "socket"
func TestEcho(t *testing.T) {
@@ -50,7 +19,7 @@ func TestEcho(t *testing.T) {
clientCreator := NewRemoteClientCreator(sockPath, SOCKET, true)
// Start server
s := server.NewSocketServer(sockPath, kvstore.NewApplication())
s := server.NewSocketServer(sockPath, kvstore.NewInMemoryApplication())
s.SetLogger(log.TestingLogger().With("module", "abci-server"))
if err := s.Start(); err != nil {
t.Fatalf("Error starting socket server: %v", err.Error())
@@ -71,13 +40,16 @@ func TestEcho(t *testing.T) {
t.Fatalf("Error starting ABCI client: %v", err.Error())
}
proxy := NewAppConnTest(cli)
proxy := NewAppConnMempool(cli, nil)
t.Log("Connected")
for i := 0; i < 1000; i++ {
proxy.EchoAsync(fmt.Sprintf("echo-%v", i))
_, err = proxy.CheckTx(context.Background(), &abci.RequestCheckTx{Tx: []byte(fmt.Sprintf("echo-%v", i))})
if err != nil {
t.Fatal(err)
}
}
if err := proxy.FlushSync(); err != nil {
if err := proxy.Flush(context.Background()); err != nil {
t.Error(err)
}
}
@@ -88,7 +60,7 @@ func BenchmarkEcho(b *testing.B) {
clientCreator := NewRemoteClientCreator(sockPath, SOCKET, true)
// Start server
s := server.NewSocketServer(sockPath, kvstore.NewApplication())
s := server.NewSocketServer(sockPath, kvstore.NewInMemoryApplication())
s.SetLogger(log.TestingLogger().With("module", "abci-server"))
if err := s.Start(); err != nil {
b.Fatalf("Error starting socket server: %v", err.Error())
@@ -109,57 +81,19 @@ func BenchmarkEcho(b *testing.B) {
b.Fatalf("Error starting ABCI client: %v", err.Error())
}
proxy := NewAppConnTest(cli)
proxy := NewAppConnMempool(cli, nil)
b.Log("Connected")
echoString := strings.Repeat(" ", 200)
b.StartTimer() // Start benchmarking tests
for i := 0; i < b.N; i++ {
proxy.EchoAsync(echoString)
_, err = proxy.CheckTx(context.Background(), &abci.RequestCheckTx{Tx: []byte("hello")})
if err != nil {
b.Error(err)
}
}
if err := proxy.FlushSync(); err != nil {
if err := proxy.Flush(context.Background()); err != nil {
b.Error(err)
}
b.StopTimer()
// info := proxy.InfoSync(types.RequestInfo{""})
// b.Log("N: ", b.N, info)
}
func TestInfo(t *testing.T) {
sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", tmrand.Str(6))
clientCreator := NewRemoteClientCreator(sockPath, SOCKET, true)
// Start server
s := server.NewSocketServer(sockPath, kvstore.NewApplication())
s.SetLogger(log.TestingLogger().With("module", "abci-server"))
if err := s.Start(); err != nil {
t.Fatalf("Error starting socket server: %v", err.Error())
}
t.Cleanup(func() {
if err := s.Stop(); err != nil {
t.Error(err)
}
})
// Start client
cli, err := clientCreator.NewABCIClient()
if err != nil {
t.Fatalf("Error creating ABCI client: %v", err.Error())
}
cli.SetLogger(log.TestingLogger().With("module", "abci-client"))
if err := cli.Start(); err != nil {
t.Fatalf("Error starting ABCI client: %v", err.Error())
}
proxy := NewAppConnTest(cli)
t.Log("Connected")
resInfo, err := proxy.InfoSync(RequestInfo)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if resInfo.Data != "{\"size\":0}" {
t.Error("Expected ResponseInfo with one element '{\"size\":0}' but got something else")
}
}
+2 -2
View File
@@ -74,9 +74,9 @@ func (r *remoteClientCreator) NewABCIClient() (abcicli.Client, error) {
func DefaultClientCreator(addr, transport, dbDir string) ClientCreator {
switch addr {
case "kvstore":
return NewLocalClientCreator(kvstore.NewApplication())
return NewLocalClientCreator(kvstore.NewInMemoryApplication())
case "persistent_kvstore":
return NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(dbDir))
return NewLocalClientCreator(kvstore.NewPersistentApplication(dbDir))
case "e2e":
app, err := e2e.NewApplication(e2e.DefaultConfig(dbDir))
if err != nil {
+51 -94
View File
@@ -3,8 +3,9 @@
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
abcicli "github.com/tendermint/tendermint/abci/client"
types "github.com/tendermint/tendermint/abci/types"
)
@@ -14,36 +15,13 @@ type AppConnConsensus struct {
mock.Mock
}
// BeginBlockSync provides a mock function with given fields: _a0
func (_m *AppConnConsensus) BeginBlockSync(_a0 types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
// Commit provides a mock function with given fields: _a0
func (_m *AppConnConsensus) Commit(_a0 context.Context) (*types.ResponseCommit, error) {
ret := _m.Called(_a0)
var r0 *types.ResponseBeginBlock
if rf, ok := ret.Get(0).(func(types.RequestBeginBlock) *types.ResponseBeginBlock); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseBeginBlock)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestBeginBlock) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CommitSync provides a mock function with given fields:
func (_m *AppConnConsensus) CommitSync() (*types.ResponseCommit, error) {
ret := _m.Called()
var r0 *types.ResponseCommit
if rf, ok := ret.Get(0).(func() *types.ResponseCommit); ok {
r0 = rf()
if rf, ok := ret.Get(0).(func(context.Context) *types.ResponseCommit); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseCommit)
@@ -51,46 +29,7 @@ func (_m *AppConnConsensus) CommitSync() (*types.ResponseCommit, error) {
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// DeliverTxAsync provides a mock function with given fields: _a0
func (_m *AppConnConsensus) DeliverTxAsync(_a0 types.RequestDeliverTx) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestDeliverTx) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// EndBlockSync provides a mock function with given fields: _a0
func (_m *AppConnConsensus) EndBlockSync(_a0 types.RequestEndBlock) (*types.ResponseEndBlock, error) {
ret := _m.Called(_a0)
var r0 *types.ResponseEndBlock
if rf, ok := ret.Get(0).(func(types.RequestEndBlock) *types.ResponseEndBlock); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseEndBlock)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestEndBlock) error); ok {
if rf, ok := ret.Get(1).(func(context.Context) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
@@ -113,13 +52,36 @@ func (_m *AppConnConsensus) Error() error {
return r0
}
// InitChainSync provides a mock function with given fields: _a0
func (_m *AppConnConsensus) InitChainSync(_a0 types.RequestInitChain) (*types.ResponseInitChain, error) {
ret := _m.Called(_a0)
// FinalizeBlock provides a mock function with given fields: _a0, _a1
func (_m *AppConnConsensus) FinalizeBlock(_a0 context.Context, _a1 *types.RequestFinalizeBlock) (*types.ResponseFinalizeBlock, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseFinalizeBlock
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestFinalizeBlock) *types.ResponseFinalizeBlock); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseFinalizeBlock)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestFinalizeBlock) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// InitChain provides a mock function with given fields: _a0, _a1
func (_m *AppConnConsensus) InitChain(_a0 context.Context, _a1 *types.RequestInitChain) (*types.ResponseInitChain, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseInitChain
if rf, ok := ret.Get(0).(func(types.RequestInitChain) *types.ResponseInitChain); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestInitChain) *types.ResponseInitChain); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseInitChain)
@@ -127,8 +89,8 @@ func (_m *AppConnConsensus) InitChainSync(_a0 types.RequestInitChain) (*types.Re
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestInitChain) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestInitChain) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -136,13 +98,13 @@ func (_m *AppConnConsensus) InitChainSync(_a0 types.RequestInitChain) (*types.Re
return r0, r1
}
// PrepareProposalSync provides a mock function with given fields: _a0
func (_m *AppConnConsensus) PrepareProposalSync(_a0 types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
ret := _m.Called(_a0)
// PrepareProposal provides a mock function with given fields: _a0, _a1
func (_m *AppConnConsensus) PrepareProposal(_a0 context.Context, _a1 *types.RequestPrepareProposal) (*types.ResponsePrepareProposal, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponsePrepareProposal
if rf, ok := ret.Get(0).(func(types.RequestPrepareProposal) *types.ResponsePrepareProposal); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestPrepareProposal) *types.ResponsePrepareProposal); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponsePrepareProposal)
@@ -150,8 +112,8 @@ func (_m *AppConnConsensus) PrepareProposalSync(_a0 types.RequestPrepareProposal
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestPrepareProposal) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestPrepareProposal) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -159,13 +121,13 @@ func (_m *AppConnConsensus) PrepareProposalSync(_a0 types.RequestPrepareProposal
return r0, r1
}
// ProcessProposalSync provides a mock function with given fields: _a0
func (_m *AppConnConsensus) ProcessProposalSync(_a0 types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
ret := _m.Called(_a0)
// ProcessProposal provides a mock function with given fields: _a0, _a1
func (_m *AppConnConsensus) ProcessProposal(_a0 context.Context, _a1 *types.RequestProcessProposal) (*types.ResponseProcessProposal, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseProcessProposal
if rf, ok := ret.Get(0).(func(types.RequestProcessProposal) *types.ResponseProcessProposal); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestProcessProposal) *types.ResponseProcessProposal); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseProcessProposal)
@@ -173,8 +135,8 @@ func (_m *AppConnConsensus) ProcessProposalSync(_a0 types.RequestProcessProposal
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestProcessProposal) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestProcessProposal) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -182,11 +144,6 @@ func (_m *AppConnConsensus) ProcessProposalSync(_a0 types.RequestProcessProposal
return r0, r1
}
// SetResponseCallback provides a mock function with given fields: _a0
func (_m *AppConnConsensus) SetResponseCallback(_a0 abcicli.Callback) {
_m.Called(_a0)
}
type mockConstructorTestingTNewAppConnConsensus interface {
mock.TestingT
Cleanup(func())
+14 -50
View File
@@ -3,8 +3,9 @@
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
abcicli "github.com/tendermint/tendermint/abci/client"
types "github.com/tendermint/tendermint/abci/types"
)
@@ -14,29 +15,13 @@ type AppConnMempool struct {
mock.Mock
}
// CheckTxAsync provides a mock function with given fields: _a0
func (_m *AppConnMempool) CheckTxAsync(_a0 types.RequestCheckTx) *abcicli.ReqRes {
ret := _m.Called(_a0)
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func(types.RequestCheckTx) *abcicli.ReqRes); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// CheckTxSync provides a mock function with given fields: _a0
func (_m *AppConnMempool) CheckTxSync(_a0 types.RequestCheckTx) (*types.ResponseCheckTx, error) {
ret := _m.Called(_a0)
// CheckTx provides a mock function with given fields: _a0, _a1
func (_m *AppConnMempool) CheckTx(_a0 context.Context, _a1 *types.RequestCheckTx) (*types.ResponseCheckTx, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseCheckTx
if rf, ok := ret.Get(0).(func(types.RequestCheckTx) *types.ResponseCheckTx); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestCheckTx) *types.ResponseCheckTx); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseCheckTx)
@@ -44,8 +29,8 @@ func (_m *AppConnMempool) CheckTxSync(_a0 types.RequestCheckTx) (*types.Response
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestCheckTx) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestCheckTx) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -67,29 +52,13 @@ func (_m *AppConnMempool) Error() error {
return r0
}
// FlushAsync provides a mock function with given fields:
func (_m *AppConnMempool) FlushAsync() *abcicli.ReqRes {
ret := _m.Called()
var r0 *abcicli.ReqRes
if rf, ok := ret.Get(0).(func() *abcicli.ReqRes); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*abcicli.ReqRes)
}
}
return r0
}
// FlushSync provides a mock function with given fields:
func (_m *AppConnMempool) FlushSync() error {
ret := _m.Called()
// Flush provides a mock function with given fields: _a0
func (_m *AppConnMempool) Flush(_a0 context.Context) error {
ret := _m.Called(_a0)
var r0 error
if rf, ok := ret.Get(0).(func() error); ok {
r0 = rf()
if rf, ok := ret.Get(0).(func(context.Context) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
@@ -97,11 +66,6 @@ func (_m *AppConnMempool) FlushSync() error {
return r0
}
// SetResponseCallback provides a mock function with given fields: _a0
func (_m *AppConnMempool) SetResponseCallback(_a0 abcicli.Callback) {
_m.Called(_a0)
}
type mockConstructorTestingTNewAppConnMempool interface {
mock.TestingT
Cleanup(func())
+23 -21
View File
@@ -3,6 +3,8 @@
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
types "github.com/tendermint/tendermint/abci/types"
@@ -13,13 +15,13 @@ type AppConnQuery struct {
mock.Mock
}
// EchoSync provides a mock function with given fields: _a0
func (_m *AppConnQuery) EchoSync(_a0 string) (*types.ResponseEcho, error) {
ret := _m.Called(_a0)
// Echo provides a mock function with given fields: _a0, _a1
func (_m *AppConnQuery) Echo(_a0 context.Context, _a1 string) (*types.ResponseEcho, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseEcho
if rf, ok := ret.Get(0).(func(string) *types.ResponseEcho); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, string) *types.ResponseEcho); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseEcho)
@@ -27,8 +29,8 @@ func (_m *AppConnQuery) EchoSync(_a0 string) (*types.ResponseEcho, error) {
}
var r1 error
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -50,13 +52,13 @@ func (_m *AppConnQuery) Error() error {
return r0
}
// InfoSync provides a mock function with given fields: _a0
func (_m *AppConnQuery) InfoSync(_a0 types.RequestInfo) (*types.ResponseInfo, error) {
ret := _m.Called(_a0)
// Info provides a mock function with given fields: _a0, _a1
func (_m *AppConnQuery) Info(_a0 context.Context, _a1 *types.RequestInfo) (*types.ResponseInfo, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseInfo
if rf, ok := ret.Get(0).(func(types.RequestInfo) *types.ResponseInfo); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestInfo) *types.ResponseInfo); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseInfo)
@@ -64,8 +66,8 @@ func (_m *AppConnQuery) InfoSync(_a0 types.RequestInfo) (*types.ResponseInfo, er
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestInfo) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestInfo) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -73,13 +75,13 @@ func (_m *AppConnQuery) InfoSync(_a0 types.RequestInfo) (*types.ResponseInfo, er
return r0, r1
}
// QuerySync provides a mock function with given fields: _a0
func (_m *AppConnQuery) QuerySync(_a0 types.RequestQuery) (*types.ResponseQuery, error) {
ret := _m.Called(_a0)
// Query provides a mock function with given fields: _a0, _a1
func (_m *AppConnQuery) Query(_a0 context.Context, _a1 *types.RequestQuery) (*types.ResponseQuery, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseQuery
if rf, ok := ret.Get(0).(func(types.RequestQuery) *types.ResponseQuery); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestQuery) *types.ResponseQuery); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseQuery)
@@ -87,8 +89,8 @@ func (_m *AppConnQuery) QuerySync(_a0 types.RequestQuery) (*types.ResponseQuery,
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestQuery) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestQuery) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
+30 -28
View File
@@ -3,6 +3,8 @@
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
types "github.com/tendermint/tendermint/abci/types"
@@ -13,13 +15,13 @@ type AppConnSnapshot struct {
mock.Mock
}
// ApplySnapshotChunkSync provides a mock function with given fields: _a0
func (_m *AppConnSnapshot) ApplySnapshotChunkSync(_a0 types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
ret := _m.Called(_a0)
// ApplySnapshotChunk provides a mock function with given fields: _a0, _a1
func (_m *AppConnSnapshot) ApplySnapshotChunk(_a0 context.Context, _a1 *types.RequestApplySnapshotChunk) (*types.ResponseApplySnapshotChunk, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseApplySnapshotChunk
if rf, ok := ret.Get(0).(func(types.RequestApplySnapshotChunk) *types.ResponseApplySnapshotChunk); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestApplySnapshotChunk) *types.ResponseApplySnapshotChunk); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseApplySnapshotChunk)
@@ -27,8 +29,8 @@ func (_m *AppConnSnapshot) ApplySnapshotChunkSync(_a0 types.RequestApplySnapshot
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestApplySnapshotChunk) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestApplySnapshotChunk) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -50,13 +52,13 @@ func (_m *AppConnSnapshot) Error() error {
return r0
}
// ListSnapshotsSync provides a mock function with given fields: _a0
func (_m *AppConnSnapshot) ListSnapshotsSync(_a0 types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
ret := _m.Called(_a0)
// ListSnapshots provides a mock function with given fields: _a0, _a1
func (_m *AppConnSnapshot) ListSnapshots(_a0 context.Context, _a1 *types.RequestListSnapshots) (*types.ResponseListSnapshots, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseListSnapshots
if rf, ok := ret.Get(0).(func(types.RequestListSnapshots) *types.ResponseListSnapshots); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestListSnapshots) *types.ResponseListSnapshots); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseListSnapshots)
@@ -64,8 +66,8 @@ func (_m *AppConnSnapshot) ListSnapshotsSync(_a0 types.RequestListSnapshots) (*t
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestListSnapshots) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestListSnapshots) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -73,13 +75,13 @@ func (_m *AppConnSnapshot) ListSnapshotsSync(_a0 types.RequestListSnapshots) (*t
return r0, r1
}
// LoadSnapshotChunkSync provides a mock function with given fields: _a0
func (_m *AppConnSnapshot) LoadSnapshotChunkSync(_a0 types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
ret := _m.Called(_a0)
// LoadSnapshotChunk provides a mock function with given fields: _a0, _a1
func (_m *AppConnSnapshot) LoadSnapshotChunk(_a0 context.Context, _a1 *types.RequestLoadSnapshotChunk) (*types.ResponseLoadSnapshotChunk, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseLoadSnapshotChunk
if rf, ok := ret.Get(0).(func(types.RequestLoadSnapshotChunk) *types.ResponseLoadSnapshotChunk); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestLoadSnapshotChunk) *types.ResponseLoadSnapshotChunk); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseLoadSnapshotChunk)
@@ -87,8 +89,8 @@ func (_m *AppConnSnapshot) LoadSnapshotChunkSync(_a0 types.RequestLoadSnapshotCh
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestLoadSnapshotChunk) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestLoadSnapshotChunk) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
@@ -96,13 +98,13 @@ func (_m *AppConnSnapshot) LoadSnapshotChunkSync(_a0 types.RequestLoadSnapshotCh
return r0, r1
}
// OfferSnapshotSync provides a mock function with given fields: _a0
func (_m *AppConnSnapshot) OfferSnapshotSync(_a0 types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
ret := _m.Called(_a0)
// OfferSnapshot provides a mock function with given fields: _a0, _a1
func (_m *AppConnSnapshot) OfferSnapshot(_a0 context.Context, _a1 *types.RequestOfferSnapshot) (*types.ResponseOfferSnapshot, error) {
ret := _m.Called(_a0, _a1)
var r0 *types.ResponseOfferSnapshot
if rf, ok := ret.Get(0).(func(types.RequestOfferSnapshot) *types.ResponseOfferSnapshot); ok {
r0 = rf(_a0)
if rf, ok := ret.Get(0).(func(context.Context, *types.RequestOfferSnapshot) *types.ResponseOfferSnapshot); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.ResponseOfferSnapshot)
@@ -110,8 +112,8 @@ func (_m *AppConnSnapshot) OfferSnapshotSync(_a0 types.RequestOfferSnapshot) (*t
}
var r1 error
if rf, ok := ret.Get(1).(func(types.RequestOfferSnapshot) error); ok {
r1 = rf(_a0)
if rf, ok := ret.Get(1).(func(context.Context, *types.RequestOfferSnapshot) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
+1 -1
View File
@@ -8,7 +8,7 @@ import (
// RequestInfo contains all the information for sending
// the abci.RequestInfo message during handshake with the app.
// It contains only compile-time version information.
var RequestInfo = abci.RequestInfo{
var RequestInfo = &abci.RequestInfo{
Version: version.TMCoreSemVer,
BlockVersion: version.BlockProtocol,
P2PVersion: version.P2PProtocol,
+3 -3
View File
@@ -14,7 +14,7 @@ import (
func ExampleHTTP_simple() {
// Start a tendermint node (and kvstore) in the background to test against
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
node := rpctest.StartTendermint(app, rpctest.SuppressStdout, rpctest.RecreateConfig)
defer rpctest.StopTendermint(node)
@@ -36,7 +36,7 @@ func ExampleHTTP_simple() {
if err != nil {
log.Fatal(err)
}
if bres.CheckTx.IsErr() || bres.DeliverTx.IsErr() {
if bres.CheckTx.IsErr() || bres.TxResult.IsErr() {
log.Fatal("BroadcastTxCommit transaction failed")
}
@@ -67,7 +67,7 @@ func ExampleHTTP_simple() {
func ExampleHTTP_batching() {
// Start a tendermint node (and kvstore) in the background to test against
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
node := rpctest.StartTendermint(app, rpctest.SuppressStdout, rpctest.RecreateConfig)
// Create our RPC client
+1 -1
View File
@@ -18,7 +18,7 @@ func TestMain(m *testing.M) {
panic(err)
}
app := kvstore.NewPersistentKVStoreApplication(dir)
app := kvstore.NewPersistentApplication(dir)
node = rpctest.StartTendermint(app)
code := m.Run()
-241
View File
@@ -1,241 +0,0 @@
package mock
import (
"context"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/bytes"
"github.com/tendermint/tendermint/proxy"
"github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types"
)
// ABCIApp will send all abci related request to the named app,
// so you can test app behavior from a client without needing
// an entire tendermint node
type ABCIApp struct {
App abci.Application
}
var (
_ client.ABCIClient = ABCIApp{}
_ client.ABCIClient = ABCIMock{}
_ client.ABCIClient = (*ABCIRecorder)(nil)
)
func (a ABCIApp) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
return &ctypes.ResultABCIInfo{Response: a.App.Info(proxy.RequestInfo)}, nil
}
func (a ABCIApp) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return a.ABCIQueryWithOptions(ctx, path, data, client.DefaultABCIQueryOptions)
}
func (a ABCIApp) ABCIQueryWithOptions(
ctx context.Context,
path string,
data bytes.HexBytes,
opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
q := a.App.Query(abci.RequestQuery{
Data: data,
Path: path,
Height: opts.Height,
Prove: opts.Prove,
})
return &ctypes.ResultABCIQuery{Response: q}, nil
}
// NOTE: Caller should call a.App.Commit() separately,
// this function does not actually wait for a commit.
// TODO: Make it wait for a commit and set res.Height appropriately.
func (a ABCIApp) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
res := ctypes.ResultBroadcastTxCommit{}
res.CheckTx = a.App.CheckTx(abci.RequestCheckTx{Tx: tx})
if res.CheckTx.IsErr() {
return &res, nil
}
res.DeliverTx = a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx})
res.Height = -1 // TODO
return &res, nil
}
func (a ABCIApp) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx})
// and this gets written in a background thread...
if !c.IsErr() {
go func() { a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx}) }()
}
return &ctypes.ResultBroadcastTx{
Code: c.Code,
Data: c.Data,
Log: c.Log,
Codespace: c.Codespace,
Hash: tx.Hash(),
}, nil
}
func (a ABCIApp) BroadcastTxSync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx})
// and this gets written in a background thread...
if !c.IsErr() {
go func() { a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx}) }()
}
return &ctypes.ResultBroadcastTx{
Code: c.Code,
Data: c.Data,
Log: c.Log,
Codespace: c.Codespace,
Hash: tx.Hash(),
}, nil
}
// ABCIMock will send all abci related request to the named app,
// so you can test app behavior from a client without needing
// an entire tendermint node
type ABCIMock struct {
Info Call
Query Call
BroadcastCommit Call
Broadcast Call
}
func (m ABCIMock) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
res, err := m.Info.GetResponse(nil)
if err != nil {
return nil, err
}
return &ctypes.ResultABCIInfo{Response: res.(abci.ResponseInfo)}, nil
}
func (m ABCIMock) ABCIQuery(ctx context.Context, path string, data bytes.HexBytes) (*ctypes.ResultABCIQuery, error) {
return m.ABCIQueryWithOptions(ctx, path, data, client.DefaultABCIQueryOptions)
}
func (m ABCIMock) ABCIQueryWithOptions(
ctx context.Context,
path string,
data bytes.HexBytes,
opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
res, err := m.Query.GetResponse(QueryArgs{path, data, opts.Height, opts.Prove})
if err != nil {
return nil, err
}
resQuery := res.(abci.ResponseQuery)
return &ctypes.ResultABCIQuery{Response: resQuery}, nil
}
func (m ABCIMock) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
res, err := m.BroadcastCommit.GetResponse(tx)
if err != nil {
return nil, err
}
return res.(*ctypes.ResultBroadcastTxCommit), nil
}
func (m ABCIMock) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
res, err := m.Broadcast.GetResponse(tx)
if err != nil {
return nil, err
}
return res.(*ctypes.ResultBroadcastTx), nil
}
func (m ABCIMock) BroadcastTxSync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
res, err := m.Broadcast.GetResponse(tx)
if err != nil {
return nil, err
}
return res.(*ctypes.ResultBroadcastTx), nil
}
// ABCIRecorder can wrap another type (ABCIApp, ABCIMock, or Client)
// and record all ABCI related calls.
type ABCIRecorder struct {
Client client.ABCIClient
Calls []Call
}
func NewABCIRecorder(client client.ABCIClient) *ABCIRecorder {
return &ABCIRecorder{
Client: client,
Calls: []Call{},
}
}
type QueryArgs struct {
Path string
Data bytes.HexBytes
Height int64
Prove bool
}
func (r *ABCIRecorder) addCall(call Call) {
r.Calls = append(r.Calls, call)
}
func (r *ABCIRecorder) ABCIInfo(ctx context.Context) (*ctypes.ResultABCIInfo, error) {
res, err := r.Client.ABCIInfo(ctx)
r.addCall(Call{
Name: "abci_info",
Response: res,
Error: err,
})
return res, err
}
func (r *ABCIRecorder) ABCIQuery(
ctx context.Context,
path string,
data bytes.HexBytes,
) (*ctypes.ResultABCIQuery, error) {
return r.ABCIQueryWithOptions(ctx, path, data, client.DefaultABCIQueryOptions)
}
func (r *ABCIRecorder) ABCIQueryWithOptions(
ctx context.Context,
path string,
data bytes.HexBytes,
opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) {
res, err := r.Client.ABCIQueryWithOptions(ctx, path, data, opts)
r.addCall(Call{
Name: "abci_query",
Args: QueryArgs{path, data, opts.Height, opts.Prove},
Response: res,
Error: err,
})
return res, err
}
func (r *ABCIRecorder) BroadcastTxCommit(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
res, err := r.Client.BroadcastTxCommit(ctx, tx)
r.addCall(Call{
Name: "broadcast_tx_commit",
Args: tx,
Response: res,
Error: err,
})
return res, err
}
func (r *ABCIRecorder) BroadcastTxAsync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
res, err := r.Client.BroadcastTxAsync(ctx, tx)
r.addCall(Call{
Name: "broadcast_tx_async",
Args: tx,
Response: res,
Error: err,
})
return res, err
}
func (r *ABCIRecorder) BroadcastTxSync(ctx context.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
res, err := r.Client.BroadcastTxSync(ctx, tx)
r.addCall(Call{
Name: "broadcast_tx_sync",
Args: tx,
Response: res,
Error: err,
})
return res, err
}
-199
View File
@@ -1,199 +0,0 @@
package mock_test
import (
"context"
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/abci/example/kvstore"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/bytes"
"github.com/tendermint/tendermint/rpc/client"
"github.com/tendermint/tendermint/rpc/client/mock"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/types"
)
func TestABCIMock(t *testing.T) {
assert, require := assert.New(t), require.New(t)
key, value := []byte("foo"), []byte("bar")
height := int64(10)
goodTx := types.Tx{0x01, 0xff}
badTx := types.Tx{0x12, 0x21}
m := mock.ABCIMock{
Info: mock.Call{Error: errors.New("foobar")},
Query: mock.Call{Response: abci.ResponseQuery{
Key: key,
Value: value,
Height: height,
}},
// Broadcast commit depends on call
BroadcastCommit: mock.Call{
Args: goodTx,
Response: &ctypes.ResultBroadcastTxCommit{
CheckTx: abci.ResponseCheckTx{Data: bytes.HexBytes("stand")},
DeliverTx: abci.ResponseDeliverTx{Data: bytes.HexBytes("deliver")},
},
Error: errors.New("bad tx"),
},
Broadcast: mock.Call{Error: errors.New("must commit")},
}
// now, let's try to make some calls
_, err := m.ABCIInfo(context.Background())
require.NotNil(err)
assert.Equal("foobar", err.Error())
// query always returns the response
_query, err := m.ABCIQueryWithOptions(context.Background(), "/", nil, client.ABCIQueryOptions{Prove: false})
query := _query.Response
require.Nil(err)
require.NotNil(query)
assert.EqualValues(key, query.Key)
assert.EqualValues(value, query.Value)
assert.Equal(height, query.Height)
// non-commit calls always return errors
_, err = m.BroadcastTxSync(context.Background(), goodTx)
require.NotNil(err)
assert.Equal("must commit", err.Error())
_, err = m.BroadcastTxAsync(context.Background(), goodTx)
require.NotNil(err)
assert.Equal("must commit", err.Error())
// commit depends on the input
_, err = m.BroadcastTxCommit(context.Background(), badTx)
require.NotNil(err)
assert.Equal("bad tx", err.Error())
bres, err := m.BroadcastTxCommit(context.Background(), goodTx)
require.Nil(err, "%+v", err)
assert.EqualValues(0, bres.CheckTx.Code)
assert.EqualValues("stand", bres.CheckTx.Data)
assert.EqualValues("deliver", bres.DeliverTx.Data)
}
func TestABCIRecorder(t *testing.T) {
assert, require := assert.New(t), require.New(t)
// This mock returns errors on everything but Query
m := mock.ABCIMock{
Info: mock.Call{Response: abci.ResponseInfo{
Data: "data",
Version: "v0.9.9",
}},
Query: mock.Call{Error: errors.New("query")},
Broadcast: mock.Call{Error: errors.New("broadcast")},
BroadcastCommit: mock.Call{Error: errors.New("broadcast_commit")},
}
r := mock.NewABCIRecorder(m)
require.Equal(0, len(r.Calls))
_, err := r.ABCIInfo(context.Background())
assert.Nil(err, "expected no err on info")
_, err = r.ABCIQueryWithOptions(
context.Background(),
"path",
bytes.HexBytes("data"),
client.ABCIQueryOptions{Prove: false},
)
assert.NotNil(err, "expected error on query")
require.Equal(2, len(r.Calls))
info := r.Calls[0]
assert.Equal("abci_info", info.Name)
assert.Nil(info.Error)
assert.Nil(info.Args)
require.NotNil(info.Response)
ir, ok := info.Response.(*ctypes.ResultABCIInfo)
require.True(ok)
assert.Equal("data", ir.Response.Data)
assert.Equal("v0.9.9", ir.Response.Version)
query := r.Calls[1]
assert.Equal("abci_query", query.Name)
assert.Nil(query.Response)
require.NotNil(query.Error)
assert.Equal("query", query.Error.Error())
require.NotNil(query.Args)
qa, ok := query.Args.(mock.QueryArgs)
require.True(ok)
assert.Equal("path", qa.Path)
assert.EqualValues("data", qa.Data)
assert.False(qa.Prove)
// now add some broadcasts (should all err)
txs := []types.Tx{{1}, {2}, {3}}
_, err = r.BroadcastTxCommit(context.Background(), txs[0])
assert.NotNil(err, "expected err on broadcast")
_, err = r.BroadcastTxSync(context.Background(), txs[1])
assert.NotNil(err, "expected err on broadcast")
_, err = r.BroadcastTxAsync(context.Background(), txs[2])
assert.NotNil(err, "expected err on broadcast")
require.Equal(5, len(r.Calls))
bc := r.Calls[2]
assert.Equal("broadcast_tx_commit", bc.Name)
assert.Nil(bc.Response)
require.NotNil(bc.Error)
assert.EqualValues(bc.Args, txs[0])
bs := r.Calls[3]
assert.Equal("broadcast_tx_sync", bs.Name)
assert.Nil(bs.Response)
require.NotNil(bs.Error)
assert.EqualValues(bs.Args, txs[1])
ba := r.Calls[4]
assert.Equal("broadcast_tx_async", ba.Name)
assert.Nil(ba.Response)
require.NotNil(ba.Error)
assert.EqualValues(ba.Args, txs[2])
}
func TestABCIApp(t *testing.T) {
assert, require := assert.New(t), require.New(t)
app := kvstore.NewApplication()
m := mock.ABCIApp{app}
// get some info
info, err := m.ABCIInfo(context.Background())
require.Nil(err)
assert.Equal(`{"size":0}`, info.Response.GetData())
// add a key
key, value := "foo", "bar"
tx := fmt.Sprintf("%s=%s", key, value)
res, err := m.BroadcastTxCommit(context.Background(), types.Tx(tx))
require.Nil(err)
assert.True(res.CheckTx.IsOK())
require.NotNil(res.DeliverTx)
assert.True(res.DeliverTx.IsOK())
// commit
// TODO: This may not be necessary in the future
if res.Height == -1 {
m.App.Commit()
}
// check the key
_qres, err := m.ABCIQueryWithOptions(
context.Background(),
"/key",
bytes.HexBytes(key),
client.ABCIQueryOptions{Prove: true},
)
qres := _qres.Response
require.Nil(err)
assert.EqualValues(value, qres.Value)
// XXX Check proof
}
+1
View File
@@ -458,6 +458,7 @@ func (_m *Client) GenesisChunked(_a0 context.Context, _a1 uint) (*coretypes.Resu
return r0, r1
}
// Header provides a mock function with given fields: ctx, height
func (_m *Client) Header(ctx context.Context, height *int64) (*coretypes.ResultHeader, error) {
ret := _m.Called(ctx, height)
+6 -6
View File
@@ -252,7 +252,7 @@ func TestAppCalls(t *testing.T) {
k, v, tx := MakeTxKV()
bres, err := c.BroadcastTxCommit(context.Background(), tx)
require.NoError(err)
require.True(bres.DeliverTx.IsOK())
require.True(bres.TxResult.IsOK())
txh := bres.Height
apph := txh + 1 // this is where the tx will be applied to the state
@@ -368,7 +368,7 @@ func TestBroadcastTxCommit(t *testing.T) {
bres, err := c.BroadcastTxCommit(context.Background(), tx)
require.Nil(err, "%d: %+v", i, err)
require.True(bres.CheckTx.IsOK())
require.True(bres.DeliverTx.IsOK())
require.True(bres.TxResult.IsOK())
require.Equal(0, mempool.Size())
}
@@ -377,9 +377,9 @@ func TestBroadcastTxCommit(t *testing.T) {
func TestUnconfirmedTxs(t *testing.T) {
_, _, tx := MakeTxKV()
ch := make(chan *abci.Response, 1)
ch := make(chan *abci.ResponseCheckTx, 1)
mempool := node.Mempool()
err := mempool.CheckTx(tx, func(resp *abci.Response) { ch <- resp }, mempl.TxInfo{})
err := mempool.CheckTx(tx, func(resp *abci.ResponseCheckTx) { ch <- resp }, mempl.TxInfo{})
require.NoError(t, err)
// wait for tx to arrive in mempoool.
@@ -407,9 +407,9 @@ func TestUnconfirmedTxs(t *testing.T) {
func TestNumUnconfirmedTxs(t *testing.T) {
_, _, tx := MakeTxKV()
ch := make(chan *abci.Response, 1)
ch := make(chan *abci.ResponseCheckTx, 1)
mempool := node.Mempool()
err := mempool.CheckTx(tx, func(resp *abci.Response) { ch <- resp }, mempl.TxInfo{})
err := mempool.CheckTx(tx, func(resp *abci.ResponseCheckTx) { ch <- resp }, mempl.TxInfo{})
require.NoError(t, err)
// wait for tx to arrive in mempoool.
+4 -2
View File
@@ -1,6 +1,8 @@
package core
import (
"context"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/libs/bytes"
"github.com/tendermint/tendermint/proxy"
@@ -17,7 +19,7 @@ func ABCIQuery(
height int64,
prove bool,
) (*ctypes.ResultABCIQuery, error) {
resQuery, err := env.ProxyAppQuery.QuerySync(abci.RequestQuery{
resQuery, err := env.ProxyAppQuery.Query(context.TODO(), &abci.RequestQuery{
Path: path,
Data: data,
Height: height,
@@ -33,7 +35,7 @@ func ABCIQuery(
// ABCIInfo gets some info about the application.
// More: https://docs.tendermint.com/main/rpc/#/ABCI/abci_info
func ABCIInfo(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) {
resInfo, err := env.ProxyAppQuery.InfoSync(proxy.RequestInfo)
resInfo, err := env.ProxyAppQuery.Info(context.TODO(), proxy.RequestInfo)
if err != nil {
return nil, err
}
+5 -6
View File
@@ -177,18 +177,17 @@ func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockR
return nil, err
}
results, err := env.StateStore.LoadABCIResponses(height)
results, err := env.StateStore.LoadFinalizeBlockResponse(height)
if err != nil {
return nil, err
}
return &ctypes.ResultBlockResults{
Height: height,
TxsResults: results.DeliverTxs,
BeginBlockEvents: results.BeginBlock.Events,
EndBlockEvents: results.EndBlock.Events,
ValidatorUpdates: results.EndBlock.ValidatorUpdates,
ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
TxsResults: results.TxResults,
FinalizeBlockEvents: results.Events,
ValidatorUpdates: results.ValidatorUpdates,
ConsensusParamUpdates: results.ConsensusParamUpdates,
}, nil
}
+8 -12
View File
@@ -10,7 +10,6 @@ import (
dbm "github.com/tendermint/tm-db"
abci "github.com/tendermint/tendermint/abci/types"
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
rpctypes "github.com/tendermint/tendermint/rpc/jsonrpc/types"
sm "github.com/tendermint/tendermint/state"
@@ -70,21 +69,19 @@ func TestBlockchainInfo(t *testing.T) {
}
func TestBlockResults(t *testing.T) {
results := &tmstate.ABCIResponses{
DeliverTxs: []*abci.ResponseDeliverTx{
results := &abci.ResponseFinalizeBlock{
TxResults: []*abci.ExecTxResult{
{Code: 0, Data: []byte{0x01}, Log: "ok"},
{Code: 0, Data: []byte{0x02}, Log: "ok"},
{Code: 1, Log: "not ok"},
},
EndBlock: &abci.ResponseEndBlock{},
BeginBlock: &abci.ResponseBeginBlock{},
}
env = &Environment{}
env.StateStore = sm.NewStore(dbm.NewMemDB(), sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
err := env.StateStore.SaveABCIResponses(100, results)
err := env.StateStore.SaveFinalizeBlockResponse(100, results)
require.NoError(t, err)
mockstore := &mocks.BlockStore{}
mockstore.On("Height").Return(int64(100))
@@ -101,11 +98,10 @@ func TestBlockResults(t *testing.T) {
{101, true, nil},
{100, false, &ctypes.ResultBlockResults{
Height: 100,
TxsResults: results.DeliverTxs,
BeginBlockEvents: results.BeginBlock.Events,
EndBlockEvents: results.EndBlock.Events,
ValidatorUpdates: results.EndBlock.ValidatorUpdates,
ConsensusParamUpdates: results.EndBlock.ConsensusParamUpdates,
TxsResults: results.TxResults,
FinalizeBlockEvents: results.Events,
ValidatorUpdates: results.ValidatorUpdates,
ConsensusParamUpdates: results.ConsensusParamUpdates,
}},
}
+24 -26
View File
@@ -32,8 +32,8 @@ func BroadcastTxAsync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadca
// DeliverTx result.
// More: https://docs.tendermint.com/main/rpc/#/Tx/broadcast_tx_sync
func BroadcastTxSync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
resCh := make(chan *abci.Response, 1)
err := env.Mempool.CheckTx(tx, func(res *abci.Response) {
resCh := make(chan *abci.ResponseCheckTx, 1)
err := env.Mempool.CheckTx(tx, func(res *abci.ResponseCheckTx) {
select {
case <-ctx.Context().Done():
case resCh <- res:
@@ -48,12 +48,11 @@ func BroadcastTxSync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcas
case <-ctx.Context().Done():
return nil, fmt.Errorf("broadcast confirmation not received: %w", ctx.Context().Err())
case res := <-resCh:
r := res.GetCheckTx()
return &ctypes.ResultBroadcastTx{
Code: r.Code,
Data: r.Data,
Log: r.Log,
Codespace: r.Codespace,
Code: res.Code,
Data: res.Data,
Log: res.Log,
Codespace: res.Codespace,
Hash: tx.Hash(),
}, nil
}
@@ -87,8 +86,8 @@ func BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadc
}()
// Broadcast tx and wait for CheckTx result
checkTxResCh := make(chan *abci.Response, 1)
err = env.Mempool.CheckTx(tx, func(res *abci.Response) {
checkTxResCh := make(chan *abci.ResponseCheckTx, 1)
err = env.Mempool.CheckTx(tx, func(res *abci.ResponseCheckTx) {
select {
case <-ctx.Context().Done():
case checkTxResCh <- res:
@@ -101,25 +100,24 @@ func BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadc
select {
case <-ctx.Context().Done():
return nil, fmt.Errorf("broadcast confirmation not received: %w", ctx.Context().Err())
case checkTxResMsg := <-checkTxResCh:
checkTxRes := checkTxResMsg.GetCheckTx()
case checkTxRes := <-checkTxResCh:
if checkTxRes.Code != abci.CodeTypeOK {
return &ctypes.ResultBroadcastTxCommit{
CheckTx: *checkTxRes,
DeliverTx: abci.ResponseDeliverTx{},
Hash: tx.Hash(),
CheckTx: *checkTxRes,
TxResult: abci.ExecTxResult{},
Hash: tx.Hash(),
}, nil
}
// Wait for the tx to be included in a block or timeout.
select {
case msg := <-deliverTxSub.Out(): // The tx was included in a block.
deliverTxRes := msg.Data().(types.EventDataTx)
txResultEvent := msg.Data().(types.EventDataTx)
return &ctypes.ResultBroadcastTxCommit{
CheckTx: *checkTxRes,
DeliverTx: deliverTxRes.Result,
Hash: tx.Hash(),
Height: deliverTxRes.Height,
CheckTx: *checkTxRes,
TxResult: txResultEvent.Result,
Hash: tx.Hash(),
Height: txResultEvent.Height,
}, nil
case <-deliverTxSub.Cancelled():
var reason string
@@ -131,17 +129,17 @@ func BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadc
err = fmt.Errorf("deliverTxSub was canceled (reason: %s)", reason)
env.Logger.Error("Error on broadcastTxCommit", "err", err)
return &ctypes.ResultBroadcastTxCommit{
CheckTx: *checkTxRes,
DeliverTx: abci.ResponseDeliverTx{},
Hash: tx.Hash(),
CheckTx: *checkTxRes,
TxResult: abci.ExecTxResult{},
Hash: tx.Hash(),
}, err
case <-time.After(env.Config.TimeoutBroadcastTxCommit):
err = errors.New("timed out waiting for tx to be included in a block")
env.Logger.Error("Error on broadcastTxCommit", "err", err)
return &ctypes.ResultBroadcastTxCommit{
CheckTx: *checkTxRes,
DeliverTx: abci.ResponseDeliverTx{},
Hash: tx.Hash(),
CheckTx: *checkTxRes,
TxResult: abci.ExecTxResult{},
Hash: tx.Hash(),
}, err
}
}
@@ -175,7 +173,7 @@ func NumUnconfirmedTxs(ctx *rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, err
// be added to the mempool either.
// More: https://docs.tendermint.com/main/rpc/#/Tx/check_tx
func CheckTx(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultCheckTx, error) {
res, err := env.ProxyAppMempool.CheckTxSync(abci.RequestCheckTx{Tx: tx})
res, err := env.ProxyAppMempool.CheckTx(context.TODO(), &abci.RequestCheckTx{Tx: tx})
if err != nil {
return nil, err
}
+16 -16
View File
@@ -52,12 +52,12 @@ type ResultCommit struct {
// ABCI results from a block
type ResultBlockResults struct {
Height int64 `json:"height"`
TxsResults []*abci.ResponseDeliverTx `json:"txs_results"`
BeginBlockEvents []abci.Event `json:"begin_block_events"`
EndBlockEvents []abci.Event `json:"end_block_events"`
ValidatorUpdates []abci.ValidatorUpdate `json:"validator_updates"`
ConsensusParamUpdates *tmproto.ConsensusParams `json:"consensus_param_updates"`
Height int64 `json:"height"`
TxsResults []*abci.ExecTxResult `json:"txs_results"`
FinalizeBlockEvents []abci.Event `json:"finalize_block_events"`
ValidatorUpdates []abci.ValidatorUpdate `json:"validator_updates"`
ConsensusParamUpdates *tmproto.ConsensusParams `json:"consensus_param_updates"`
AgreedAppData []byte `json:"agreed_app_data"`
}
// NewResultCommit is a helper to initialize the ResultCommit with
@@ -183,10 +183,10 @@ type ResultBroadcastTx struct {
// CheckTx and DeliverTx results
type ResultBroadcastTxCommit struct {
CheckTx abci.ResponseCheckTx `json:"check_tx"`
DeliverTx abci.ResponseDeliverTx `json:"deliver_tx"`
Hash bytes.HexBytes `json:"hash"`
Height int64 `json:"height"`
CheckTx abci.ResponseCheckTx `json:"check_tx"`
TxResult abci.ExecTxResult `json:"tx_result"`
Hash bytes.HexBytes `json:"hash"`
Height int64 `json:"height"`
}
// ResultCheckTx wraps abci.ResponseCheckTx.
@@ -196,12 +196,12 @@ type ResultCheckTx struct {
// Result of querying for a tx
type ResultTx struct {
Hash bytes.HexBytes `json:"hash"`
Height int64 `json:"height"`
Index uint32 `json:"index"`
TxResult abci.ResponseDeliverTx `json:"tx_result"`
Tx types.Tx `json:"tx"`
Proof types.TxProof `json:"proof,omitempty"`
Hash bytes.HexBytes `json:"hash"`
Height int64 `json:"height"`
Index uint32 `json:"index"`
TxResult abci.ExecTxResult `json:"tx_result"`
Tx types.Tx `json:"tx"`
Proof types.TxProof `json:"proof,omitempty"`
}
// Result of searching for txs
+4 -4
View File
@@ -30,10 +30,10 @@ func (bapi *broadcastAPI) BroadcastTx(ctx context.Context, req *RequestBroadcast
Data: res.CheckTx.Data,
Log: res.CheckTx.Log,
},
DeliverTx: &abci.ResponseDeliverTx{
Code: res.DeliverTx.Code,
Data: res.DeliverTx.Data,
Log: res.DeliverTx.Log,
TxResult: &abci.ExecTxResult{
Code: res.TxResult.Code,
Data: res.TxResult.Data,
Log: res.TxResult.Log,
},
}, nil
}
+2 -2
View File
@@ -14,7 +14,7 @@ import (
func TestMain(m *testing.M) {
// start a tendermint node in the background to test against
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
node := rpctest.StartTendermint(app)
code := m.Run()
@@ -31,5 +31,5 @@ func TestBroadcastTx(t *testing.T) {
)
require.NoError(t, err)
require.EqualValues(t, 0, res.CheckTx.Code)
require.EqualValues(t, 0, res.DeliverTx.Code)
require.EqualValues(t, 0, res.TxResult.Code)
}
+30 -31
View File
@@ -6,8 +6,7 @@ package coregrpc
import (
context "context"
fmt "fmt"
grpc1 "github.com/cosmos/gogoproto/grpc"
proto "github.com/cosmos/gogoproto/proto"
proto "github.com/gogo/protobuf/proto"
types "github.com/tendermint/tendermint/abci/types"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
@@ -145,8 +144,8 @@ func (m *ResponsePing) XXX_DiscardUnknown() {
var xxx_messageInfo_ResponsePing proto.InternalMessageInfo
type ResponseBroadcastTx struct {
CheckTx *types.ResponseCheckTx `protobuf:"bytes,1,opt,name=check_tx,json=checkTx,proto3" json:"check_tx,omitempty"`
DeliverTx *types.ResponseDeliverTx `protobuf:"bytes,2,opt,name=deliver_tx,json=deliverTx,proto3" json:"deliver_tx,omitempty"`
CheckTx *types.ResponseCheckTx `protobuf:"bytes,1,opt,name=check_tx,json=checkTx,proto3" json:"check_tx,omitempty"`
TxResult *types.ExecTxResult `protobuf:"bytes,2,opt,name=tx_result,json=txResult,proto3" json:"tx_result,omitempty"`
}
func (m *ResponseBroadcastTx) Reset() { *m = ResponseBroadcastTx{} }
@@ -189,9 +188,9 @@ func (m *ResponseBroadcastTx) GetCheckTx() *types.ResponseCheckTx {
return nil
}
func (m *ResponseBroadcastTx) GetDeliverTx() *types.ResponseDeliverTx {
func (m *ResponseBroadcastTx) GetTxResult() *types.ExecTxResult {
if m != nil {
return m.DeliverTx
return m.TxResult
}
return nil
}
@@ -206,7 +205,7 @@ func init() {
func init() { proto.RegisterFile("tendermint/rpc/grpc/types.proto", fileDescriptor_0ffff5682c662b95) }
var fileDescriptor_0ffff5682c662b95 = []byte{
// 316 bytes of a gzipped FileDescriptorProto
// 320 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2f, 0x49, 0xcd, 0x4b,
0x49, 0x2d, 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x2f, 0x2a, 0x48, 0xd6, 0x4f, 0x07, 0x11, 0x25, 0x95,
0x05, 0xa9, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xc2, 0x08, 0x05, 0x7a, 0x45, 0x05,
@@ -214,19 +213,19 @@ var fileDescriptor_0ffff5682c662b95 = []byte{
0x72, 0x71, 0x07, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x04, 0x64, 0xe6, 0xa5, 0x2b, 0xa9, 0x70,
0x09, 0x41, 0xb9, 0x4e, 0x45, 0xf9, 0x89, 0x29, 0xc9, 0x89, 0xc5, 0x25, 0x21, 0x15, 0x42, 0x7c,
0x5c, 0x4c, 0x25, 0x15, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x3c, 0x41, 0x4c, 0x25, 0x15, 0x4a, 0x7c,
0x5c, 0x3c, 0x41, 0xa9, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x60, 0x5d, 0x53, 0x19, 0xb9, 0x84,
0x61, 0x02, 0xc8, 0xfa, 0xac, 0xb9, 0x38, 0x92, 0x33, 0x52, 0x93, 0xb3, 0xe3, 0xa1, 0xba, 0xb9,
0x8d, 0x14, 0xf4, 0x90, 0x5c, 0x08, 0x72, 0x8c, 0x1e, 0x4c, 0x9f, 0x33, 0x48, 0x61, 0x48, 0x45,
0x10, 0x7b, 0x32, 0x84, 0x21, 0xe4, 0xc8, 0xc5, 0x95, 0x92, 0x9a, 0x93, 0x59, 0x96, 0x5a, 0x04,
0xd2, 0xce, 0x04, 0xd6, 0xae, 0x84, 0x53, 0xbb, 0x0b, 0x44, 0x69, 0x48, 0x45, 0x10, 0x67, 0x0a,
0x8c, 0x69, 0xb4, 0x97, 0x91, 0x8b, 0x07, 0xee, 0x1e, 0xc7, 0x00, 0x4f, 0x21, 0x6f, 0x2e, 0x16,
0x90, 0x83, 0x85, 0x50, 0x9c, 0x01, 0x0b, 0x28, 0x3d, 0xa4, 0x80, 0x90, 0x52, 0xc4, 0xa1, 0x02,
0xe1, 0x6b, 0xa1, 0x04, 0x2e, 0x6e, 0x64, 0xcf, 0xaa, 0xe3, 0x33, 0x13, 0x49, 0xa1, 0x94, 0x06,
0x5e, 0xa3, 0x91, 0x54, 0x3a, 0xf9, 0x9c, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83,
0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43,
0x94, 0x51, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x3e, 0x52, 0xf4, 0x62,
0x49, 0x1f, 0xd6, 0xc9, 0xf9, 0x45, 0xa9, 0x20, 0x46, 0x12, 0x1b, 0x38, 0xc6, 0x8d, 0x01, 0x01,
0x00, 0x00, 0xff, 0xff, 0xf6, 0x4b, 0x02, 0xd8, 0x46, 0x02, 0x00, 0x00,
0x5c, 0x3c, 0x41, 0xa9, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x60, 0x5d, 0x7d, 0x8c, 0x5c, 0xc2,
0x30, 0x01, 0x64, 0x7d, 0xd6, 0x5c, 0x1c, 0xc9, 0x19, 0xa9, 0xc9, 0xd9, 0xf1, 0x50, 0xdd, 0xdc,
0x46, 0x0a, 0x7a, 0x48, 0x2e, 0x04, 0x39, 0x46, 0x0f, 0xa6, 0xcf, 0x19, 0xa4, 0x30, 0xa4, 0x22,
0x88, 0x3d, 0x19, 0xc2, 0x10, 0xb2, 0xe2, 0xe2, 0x2c, 0xa9, 0x88, 0x2f, 0x4a, 0x2d, 0x2e, 0xcd,
0x29, 0x91, 0x60, 0x02, 0xeb, 0x96, 0xc5, 0xd0, 0xed, 0x5a, 0x91, 0x9a, 0x1c, 0x52, 0x11, 0x04,
0x56, 0x14, 0xc4, 0x51, 0x02, 0x65, 0x19, 0xed, 0x65, 0xe4, 0xe2, 0x81, 0x3b, 0xc4, 0x31, 0xc0,
0x53, 0xc8, 0x9b, 0x8b, 0x05, 0xe4, 0x52, 0x21, 0x14, 0xfb, 0x61, 0x21, 0xa4, 0x87, 0x14, 0x02,
0x52, 0x8a, 0x38, 0x54, 0x20, 0xbc, 0x2b, 0x94, 0xc0, 0xc5, 0x8d, 0xec, 0x4b, 0x75, 0x7c, 0x66,
0x22, 0x29, 0x94, 0xd2, 0xc0, 0x6b, 0x34, 0x92, 0x4a, 0x27, 0x9f, 0x13, 0x8f, 0xe4, 0x18, 0x2f,
0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18,
0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4a, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf,
0xd5, 0x47, 0x8a, 0x57, 0x2c, 0x09, 0xc3, 0x3a, 0x39, 0xbf, 0x28, 0x15, 0xc4, 0x48, 0x62, 0x03,
0x47, 0xb5, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x93, 0xa5, 0x24, 0x3f, 0x02, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -246,10 +245,10 @@ type BroadcastAPIClient interface {
}
type broadcastAPIClient struct {
cc grpc1.ClientConn
cc *grpc.ClientConn
}
func NewBroadcastAPIClient(cc grpc1.ClientConn) BroadcastAPIClient {
func NewBroadcastAPIClient(cc *grpc.ClientConn) BroadcastAPIClient {
return &broadcastAPIClient{cc}
}
@@ -288,7 +287,7 @@ func (*UnimplementedBroadcastAPIServer) BroadcastTx(ctx context.Context, req *Re
return nil, status.Errorf(codes.Unimplemented, "method BroadcastTx not implemented")
}
func RegisterBroadcastAPIServer(s grpc1.Server, srv BroadcastAPIServer) {
func RegisterBroadcastAPIServer(s *grpc.Server, srv BroadcastAPIServer) {
s.RegisterService(&_BroadcastAPI_serviceDesc, srv)
}
@@ -441,9 +440,9 @@ func (m *ResponseBroadcastTx) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.DeliverTx != nil {
if m.TxResult != nil {
{
size, err := m.DeliverTx.MarshalToSizedBuffer(dAtA[:i])
size, err := m.TxResult.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
@@ -520,8 +519,8 @@ func (m *ResponseBroadcastTx) Size() (n int) {
l = m.CheckTx.Size()
n += 1 + l + sovTypes(uint64(l))
}
if m.DeliverTx != nil {
l = m.DeliverTx.Size()
if m.TxResult != nil {
l = m.TxResult.Size()
n += 1 + l + sovTypes(uint64(l))
}
return n
@@ -784,7 +783,7 @@ func (m *ResponseBroadcastTx) Unmarshal(dAtA []byte) error {
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field DeliverTx", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field TxResult", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
@@ -811,10 +810,10 @@ func (m *ResponseBroadcastTx) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.DeliverTx == nil {
m.DeliverTx = &types.ResponseDeliverTx{}
if m.TxResult == nil {
m.TxResult = &types.ExecTxResult{}
}
if err := m.DeliverTx.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
if err := m.TxResult.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
+1 -1
View File
@@ -103,4 +103,4 @@ func (e ErrNoABCIResponsesForHeight) Error() string {
return fmt.Sprintf("could not find results for height #%d", e.Height)
}
var ErrABCIResponsesNotPersisted = errors.New("node is not persisting abci responses")
var ErrFinalizeBlockResponsesNotPersisted = errors.New("node is not persisting finalize block responses")
+55 -100
View File
@@ -1,7 +1,7 @@
package state
import (
"errors"
"context"
"fmt"
"time"
@@ -10,7 +10,6 @@ import (
"github.com/tendermint/tendermint/libs/fail"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/mempool"
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
"github.com/tendermint/tendermint/proxy"
"github.com/tendermint/tendermint/types"
)
@@ -112,8 +111,8 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
block := state.MakeBlock(height, txs, commit, evidence, proposerAddr)
localLastCommit := buildLastCommitInfo(block, blockExec.store, state.InitialHeight)
rpp, err := blockExec.proxyApp.PrepareProposalSync(
abci.RequestPrepareProposal{
rpp, err := blockExec.proxyApp.PrepareProposal(context.TODO(),
&abci.RequestPrepareProposal{
MaxTxBytes: maxDataBytes,
Txs: block.Txs.ToSliceOfBytes(),
LocalLastCommit: extendedCommitInfo(localLastCommit, votes),
@@ -148,7 +147,7 @@ func (blockExec *BlockExecutor) ProcessProposal(
block *types.Block,
state State,
) (bool, error) {
resp, err := blockExec.proxyApp.ProcessProposalSync(abci.RequestProcessProposal{
resp, err := blockExec.proxyApp.ProcessProposal(context.TODO(), &abci.RequestProcessProposal{
Hash: block.Header.Hash(),
Height: block.Header.Height,
Time: block.Header.Time,
@@ -195,7 +194,7 @@ func (blockExec *BlockExecutor) ApplyBlock(
}
startTime := time.Now().UnixNano()
abciResponses, err := execBlockOnProxyApp(
abciResponse, err := execBlockOnProxyApp(
blockExec.logger, blockExec.proxyApp, block, blockExec.store, state.InitialHeight,
)
endTime := time.Now().UnixNano()
@@ -207,20 +206,19 @@ func (blockExec *BlockExecutor) ApplyBlock(
fail.Fail() // XXX
// Save the results before we commit.
if err := blockExec.store.SaveABCIResponses(block.Height, abciResponses); err != nil {
if err := blockExec.store.SaveFinalizeBlockResponse(block.Height, abciResponse); err != nil {
return state, 0, err
}
fail.Fail() // XXX
// validate the validator updates and convert to tendermint types
abciValUpdates := abciResponses.EndBlock.ValidatorUpdates
err = validateValidatorUpdates(abciValUpdates, state.ConsensusParams.Validator)
err = validateValidatorUpdates(abciResponse.ValidatorUpdates, state.ConsensusParams.Validator)
if err != nil {
return state, 0, fmt.Errorf("error in validator updates: %v", err)
}
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciValUpdates)
validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponse.ValidatorUpdates)
if err != nil {
return state, 0, err
}
@@ -228,18 +226,18 @@ func (blockExec *BlockExecutor) ApplyBlock(
blockExec.logger.Debug("updates to validators", "updates", types.ValidatorListString(validatorUpdates))
blockExec.metrics.ValidatorSetUpdates.Add(1)
}
if abciResponses.EndBlock.ConsensusParamUpdates != nil {
if abciResponse.ConsensusParamUpdates != nil {
blockExec.metrics.ConsensusParamUpdates.Add(1)
}
// Update the state with the block and responses.
state, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates)
state, err = updateState(state, blockID, &block.Header, abciResponse, validatorUpdates)
if err != nil {
return state, 0, fmt.Errorf("commit failed for application: %v", err)
}
// Lock mempool, commit app state, update mempoool.
appHash, retainHeight, err := blockExec.Commit(state, block, abciResponses.DeliverTxs)
retainHeight, err := blockExec.Commit(state, block, abciResponse)
if err != nil {
return state, 0, fmt.Errorf("commit failed for application: %v", err)
}
@@ -250,7 +248,7 @@ func (blockExec *BlockExecutor) ApplyBlock(
fail.Fail() // XXX
// Update the app hash and save the state.
state.AppHash = appHash
state.AppHash = abciResponse.AgreedAppData
if err := blockExec.store.Save(state); err != nil {
return state, 0, err
}
@@ -259,7 +257,7 @@ func (blockExec *BlockExecutor) ApplyBlock(
// Events are fired after everything else.
// NOTE: if we crash between Commit and Save, events wont be fired during replay
fireEvents(blockExec.logger, blockExec.eventBus, block, abciResponses, validatorUpdates)
fireEvents(blockExec.logger, blockExec.eventBus, block, blockID, abciResponse, validatorUpdates)
return state, retainHeight, nil
}
@@ -273,8 +271,8 @@ func (blockExec *BlockExecutor) ApplyBlock(
func (blockExec *BlockExecutor) Commit(
state State,
block *types.Block,
deliverTxResponses []*abci.ResponseDeliverTx,
) ([]byte, int64, error) {
abciResponse *abci.ResponseFinalizeBlock,
) (int64, error) {
blockExec.mempool.Lock()
defer blockExec.mempool.Unlock()
@@ -283,14 +281,14 @@ func (blockExec *BlockExecutor) Commit(
err := blockExec.mempool.FlushAppConn()
if err != nil {
blockExec.logger.Error("client error during mempool.FlushAppConn", "err", err)
return nil, 0, err
return 0, err
}
// Commit block, get hash back
res, err := blockExec.proxyApp.CommitSync()
res, err := blockExec.proxyApp.Commit(context.TODO())
if err != nil {
blockExec.logger.Error("client error during proxyAppConn.CommitSync", "err", err)
return nil, 0, err
return 0, err
}
// ResponseCommit has no error code - just data
@@ -298,19 +296,19 @@ func (blockExec *BlockExecutor) Commit(
"committed state",
"height", block.Height,
"num_txs", len(block.Txs),
"app_hash", fmt.Sprintf("%X", res.Data),
"agreed_data", fmt.Sprintf("%X", abciResponse.AgreedAppData),
)
// Update mempool.
err = blockExec.mempool.Update(
block.Height,
block.Txs,
deliverTxResponses,
abciResponse.TxResults,
TxPreCheck(state),
TxPostCheck(state),
)
return res.Data, res.RetainHeight, err
return res.RetainHeight, err
}
//---------------------------------------------------------
@@ -324,71 +322,30 @@ func execBlockOnProxyApp(
block *types.Block,
store Store,
initialHeight int64,
) (*tmstate.ABCIResponses, error) {
var validTxs, invalidTxs = 0, 0
txIndex := 0
abciResponses := new(tmstate.ABCIResponses)
dtxs := make([]*abci.ResponseDeliverTx, len(block.Txs))
abciResponses.DeliverTxs = dtxs
// Execute transactions and get hash.
proxyCb := func(req *abci.Request, res *abci.Response) {
if r, ok := res.Value.(*abci.Response_DeliverTx); ok {
// TODO: make use of res.Log
// TODO: make use of this info
// Blocks may include invalid txs.
txRes := r.DeliverTx
if txRes.Code == abci.CodeTypeOK {
validTxs++
} else {
logger.Debug("invalid tx", "code", txRes.Code, "log", txRes.Log)
invalidTxs++
}
abciResponses.DeliverTxs[txIndex] = txRes
txIndex++
}
}
proxyAppConn.SetResponseCallback(proxyCb)
) (*abci.ResponseFinalizeBlock, error) {
commitInfo := buildLastCommitInfo(block, store, initialHeight)
// Begin block
var err error
pbh := block.Header.ToProto()
if pbh == nil {
return nil, errors.New("nil header")
}
abciResponses.BeginBlock, err = proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{
Hash: block.Hash(),
Header: *pbh,
LastCommitInfo: commitInfo,
ByzantineValidators: block.Evidence.Evidence.ToABCI(),
resp, err := proxyAppConn.FinalizeBlock(context.TODO(), &abci.RequestFinalizeBlock{
Hash: block.Hash(),
NextValidatorsHash: block.NextValidatorsHash,
ProposerAddress: block.ProposerAddress,
Height: block.Height,
DecidedLastCommit: commitInfo,
Misbehavior: block.Evidence.Evidence.ToABCI(),
Txs: block.Txs.ToSliceOfBytes(),
})
if err != nil {
logger.Error("error in proxyAppConn.BeginBlock", "err", err)
logger.Error("error in proxyAppConn.FinalizeBlock", "err", err)
return nil, err
}
// run txs of block
for _, tx := range block.Txs {
proxyAppConn.DeliverTxAsync(abci.RequestDeliverTx{Tx: tx})
if err := proxyAppConn.Error(); err != nil {
return nil, err
}
// Assert that the application correctly returned tx results for each of the transactions provided in the block
if len(block.Data.Txs) != len(resp.TxResults) {
return nil, fmt.Errorf("expected tx results length to match size of transactions in block. Expected %d, got %d", len(block.Data.Txs), len(resp.TxResults))
}
// End block.
abciResponses.EndBlock, err = proxyAppConn.EndBlockSync(abci.RequestEndBlock{Height: block.Height})
if err != nil {
logger.Error("error in proxyAppConn.EndBlock", "err", err)
return nil, err
}
logger.Info("executed block", "height", block.Height, "num_valid_txs", validTxs, "num_invalid_txs", invalidTxs)
return abciResponses, nil
logger.Info("executed block", "height", block.Height, "agreed_data", resp.AgreedAppData)
return resp, nil
}
func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) abci.CommitInfo {
@@ -480,7 +437,7 @@ func updateState(
state State,
blockID types.BlockID,
header *types.Header,
abciResponses *tmstate.ABCIResponses,
abciResponse *abci.ResponseFinalizeBlock,
validatorUpdates []*types.Validator,
) (State, error) {
@@ -488,7 +445,7 @@ func updateState(
// and update s.LastValidators and s.Validators.
nValSet := state.NextValidators.Copy()
// Update the validator set with the latest abciResponses.
// Update the validator set with the latest abciResponse.
lastHeightValsChanged := state.LastHeightValidatorsChanged
if len(validatorUpdates) > 0 {
err := nValSet.UpdateWithChangeSet(validatorUpdates)
@@ -502,12 +459,12 @@ func updateState(
// Update validator proposer priority and set state variables.
nValSet.IncrementProposerPriority(1)
// Update the params with the latest abciResponses.
// Update the params with the latest abciResponse.
nextParams := state.ConsensusParams
lastHeightParamsChanged := state.LastHeightConsensusParamsChanged
if abciResponses.EndBlock.ConsensusParamUpdates != nil {
if abciResponse.ConsensusParamUpdates != nil {
// NOTE: must not mutate s.ConsensusParams
nextParams = state.ConsensusParams.Update(abciResponses.EndBlock.ConsensusParamUpdates)
nextParams = state.ConsensusParams.Update(abciResponse.ConsensusParamUpdates)
err := nextParams.ValidateBasic()
if err != nil {
return state, fmt.Errorf("error updating consensus params: %v", err)
@@ -536,7 +493,7 @@ func updateState(
LastHeightValidatorsChanged: lastHeightValsChanged,
ConsensusParams: nextParams,
LastHeightConsensusParamsChanged: lastHeightParamsChanged,
LastResultsHash: ABCIResponsesResultsHash(abciResponses),
LastResultsHash: TxResultsHash(abciResponse.TxResults),
AppHash: nil,
}, nil
}
@@ -548,22 +505,21 @@ func fireEvents(
logger log.Logger,
eventBus types.BlockEventPublisher,
block *types.Block,
abciResponses *tmstate.ABCIResponses,
blockID types.BlockID,
abciResponse *abci.ResponseFinalizeBlock,
validatorUpdates []*types.Validator,
) {
if err := eventBus.PublishEventNewBlock(types.EventDataNewBlock{
Block: block,
ResultBeginBlock: *abciResponses.BeginBlock,
ResultEndBlock: *abciResponses.EndBlock,
Block: block,
BlockID: blockID,
ResultFinalizeBlock: *abciResponse,
}); err != nil {
logger.Error("failed publishing new block", "err", err)
}
if err := eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{
Header: block.Header,
NumTxs: int64(len(block.Txs)),
ResultBeginBlock: *abciResponses.BeginBlock,
ResultEndBlock: *abciResponses.EndBlock,
Header: block.Header,
NumTxs: int64(len(block.Txs)),
}); err != nil {
logger.Error("failed publishing new block header", "err", err)
}
@@ -584,7 +540,7 @@ func fireEvents(
Height: block.Height,
Index: uint32(i),
Tx: tx,
Result: *(abciResponses.DeliverTxs[i]),
Result: *(abciResponse.TxResults[i]),
}}); err != nil {
logger.Error("failed publishing event TX", "err", err)
}
@@ -609,20 +565,19 @@ func ExecCommitBlock(
logger log.Logger,
store Store,
initialHeight int64,
) ([]byte, error) {
) error {
_, err := execBlockOnProxyApp(logger, appConnConsensus, block, store, initialHeight)
if err != nil {
logger.Error("failed executing block on proxy app", "height", block.Height, "err", err)
return nil, err
return err
}
// Commit block, get hash back
res, err := appConnConsensus.CommitSync()
res, err := appConnConsensus.Commit(context.TODO())
if err != nil {
logger.Error("client error during proxyAppConn.CommitSync", "err", res)
return nil, err
return err
}
// ResponseCommit has no error or log, just data
return res.Data, nil
return nil
}
+24 -24
View File
@@ -45,7 +45,7 @@ func TestApplyBlock(t *testing.T) {
state, stateDB, _ := makeState(1, 1)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
mp := &mpmocks.Mempool{}
mp.On("Lock").Return()
@@ -85,7 +85,7 @@ func TestBeginBlockValidators(t *testing.T) {
state, stateDB, _ := makeState(2, 2)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
prevHash := state.LastBlockID.Hash
@@ -121,7 +121,7 @@ func TestBeginBlockValidators(t *testing.T) {
// block for height 2
block := makeBlock(state, 2, lastCommit)
_, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1)
err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateStore, 1)
require.Nil(t, err, tc.desc)
// -> app receives a list of validators with a bool indicating if they signed
@@ -150,7 +150,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) {
state, stateDB, privVals := makeState(1, 1)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
defaultEvidenceTime := time.Date(2019, 1, 1, 0, 0, 0, 0, time.UTC)
@@ -255,7 +255,7 @@ func TestProcessProposal(t *testing.T) {
txs := test.MakeNTxs(height, 10)
logger := log.NewNopLogger()
app := abcimocks.NewBaseMock()
app := &abcimocks.Application{}
app.On("ProcessProposal", mock.Anything).Return(abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT})
cc := proxy.NewLocalClientCreator(app)
@@ -266,7 +266,7 @@ func TestProcessProposal(t *testing.T) {
state, stateDB, privVals := makeState(1, height)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
eventBus := types.NewEventBus()
@@ -473,7 +473,7 @@ func TestEndBlockValidatorUpdates(t *testing.T) {
state, stateDB, _ := makeState(1, 1)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
mp := &mpmocks.Mempool{}
mp.On("Lock").Return()
@@ -560,7 +560,7 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
state, stateDB, _ := makeState(1, 1)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
blockExec := sm.NewBlockExecutor(
stateStore,
@@ -590,7 +590,7 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) {
func TestEmptyPrepareProposal(t *testing.T) {
const height = 2
app := abcimocks.NewBaseMock()
app := &abcimocks.Application{}
cc := proxy.NewLocalClientCreator(app)
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
err := proxyApp.Start()
@@ -599,7 +599,7 @@ func TestEmptyPrepareProposal(t *testing.T) {
state, stateDB, privVals := makeState(1, height)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
mp := &mpmocks.Mempool{}
mp.On("Lock").Return()
@@ -635,7 +635,7 @@ func TestPrepareProposalTxsAllIncluded(t *testing.T) {
state, stateDB, privVals := makeState(1, height)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
evpool := &mocks.EvidencePool{}
@@ -643,11 +643,11 @@ func TestPrepareProposalTxsAllIncluded(t *testing.T) {
txs := test.MakeNTxs(height, 10)
mp := &mpmocks.Mempool{}
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs[2:]))
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs[2:])
app := abcimocks.NewBaseMock()
app := &abcimocks.Application{}
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
Txs: types.Txs(txs).ToSliceOfBytes(),
Txs: txs.ToSliceOfBytes(),
})
cc := proxy.NewLocalClientCreator(app)
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
@@ -682,7 +682,7 @@ func TestPrepareProposalReorderTxs(t *testing.T) {
state, stateDB, privVals := makeState(1, height)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
evpool := &mocks.EvidencePool{}
@@ -690,14 +690,14 @@ func TestPrepareProposalReorderTxs(t *testing.T) {
txs := test.MakeNTxs(height, 10)
mp := &mpmocks.Mempool{}
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs)
txs = txs[2:]
txs = append(txs[len(txs)/2:], txs[:len(txs)/2]...)
app := abcimocks.NewBaseMock()
app := &abcimocks.Application{}
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
Txs: types.Txs(txs).ToSliceOfBytes(),
Txs: txs.ToSliceOfBytes(),
})
cc := proxy.NewLocalClientCreator(app)
@@ -735,7 +735,7 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) {
// limit max block size
state.ConsensusParams.Block.MaxBytes = 60 * 1024
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
evpool := &mocks.EvidencePool{}
@@ -746,11 +746,11 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) {
maxDataBytes := types.MaxDataBytes(state.ConsensusParams.Block.MaxBytes, 0, nValidators)
txs := test.MakeNTxs(height, maxDataBytes/bytesPerTx+2) // +2 so that tx don't fit
mp := &mpmocks.Mempool{}
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs)
app := abcimocks.NewBaseMock()
app := &abcimocks.Application{}
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
Txs: types.Txs(txs).ToSliceOfBytes(),
Txs: txs.ToSliceOfBytes(),
})
cc := proxy.NewLocalClientCreator(app)
@@ -784,7 +784,7 @@ func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) {
state, stateDB, privVals := makeState(1, height)
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
evpool := &mocks.EvidencePool{}
@@ -792,7 +792,7 @@ func TestPrepareProposalErrorOnPrepareProposalError(t *testing.T) {
txs := test.MakeNTxs(height, 10)
mp := &mpmocks.Mempool{}
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(txs)
cm := &abciclientmocks.Client{}
cm.On("SetLogger", mock.Anything).Return()
+3 -4
View File
@@ -4,7 +4,6 @@ import (
dbm "github.com/tendermint/tm-db"
abci "github.com/tendermint/tendermint/abci/types"
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
"github.com/tendermint/tendermint/types"
)
@@ -27,10 +26,10 @@ func UpdateState(
state State,
blockID types.BlockID,
header *types.Header,
abciResponses *tmstate.ABCIResponses,
resp *abci.ResponseFinalizeBlock,
validatorUpdates []*types.Validator,
) (State, error) {
return updateState(state, blockID, header, abciResponses, validatorUpdates)
return updateState(state, blockID, header, resp, validatorUpdates)
}
// ValidateValidatorUpdates is an alias for validateValidatorUpdates exported
@@ -42,6 +41,6 @@ func ValidateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, params types.V
// SaveValidatorsInfo is an alias for the private saveValidatorsInfo method in
// store.go, exported exclusively and explicitly for testing.
func SaveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) error {
stateStore := dbStore{db, StoreOptions{DiscardABCIResponses: false}}
stateStore := dbStore{db, StoreOptions{DiscardFinalizeBlockResponses: false}}
return stateStore.saveValidatorsInfo(height, lastHeightChanged, valSet)
}
+34 -54
View File
@@ -2,6 +2,7 @@ package state_test
import (
"bytes"
"context"
"fmt"
"testing"
"time"
@@ -12,7 +13,6 @@ import (
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/internal/test"
tmstate "github.com/tendermint/tendermint/proto/tendermint/state"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
@@ -124,7 +124,7 @@ func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValida
stateDB := dbm.NewMemDB()
stateStore := sm.NewStore(stateDB, sm.StoreOptions{
DiscardABCIResponses: false,
DiscardFinalizeBlockResponses: false,
})
if err := stateStore.Save(s); err != nil {
panic(err)
@@ -153,21 +153,16 @@ func makeHeaderPartsResponsesValPubKeyChange(
t *testing.T,
state sm.State,
pubkey crypto.PubKey,
) (types.Header, types.BlockID, *tmstate.ABCIResponses) {
) (types.Header, types.BlockID, *abci.ResponseFinalizeBlock) {
block := makeBlock(state, state.LastBlockHeight+1, new(types.Commit))
abciResponses := &tmstate.ABCIResponses{
BeginBlock: &abci.ResponseBeginBlock{},
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
abciResponses := &abci.ResponseFinalizeBlock{}
// If the pubkey is new, remove the old and add the new.
_, val := state.NextValidators.GetByIndex(0)
if !bytes.Equal(pubkey.Bytes(), val.PubKey.Bytes()) {
abciResponses.EndBlock = &abci.ResponseEndBlock{
ValidatorUpdates: []abci.ValidatorUpdate{
types.TM2PB.NewValidatorUpdate(val.PubKey, 0),
types.TM2PB.NewValidatorUpdate(pubkey, 10),
},
abciResponses.ValidatorUpdates = []abci.ValidatorUpdate{
types.TM2PB.NewValidatorUpdate(val.PubKey, 0),
types.TM2PB.NewValidatorUpdate(pubkey, 10),
}
}
@@ -178,21 +173,16 @@ func makeHeaderPartsResponsesValPowerChange(
t *testing.T,
state sm.State,
power int64,
) (types.Header, types.BlockID, *tmstate.ABCIResponses) {
) (types.Header, types.BlockID, *abci.ResponseFinalizeBlock) {
block := makeBlock(state, state.LastBlockHeight+1, new(types.Commit))
abciResponses := &tmstate.ABCIResponses{
BeginBlock: &abci.ResponseBeginBlock{},
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil},
}
abciResponses := &abci.ResponseFinalizeBlock{}
// If the pubkey is new, remove the old and add the new.
_, val := state.NextValidators.GetByIndex(0)
if val.VotingPower != power {
abciResponses.EndBlock = &abci.ResponseEndBlock{
ValidatorUpdates: []abci.ValidatorUpdate{
types.TM2PB.NewValidatorUpdate(val.PubKey, power),
},
abciResponses.ValidatorUpdates = []abci.ValidatorUpdate{
types.TM2PB.NewValidatorUpdate(val.PubKey, power),
}
}
@@ -203,12 +193,11 @@ func makeHeaderPartsResponsesParams(
t *testing.T,
state sm.State,
params tmproto.ConsensusParams,
) (types.Header, types.BlockID, *tmstate.ABCIResponses) {
) (types.Header, types.BlockID, *abci.ResponseFinalizeBlock) {
block := makeBlock(state, state.LastBlockHeight+1, new(types.Commit))
abciResponses := &tmstate.ABCIResponses{
BeginBlock: &abci.ResponseBeginBlock{},
EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: &params},
abciResponses := &abci.ResponseFinalizeBlock{
ConsensusParamUpdates: &params,
}
return block.Header, types.BlockID{Hash: block.Hash(), PartSetHeader: types.PartSetHeader{}}, abciResponses
}
@@ -238,49 +227,40 @@ type testApp struct {
CommitVotes []abci.VoteInfo
Misbehavior []abci.Misbehavior
ValidatorUpdates []abci.ValidatorUpdate
AgreedAppData []byte
}
var _ abci.Application = (*testApp)(nil)
func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
return abci.ResponseInfo{}
}
func (app *testApp) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
app.CommitVotes = req.DecidedLastCommit.Votes
app.Misbehavior = req.Misbehavior
txResults := make([]*abci.ExecTxResult, len(req.Txs))
for idx := range req.Txs {
txResults[idx] = &abci.ExecTxResult{
Code: abci.CodeTypeOK,
}
}
func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
app.CommitVotes = req.LastCommitInfo.Votes
app.Misbehavior = req.ByzantineValidators
return abci.ResponseBeginBlock{}
}
func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
return abci.ResponseEndBlock{
return &abci.ResponseFinalizeBlock{
ValidatorUpdates: app.ValidatorUpdates,
ConsensusParamUpdates: &tmproto.ConsensusParams{
Version: &tmproto.VersionParams{
App: 1}}}
App: 1}},
TxResults: txResults,
AgreedAppData: app.AgreedAppData,
}, nil
}
func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
return abci.ResponseDeliverTx{Events: []abci.Event{}}
func (app *testApp) Commit(_ context.Context, _ *abci.RequestCommit) (*abci.ResponseCommit, error) {
return &abci.ResponseCommit{RetainHeight: 1}, nil
}
func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
return abci.ResponseCheckTx{}
}
func (app *testApp) Commit() abci.ResponseCommit {
return abci.ResponseCommit{RetainHeight: 1}
}
func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
return
}
func (app *testApp) ProcessProposal(req abci.RequestProcessProposal) abci.ResponseProcessProposal {
func (app *testApp) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
for _, tx := range req.Txs {
if len(tx) == 0 {
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
}
}
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
}
+1 -1
View File
@@ -16,7 +16,7 @@ type BlockIndexer interface {
Has(height int64) (bool, error)
// Index indexes BeginBlock and EndBlock events for a given block by its height.
Index(types.EventDataNewBlockHeader) error
Index(types.EventDataNewBlock) error
// Search performs a query for block heights that match a given BeginBlock
// and Endblock event search criteria.
+4 -9
View File
@@ -49,11 +49,11 @@ func (idx *BlockerIndexer) Has(height int64) (bool, error) {
// primary key: encode(block.height | height) => encode(height)
// BeginBlock events: encode(eventType.eventAttr|eventValue|height|begin_block) => encode(height)
// EndBlock events: encode(eventType.eventAttr|eventValue|height|end_block) => encode(height)
func (idx *BlockerIndexer) Index(bh types.EventDataNewBlockHeader) error {
func (idx *BlockerIndexer) Index(bh types.EventDataNewBlock) error {
batch := idx.store.NewBatch()
defer batch.Close()
height := bh.Header.Height
height := bh.Block.Height
// 1. index by height
key, err := heightKey(height)
@@ -64,16 +64,11 @@ func (idx *BlockerIndexer) Index(bh types.EventDataNewBlockHeader) error {
return err
}
// 2. index BeginBlock events
if err := idx.indexEvents(batch, bh.ResultBeginBlock.Events, "begin_block", height); err != nil {
// 2. index block events
if err := idx.indexEvents(batch, bh.ResultFinalizeBlock.Events, "begin_block", height); err != nil {
return fmt.Errorf("failed to index BeginBlock events: %w", err)
}
// 3. index EndBlock events
if err := idx.indexEvents(batch, bh.ResultEndBlock.Events, "end_block", height); err != nil {
return fmt.Errorf("failed to index EndBlock events: %w", err)
}
return batch.WriteSync()
}
+6 -14
View File
@@ -18,9 +18,9 @@ func TestBlockIndexer(t *testing.T) {
store := db.NewPrefixDB(db.NewMemDB(), []byte("block_events"))
indexer := blockidxkv.New(store)
require.NoError(t, indexer.Index(types.EventDataNewBlockHeader{
Header: types.Header{Height: 1},
ResultBeginBlock: abci.ResponseBeginBlock{
require.NoError(t, indexer.Index(types.EventDataNewBlock{
Block: &types.Block{Header: types.Header{Height: 1}},
ResultFinalizeBlock: abci.ResponseFinalizeBlock{
Events: []abci.Event{
{
Type: "begin_event",
@@ -32,10 +32,6 @@ func TestBlockIndexer(t *testing.T) {
},
},
},
},
},
ResultEndBlock: abci.ResponseEndBlock{
Events: []abci.Event{
{
Type: "end_event",
Attributes: []abci.EventAttribute{
@@ -56,9 +52,9 @@ func TestBlockIndexer(t *testing.T) {
index = true
}
require.NoError(t, indexer.Index(types.EventDataNewBlockHeader{
Header: types.Header{Height: int64(i)},
ResultBeginBlock: abci.ResponseBeginBlock{
require.NoError(t, indexer.Index(types.EventDataNewBlock{
Block: &types.Block{Header: types.Header{Height: int64(i)}},
ResultFinalizeBlock: abci.ResponseFinalizeBlock{
Events: []abci.Event{
{
Type: "begin_event",
@@ -70,10 +66,6 @@ func TestBlockIndexer(t *testing.T) {
},
},
},
},
},
ResultEndBlock: abci.ResponseEndBlock{
Events: []abci.Event{
{
Type: "end_event",
Attributes: []abci.EventAttribute{
+1 -1
View File
@@ -18,7 +18,7 @@ func (idx *BlockerIndexer) Has(height int64) (bool, error) {
return false, errors.New(`indexing is disabled (set 'tx_index = "kv"' in config)`)
}
func (idx *BlockerIndexer) Index(types.EventDataNewBlockHeader) error {
func (idx *BlockerIndexer) Index(types.EventDataNewBlock) error {
return nil
}
+2 -2
View File
@@ -39,11 +39,11 @@ func (_m *BlockIndexer) Has(height int64) (bool, error) {
}
// Index provides a mock function with given fields: _a0
func (_m *BlockIndexer) Index(_a0 types.EventDataNewBlockHeader) error {
func (_m *BlockIndexer) Index(_a0 types.EventDataNewBlock) error {
ret := _m.Called(_a0)
var r0 error
if rf, ok := ret.Get(0).(func(types.EventDataNewBlockHeader) error); ok {
if rf, ok := ret.Get(0).(func(types.EventDataNewBlock) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
+1 -1
View File
@@ -77,7 +77,7 @@ func (BackportBlockIndexer) Has(height int64) (bool, error) {
// Index indexes block begin and end events for the specified block. It is
// part of the BlockIndexer interface.
func (b BackportBlockIndexer) Index(block types.EventDataNewBlockHeader) error {
func (b BackportBlockIndexer) Index(block types.EventDataNewBlock) error {
return b.psql.IndexBlockEvents(block)
}

Some files were not shown because too many files have changed in this diff Show More