abci: implement finalize block (#9468)

Adds the `FinalizeBlock` method which replaces `BeginBlock`, `DeliverTx`, and `EndBlock` in a single call.
This commit is contained in:
Callum Waters
2022-11-28 23:12:28 +01:00
committed by GitHub
parent 001cd50fc7
commit c5c2aafad2
142 changed files with 6717 additions and 8420 deletions
+60 -48
View File
@@ -2,6 +2,7 @@ package app
import (
"bytes"
"context"
"encoding/base64"
"errors"
"fmt"
@@ -10,7 +11,7 @@ import (
"strconv"
"time"
"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/libs/log"
"github.com/tendermint/tendermint/version"
@@ -76,7 +77,8 @@ type Config struct {
PrepareProposalDelay time.Duration `toml:"prepare_proposal_delay"`
ProcessProposalDelay time.Duration `toml:"process_proposal_delay"`
CheckTxDelay time.Duration `toml:"check_tx_delay"`
// TODO: add vote extension and finalize block delays once completed (@cmwaters)
FinalizeBlockDelay time.Duration `toml:"finalize_block_delay"`
// TODO: add vote extension delays once completed (@cmwaters)
}
func DefaultConfig(dir string) *Config {
@@ -106,17 +108,17 @@ func NewApplication(cfg *Config) (abci.Application, error) {
}
// Info implements ABCI.
func (app *Application) Info(req abci.RequestInfo) abci.ResponseInfo {
return abci.ResponseInfo{
func (app *Application) Info(_ context.Context, req *abci.RequestInfo) (*abci.ResponseInfo, error) {
return &abci.ResponseInfo{
Version: version.ABCIVersion,
AppVersion: appVersion,
LastBlockHeight: int64(app.state.Height),
LastBlockAppHash: app.state.Hash,
}
}, nil
}
// Info implements ABCI.
func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
func (app *Application) InitChain(_ context.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
var err error
app.state.initialHeight = uint64(req.InitialHeight)
if len(req.AppStateBytes) > 0 {
@@ -125,51 +127,59 @@ func (app *Application) InitChain(req abci.RequestInitChain) abci.ResponseInitCh
panic(err)
}
}
resp := abci.ResponseInitChain{
resp := &abci.ResponseInitChain{
AppHash: app.state.Hash,
}
if resp.Validators, err = app.validatorUpdates(0); err != nil {
panic(err)
}
return resp
return resp, nil
}
// CheckTx implements ABCI.
func (app *Application) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
func (app *Application) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) {
_, _, err := parseTx(req.Tx)
if err != nil {
return abci.ResponseCheckTx{
Code: code.CodeTypeEncodingError,
return &abci.ResponseCheckTx{
Code: kvstore.CodeTypeEncodingError,
Log: err.Error(),
}
}, nil
}
if app.cfg.CheckTxDelay != 0 {
time.Sleep(app.cfg.CheckTxDelay)
}
return abci.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
return &abci.ResponseCheckTx{Code: kvstore.CodeTypeOK, GasWanted: 1}, nil
}
// DeliverTx implements ABCI.
func (app *Application) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
key, value, err := parseTx(req.Tx)
if err != nil {
panic(err) // shouldn't happen since we verified it in CheckTx
// FinalizeBlock implements ABCI.
func (app *Application) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
var txs = make([]*abci.ExecTxResult, len(req.Txs))
for i, tx := range req.Txs {
key, value, err := parseTx(tx)
if err != nil {
panic(err) // shouldn't happen since we verified it in CheckTx
}
app.state.Set(key, value)
txs[i] = &abci.ExecTxResult{Code: kvstore.CodeTypeOK}
}
app.state.Set(key, value)
return abci.ResponseDeliverTx{Code: code.CodeTypeOK}
}
// EndBlock implements ABCI.
func (app *Application) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
valUpdates, err := app.validatorUpdates(uint64(req.Height))
if err != nil {
panic(err)
}
return abci.ResponseEndBlock{
if app.cfg.FinalizeBlockDelay != 0 {
time.Sleep(app.cfg.FinalizeBlockDelay)
}
return &abci.ResponseFinalizeBlock{
TxResults: txs,
ValidatorUpdates: valUpdates,
AgreedAppData: app.state.Finalize(),
Events: []abci.Event{
{
Type: "val_updates",
@@ -185,12 +195,12 @@ func (app *Application) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock
},
},
},
}
}, nil
}
// Commit implements ABCI.
func (app *Application) Commit() abci.ResponseCommit {
height, hash, err := app.state.Commit()
func (app *Application) Commit(_ context.Context, _ *abci.RequestCommit) (*abci.ResponseCommit, error) {
height, err := app.state.Commit()
if err != nil {
panic(err)
}
@@ -199,57 +209,60 @@ func (app *Application) Commit() abci.ResponseCommit {
if err != nil {
panic(err)
}
app.logger.Info("Created state sync snapshot", "height", snapshot.Height)
app.logger.Info("created state sync snapshot", "height", snapshot.Height)
err = app.snapshots.Prune(maxSnapshotCount)
if err != nil {
app.logger.Error("failed to prune snapshots", "err", err)
}
}
retainHeight := int64(0)
if app.cfg.RetainBlocks > 0 {
retainHeight = int64(height - app.cfg.RetainBlocks + 1)
}
return abci.ResponseCommit{
Data: hash,
return &abci.ResponseCommit{
RetainHeight: retainHeight,
}
}, nil
}
// Query implements ABCI.
func (app *Application) Query(req abci.RequestQuery) abci.ResponseQuery {
return abci.ResponseQuery{
func (app *Application) Query(_ context.Context, req *abci.RequestQuery) (*abci.ResponseQuery, error) {
return &abci.ResponseQuery{
Height: int64(app.state.Height),
Key: req.Data,
Value: []byte(app.state.Get(string(req.Data))),
}
}, nil
}
// ListSnapshots implements ABCI.
func (app *Application) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots {
func (app *Application) ListSnapshots(_ context.Context, req *abci.RequestListSnapshots) (*abci.ResponseListSnapshots, error) {
snapshots, err := app.snapshots.List()
if err != nil {
panic(err)
}
return abci.ResponseListSnapshots{Snapshots: snapshots}
return &abci.ResponseListSnapshots{Snapshots: snapshots}, nil
}
// LoadSnapshotChunk implements ABCI.
func (app *Application) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk {
func (app *Application) LoadSnapshotChunk(_ context.Context, req *abci.RequestLoadSnapshotChunk) (*abci.ResponseLoadSnapshotChunk, error) {
chunk, err := app.snapshots.LoadChunk(req.Height, req.Format, req.Chunk)
if err != nil {
panic(err)
}
return abci.ResponseLoadSnapshotChunk{Chunk: chunk}
return &abci.ResponseLoadSnapshotChunk{Chunk: chunk}, nil
}
// OfferSnapshot implements ABCI.
func (app *Application) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot {
func (app *Application) OfferSnapshot(_ context.Context, req *abci.RequestOfferSnapshot) (*abci.ResponseOfferSnapshot, error) {
if app.restoreSnapshot != nil {
panic("A snapshot is already being restored")
}
app.restoreSnapshot = req.Snapshot
app.restoreChunks = [][]byte{}
return abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}
return &abci.ResponseOfferSnapshot{Result: abci.ResponseOfferSnapshot_ACCEPT}, nil
}
// ApplySnapshotChunk implements ABCI.
func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk {
func (app *Application) ApplySnapshotChunk(_ context.Context, req *abci.RequestApplySnapshotChunk) (*abci.ResponseApplySnapshotChunk, error) {
if app.restoreSnapshot == nil {
panic("No restore in progress")
}
@@ -266,12 +279,11 @@ func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) a
app.restoreSnapshot = nil
app.restoreChunks = nil
}
return abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}
return &abci.ResponseApplySnapshotChunk{Result: abci.ResponseApplySnapshotChunk_ACCEPT}, nil
}
func (app *Application) PrepareProposal(
req abci.RequestPrepareProposal,
) abci.ResponsePrepareProposal {
_ context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) {
txs := make([][]byte, 0, len(req.Txs))
var totalBytes int64
for _, tx := range req.Txs {
@@ -286,16 +298,16 @@ func (app *Application) PrepareProposal(
time.Sleep(app.cfg.PrepareProposalDelay)
}
return abci.ResponsePrepareProposal{Txs: txs}
return &abci.ResponsePrepareProposal{Txs: txs}, nil
}
// ProcessProposal implements part of the Application interface.
// It accepts any proposal that does not contain a malformed transaction.
func (app *Application) ProcessProposal(req abci.RequestProcessProposal) abci.ResponseProcessProposal {
func (app *Application) ProcessProposal(_ context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
for _, tx := range req.Txs {
_, _, err := parseTx(tx)
if err != nil {
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_REJECT}, nil
}
}
@@ -303,7 +315,7 @@ func (app *Application) ProcessProposal(req abci.RequestProcessProposal) abci.Re
time.Sleep(app.cfg.ProcessProposalDelay)
}
return abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
}
func (app *Application) Rollback() error {
+26 -2
View File
@@ -14,6 +14,9 @@ import (
const (
snapshotChunkSize = 1e6
// Keep only the most recent 10 snapshots. Older snapshots are pruned
maxSnapshotCount = 10
)
// SnapshotStore stores state sync snapshots. Snapshots are stored simply as
@@ -28,7 +31,7 @@ type SnapshotStore struct {
// NewSnapshotStore creates a new snapshot store.
func NewSnapshotStore(dir string) (*SnapshotStore, error) {
store := &SnapshotStore{dir: dir}
if err := os.MkdirAll(dir, 0o755); err != nil {
if err := os.MkdirAll(dir, 0755); err != nil {
return nil, err
}
if err := store.loadMetadata(); err != nil {
@@ -88,7 +91,7 @@ func (s *SnapshotStore) Create(state *State) (abci.Snapshot, error) {
snapshot := abci.Snapshot{
Height: state.Height,
Format: 1,
Hash: hashItems(state.Values),
Hash: hashItems(state.Values, state.Height),
Chunks: byteChunks(bz),
}
err = os.WriteFile(filepath.Join(s.dir, fmt.Sprintf("%v.json", state.Height)), bz, 0o644) //nolint:gosec
@@ -103,6 +106,27 @@ func (s *SnapshotStore) Create(state *State) (abci.Snapshot, error) {
return snapshot, nil
}
// Prune removes old snapshots ensuring only the most recent n snapshots remain
func (s *SnapshotStore) Prune(n int) error {
s.Lock()
defer s.Unlock()
// snapshots are appended to the metadata struct, hence pruning removes from
// the front of the array
i := 0
for ; i < len(s.metadata)-n; i++ {
h := s.metadata[i].Height
if err := os.Remove(filepath.Join(s.dir, fmt.Sprintf("%v.json", h))); err != nil {
return err
}
}
// update metadata by removing the deleted snapshots
pruned := make([]abci.Snapshot, len(s.metadata[i:]))
copy(pruned, s.metadata[i:])
s.metadata = pruned
return nil
}
// List lists available snapshots.
func (s *SnapshotStore) List() ([]*abci.Snapshot, error) {
s.RLock()
+19 -8
View File
@@ -2,6 +2,7 @@ package app
import (
"crypto/sha256"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
@@ -39,7 +40,7 @@ func NewState(dir string, persistInterval uint64) (*State, error) {
previousFile: filepath.Join(dir, prevStateFileName),
persistInterval: persistInterval,
}
state.Hash = hashItems(state.Values)
state.Hash = hashItems(state.Values, state.Height)
err := state.load()
switch {
case errors.Is(err, os.ErrNotExist):
@@ -115,7 +116,7 @@ func (s *State) Import(height uint64, jsonBytes []byte) error {
}
s.Height = height
s.Values = values
s.Hash = hashItems(values)
s.Hash = hashItems(values, height)
return s.save()
}
@@ -137,11 +138,10 @@ func (s *State) Set(key, value string) {
}
}
// Commit commits the current state.
func (s *State) Commit() (uint64, []byte, error) {
// Finalize is called after applying a block, updating the height and returning the new app_hash
func (s *State) Finalize() []byte {
s.Lock()
defer s.Unlock()
s.Hash = hashItems(s.Values)
switch {
case s.Height > 0:
s.Height++
@@ -150,13 +150,21 @@ func (s *State) Commit() (uint64, []byte, error) {
default:
s.Height = 1
}
s.Hash = hashItems(s.Values, s.Height)
return s.Hash
}
// Commit commits the current state.
func (s *State) Commit() (uint64, error) {
s.Lock()
defer s.Unlock()
if s.persistInterval > 0 && s.Height%s.persistInterval == 0 {
err := s.save()
if err != nil {
return 0, nil, err
return 0, err
}
}
return s.Height, s.Hash, nil
return s.Height, nil
}
func (s *State) Rollback() error {
@@ -172,7 +180,7 @@ func (s *State) Rollback() error {
}
// hashItems hashes a set of key/value items.
func hashItems(items map[string]string) []byte {
func hashItems(items map[string]string, height uint64) []byte {
keys := make([]string, 0, len(items))
for key := range items {
keys = append(keys, key)
@@ -180,6 +188,9 @@ func hashItems(items map[string]string) []byte {
sort.Strings(keys)
hasher := sha256.New()
var b [8]byte
binary.BigEndian.PutUint64(b[:], height)
_, _ = hasher.Write(b[:])
for _, key := range keys {
_, _ = hasher.Write([]byte(key))
_, _ = hasher.Write([]byte{0})
+26 -37
View File
@@ -1,12 +1,13 @@
package app
import (
"context"
"sync"
abci "github.com/tendermint/tendermint/abci/types"
)
// SyncApplication wraps an Application, managing its own synchronization. This
// SyncApplication wraps the e2e Application, managing its own synchronization. This
// allows it to be called from an unsynchronized local client, as it is
// implemented in a thread-safe way.
type SyncApplication struct {
@@ -26,86 +27,74 @@ func NewSyncApplication(cfg *Config) (abci.Application, error) {
}, nil
}
func (app *SyncApplication) Info(req abci.RequestInfo) abci.ResponseInfo {
func (app *SyncApplication) Info(ctx context.Context, req *abci.RequestInfo) (*abci.ResponseInfo, error) {
app.mtx.RLock()
defer app.mtx.RUnlock()
return app.app.Info(req)
return app.app.Info(ctx, req)
}
func (app *SyncApplication) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
func (app *SyncApplication) InitChain(ctx context.Context, req *abci.RequestInitChain) (*abci.ResponseInitChain, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.app.InitChain(req)
return app.app.InitChain(ctx, req)
}
func (app *SyncApplication) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx {
func (app *SyncApplication) CheckTx(ctx context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) {
app.mtx.RLock()
defer app.mtx.RUnlock()
return app.app.CheckTx(req)
return app.app.CheckTx(ctx, req)
}
func (app *SyncApplication) PrepareProposal(req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
func (app *SyncApplication) PrepareProposal(ctx context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) {
// app.app.PrepareProposal does not modify state
app.mtx.RLock()
defer app.mtx.RUnlock()
return app.app.PrepareProposal(req)
return app.app.PrepareProposal(ctx, req)
}
func (app *SyncApplication) ProcessProposal(req abci.RequestProcessProposal) abci.ResponseProcessProposal {
func (app *SyncApplication) ProcessProposal(ctx context.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
// app.app.ProcessProposal does not modify state
app.mtx.RLock()
defer app.mtx.RUnlock()
return app.app.ProcessProposal(req)
return app.app.ProcessProposal(ctx, req)
}
func (app *SyncApplication) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
func (app *SyncApplication) FinalizeBlock(ctx context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.app.DeliverTx(req)
return app.app.FinalizeBlock(ctx, req)
}
func (app *SyncApplication) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
func (app *SyncApplication) Commit(ctx context.Context, req *abci.RequestCommit) (*abci.ResponseCommit, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.app.BeginBlock(req)
return app.app.Commit(ctx, req)
}
func (app *SyncApplication) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.app.EndBlock(req)
}
func (app *SyncApplication) Commit() abci.ResponseCommit {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.app.Commit()
}
func (app *SyncApplication) Query(req abci.RequestQuery) abci.ResponseQuery {
func (app *SyncApplication) Query(ctx context.Context, req *abci.RequestQuery) (*abci.ResponseQuery, error) {
app.mtx.RLock()
defer app.mtx.RUnlock()
return app.app.Query(req)
return app.app.Query(ctx, req)
}
func (app *SyncApplication) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) abci.ResponseApplySnapshotChunk {
func (app *SyncApplication) ApplySnapshotChunk(ctx context.Context, req *abci.RequestApplySnapshotChunk) (*abci.ResponseApplySnapshotChunk, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.app.ApplySnapshotChunk(req)
return app.app.ApplySnapshotChunk(ctx, req)
}
func (app *SyncApplication) ListSnapshots(req abci.RequestListSnapshots) abci.ResponseListSnapshots {
func (app *SyncApplication) ListSnapshots(ctx context.Context, req *abci.RequestListSnapshots) (*abci.ResponseListSnapshots, error) {
// Calls app.snapshots.List(), which is thread-safe.
return app.app.ListSnapshots(req)
return app.app.ListSnapshots(ctx, req)
}
func (app *SyncApplication) LoadSnapshotChunk(req abci.RequestLoadSnapshotChunk) abci.ResponseLoadSnapshotChunk {
func (app *SyncApplication) LoadSnapshotChunk(ctx context.Context, req *abci.RequestLoadSnapshotChunk) (*abci.ResponseLoadSnapshotChunk, error) {
// Calls app.snapshots.LoadChunk, which is thread-safe.
return app.app.LoadSnapshotChunk(req)
return app.app.LoadSnapshotChunk(ctx, req)
}
func (app *SyncApplication) OfferSnapshot(req abci.RequestOfferSnapshot) abci.ResponseOfferSnapshot {
func (app *SyncApplication) OfferSnapshot(ctx context.Context, req *abci.RequestOfferSnapshot) (*abci.ResponseOfferSnapshot, error) {
app.mtx.Lock()
defer app.mtx.Unlock()
return app.app.OfferSnapshot(req)
return app.app.OfferSnapshot(ctx, req)
}
+3 -2
View File
@@ -15,11 +15,12 @@ import (
func TestGenerator(t *testing.T) {
manifests, err := Generate(rand.New(rand.NewSource(randomSeed)))
require.NoError(t, err)
require.True(t, len(manifests) >= 24, "insufficient combinations %d", len(manifests))
for idx, m := range manifests {
t.Run(fmt.Sprintf("Case%04d", idx), func(t *testing.T) {
_, err := e2e.NewTestnetFromManifest(m, filepath.Join(t.TempDir(), fmt.Sprintf("Case%04d", idx)))
infra, err := e2e.NewDockerInfrastructureData(m)
require.NoError(t, err)
_, err = e2e.NewTestnetFromManifest(m, filepath.Join(t.TempDir(), fmt.Sprintf("Case%04d", idx)), infra)
require.NoError(t, err)
})
}
+1 -1
View File
@@ -79,7 +79,7 @@ func NewCLI() *CLI {
return fmt.Errorf("unknown infrastructure type '%s'", inft)
}
testnet, err := e2e.LoadTestnet(m, file, ifd)
testnet, err := e2e.LoadTestnet(file, ifd)
if err != nil {
return fmt.Errorf("loading testnet: %s", err)
}
+14 -7
View File
@@ -42,15 +42,22 @@ func TestApp_Hash(t *testing.T) {
require.NoError(t, err)
require.NotEmpty(t, info.Response.LastBlockAppHash, "expected app to return app hash")
block, err := client.Block(ctx, nil)
require.NoError(t, err)
require.EqualValues(t, info.Response.LastBlockAppHash, block.Block.AppHash,
"app hash does not match last block's app hash")
// In next-block execution, the app hash is stored in the next block
requestedHeight := info.Response.LastBlockHeight + 1
status, err := client.Status(ctx)
require.Eventually(t, func() bool {
status, err := client.Status(ctx)
require.NoError(t, err)
require.NotZero(t, status.SyncInfo.LatestBlockHeight)
return status.SyncInfo.LatestBlockHeight >= requestedHeight
}, 5*time.Second, 500*time.Millisecond)
block, err := client.Block(ctx, &requestedHeight)
require.NoError(t, err)
require.EqualValues(t, info.Response.LastBlockAppHash, status.SyncInfo.LatestAppHash,
"app hash does not match node status")
require.Equal(t,
fmt.Sprintf("%x", info.Response.LastBlockAppHash),
fmt.Sprintf("%x", block.Block.AppHash.Bytes()),
"app hash does not match last block's app hash")
})
}
+1 -1
View File
@@ -84,7 +84,7 @@ func loadTestnet(t *testing.T) e2e.Testnet {
ifd, err := e2e.NewDockerInfrastructureData(m)
require.NoError(t, err)
testnet, err := e2e.LoadTestnet(m, manifestFile, ifd)
testnet, err := e2e.LoadTestnet(manifestFile, ifd)
require.NoError(t, err)
testnetCache[manifestFile] = *testnet
return *testnet
+1 -1
View File
@@ -11,7 +11,7 @@ import (
var mempool mempl.Mempool
func init() {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
appConnMem, _ := cc.NewABCIClient()
err := appConnMem.Start()
+1 -1
View File
@@ -13,7 +13,7 @@ import (
var mempool mempl.Mempool
func init() {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
cc := proxy.NewLocalClientCreator(app)
appConnMem, _ := cc.NewABCIClient()
err := appConnMem.Start()
+1 -1
View File
@@ -15,7 +15,7 @@ import (
)
func FuzzMempool(f *testing.F) {
app := kvstore.NewApplication()
app := kvstore.NewInMemoryApplication()
logger := log.NewNopLogger()
mtx := new(tmsync.Mutex)
conn := abciclient.NewLocalClient(mtx, app)