mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-07 08:37:01 +00:00
abci: implement finalize block (#9468)
Adds the `FinalizeBlock` method which replaces `BeginBlock`, `DeliverTx`, and `EndBlock` in a single call.
This commit is contained in:
+60
-48
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user