Compare commits

...

15 Commits

Author SHA1 Message Date
William Banfield
1f2fb500ae wip 2021-07-28 18:13:34 -04:00
William Banfield
b86acbbeab libs/net: fix GetFreePort to make port immediately usable 2021-07-28 15:22:53 -04:00
William Banfield
9e41414a53 light: replace homegrown mock with mockery (#6735)
This pull request removes the homegrown mocks in `light/provider/mock` in favor of mockery mocks.

Adds a simple benchmark only mock to avoid the overhead of `reflection` that `mockery` incurs.

part of #5274
2021-07-28 16:12:11 +00:00
Callum Waters
6ff4c3139c blockchain: rename to blocksync service (#6755) 2021-07-28 17:25:42 +02:00
JayT106
e87b0391cb cli/indexer: Reindex events (#6676) 2021-07-28 00:04:54 +02:00
Aleksandr Bezobchuk
4f73748bc8 mempool v1: tweak broadcastTxRoutine (#6771) 2021-07-27 15:34:06 -04:00
Callum Waters
9a3861fb82 light: wait for tendermint node to start before running example test (#6744) 2021-07-27 18:59:34 +02:00
William Banfield
44ac57489f abci: add changelog entry for mempool_error field (#6770)
Follow up from PR: #6740
2021-07-27 13:33:20 +00:00
Marko
76376e3161 avoid issues already labeled (#6767) 2021-07-27 06:17:32 +00:00
William Banfield
dd97ac6e1c test/fuzz: add mechanism to reproduce found fuzz errors (#6768)
* test/fuzz: add test to reproduce found fuzz errors
2021-07-26 17:30:43 -04:00
William Banfield
a751eee7f2 p2p: add test for pqueue dequeue full error (#6760)
This adds a test for closing the `pqueue` while the `pqueue` contains data that has not yet been dequeued.

This issue was found while debugging #6705 

This test will fail until @cmwaters fix for this condition is merged.
2021-07-26 19:22:32 +00:00
William Banfield
c5dc3b267f test/fuzz: add test to reproduce found fuzz errors (#6757)
This change does two things:
1. It fixes the json fuzzer to account for receiving array results. Arrays are returned by the rpc server when the input data is an array.
2. Adds a `fuzz_test.go` file and corresponding `testdata` directory containing the failing test case.

This seems like a reasonable way to add and track previous crash issues in our fuzz test cases. The upcoming stdlib go fuzz tool does effectively this automatically.
2021-07-26 14:58:51 +00:00
dependabot[bot]
93f462ef86 build(deps): Bump codecov/codecov-action from 2.0.1 to 2.0.2 (#6764)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 2.0.1 to 2.0.2.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v2.0.1...v2.0.2)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Marko <marbar3778@yahoo.com>
2021-07-26 14:17:02 +00:00
Callum Waters
91e277d7b7 enable pex reactor depending on config param (#6762) 2021-07-26 15:22:12 +02:00
Callum Waters
a341a626e0 p2p: avoid blocking on the dequeCh (#6765) 2021-07-26 09:09:07 -04:00
110 changed files with 2247 additions and 999 deletions

View File

@@ -121,7 +121,7 @@ jobs:
- run: |
cat ./*profile.out | grep -v "mode: atomic" >> coverage.txt
if: env.GIT_DIFF
- uses: codecov/codecov-action@v2.0.1
- uses: codecov/codecov-action@v2.0.2
with:
file: ./coverage.txt
if: env.GIT_DIFF

View File

@@ -23,9 +23,14 @@ jobs:
working-directory: test/fuzz
run: go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-build
- name: Fuzz mempool
- name: Fuzz mempool-v1
working-directory: test/fuzz
run: timeout -s SIGINT --preserve-status 10m make fuzz-mempool
run: timeout -s SIGINT --preserve-status 10m make fuzz-mempool-v1
continue-on-error: true
- name: Fuzz mempool-v0
working-directory: test/fuzz
run: timeout -s SIGINT --preserve-status 10m make fuzz-mempool-v0
continue-on-error: true
- name: Fuzz p2p-addrbook

View File

@@ -14,6 +14,7 @@ jobs:
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions."
days-before-stale: -1
days-before-close: -1
days-before-pr-stale: 10
days-before-pr-close: 4
exempt-pr-labels: "S:wip"

View File

@@ -30,6 +30,7 @@ Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermi
- [ABCI] \#5447 Remove `SetOption` method from `ABCI.Client` interface
- [ABCI] \#5447 Reset `Oneof` indexes for `Request` and `Response`.
- [ABCI] \#5818 Use protoio for msg length delimitation. Migrates from int64 to uint64 length delimiters.
- [ABCI] \#3546 Add `mempool_error` field to `ResponseCheckTx`. This field will contain an error string if Tendermint encountered an error while adding a transaction to the mempool. (@williambanfield)
- [Version] \#6494 `TMCoreSemVer` has been renamed to `TMVersion`.
- It is not required any longer to set ldflags to set version strings
- [abci/counter] \#6684 Delete counter example app
@@ -71,6 +72,8 @@ Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermi
- [crypto/sr25519] \#6526 Do not re-execute the Ed25519-style key derivation step when doing signing and verification. The derivation is now done once and only once. This breaks `sr25519.GenPrivKeyFromSecret` output compatibility. (@Yawning)
- [types] \#6627 Move `NodeKey` to types to make the type public.
- [config] \#6627 Extend `config` to contain methods `LoadNodeKeyID` and `LoadorGenNodeKeyID`
- [blocksync] \#6755 Rename `FastSync` and `Blockchain` package to `BlockSync`
(@cmwaters)
- Blockchain Protocol
@@ -81,6 +84,7 @@ Friendly reminder: We have a [bug bounty program](https://hackerone.com/tendermi
- Tooling
- [tools] \#6498 Set OS home dir to instead of the hardcoded PATH. (@JayT106)
- [cli/indexer] \#6676 Reindex events command line tooling. (@JayT106)
### FEATURES

View File

@@ -17,7 +17,7 @@ This guide provides instructions for upgrading to specific versions of Tendermin
### Config Changes
* `fast_sync = "v1"` is no longer supported. Please use `v2` instead.
* `fast_sync = "v1"` and `fast_sync = "v2"` are no longer supported. Please use `v0` instead.
* All config parameters are now hyphen-case (also known as kebab-case) instead of snake_case. Before restarting the node make sure
you have updated all the variables in your `config.toml` file.
@@ -29,10 +29,11 @@ This guide provides instructions for upgrading to specific versions of Tendermin
`Seeds`. Bootstrap peers are connected with on startup if needed for peer discovery. Unlike
persistent peers, there's no gaurantee that the node will remain connected with these peers.
- configuration values starting with `priv-validator-` have moved to the new
* configuration values starting with `priv-validator-` have moved to the new
`priv-validator` section, without the `priv-validator-` prefix.
* Fast Sync v2 has been deprecated, please use v0 to sync a node.
* The fast sync process as well as the blockchain package and service has all
been renamed to block sync
### CLI Changes

View File

@@ -1827,17 +1827,19 @@ func (m *ResponseBeginBlock) GetEvents() []Event {
}
type ResponseCheckTx struct {
Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"`
Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"`
GasWanted int64 `protobuf:"varint,5,opt,name=gas_wanted,proto3" json:"gas_wanted,omitempty"`
GasUsed int64 `protobuf:"varint,6,opt,name=gas_used,proto3" json:"gas_used,omitempty"`
Events []Event `protobuf:"bytes,7,rep,name=events,proto3" json:"events,omitempty"`
Codespace string `protobuf:"bytes,8,opt,name=codespace,proto3" json:"codespace,omitempty"`
Sender string `protobuf:"bytes,9,opt,name=sender,proto3" json:"sender,omitempty"`
Priority int64 `protobuf:"varint,10,opt,name=priority,proto3" json:"priority,omitempty"`
MempoolError string `protobuf:"bytes,11,opt,name=mempool_error,json=mempoolError,proto3" json:"mempool_error,omitempty"`
Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"`
Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"`
GasWanted int64 `protobuf:"varint,5,opt,name=gas_wanted,proto3" json:"gas_wanted,omitempty"`
GasUsed int64 `protobuf:"varint,6,opt,name=gas_used,proto3" json:"gas_used,omitempty"`
Events []Event `protobuf:"bytes,7,rep,name=events,proto3" json:"events,omitempty"`
Codespace string `protobuf:"bytes,8,opt,name=codespace,proto3" json:"codespace,omitempty"`
Sender string `protobuf:"bytes,9,opt,name=sender,proto3" json:"sender,omitempty"`
Priority int64 `protobuf:"varint,10,opt,name=priority,proto3" json:"priority,omitempty"`
// mempool_error is set by Tendermint.
// ABCI applictions creating a ResponseCheckTX should not set mempool_error.
MempoolError string `protobuf:"bytes,11,opt,name=mempool_error,json=mempoolError,proto3" json:"mempool_error,omitempty"`
}
func (m *ResponseCheckTx) Reset() { *m = ResponseCheckTx{} }

View File

@@ -0,0 +1,251 @@
package commands
import (
"errors"
"fmt"
"strings"
"github.com/spf13/cobra"
tmdb "github.com/tendermint/tm-db"
abcitypes "github.com/tendermint/tendermint/abci/types"
tmcfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/internal/libs/progressbar"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/state/indexer"
"github.com/tendermint/tendermint/state/indexer/sink/kv"
"github.com/tendermint/tendermint/state/indexer/sink/psql"
"github.com/tendermint/tendermint/store"
"github.com/tendermint/tendermint/types"
)
const (
reindexFailed = "event re-index failed: "
)
// ReIndexEventCmd allows re-index the event by given block height interval
var ReIndexEventCmd = &cobra.Command{
Use: "reindex-event",
Short: "reindex events to the event store backends",
Long: `
reindex-event is an offline tooling to re-index block and tx events to the eventsinks,
you can run this command when the event store backend dropped/disconnected or you want to replace the backend.
The default start-height is 0, meaning the tooling will start reindex from the base block height(inclusive); and the
default end-height is 0, meaning the tooling will reindex until the latest block height(inclusive). User can omits
either or both arguments.
`,
Example: `
tendermint reindex-event
tendermint reindex-event --start-height 2
tendermint reindex-event --end-height 10
tendermint reindex-event --start-height 2 --end-height 10
`,
Run: func(cmd *cobra.Command, args []string) {
bs, ss, err := loadStateAndBlockStore(config)
if err != nil {
fmt.Println(reindexFailed, err)
return
}
if err := checkValidHeight(bs); err != nil {
fmt.Println(reindexFailed, err)
return
}
es, err := loadEventSinks(config)
if err != nil {
fmt.Println(reindexFailed, err)
return
}
if err = eventReIndex(cmd, es, bs, ss); err != nil {
fmt.Println(reindexFailed, err)
return
}
fmt.Println("event re-index finished")
},
}
var (
startHeight int64
endHeight int64
)
func init() {
ReIndexEventCmd.Flags().Int64Var(&startHeight, "start-height", 0, "the block height would like to start for re-index")
ReIndexEventCmd.Flags().Int64Var(&endHeight, "end-height", 0, "the block height would like to finish for re-index")
}
func loadEventSinks(cfg *tmcfg.Config) ([]indexer.EventSink, error) {
// Check duplicated sinks.
sinks := map[string]bool{}
for _, s := range cfg.TxIndex.Indexer {
sl := strings.ToLower(s)
if sinks[sl] {
return nil, errors.New("found duplicated sinks, please check the tx-index section in the config.toml")
}
sinks[sl] = true
}
eventSinks := []indexer.EventSink{}
for k := range sinks {
switch k {
case string(indexer.NULL):
return nil, errors.New("found null event sink, please check the tx-index section in the config.toml")
case string(indexer.KV):
store, err := tmcfg.DefaultDBProvider(&tmcfg.DBContext{ID: "tx_index", Config: cfg})
if err != nil {
return nil, err
}
eventSinks = append(eventSinks, kv.NewEventSink(store))
case string(indexer.PSQL):
conn := cfg.TxIndex.PsqlConn
if conn == "" {
return nil, errors.New("the psql connection settings cannot be empty")
}
es, _, err := psql.NewEventSink(conn, chainID)
if err != nil {
return nil, err
}
eventSinks = append(eventSinks, es)
default:
return nil, errors.New("unsupported event sink type")
}
}
if len(eventSinks) == 0 {
return nil, errors.New("no proper event sink can do event re-indexing," +
" please check the tx-index section in the config.toml")
}
if !indexer.IndexingEnabled(eventSinks) {
return nil, fmt.Errorf("no event sink has been enabled")
}
return eventSinks, nil
}
func loadStateAndBlockStore(cfg *tmcfg.Config) (*store.BlockStore, state.Store, error) {
dbType := tmdb.BackendType(cfg.DBBackend)
// Get BlockStore
blockStoreDB, err := tmdb.NewDB("blockstore", dbType, cfg.DBDir())
if err != nil {
return nil, nil, err
}
blockStore := store.NewBlockStore(blockStoreDB)
// Get StateStore
stateDB, err := tmdb.NewDB("state", dbType, cfg.DBDir())
if err != nil {
return nil, nil, err
}
stateStore := state.NewStore(stateDB)
return blockStore, stateStore, nil
}
func eventReIndex(cmd *cobra.Command, es []indexer.EventSink, bs state.BlockStore, ss state.Store) error {
var bar progressbar.Bar
bar.NewOption(startHeight-1, endHeight)
fmt.Println("start re-indexing events:")
defer bar.Finish()
for i := startHeight; i <= endHeight; i++ {
select {
case <-cmd.Context().Done():
return fmt.Errorf("event re-index terminated at height %d: %w", i, cmd.Context().Err())
default:
b := bs.LoadBlock(i)
if b == nil {
return fmt.Errorf("not able to load block at height %d from the blockstore", i)
}
r, err := ss.LoadABCIResponses(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,
}
var batch *indexer.Batch
if e.NumTxs > 0 {
batch = indexer.NewBatch(e.NumTxs)
for i, tx := range b.Data.Txs {
tr := abcitypes.TxResult{
Height: b.Height,
Index: uint32(i),
Tx: tx,
Result: *(r.DeliverTxs[i]),
}
_ = batch.Add(&tr)
}
}
for _, sink := range es {
if err := sink.IndexBlockEvents(e); err != nil {
return fmt.Errorf("block event re-index at height %d failed: %w", i, err)
}
if batch != nil {
if err := sink.IndexTxEvents(batch.Ops); err != nil {
return fmt.Errorf("tx event re-index at height %d failed: %w", i, err)
}
}
}
}
bar.Play(i)
}
return nil
}
func checkValidHeight(bs state.BlockStore) error {
base := bs.Base()
if startHeight == 0 {
startHeight = base
fmt.Printf("set the start block height to the base height of the blockstore %d \n", base)
}
if startHeight < base {
return fmt.Errorf("%s (requested start height: %d, base height: %d)", ctypes.ErrHeightNotAvailable, startHeight, base)
}
height := bs.Height()
if startHeight > height {
return fmt.Errorf(
"%s (requested start height: %d, store height: %d)", ctypes.ErrHeightNotAvailable, startHeight, height)
}
if endHeight == 0 || endHeight > height {
endHeight = height
fmt.Printf("set the end block height to the latest height of the blockstore %d \n", height)
}
if endHeight < base {
return fmt.Errorf(
"%s (requested end height: %d, base height: %d)", ctypes.ErrHeightNotAvailable, endHeight, base)
}
if endHeight < startHeight {
return fmt.Errorf(
"%s (requested the end height: %d is less than the start height: %d)",
ctypes.ErrInvalidRequest, startHeight, endHeight)
}
return nil
}

View File

@@ -0,0 +1,171 @@
package commands
import (
"context"
"errors"
"testing"
"github.com/spf13/cobra"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
abcitypes "github.com/tendermint/tendermint/abci/types"
tmcfg "github.com/tendermint/tendermint/config"
prototmstate "github.com/tendermint/tendermint/proto/tendermint/state"
"github.com/tendermint/tendermint/state/indexer"
"github.com/tendermint/tendermint/state/mocks"
"github.com/tendermint/tendermint/types"
)
const (
height int64 = 10
base int64 = 2
)
func setupReIndexEventCmd() *cobra.Command {
reIndexEventCmd := &cobra.Command{
Use: ReIndexEventCmd.Use,
Run: func(cmd *cobra.Command, args []string) {},
}
_ = reIndexEventCmd.ExecuteContext(context.Background())
return reIndexEventCmd
}
func TestReIndexEventCheckHeight(t *testing.T) {
mockBlockStore := &mocks.BlockStore{}
mockBlockStore.
On("Base").Return(base).
On("Height").Return(height)
testCases := []struct {
startHeight int64
endHeight int64
validHeight bool
}{
{0, 0, true},
{0, base, true},
{0, base - 1, false},
{0, height, true},
{0, height + 1, true},
{0, 0, true},
{base - 1, 0, false},
{base, 0, true},
{base, base, true},
{base, base - 1, false},
{base, height, true},
{base, height + 1, true},
{height, 0, true},
{height, base, false},
{height, height - 1, false},
{height, height, true},
{height, height + 1, true},
{height + 1, 0, false},
}
for _, tc := range testCases {
startHeight = tc.startHeight
endHeight = tc.endHeight
err := checkValidHeight(mockBlockStore)
if tc.validHeight {
require.NoError(t, err)
} else {
require.Error(t, err)
}
}
}
func TestLoadEventSink(t *testing.T) {
testCases := []struct {
sinks []string
connURL string
loadErr bool
}{
{[]string{}, "", true},
{[]string{"NULL"}, "", true},
{[]string{"KV"}, "", false},
{[]string{"KV", "KV"}, "", true},
{[]string{"PSQL"}, "", true}, // true because empty connect url
{[]string{"PSQL"}, "wrongUrl", true}, // true because wrong connect url
// skip to test PSQL connect with correct url
{[]string{"UnsupportedSinkType"}, "wrongUrl", true},
}
for _, tc := range testCases {
cfg := tmcfg.TestConfig()
cfg.TxIndex.Indexer = tc.sinks
cfg.TxIndex.PsqlConn = tc.connURL
_, err := loadEventSinks(cfg)
if tc.loadErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
}
}
func TestLoadBlockStore(t *testing.T) {
bs, ss, err := loadStateAndBlockStore(tmcfg.TestConfig())
require.NoError(t, err)
require.NotNil(t, bs)
require.NotNil(t, ss)
}
func TestReIndexEvent(t *testing.T) {
mockBlockStore := &mocks.BlockStore{}
mockStateStore := &mocks.Store{}
mockEventSink := &mocks.EventSink{}
mockBlockStore.
On("Base").Return(base).
On("Height").Return(height).
On("LoadBlock", base).Return(nil).Once().
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)}}})
mockEventSink.
On("Type").Return(indexer.KV).
On("IndexBlockEvents", mock.AnythingOfType("types.EventDataNewBlockHeader")).Return(errors.New("")).Once().
On("IndexBlockEvents", mock.AnythingOfType("types.EventDataNewBlockHeader")).Return(nil).
On("IndexTxEvents", mock.AnythingOfType("[]*types.TxResult")).Return(errors.New("")).Once().
On("IndexTxEvents", mock.AnythingOfType("[]*types.TxResult")).Return(nil)
dtx := abcitypes.ResponseDeliverTx{}
abciResp := &prototmstate.ABCIResponses{
DeliverTxs: []*abcitypes.ResponseDeliverTx{&dtx},
EndBlock: &abcitypes.ResponseEndBlock{},
BeginBlock: &abcitypes.ResponseBeginBlock{},
}
mockStateStore.
On("LoadABCIResponses", base).Return(nil, errors.New("")).Once().
On("LoadABCIResponses", base).Return(abciResp, nil).
On("LoadABCIResponses", height).Return(abciResp, nil)
testCases := []struct {
startHeight int64
endHeight int64
reIndexErr bool
}{
{base, height, true}, // LoadBlock error
{base, height, true}, // LoadABCIResponses error
{base, height, true}, // index block event error
{base, height, true}, // index tx event error
{base, base, false},
{height, height, false},
}
for _, tc := range testCases {
startHeight = tc.startHeight
endHeight = tc.endHeight
err := eventReIndex(setupReIndexEventCmd(), []indexer.EventSink{mockEventSink}, mockBlockStore, mockStateStore)
if tc.reIndexErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
}
}

View File

@@ -15,6 +15,7 @@ func main() {
rootCmd := cmd.RootCmd
rootCmd.AddCommand(
cmd.GenValidatorCmd,
cmd.ReIndexEventCmd,
cmd.InitFilesCmd,
cmd.ProbeUpnpCmd,
cmd.LightCmd,

View File

@@ -29,8 +29,8 @@ const (
ModeValidator = "validator"
ModeSeed = "seed"
BlockchainV0 = "v0"
BlockchainV2 = "v2"
BlockSyncV0 = "v0"
BlockSyncV2 = "v2"
MempoolV0 = "v0"
MempoolV1 = "v1"
@@ -76,7 +76,7 @@ type Config struct {
P2P *P2PConfig `mapstructure:"p2p"`
Mempool *MempoolConfig `mapstructure:"mempool"`
StateSync *StateSyncConfig `mapstructure:"statesync"`
FastSync *FastSyncConfig `mapstructure:"fastsync"`
BlockSync *BlockSyncConfig `mapstructure:"fastsync"`
Consensus *ConsensusConfig `mapstructure:"consensus"`
TxIndex *TxIndexConfig `mapstructure:"tx-index"`
Instrumentation *InstrumentationConfig `mapstructure:"instrumentation"`
@@ -91,7 +91,7 @@ func DefaultConfig() *Config {
P2P: DefaultP2PConfig(),
Mempool: DefaultMempoolConfig(),
StateSync: DefaultStateSyncConfig(),
FastSync: DefaultFastSyncConfig(),
BlockSync: DefaultBlockSyncConfig(),
Consensus: DefaultConsensusConfig(),
TxIndex: DefaultTxIndexConfig(),
Instrumentation: DefaultInstrumentationConfig(),
@@ -114,7 +114,7 @@ func TestConfig() *Config {
P2P: TestP2PConfig(),
Mempool: TestMempoolConfig(),
StateSync: TestStateSyncConfig(),
FastSync: TestFastSyncConfig(),
BlockSync: TestBlockSyncConfig(),
Consensus: TestConsensusConfig(),
TxIndex: TestTxIndexConfig(),
Instrumentation: TestInstrumentationConfig(),
@@ -151,7 +151,7 @@ func (cfg *Config) ValidateBasic() error {
if err := cfg.StateSync.ValidateBasic(); err != nil {
return fmt.Errorf("error in [statesync] section: %w", err)
}
if err := cfg.FastSync.ValidateBasic(); err != nil {
if err := cfg.BlockSync.ValidateBasic(); err != nil {
return fmt.Errorf("error in [fastsync] section: %w", err)
}
if err := cfg.Consensus.ValidateBasic(); err != nil {
@@ -197,6 +197,7 @@ type BaseConfig struct { //nolint: maligned
// If this node is many blocks behind the tip of the chain, FastSync
// allows them to catchup quickly by downloading blocks in parallel
// and verifying their commits
// TODO: This should be moved to the blocksync config
FastSyncMode bool `mapstructure:"fast-sync"`
// Database backend: goleveldb | cleveldb | boltdb | rocksdb
@@ -911,7 +912,7 @@ func DefaultStateSyncConfig() *StateSyncConfig {
}
}
// TestFastSyncConfig returns a default configuration for the state sync service
// TestStateSyncConfig returns a default configuration for the state sync service
func TestStateSyncConfig() *StateSyncConfig {
return DefaultStateSyncConfig()
}
@@ -967,34 +968,33 @@ func (cfg *StateSyncConfig) ValidateBasic() error {
}
//-----------------------------------------------------------------------------
// FastSyncConfig
// FastSyncConfig defines the configuration for the Tendermint fast sync service
type FastSyncConfig struct {
// BlockSyncConfig (formerly known as FastSync) defines the configuration for the Tendermint block sync service
type BlockSyncConfig struct {
Version string `mapstructure:"version"`
}
// DefaultFastSyncConfig returns a default configuration for the fast sync service
func DefaultFastSyncConfig() *FastSyncConfig {
return &FastSyncConfig{
Version: BlockchainV0,
// DefaultBlockSyncConfig returns a default configuration for the block sync service
func DefaultBlockSyncConfig() *BlockSyncConfig {
return &BlockSyncConfig{
Version: BlockSyncV0,
}
}
// TestFastSyncConfig returns a default configuration for the fast sync.
func TestFastSyncConfig() *FastSyncConfig {
return DefaultFastSyncConfig()
// TestBlockSyncConfig returns a default configuration for the block sync.
func TestBlockSyncConfig() *BlockSyncConfig {
return DefaultBlockSyncConfig()
}
// ValidateBasic performs basic validation.
func (cfg *FastSyncConfig) ValidateBasic() error {
func (cfg *BlockSyncConfig) ValidateBasic() error {
switch cfg.Version {
case BlockchainV0:
case BlockSyncV0:
return nil
case BlockchainV2:
return errors.New("fastsync version v2 is no longer supported. Please use v0")
case BlockSyncV2:
return errors.New("blocksync version v2 is no longer supported. Please use v0")
default:
return fmt.Errorf("unknown fastsync version %s", cfg.Version)
return fmt.Errorf("unknown blocksync version %s", cfg.Version)
}
}

View File

@@ -125,8 +125,8 @@ func TestStateSyncConfigValidateBasic(t *testing.T) {
require.NoError(t, cfg.ValidateBasic())
}
func TestFastSyncConfigValidateBasic(t *testing.T) {
cfg := TestFastSyncConfig()
func TestBlockSyncConfigValidateBasic(t *testing.T) {
cfg := TestBlockSyncConfig()
assert.NoError(t, cfg.ValidateBasic())
// tamper with version

View File

@@ -452,14 +452,14 @@ chunk-request-timeout = "{{ .StateSync.ChunkRequestTimeout }}"
fetchers = "{{ .StateSync.Fetchers }}"
#######################################################
### Fast Sync Configuration Connections ###
### Block Sync Configuration Connections ###
#######################################################
[fastsync]
# Fast Sync version to use:
# 1) "v0" (default) - the legacy fast sync implementation
# Block Sync version to use:
# 1) "v0" (default) - the legacy block sync implementation
# 2) "v2" - DEPRECATED, please use v0
version = "{{ .FastSync.Version }}"
version = "{{ .BlockSync.Version }}"
#######################################################
### Consensus Configuration Options ###

View File

@@ -36,7 +36,7 @@ proxy-app = "tcp://127.0.0.1:26658"
# A custom human readable name for this node
moniker = "anonymous"
# If this node is many blocks behind the tip of the chain, FastSync
# If this node is many blocks behind the tip of the chain, BlockSync
# allows them to catchup quickly by downloading blocks in parallel
# and verifying their commits
fast-sync = true
@@ -354,12 +354,12 @@ discovery-time = "15s"
temp-dir = ""
#######################################################
### Fast Sync Configuration Connections ###
### BlockSync Configuration Connections ###
#######################################################
[fastsync]
# Fast Sync version to use:
# 1) "v0" (default) - the legacy fast sync implementation
# Block Sync version to use:
# 1) "v0" (default) - the legacy block sync implementation
# 2) "v2" - complete redesign of v0, optimized for testability & readability
version = "v0"

View File

@@ -14,7 +14,7 @@ This section dives into the internals of Go-Tendermint.
- [Subscribing to events](./subscription.md)
- [Block Structure](./block-structure.md)
- [RPC](./rpc.md)
- [Fast Sync](./fast-sync.md)
- [Block Sync](./block-sync.md)
- [State Sync](./state-sync.md)
- [Mempool](./mempool.md)
- [Light Client](./light-client.md)

View File

@@ -2,7 +2,8 @@
order: 10
---
# Fast Sync
# Block Sync
*Formerly known as Fast Sync*
In a proof of work blockchain, syncing with the chain is the same
process as staying up-to-date with the consensus: download blocks, and
@@ -14,7 +15,7 @@ scratch can take a very long time. It's much faster to just download
blocks and check the merkle tree of validators than to run the real-time
consensus gossip protocol.
## Using Fast Sync
## Using Block Sync
To support faster syncing, Tendermint offers a `fast-sync` mode, which
is enabled by default, and can be toggled in the `config.toml` or via
@@ -22,36 +23,36 @@ is enabled by default, and can be toggled in the `config.toml` or via
In this mode, the Tendermint daemon will sync hundreds of times faster
than if it used the real-time consensus process. Once caught up, the
daemon will switch out of fast sync and into the normal consensus mode.
daemon will switch out of Block Sync and into the normal consensus mode.
After running for some time, the node is considered `caught up` if it
has at least one peer and it's height is at least as high as the max
reported peer height. See [the IsCaughtUp
method](https://github.com/tendermint/tendermint/blob/b467515719e686e4678e6da4e102f32a491b85a0/blockchain/pool.go#L128).
Note: There are three versions of fast sync. We recommend using v0 as v2 is still in beta.
Note: There are two versions of Block Sync. We recommend using v0 as v2 is still in beta.
If you would like to use a different version you can do so by changing the version in the `config.toml`:
```toml
#######################################################
### Fast Sync Configuration Connections ###
### Block Sync Configuration Connections ###
#######################################################
[fastsync]
# Fast Sync version to use:
# 1) "v0" (default) - the legacy fast sync implementation
# Block Sync version to use:
# 1) "v0" (default) - the legacy Block Sync implementation
# 2) "v2" - complete redesign of v0, optimized for testability & readability
version = "v0"
```
If we're lagging sufficiently, we should go back to fast syncing, but
If we're lagging sufficiently, we should go back to block syncing, but
this is an [open issue](https://github.com/tendermint/tendermint/issues/129).
## The Fast Sync event
When the tendermint blockchain core launches, it might switch to the `fast-sync`
## The Block Sync event
When the tendermint blockchain core launches, it might switch to the `block-sync`
mode to catch up the states to the current network best height. the core will emits
a fast-sync event to expose the current status and the sync height. Once it catched
the network best height, it will switches to the state sync mechanism and then emit
another event for exposing the fast-sync `complete` status and the state `height`.
The user can query the events by subscribing `EventQueryFastSyncStatus`
The user can query the events by subscribing `EventQueryBlockSyncStatus`
Please check [types](https://pkg.go.dev/github.com/tendermint/tendermint/types?utm_source=godoc#pkg-constants) for the details.

View File

@@ -4,7 +4,7 @@ order: 11
# State Sync
With fast sync a node is downloading all of the data of an application from genesis and verifying it.
With block sync a node is downloading all of the data of an application from genesis and verifying it.
With state sync your node will download data related to the head or near the head of the chain and verify the data.
This leads to drastically shorter times for joining a network.

View File

@@ -1,19 +1,19 @@
/*
Package blockchain implements two versions of a reactor Service that are
responsible for block propagation and gossip between peers. This mechanism is
more formally known as fast-sync.
Package blocksync implements two versions of a reactor Service that are
responsible for block propagation and gossip between peers. This mechanism was
formerly known as fast-sync.
In order for a full node to successfully participate in consensus, it must have
the latest view of state. The fast-sync protocol is a mechanism in which peers
the latest view of state. The blocksync protocol is a mechanism in which peers
may exchange and gossip entire blocks with one another, in a request/response
type model, until they've successfully synced to the latest head block. Once
succussfully synced, the full node can switch to an active role in consensus and
will no longer fast-sync and thus no longer run the fast-sync process.
will no longer blocksync and thus no longer run the blocksync process.
Note, the blockchain reactor Service gossips entire block and relevant data such
that each receiving peer may construct the entire view of the blockchain state.
Note, the blocksync reactor Service gossips entire block and relevant data such
that each receiving peer may construct the entire view of the blocksync state.
There are two versions of the blockchain reactor Service, i.e. fast-sync:
There are currently two versions of the blocksync reactor Service:
- v0: The initial implementation that is battle-tested, but whose test coverage
is lacking and is not formally verifiable.
@@ -22,7 +22,7 @@ There are two versions of the blockchain reactor Service, i.e. fast-sync:
is known to have various bugs that could make it unreliable in production
environments.
The v0 blockchain reactor Service has one p2p channel, BlockchainChannel. This
The v0 blocksync reactor Service has one p2p channel, BlockchainChannel. This
channel is responsible for handling messages that both request blocks and respond
to block requests from peers. For every block request from a peer, the reactor
will execute respondToPeer which will fetch the block from the node's state store
@@ -33,4 +33,4 @@ Internally, v0 runs a poolRoutine that constantly checks for what blocks it need
and requests them. The poolRoutine is also responsible for taking blocks from the
pool, saving and executing each block.
*/
package blockchain
package blocksync

View File

@@ -1,7 +1,7 @@
package blockchain
package blocksync
import (
bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blocksync"
"github.com/tendermint/tendermint/types"
)

View File

@@ -65,7 +65,7 @@ type BlockRequest struct {
PeerID types.NodeID
}
// BlockPool keeps track of the fast sync peers, block requests and block responses.
// BlockPool keeps track of the block sync peers, block requests and block responses.
type BlockPool struct {
service.BaseService
lastAdvance time.Time

View File

@@ -6,13 +6,13 @@ import (
"sync"
"time"
bc "github.com/tendermint/tendermint/internal/blockchain"
bc "github.com/tendermint/tendermint/internal/blocksync"
cons "github.com/tendermint/tendermint/internal/consensus"
"github.com/tendermint/tendermint/internal/p2p"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/libs/service"
tmSync "github.com/tendermint/tendermint/libs/sync"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blocksync"
sm "github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/store"
"github.com/tendermint/tendermint/types"
@@ -60,7 +60,7 @@ const (
)
type consensusReactor interface {
// For when we switch from blockchain reactor and fast sync to the consensus
// For when we switch from blockchain reactor and block sync to the consensus
// machine.
SwitchToConsensus(state sm.State, skipWAL bool)
}
@@ -85,7 +85,7 @@ type Reactor struct {
store *store.BlockStore
pool *BlockPool
consReactor consensusReactor
fastSync *tmSync.AtomicBool
blockSync *tmSync.AtomicBool
blockchainCh *p2p.Channel
// blockchainOutBridgeCh defines a channel that acts as a bridge between sending Envelope
@@ -121,7 +121,7 @@ func NewReactor(
consReactor consensusReactor,
blockchainCh *p2p.Channel,
peerUpdates *p2p.PeerUpdates,
fastSync bool,
blockSync bool,
metrics *cons.Metrics,
) (*Reactor, error) {
if state.LastBlockHeight != store.Height() {
@@ -142,7 +142,7 @@ func NewReactor(
store: store,
pool: NewBlockPool(startHeight, requestsCh, errorsCh),
consReactor: consReactor,
fastSync: tmSync.NewBool(fastSync),
blockSync: tmSync.NewBool(blockSync),
requestsCh: requestsCh,
errorsCh: errorsCh,
blockchainCh: blockchainCh,
@@ -162,10 +162,10 @@ func NewReactor(
// messages on that p2p channel accordingly. The caller must be sure to execute
// OnStop to ensure the outbound p2p Channels are closed.
//
// If fastSync is enabled, we also start the pool and the pool processing
// If blockSync is enabled, we also start the pool and the pool processing
// goroutine. If the pool fails to start, an error is returned.
func (r *Reactor) OnStart() error {
if r.fastSync.IsSet() {
if r.blockSync.IsSet() {
if err := r.pool.Start(); err != nil {
return err
}
@@ -183,7 +183,7 @@ func (r *Reactor) OnStart() error {
// OnStop stops the reactor by signaling to all spawned goroutines to exit and
// blocking until they all exit.
func (r *Reactor) OnStop() {
if r.fastSync.IsSet() {
if r.blockSync.IsSet() {
if err := r.pool.Stop(); err != nil {
r.Logger.Error("failed to stop pool", "err", err)
}
@@ -371,10 +371,10 @@ func (r *Reactor) processPeerUpdates() {
}
}
// SwitchToFastSync is called by the state sync reactor when switching to fast
// SwitchToBlockSync is called by the state sync reactor when switching to fast
// sync.
func (r *Reactor) SwitchToFastSync(state sm.State) error {
r.fastSync.Set()
func (r *Reactor) SwitchToBlockSync(state sm.State) error {
r.blockSync.Set()
r.initialState = state
r.pool.height = state.LastBlockHeight + 1
@@ -496,7 +496,7 @@ FOR_LOOP:
r.Logger.Error("failed to stop pool", "err", err)
}
r.fastSync.UnSet()
r.blockSync.UnSet()
if r.consReactor != nil {
r.consReactor.SwitchToConsensus(state, blocksSynced > 0 || stateSynced)
@@ -591,7 +591,7 @@ FOR_LOOP:
if blocksSynced%100 == 0 {
lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
r.Logger.Info(
"fast sync rate",
"block sync rate",
"height", r.pool.height,
"max_peer_height", r.pool.MaxPeerHeight(),
"blocks/s", lastRate,
@@ -614,14 +614,14 @@ func (r *Reactor) GetMaxPeerBlockHeight() int64 {
}
func (r *Reactor) GetTotalSyncedTime() time.Duration {
if !r.fastSync.IsSet() || r.syncStartTime.IsZero() {
if !r.blockSync.IsSet() || r.syncStartTime.IsZero() {
return time.Duration(0)
}
return time.Since(r.syncStartTime)
}
func (r *Reactor) GetRemainingSyncTime() time.Duration {
if !r.fastSync.IsSet() {
if !r.blockSync.IsSet() {
return time.Duration(0)
}

View File

@@ -15,7 +15,7 @@ import (
"github.com/tendermint/tendermint/internal/p2p/p2ptest"
"github.com/tendermint/tendermint/internal/test/factory"
"github.com/tendermint/tendermint/libs/log"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blocksync"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
sf "github.com/tendermint/tendermint/state/test/factory"
@@ -36,7 +36,7 @@ type reactorTestSuite struct {
peerChans map[types.NodeID]chan p2p.PeerUpdate
peerUpdates map[types.NodeID]*p2p.PeerUpdates
fastSync bool
blockSync bool
}
func setup(
@@ -61,7 +61,7 @@ func setup(
blockchainChannels: make(map[types.NodeID]*p2p.Channel, numNodes),
peerChans: make(map[types.NodeID]chan p2p.PeerUpdate, numNodes),
peerUpdates: make(map[types.NodeID]*p2p.PeerUpdates, numNodes),
fastSync: true,
blockSync: true,
}
chDesc := p2p.ChannelDescriptor{ID: byte(BlockchainChannel)}
@@ -163,7 +163,7 @@ func (rts *reactorTestSuite) addNode(t *testing.T,
nil,
rts.blockchainChannels[nodeID],
rts.peerUpdates[nodeID],
rts.fastSync,
rts.blockSync,
cons.NopMetrics())
require.NoError(t, err)

View File

@@ -4,7 +4,7 @@ import (
"sync"
"testing"
bh "github.com/tendermint/tendermint/internal/blockchain/v2/internal/behavior"
bh "github.com/tendermint/tendermint/internal/blocksync/v2/internal/behavior"
"github.com/tendermint/tendermint/types"
)

View File

@@ -5,7 +5,7 @@ import (
"github.com/gogo/protobuf/proto"
"github.com/tendermint/tendermint/internal/p2p"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blocksync"
"github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
@@ -42,7 +42,7 @@ const (
)
type consensusReactor interface {
// for when we switch from blockchain reactor and fast sync to
// for when we switch from blockchain reactor and block sync to
// the consensus machine
SwitchToConsensus(state state.State, skipWAL bool)
}

View File

@@ -7,14 +7,14 @@ import (
proto "github.com/gogo/protobuf/proto"
bc "github.com/tendermint/tendermint/internal/blockchain"
"github.com/tendermint/tendermint/internal/blockchain/v2/internal/behavior"
bc "github.com/tendermint/tendermint/internal/blocksync"
"github.com/tendermint/tendermint/internal/blocksync/v2/internal/behavior"
cons "github.com/tendermint/tendermint/internal/consensus"
tmsync "github.com/tendermint/tendermint/internal/libs/sync"
"github.com/tendermint/tendermint/internal/p2p"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/libs/sync"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blocksync"
"github.com/tendermint/tendermint/state"
"github.com/tendermint/tendermint/types"
)
@@ -31,12 +31,12 @@ type blockStore interface {
Height() int64
}
// BlockchainReactor handles fast sync protocol.
// BlockchainReactor handles block sync protocol.
type BlockchainReactor struct {
p2p.BaseReactor
fastSync *sync.AtomicBool // enable fast sync on start when it's been Set
stateSynced bool // set to true when SwitchToFastSync is called by state sync
blockSync *sync.AtomicBool // enable block sync on start when it's been Set
stateSynced bool // set to true when SwitchToBlockSync is called by state sync
scheduler *Routine
processor *Routine
logger log.Logger
@@ -44,7 +44,7 @@ type BlockchainReactor struct {
mtx tmsync.RWMutex
maxPeerHeight int64
syncHeight int64
events chan Event // non-nil during a fast sync
events chan Event // non-nil during a block sync
reporter behavior.Reporter
io iIO
@@ -61,7 +61,7 @@ type blockApplier interface {
// XXX: unify naming in this package around tmState
func newReactor(state state.State, store blockStore, reporter behavior.Reporter,
blockApplier blockApplier, fastSync bool, metrics *cons.Metrics) *BlockchainReactor {
blockApplier blockApplier, blockSync bool, metrics *cons.Metrics) *BlockchainReactor {
initHeight := state.LastBlockHeight + 1
if initHeight == 1 {
initHeight = state.InitialHeight
@@ -78,7 +78,7 @@ func newReactor(state state.State, store blockStore, reporter behavior.Reporter,
store: store,
reporter: reporter,
logger: log.NewNopLogger(),
fastSync: sync.NewBool(fastSync),
blockSync: sync.NewBool(blockSync),
syncStartHeight: initHeight,
syncStartTime: time.Time{},
lastSyncRate: 0,
@@ -90,10 +90,10 @@ func NewBlockchainReactor(
state state.State,
blockApplier blockApplier,
store blockStore,
fastSync bool,
blockSync bool,
metrics *cons.Metrics) *BlockchainReactor {
reporter := behavior.NewMockReporter()
return newReactor(state, store, reporter, blockApplier, fastSync, metrics)
return newReactor(state, store, reporter, blockApplier, blockSync, metrics)
}
// SetSwitch implements Reactor interface.
@@ -137,22 +137,22 @@ func (r *BlockchainReactor) SetLogger(logger log.Logger) {
// Start implements cmn.Service interface
func (r *BlockchainReactor) Start() error {
r.reporter = behavior.NewSwitchReporter(r.BaseReactor.Switch)
if r.fastSync.IsSet() {
if r.blockSync.IsSet() {
err := r.startSync(nil)
if err != nil {
return fmt.Errorf("failed to start fast sync: %w", err)
return fmt.Errorf("failed to start block sync: %w", err)
}
}
return nil
}
// startSync begins a fast sync, signaled by r.events being non-nil. If state is non-nil,
// startSync begins a block sync, signaled by r.events being non-nil. If state is non-nil,
// the scheduler and processor is updated with this state on startup.
func (r *BlockchainReactor) startSync(state *state.State) error {
r.mtx.Lock()
defer r.mtx.Unlock()
if r.events != nil {
return errors.New("fast sync already in progress")
return errors.New("block sync already in progress")
}
r.events = make(chan Event, chBufferSize)
go r.scheduler.start()
@@ -167,7 +167,7 @@ func (r *BlockchainReactor) startSync(state *state.State) error {
return nil
}
// endSync ends a fast sync
// endSync ends a block sync
func (r *BlockchainReactor) endSync() {
r.mtx.Lock()
defer r.mtx.Unlock()
@@ -179,8 +179,8 @@ func (r *BlockchainReactor) endSync() {
r.processor.stop()
}
// SwitchToFastSync is called by the state sync reactor when switching to fast sync.
func (r *BlockchainReactor) SwitchToFastSync(state state.State) error {
// SwitchToBlockSync is called by the state sync reactor when switching to block sync.
func (r *BlockchainReactor) SwitchToBlockSync(state state.State) error {
r.stateSynced = true
state = state.Copy()
@@ -434,7 +434,7 @@ func (r *BlockchainReactor) demux(events <-chan Event) {
} else {
r.lastSyncRate = 0.9*r.lastSyncRate + 0.1*newSyncRate
}
r.logger.Info("Fast Sync Rate", "height", r.syncHeight,
r.logger.Info("block sync Rate", "height", r.syncHeight,
"max_peer_height", r.maxPeerHeight, "blocks/s", r.lastSyncRate)
lastHundred = time.Now()
}
@@ -442,12 +442,12 @@ func (r *BlockchainReactor) demux(events <-chan Event) {
case pcBlockVerificationFailure:
r.scheduler.send(event)
case pcFinished:
r.logger.Info("Fast sync complete, switching to consensus")
r.logger.Info("block sync complete, switching to consensus")
if !r.io.trySwitchToConsensus(event.tmState, event.blocksSynced > 0 || r.stateSynced) {
r.logger.Error("Failed to switch to consensus reactor")
}
r.endSync()
r.fastSync.UnSet()
r.blockSync.UnSet()
return
case noOpEvent:
default:
@@ -617,14 +617,14 @@ func (r *BlockchainReactor) GetMaxPeerBlockHeight() int64 {
}
func (r *BlockchainReactor) GetTotalSyncedTime() time.Duration {
if !r.fastSync.IsSet() || r.syncStartTime.IsZero() {
if !r.blockSync.IsSet() || r.syncStartTime.IsZero() {
return time.Duration(0)
}
return time.Since(r.syncStartTime)
}
func (r *BlockchainReactor) GetRemainingSyncTime() time.Duration {
if !r.fastSync.IsSet() {
if !r.blockSync.IsSet() {
return time.Duration(0)
}

View File

@@ -15,7 +15,7 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/internal/blockchain/v2/internal/behavior"
"github.com/tendermint/tendermint/internal/blocksync/v2/internal/behavior"
cons "github.com/tendermint/tendermint/internal/consensus"
"github.com/tendermint/tendermint/internal/mempool/mock"
"github.com/tendermint/tendermint/internal/p2p"
@@ -23,7 +23,7 @@ import (
"github.com/tendermint/tendermint/internal/test/factory"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/libs/service"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blocksync"
"github.com/tendermint/tendermint/proxy"
sm "github.com/tendermint/tendermint/state"
sf "github.com/tendermint/tendermint/state/test/factory"

View File

@@ -163,7 +163,7 @@ type scheduler struct {
height int64
// lastAdvance tracks the last time a block execution happened.
// syncTimeout is the maximum time the scheduler waits to advance in the fast sync process before finishing.
// syncTimeout is the maximum time the scheduler waits to advance in the block sync process before finishing.
// This covers the cases where there are no peers or all peers have a lower height.
lastAdvance time.Time
syncTimeout time.Duration

View File

@@ -54,8 +54,8 @@ type Metrics struct {
TotalTxs metrics.Gauge
// The latest block height.
CommittedHeight metrics.Gauge
// Whether or not a node is fast syncing. 1 if yes, 0 if no.
FastSyncing metrics.Gauge
// Whether or not a node is block syncing. 1 if yes, 0 if no.
BlockSyncing metrics.Gauge
// Whether or not a node is state syncing. 1 if yes, 0 if no.
StateSyncing metrics.Gauge
@@ -169,11 +169,11 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics {
Name: "latest_block_height",
Help: "The latest block height.",
}, labels).With(labelsAndValues...),
FastSyncing: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
BlockSyncing: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
Namespace: namespace,
Subsystem: MetricsSubsystem,
Name: "fast_syncing",
Help: "Whether or not a node is fast syncing. 1 if yes, 0 if no.",
Name: "block_syncing",
Help: "Whether or not a node is block syncing. 1 if yes, 0 if no.",
}, labels).With(labelsAndValues...),
StateSyncing: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
Namespace: namespace,
@@ -214,7 +214,7 @@ func NopMetrics() *Metrics {
BlockSizeBytes: discard.NewHistogram(),
TotalTxs: discard.NewGauge(),
CommittedHeight: discard.NewGauge(),
FastSyncing: discard.NewGauge(),
BlockSyncing: discard.NewGauge(),
StateSyncing: discard.NewGauge(),
BlockParts: discard.NewCounter(),
}

View File

@@ -12,8 +12,8 @@ type ConsSyncReactor struct {
mock.Mock
}
// SetFastSyncingMetrics provides a mock function with given fields: _a0
func (_m *ConsSyncReactor) SetFastSyncingMetrics(_a0 float64) {
// SetBlockSyncingMetrics provides a mock function with given fields: _a0
func (_m *ConsSyncReactor) SetBlockSyncingMetrics(_a0 float64) {
_m.Called(_a0)
}

View File

@@ -9,13 +9,13 @@ import (
time "time"
)
// FastSyncReactor is an autogenerated mock type for the FastSyncReactor type
type FastSyncReactor struct {
// BlockSyncReactor is an autogenerated mock type for the BlockSyncReactor type
type BlockSyncReactor struct {
mock.Mock
}
// GetMaxPeerBlockHeight provides a mock function with given fields:
func (_m *FastSyncReactor) GetMaxPeerBlockHeight() int64 {
func (_m *BlockSyncReactor) GetMaxPeerBlockHeight() int64 {
ret := _m.Called()
var r0 int64
@@ -29,7 +29,7 @@ func (_m *FastSyncReactor) GetMaxPeerBlockHeight() int64 {
}
// GetRemainingSyncTime provides a mock function with given fields:
func (_m *FastSyncReactor) GetRemainingSyncTime() time.Duration {
func (_m *BlockSyncReactor) GetRemainingSyncTime() time.Duration {
ret := _m.Called()
var r0 time.Duration
@@ -43,7 +43,7 @@ func (_m *FastSyncReactor) GetRemainingSyncTime() time.Duration {
}
// GetTotalSyncedTime provides a mock function with given fields:
func (_m *FastSyncReactor) GetTotalSyncedTime() time.Duration {
func (_m *BlockSyncReactor) GetTotalSyncedTime() time.Duration {
ret := _m.Called()
var r0 time.Duration
@@ -56,8 +56,8 @@ func (_m *FastSyncReactor) GetTotalSyncedTime() time.Duration {
return r0
}
// SwitchToFastSync provides a mock function with given fields: _a0
func (_m *FastSyncReactor) SwitchToFastSync(_a0 state.State) error {
// SwitchToBlockSync provides a mock function with given fields: _a0
func (_m *BlockSyncReactor) SwitchToBlockSync(_a0 state.State) error {
ret := _m.Called(_a0)
var r0 error

View File

@@ -96,18 +96,18 @@ const (
type ReactorOption func(*Reactor)
// Temporary interface for switching to fast sync, we should get rid of v0.
// NOTE: Temporary interface for switching to block sync, we should get rid of v0.
// See: https://github.com/tendermint/tendermint/issues/4595
type FastSyncReactor interface {
SwitchToFastSync(sm.State) error
type BlockSyncReactor interface {
SwitchToBlockSync(sm.State) error
GetMaxPeerBlockHeight() int64
// GetTotalSyncedTime returns the time duration since the fastsync starting.
// GetTotalSyncedTime returns the time duration since the blocksync starting.
GetTotalSyncedTime() time.Duration
// GetRemainingSyncTime returns the estimating time the node will be fully synced,
// if will return 0 if the fastsync does not perform or the number of block synced is
// if will return 0 if the blocksync does not perform or the number of block synced is
// too small (less than 100).
GetRemainingSyncTime() time.Duration
}
@@ -117,7 +117,7 @@ type FastSyncReactor interface {
type ConsSyncReactor interface {
SwitchToConsensus(sm.State, bool)
SetStateSyncingMetrics(float64)
SetFastSyncingMetrics(float64)
SetBlockSyncingMetrics(float64)
}
// Reactor defines a reactor for the consensus service.
@@ -265,7 +265,7 @@ func (r *Reactor) SetEventBus(b *types.EventBus) {
r.state.SetEventBus(b)
}
// WaitSync returns whether the consensus reactor is waiting for state/fast sync.
// WaitSync returns whether the consensus reactor is waiting for state/block sync.
func (r *Reactor) WaitSync() bool {
r.mtx.RLock()
defer r.mtx.RUnlock()
@@ -278,8 +278,8 @@ func ReactorMetrics(metrics *Metrics) ReactorOption {
return func(r *Reactor) { r.Metrics = metrics }
}
// SwitchToConsensus switches from fast-sync mode to consensus mode. It resets
// the state, turns off fast-sync, and starts the consensus state-machine.
// SwitchToConsensus switches from block-sync mode to consensus mode. It resets
// the state, turns off block-sync, and starts the consensus state-machine.
func (r *Reactor) SwitchToConsensus(state sm.State, skipWAL bool) {
r.Logger.Info("switching to consensus")
@@ -296,7 +296,7 @@ func (r *Reactor) SwitchToConsensus(state sm.State, skipWAL bool) {
r.waitSync = false
r.mtx.Unlock()
r.Metrics.FastSyncing.Set(0)
r.Metrics.BlockSyncing.Set(0)
r.Metrics.StateSyncing.Set(0)
if skipWAL {
@@ -313,9 +313,9 @@ conR:
%+v`, err, r.state, r))
}
d := types.EventDataFastSyncStatus{Complete: true, Height: state.LastBlockHeight}
if err := r.eventBus.PublishEventFastSyncStatus(d); err != nil {
r.Logger.Error("failed to emit the fastsync complete event", "err", err)
d := types.EventDataBlockSyncStatus{Complete: true, Height: state.LastBlockHeight}
if err := r.eventBus.PublishEventBlockSyncStatus(d); err != nil {
r.Logger.Error("failed to emit the blocksync complete event", "err", err)
}
}
@@ -969,7 +969,7 @@ func (r *Reactor) processPeerUpdate(peerUpdate p2p.PeerUpdate) {
go r.gossipVotesRoutine(ps)
go r.queryMaj23Routine(ps)
// Send our state to the peer. If we're fast-syncing, broadcast a
// Send our state to the peer. If we're block-syncing, broadcast a
// RoundStepMessage later upon SwitchToConsensus().
if !r.waitSync {
go r.sendNewRoundStepMessage(ps.peerID)
@@ -1219,7 +1219,7 @@ func (r *Reactor) handleVoteSetBitsMessage(envelope p2p.Envelope, msgI Message)
// It will handle errors and any possible panics gracefully. A caller can handle
// any error returned by sending a PeerError on the respective channel.
//
// NOTE: We process these messages even when we're fast_syncing. Messages affect
// NOTE: We process these messages even when we're block syncing. Messages affect
// either a peer state or the consensus state. Peer state updates can happen in
// parallel, but processing of proposals, block parts, and votes are ordered by
// the p2p channel.
@@ -1442,6 +1442,6 @@ func (r *Reactor) SetStateSyncingMetrics(v float64) {
r.Metrics.StateSyncing.Set(v)
}
func (r *Reactor) SetFastSyncingMetrics(v float64) {
r.Metrics.FastSyncing.Set(v)
func (r *Reactor) SetBlockSyncingMetrics(v float64) {
r.Metrics.BlockSyncing.Set(v)
}

View File

@@ -43,7 +43,7 @@ type reactorTestSuite struct {
states map[types.NodeID]*State
reactors map[types.NodeID]*Reactor
subs map[types.NodeID]types.Subscription
fastsyncSubs map[types.NodeID]types.Subscription
blocksyncSubs map[types.NodeID]types.Subscription
stateChannels map[types.NodeID]*p2p.Channel
dataChannels map[types.NodeID]*p2p.Channel
voteChannels map[types.NodeID]*p2p.Channel
@@ -60,11 +60,11 @@ func setup(t *testing.T, numNodes int, states []*State, size int) *reactorTestSu
t.Helper()
rts := &reactorTestSuite{
network: p2ptest.MakeNetwork(t, p2ptest.NetworkOptions{NumNodes: numNodes}),
states: make(map[types.NodeID]*State),
reactors: make(map[types.NodeID]*Reactor, numNodes),
subs: make(map[types.NodeID]types.Subscription, numNodes),
fastsyncSubs: make(map[types.NodeID]types.Subscription, numNodes),
network: p2ptest.MakeNetwork(t, p2ptest.NetworkOptions{NumNodes: numNodes}),
states: make(map[types.NodeID]*State),
reactors: make(map[types.NodeID]*Reactor, numNodes),
subs: make(map[types.NodeID]types.Subscription, numNodes),
blocksyncSubs: make(map[types.NodeID]types.Subscription, numNodes),
}
rts.stateChannels = rts.network.MakeChannelsNoCleanup(t, chDesc(StateChannel), new(tmcons.Message), size)
@@ -94,13 +94,13 @@ func setup(t *testing.T, numNodes int, states []*State, size int) *reactorTestSu
blocksSub, err := state.eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock, size)
require.NoError(t, err)
fsSub, err := state.eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryFastSyncStatus, size)
fsSub, err := state.eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryBlockSyncStatus, size)
require.NoError(t, err)
rts.states[nodeID] = state
rts.subs[nodeID] = blocksSub
rts.reactors[nodeID] = reactor
rts.fastsyncSubs[nodeID] = fsSub
rts.blocksyncSubs[nodeID] = fsSub
// simulate handle initChain in handshake
if state.state.LastBlockHeight == 0 {
@@ -263,9 +263,9 @@ func waitForBlockWithUpdatedValsAndValidateIt(
wg.Wait()
}
func ensureFastSyncStatus(t *testing.T, msg tmpubsub.Message, complete bool, height int64) {
func ensureBlockSyncStatus(t *testing.T, msg tmpubsub.Message, complete bool, height int64) {
t.Helper()
status, ok := msg.Data().(types.EventDataFastSyncStatus)
status, ok := msg.Data().(types.EventDataBlockSyncStatus)
require.True(t, ok)
require.Equal(t, complete, status.Complete)
@@ -301,14 +301,14 @@ func TestReactorBasic(t *testing.T) {
wg.Wait()
for _, sub := range rts.fastsyncSubs {
for _, sub := range rts.blocksyncSubs {
wg.Add(1)
// wait till everyone makes the consensus switch
go func(s types.Subscription) {
defer wg.Done()
msg := <-s.Out()
ensureFastSyncStatus(t, msg, true, 0)
ensureBlockSyncStatus(t, msg, true, 0)
}(sub)
}

View File

@@ -0,0 +1,41 @@
package progressbar
import "fmt"
// the progressbar indicates the current status and progress would be desired.
// ref: https://www.pixelstech.net/article/1596946473-A-simple-example-on-implementing-progress-bar-in-GoLang
type Bar struct {
percent int64 // progress percentage
cur int64 // current progress
start int64 // the init starting value for progress
total int64 // total value for progress
rate string // the actual progress bar to be printed
graph string // the fill value for progress bar
}
func (bar *Bar) NewOption(start, total int64) {
bar.cur = start
bar.start = start
bar.total = total
bar.graph = "█"
bar.percent = bar.getPercent()
}
func (bar *Bar) getPercent() int64 {
return int64(float32(bar.cur-bar.start) / float32(bar.total-bar.start) * 100)
}
func (bar *Bar) Play(cur int64) {
bar.cur = cur
last := bar.percent
bar.percent = bar.getPercent()
if bar.percent != last && bar.percent%2 == 0 {
bar.rate += bar.graph
}
fmt.Printf("\r[%-50s]%3d%% %8d/%d", bar.rate, bar.percent, bar.cur, bar.total)
}
func (bar *Bar) Finish() {
fmt.Println()
}

View File

@@ -0,0 +1,41 @@
package progressbar
import (
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestProgressBar(t *testing.T) {
zero := int64(0)
hundred := int64(100)
var bar Bar
bar.NewOption(zero, hundred)
require.Equal(t, zero, bar.start)
require.Equal(t, zero, bar.cur)
require.Equal(t, hundred, bar.total)
require.Equal(t, zero, bar.percent)
require.Equal(t, "█", bar.graph)
require.Equal(t, "", bar.rate)
defer bar.Finish()
for i := zero; i <= hundred; i++ {
time.Sleep(1 * time.Millisecond)
bar.Play(i)
}
require.Equal(t, zero, bar.start)
require.Equal(t, hundred, bar.cur)
require.Equal(t, hundred, bar.total)
require.Equal(t, hundred, bar.percent)
var rate string
for i := zero; i < hundred/2; i++ {
rate += "█"
}
require.Equal(t, rate, bar.rate)
}

View File

@@ -188,8 +188,8 @@ func (txmp *TxMempool) WaitForNextTx() <-chan struct{} {
// NextGossipTx returns the next valid transaction to gossip. A caller must wait
// for WaitForNextTx to signal a transaction is available to gossip first. It is
// thread-safe.
func (txmp *TxMempool) NextGossipTx() *WrappedTx {
return txmp.gossipIndex.Front().Value.(*WrappedTx)
func (txmp *TxMempool) NextGossipTx() *clist.CElement {
return txmp.gossipIndex.Front()
}
// EnableTxsAvailable enables the mempool to trigger events when transactions

View File

@@ -9,6 +9,7 @@ import (
"time"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/internal/libs/clist"
tmsync "github.com/tendermint/tendermint/internal/libs/sync"
"github.com/tendermint/tendermint/internal/mempool"
"github.com/tendermint/tendermint/internal/p2p"
@@ -306,7 +307,7 @@ func (r *Reactor) processPeerUpdates() {
func (r *Reactor) broadcastTxRoutine(peerID types.NodeID, closer *tmsync.Closer) {
peerMempoolID := r.ids.GetForPeer(peerID)
var memTx *WrappedTx
var nextGossipTx *clist.CElement
// remove the peer ID from the map of routines and mark the waitgroup as done
defer func() {
@@ -333,10 +334,10 @@ func (r *Reactor) broadcastTxRoutine(peerID types.NodeID, closer *tmsync.Closer)
// This happens because the CElement we were looking at got garbage
// collected (removed). That is, .NextWait() returned nil. Go ahead and
// start from the beginning.
if memTx == nil {
if nextGossipTx == nil {
select {
case <-r.mempool.WaitForNextTx(): // wait until a tx is available
if memTx = r.mempool.NextGossipTx(); memTx == nil {
if nextGossipTx = r.mempool.NextGossipTx(); nextGossipTx == nil {
continue
}
@@ -352,6 +353,8 @@ func (r *Reactor) broadcastTxRoutine(peerID types.NodeID, closer *tmsync.Closer)
}
}
memTx := nextGossipTx.Value.(*WrappedTx)
if r.peerMgr != nil {
height := r.peerMgr.GetHeight(peerID)
if height > 0 && height < memTx.height-1 {
@@ -380,16 +383,8 @@ func (r *Reactor) broadcastTxRoutine(peerID types.NodeID, closer *tmsync.Closer)
}
select {
case <-memTx.gossipEl.NextWaitChan():
// If there is a next element in gossip index, we point memTx to that node's
// value, otherwise we reset memTx to nil which will be checked at the
// parent for loop.
next := memTx.gossipEl.Next()
if next != nil {
memTx = next.Value.(*WrappedTx)
} else {
memTx = nil
}
case <-nextGossipTx.NextWaitChan():
nextGossipTx = nextGossipTx.Next()
case <-closer.Done():
// The peer is marked for removal via a PeerUpdate as the doneCh was

View File

@@ -257,7 +257,11 @@ func (s *pqScheduler) process() {
s.metrics.PeerSendBytesTotal.With(
"chID", chIDStr,
"peer_id", string(pqEnv.envelope.To)).Add(float64(pqEnv.size))
s.dequeueCh <- pqEnv.envelope
select {
case s.dequeueCh <- pqEnv.envelope:
case <-s.closer.Done():
return
}
}
case <-s.closer.Done():

View File

@@ -0,0 +1,39 @@
package p2p
import (
"testing"
"time"
"github.com/tendermint/tendermint/libs/log"
)
func TestCloseWhileDequeueFull(t *testing.T) {
enqueueLength := 5
chDescs := []ChannelDescriptor{
{ID: 0x01, Priority: 1, MaxSendBytes: 4},
}
pqueue := newPQScheduler(log.NewNopLogger(), NopMetrics(), chDescs, uint(enqueueLength), 1, 120)
for i := 0; i < enqueueLength; i++ {
pqueue.enqueue() <- Envelope{
channelID: 0x01,
Message: &testMessage{Value: "foo"}, // 5 bytes
}
}
go pqueue.process()
// sleep to allow context switch for process() to run
time.Sleep(10 * time.Millisecond)
doneCh := make(chan struct{})
go func() {
pqueue.close()
close(doneCh)
}()
select {
case <-doneCh:
case <-time.After(2 * time.Second):
t.Fatal("pqueue failed to close")
}
}

View File

@@ -33,11 +33,8 @@ func GetFreePort() (int, error) {
if err != nil {
return 0, err
}
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return 0, err
}
defer l.Close()
return l.Addr().(*net.TCPAddr).Port, nil
}

View File

@@ -10,8 +10,8 @@ import (
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/light"
"github.com/tendermint/tendermint/light/provider"
mockp "github.com/tendermint/tendermint/light/provider/mock"
dbs "github.com/tendermint/tendermint/light/store/db"
"github.com/tendermint/tendermint/types"
)
// NOTE: block is produced every minute. Make sure the verification time
@@ -21,12 +21,50 @@ import (
// or -benchtime 100x.
//
// Remember that none of these benchmarks account for network latency.
var (
benchmarkFullNode = mockp.New(genMockNode(chainID, 1000, 100, 1, bTime))
genesisBlock, _ = benchmarkFullNode.LightBlock(context.Background(), 1)
)
var ()
type providerBenchmarkImpl struct {
currentHeight int64
blocks map[int64]*types.LightBlock
}
func newProviderBenchmarkImpl(headers map[int64]*types.SignedHeader,
vals map[int64]*types.ValidatorSet) provider.Provider {
impl := providerBenchmarkImpl{
blocks: make(map[int64]*types.LightBlock, len(headers)),
}
for height, header := range headers {
if height > impl.currentHeight {
impl.currentHeight = height
}
impl.blocks[height] = &types.LightBlock{
SignedHeader: header,
ValidatorSet: vals[height],
}
}
return &impl
}
func (impl *providerBenchmarkImpl) LightBlock(ctx context.Context, height int64) (*types.LightBlock, error) {
if height == 0 {
return impl.blocks[impl.currentHeight], nil
}
lb, ok := impl.blocks[height]
if !ok {
return nil, provider.ErrLightBlockNotFound
}
return lb, nil
}
func (impl *providerBenchmarkImpl) ReportEvidence(_ context.Context, _ types.Evidence) error {
panic("not implemented")
}
func BenchmarkSequence(b *testing.B) {
headers, vals, _ := genLightBlocksWithKeys(chainID, 1000, 100, 1, bTime)
benchmarkFullNode := newProviderBenchmarkImpl(headers, vals)
genesisBlock, _ := benchmarkFullNode.LightBlock(context.Background(), 1)
c, err := light.NewClient(
context.Background(),
chainID,
@@ -55,6 +93,10 @@ func BenchmarkSequence(b *testing.B) {
}
func BenchmarkBisection(b *testing.B) {
headers, vals, _ := genLightBlocksWithKeys(chainID, 1000, 100, 1, bTime)
benchmarkFullNode := newProviderBenchmarkImpl(headers, vals)
genesisBlock, _ := benchmarkFullNode.LightBlock(context.Background(), 1)
c, err := light.NewClient(
context.Background(),
chainID,
@@ -82,7 +124,10 @@ func BenchmarkBisection(b *testing.B) {
}
func BenchmarkBackwards(b *testing.B) {
headers, vals, _ := genLightBlocksWithKeys(chainID, 1000, 100, 1, bTime)
benchmarkFullNode := newProviderBenchmarkImpl(headers, vals)
trustedBlock, _ := benchmarkFullNode.LightBlock(context.Background(), 0)
c, err := light.NewClient(
context.Background(),
chainID,

View File

@@ -3,11 +3,13 @@ package light_test
import (
"context"
"errors"
"fmt"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tm-db"
@@ -16,7 +18,7 @@ import (
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/light"
"github.com/tendermint/tendermint/light/provider"
mockp "github.com/tendermint/tendermint/light/provider/mock"
provider_mocks "github.com/tendermint/tendermint/light/provider/mocks"
dbs "github.com/tendermint/tendermint/light/store/db"
"github.com/tendermint/tendermint/types"
)
@@ -57,14 +59,9 @@ var (
// last header (3/3 signed)
3: h3,
}
l1 = &types.LightBlock{SignedHeader: h1, ValidatorSet: vals}
fullNode = mockp.New(
chainID,
headerSet,
valSet,
)
deadNode = mockp.NewDeadMock(chainID)
largeFullNode = mockp.New(genMockNode(chainID, 10, 3, 0, bTime))
l1 = &types.LightBlock{SignedHeader: h1, ValidatorSet: vals}
l2 = &types.LightBlock{SignedHeader: h2, ValidatorSet: vals}
l3 = &types.LightBlock{SignedHeader: h3, ValidatorSet: vals}
)
func TestValidateTrustOptions(t *testing.T) {
@@ -113,11 +110,6 @@ func TestValidateTrustOptions(t *testing.T) {
}
func TestMock(t *testing.T) {
l, _ := fullNode.LightBlock(ctx, 3)
assert.Equal(t, int64(3), l.Height)
}
func TestClient_SequentialVerification(t *testing.T) {
newKeys := genPrivKeys(4)
newVals := newKeys.ToValidators(10, 1)
@@ -216,28 +208,22 @@ func TestClient_SequentialVerification(t *testing.T) {
}
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
testCase := tc
t.Run(testCase.name, func(t *testing.T) {
mockNode := mockNodeFromHeadersAndVals(testCase.otherHeaders, testCase.vals)
mockNode.On("LightBlock", mock.Anything, mock.Anything).Return(nil, provider.ErrLightBlockNotFound)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
mockp.New(
chainID,
tc.otherHeaders,
tc.vals,
),
[]provider.Provider{mockp.New(
chainID,
tc.otherHeaders,
tc.vals,
)},
mockNode,
[]provider.Provider{mockNode},
dbs.New(dbm.NewMemDB()),
light.SequentialVerification(),
light.Logger(log.TestingLogger()),
)
if tc.initErr {
if testCase.initErr {
require.Error(t, err)
return
}
@@ -245,11 +231,12 @@ func TestClient_SequentialVerification(t *testing.T) {
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(3*time.Hour))
if tc.verifyErr {
if testCase.verifyErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
mockNode.AssertExpectations(t)
})
}
}
@@ -343,20 +330,14 @@ func TestClient_SkippingVerification(t *testing.T) {
for _, tc := range testCases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
mockNode := mockNodeFromHeadersAndVals(tc.otherHeaders, tc.vals)
mockNode.On("LightBlock", mock.Anything, mock.Anything).Return(nil, provider.ErrLightBlockNotFound)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
mockp.New(
chainID,
tc.otherHeaders,
tc.vals,
),
[]provider.Provider{mockp.New(
chainID,
tc.otherHeaders,
tc.vals,
)},
mockNode,
[]provider.Provider{mockNode},
dbs.New(dbm.NewMemDB()),
light.SkippingVerification(light.DefaultTrustLevel),
light.Logger(log.TestingLogger()),
@@ -382,8 +363,23 @@ func TestClient_SkippingVerification(t *testing.T) {
// start from a large light block to make sure that the pivot height doesn't select a height outside
// the appropriate range
func TestClientLargeBisectionVerification(t *testing.T) {
veryLargeFullNode := mockp.New(genMockNode(chainID, 100, 3, 0, bTime))
trustedLightBlock, err := veryLargeFullNode.LightBlock(ctx, 5)
numBlocks := int64(300)
mockHeaders, mockVals, _ := genLightBlocksWithKeys(chainID, numBlocks, 101, 2, bTime)
lastBlock := &types.LightBlock{SignedHeader: mockHeaders[numBlocks], ValidatorSet: mockVals[numBlocks]}
mockNode := &provider_mocks.Provider{}
mockNode.On("LightBlock", mock.Anything, numBlocks).
Return(lastBlock, nil)
mockNode.On("LightBlock", mock.Anything, int64(200)).
Return(&types.LightBlock{SignedHeader: mockHeaders[200], ValidatorSet: mockVals[200]}, nil)
mockNode.On("LightBlock", mock.Anything, int64(256)).
Return(&types.LightBlock{SignedHeader: mockHeaders[256], ValidatorSet: mockVals[256]}, nil)
mockNode.On("LightBlock", mock.Anything, int64(0)).Return(lastBlock, nil)
trustedLightBlock, err := mockNode.LightBlock(ctx, int64(200))
require.NoError(t, err)
c, err := light.NewClient(
ctx,
@@ -393,20 +389,25 @@ func TestClientLargeBisectionVerification(t *testing.T) {
Height: trustedLightBlock.Height,
Hash: trustedLightBlock.Hash(),
},
veryLargeFullNode,
[]provider.Provider{veryLargeFullNode},
mockNode,
[]provider.Provider{mockNode},
dbs.New(dbm.NewMemDB()),
light.SkippingVerification(light.DefaultTrustLevel),
)
require.NoError(t, err)
h, err := c.Update(ctx, bTime.Add(100*time.Minute))
h, err := c.Update(ctx, bTime.Add(300*time.Minute))
assert.NoError(t, err)
h2, err := veryLargeFullNode.LightBlock(ctx, 100)
height, err := c.LastTrustedHeight()
require.NoError(t, err)
require.Equal(t, numBlocks, height)
h2, err := mockNode.LightBlock(ctx, numBlocks)
require.NoError(t, err)
assert.Equal(t, h, h2)
mockNode.AssertExpectations(t)
}
func TestClientBisectionBetweenTrustedHeaders(t *testing.T) {
mockFullNode := mockNodeFromHeadersAndVals(headerSet, valSet)
c, err := light.NewClient(
ctx,
chainID,
@@ -415,8 +416,8 @@ func TestClientBisectionBetweenTrustedHeaders(t *testing.T) {
Height: 1,
Hash: h1.Hash(),
},
fullNode,
[]provider.Provider{fullNode},
mockFullNode,
[]provider.Provider{mockFullNode},
dbs.New(dbm.NewMemDB()),
light.SkippingVerification(light.DefaultTrustLevel),
)
@@ -432,15 +433,18 @@ func TestClientBisectionBetweenTrustedHeaders(t *testing.T) {
// verify using bisection the light block between the two trusted light blocks
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(1*time.Hour))
assert.NoError(t, err)
mockFullNode.AssertExpectations(t)
}
func TestClient_Cleanup(t *testing.T) {
mockFullNode := &provider_mocks.Provider{}
mockFullNode.On("LightBlock", mock.Anything, int64(1)).Return(l1, nil)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
[]provider.Provider{fullNode},
mockFullNode,
[]provider.Provider{mockFullNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
@@ -455,12 +459,14 @@ func TestClient_Cleanup(t *testing.T) {
l, err := c.TrustedLightBlock(1)
assert.Error(t, err)
assert.Nil(t, l)
mockFullNode.AssertExpectations(t)
}
// trustedHeader.Height == options.Height
func TestClientRestoresTrustedHeaderAfterStartup(t *testing.T) {
// 1. options.Hash == trustedHeader.Hash
{
t.Run("hashes should match", func(t *testing.T) {
mockNode := &provider_mocks.Provider{}
trustedStore := dbs.New(dbm.NewMemDB())
err := trustedStore.SaveLightBlock(l1)
require.NoError(t, err)
@@ -469,8 +475,8 @@ func TestClientRestoresTrustedHeaderAfterStartup(t *testing.T) {
ctx,
chainID,
trustOptions,
fullNode,
[]provider.Provider{fullNode},
mockNode,
[]provider.Provider{mockNode},
trustedStore,
light.Logger(log.TestingLogger()),
)
@@ -481,10 +487,11 @@ func TestClientRestoresTrustedHeaderAfterStartup(t *testing.T) {
assert.NotNil(t, l)
assert.Equal(t, l.Hash(), h1.Hash())
assert.Equal(t, l.ValidatorSet.Hash(), h1.ValidatorsHash.Bytes())
}
mockNode.AssertExpectations(t)
})
// 2. options.Hash != trustedHeader.Hash
{
t.Run("hashes should not match", func(t *testing.T) {
trustedStore := dbs.New(dbm.NewMemDB())
err := trustedStore.SaveLightBlock(l1)
require.NoError(t, err)
@@ -492,15 +499,7 @@ func TestClientRestoresTrustedHeaderAfterStartup(t *testing.T) {
// header1 != h1
header1 := keys.GenSignedHeader(chainID, 1, bTime.Add(1*time.Hour), nil, vals, vals,
hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys))
primary := mockp.New(
chainID,
map[int64]*types.SignedHeader{
// trusted header
1: header1,
},
valSet,
)
mockNode := &provider_mocks.Provider{}
c, err := light.NewClient(
ctx,
@@ -510,8 +509,8 @@ func TestClientRestoresTrustedHeaderAfterStartup(t *testing.T) {
Height: 1,
Hash: header1.Hash(),
},
primary,
[]provider.Provider{primary},
mockNode,
[]provider.Provider{mockNode},
trustedStore,
light.Logger(log.TestingLogger()),
)
@@ -524,16 +523,21 @@ func TestClientRestoresTrustedHeaderAfterStartup(t *testing.T) {
assert.Equal(t, l.Hash(), l1.Hash())
assert.NoError(t, l.ValidateBasic(chainID))
}
}
mockNode.AssertExpectations(t)
})
}
func TestClient_Update(t *testing.T) {
mockFullNode := &provider_mocks.Provider{}
mockFullNode.On("LightBlock", mock.Anything, int64(0)).Return(l3, nil)
mockFullNode.On("LightBlock", mock.Anything, int64(1)).Return(l1, nil)
mockFullNode.On("LightBlock", mock.Anything, int64(3)).Return(l3, nil)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
[]provider.Provider{fullNode},
mockFullNode,
[]provider.Provider{mockFullNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
@@ -546,15 +550,19 @@ func TestClient_Update(t *testing.T) {
assert.EqualValues(t, 3, l.Height)
assert.NoError(t, l.ValidateBasic(chainID))
}
mockFullNode.AssertExpectations(t)
}
func TestClient_Concurrency(t *testing.T) {
mockFullNode := &provider_mocks.Provider{}
mockFullNode.On("LightBlock", mock.Anything, int64(2)).Return(l2, nil)
mockFullNode.On("LightBlock", mock.Anything, int64(1)).Return(l1, nil)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
[]provider.Provider{fullNode},
mockFullNode,
[]provider.Provider{mockFullNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
@@ -587,15 +595,20 @@ func TestClient_Concurrency(t *testing.T) {
}
wg.Wait()
mockFullNode.AssertExpectations(t)
}
func TestClient_AddProviders(t *testing.T) {
mockFullNode := mockNodeFromHeadersAndVals(map[int64]*types.SignedHeader{
1: h1,
2: h2,
}, valSet)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
[]provider.Provider{fullNode},
mockFullNode,
[]provider.Provider{mockFullNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
@@ -610,22 +623,28 @@ func TestClient_AddProviders(t *testing.T) {
}()
// NOTE: the light client doesn't check uniqueness of providers
c.AddProvider(fullNode)
c.AddProvider(mockFullNode)
require.Len(t, c.Witnesses(), 2)
select {
case <-closeCh:
case <-time.After(5 * time.Second):
t.Fatal("concurent light block verification failed to finish in 5s")
}
mockFullNode.AssertExpectations(t)
}
func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
mockFullNode := &provider_mocks.Provider{}
mockFullNode.On("LightBlock", mock.Anything, mock.Anything).Return(l1, nil)
mockDeadNode := &provider_mocks.Provider{}
mockDeadNode.On("LightBlock", mock.Anything, mock.Anything).Return(nil, provider.ErrNoResponse)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
deadNode,
[]provider.Provider{fullNode, fullNode},
mockDeadNode,
[]provider.Provider{mockFullNode, mockFullNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
@@ -635,16 +654,25 @@ func TestClientReplacesPrimaryWithWitnessIfPrimaryIsUnavailable(t *testing.T) {
require.NoError(t, err)
// the primary should no longer be the deadNode
assert.NotEqual(t, c.Primary(), deadNode)
assert.NotEqual(t, c.Primary(), mockDeadNode)
// we should still have the dead node as a witness because it
// hasn't repeatedly been unresponsive yet
assert.Equal(t, 2, len(c.Witnesses()))
mockDeadNode.AssertExpectations(t)
mockFullNode.AssertExpectations(t)
}
func TestClient_BackwardsVerification(t *testing.T) {
{
trustHeader, _ := largeFullNode.LightBlock(ctx, 6)
headers, vals, _ := genLightBlocksWithKeys(chainID, 9, 3, 0, bTime)
delete(headers, 1)
delete(headers, 2)
delete(vals, 1)
delete(vals, 2)
mockLargeFullNode := mockNodeFromHeadersAndVals(headers, vals)
trustHeader, _ := mockLargeFullNode.LightBlock(ctx, 6)
c, err := light.NewClient(
ctx,
chainID,
@@ -653,8 +681,8 @@ func TestClient_BackwardsVerification(t *testing.T) {
Height: trustHeader.Height,
Hash: trustHeader.Hash(),
},
largeFullNode,
[]provider.Provider{largeFullNode},
mockLargeFullNode,
[]provider.Provider{mockLargeFullNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
@@ -692,41 +720,36 @@ func TestClient_BackwardsVerification(t *testing.T) {
// so expect error
_, err = c.VerifyLightBlockAtHeight(ctx, 8, bTime.Add(12*time.Minute))
assert.Error(t, err)
mockLargeFullNode.AssertExpectations(t)
}
{
testCases := []struct {
provider provider.Provider
headers map[int64]*types.SignedHeader
vals map[int64]*types.ValidatorSet
}{
{
// 7) provides incorrect height
mockp.New(
chainID,
map[int64]*types.SignedHeader{
1: h1,
2: keys.GenSignedHeader(chainID, 1, bTime.Add(30*time.Minute), nil, vals, vals,
hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
3: h3,
},
valSet,
),
headers: map[int64]*types.SignedHeader{
2: keys.GenSignedHeader(chainID, 1, bTime.Add(30*time.Minute), nil, vals, vals,
hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(keys)),
3: h3,
},
vals: valSet,
},
{
// 8) provides incorrect hash
mockp.New(
chainID,
map[int64]*types.SignedHeader{
1: h1,
2: keys.GenSignedHeader(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
hash("app_hash2"), hash("cons_hash23"), hash("results_hash30"), 0, len(keys)),
3: h3,
},
valSet,
),
headers: map[int64]*types.SignedHeader{
2: keys.GenSignedHeader(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
hash("app_hash2"), hash("cons_hash23"), hash("results_hash30"), 0, len(keys)),
3: h3,
},
vals: valSet,
},
}
for idx, tc := range testCases {
mockNode := mockNodeFromHeadersAndVals(tc.headers, tc.vals)
c, err := light.NewClient(
ctx,
chainID,
@@ -735,8 +758,8 @@ func TestClient_BackwardsVerification(t *testing.T) {
Height: 3,
Hash: h3.Hash(),
},
tc.provider,
[]provider.Provider{tc.provider},
mockNode,
[]provider.Provider{mockNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
@@ -744,6 +767,7 @@ func TestClient_BackwardsVerification(t *testing.T) {
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(1*time.Hour).Add(1*time.Second))
assert.Error(t, err, idx)
mockNode.AssertExpectations(t)
}
}
}
@@ -753,60 +777,62 @@ func TestClient_NewClientFromTrustedStore(t *testing.T) {
db := dbs.New(dbm.NewMemDB())
err := db.SaveLightBlock(l1)
require.NoError(t, err)
mockNode := &provider_mocks.Provider{}
c, err := light.NewClientFromTrustedStore(
chainID,
trustPeriod,
deadNode,
[]provider.Provider{deadNode},
mockNode,
[]provider.Provider{mockNode},
db,
)
require.NoError(t, err)
// 2) Check light block exists (deadNode is being used to ensure we're not getting
// it from primary)
// 2) Check light block exists
h, err := c.TrustedLightBlock(1)
assert.NoError(t, err)
assert.EqualValues(t, l1.Height, h.Height)
mockNode.AssertExpectations(t)
}
func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
// different headers hash then primary plus less than 1/3 signed (no fork)
badProvider1 := mockp.New(
chainID,
map[int64]*types.SignedHeader{
1: h1,
2: keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
hash("app_hash2"), hash("cons_hash"), hash("results_hash"),
len(keys), len(keys), types.BlockID{Hash: h1.Hash()}),
},
map[int64]*types.ValidatorSet{
1: vals,
2: vals,
},
)
// header is empty
badProvider2 := mockp.New(
chainID,
map[int64]*types.SignedHeader{
1: h1,
2: h2,
},
map[int64]*types.ValidatorSet{
1: vals,
2: vals,
},
)
headers1 := map[int64]*types.SignedHeader{
1: h1,
2: keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
hash("app_hash2"), hash("cons_hash"), hash("results_hash"),
len(keys), len(keys), types.BlockID{Hash: h1.Hash()}),
}
vals1 := map[int64]*types.ValidatorSet{
1: vals,
2: vals,
}
mockBadNode1 := mockNodeFromHeadersAndVals(headers1, vals1)
mockBadNode1.On("LightBlock", mock.Anything, mock.Anything).Return(nil, provider.ErrLightBlockNotFound)
lb1, _ := badProvider1.LightBlock(ctx, 2)
// header is empty
headers2 := map[int64]*types.SignedHeader{
1: h1,
2: h2,
}
vals2 := map[int64]*types.ValidatorSet{
1: vals,
2: vals,
}
mockBadNode2 := mockNodeFromHeadersAndVals(headers2, vals2)
mockBadNode2.On("LightBlock", mock.Anything, mock.Anything).Return(nil, provider.ErrLightBlockNotFound)
mockFullNode := mockNodeFromHeadersAndVals(headerSet, valSet)
lb1, _ := mockBadNode1.LightBlock(ctx, 2)
require.NotEqual(t, lb1.Hash(), l1.Hash())
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
[]provider.Provider{badProvider1, badProvider2},
mockFullNode,
[]provider.Provider{mockBadNode1, mockBadNode2},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
@@ -828,12 +854,13 @@ func TestClientRemovesWitnessIfItSendsUsIncorrectHeader(t *testing.T) {
}
// witness does not have a light block -> left in the list
assert.EqualValues(t, 1, len(c.Witnesses()))
mockBadNode1.AssertExpectations(t)
mockBadNode2.AssertExpectations(t)
}
func TestClient_TrustedValidatorSet(t *testing.T) {
differentVals, _ := factory.RandValidatorSet(10, 100)
badValSetNode := mockp.New(
chainID,
mockBadValSetNode := mockNodeFromHeadersAndVals(
map[int64]*types.SignedHeader{
1: h1,
// 3/3 signed, but validator set at height 2 below is invalid -> witness
@@ -841,21 +868,27 @@ func TestClient_TrustedValidatorSet(t *testing.T) {
2: keys.GenSignedHeaderLastBlockID(chainID, 2, bTime.Add(30*time.Minute), nil, vals, vals,
hash("app_hash2"), hash("cons_hash"), hash("results_hash"),
0, len(keys), types.BlockID{Hash: h1.Hash()}),
3: h3,
},
map[int64]*types.ValidatorSet{
1: vals,
2: differentVals,
3: differentVals,
})
mockFullNode := mockNodeFromHeadersAndVals(
map[int64]*types.SignedHeader{
1: h1,
2: h2,
},
)
map[int64]*types.ValidatorSet{
1: vals,
2: vals,
})
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
[]provider.Provider{badValSetNode, fullNode},
mockFullNode,
[]provider.Provider{mockBadValSetNode, mockFullNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
@@ -865,15 +898,29 @@ func TestClient_TrustedValidatorSet(t *testing.T) {
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(2*time.Hour).Add(1*time.Second))
assert.NoError(t, err)
assert.Equal(t, 1, len(c.Witnesses()))
mockBadValSetNode.AssertExpectations(t)
mockFullNode.AssertExpectations(t)
}
func TestClientPrunesHeadersAndValidatorSets(t *testing.T) {
mockFullNode := mockNodeFromHeadersAndVals(
map[int64]*types.SignedHeader{
1: h1,
3: h3,
0: h3,
},
map[int64]*types.ValidatorSet{
1: vals,
3: vals,
0: vals,
})
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
fullNode,
[]provider.Provider{fullNode},
mockFullNode,
[]provider.Provider{mockFullNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.PruningSize(1),
@@ -888,6 +935,7 @@ func TestClientPrunesHeadersAndValidatorSets(t *testing.T) {
_, err = c.TrustedLightBlock(1)
assert.Error(t, err)
mockFullNode.AssertExpectations(t)
}
func TestClientEnsureValidHeadersAndValSets(t *testing.T) {
@@ -899,86 +947,108 @@ func TestClientEnsureValidHeadersAndValSets(t *testing.T) {
testCases := []struct {
headers map[int64]*types.SignedHeader
vals map[int64]*types.ValidatorSet
err bool
errorToThrow error
errorHeight int64
err bool
}{
{
headerSet,
valSet,
false,
},
{
headerSet,
map[int64]*types.ValidatorSet{
1: vals,
2: vals,
3: nil,
},
true,
},
{
map[int64]*types.SignedHeader{
headers: map[int64]*types.SignedHeader{
1: h1,
2: h2,
3: nil,
3: h3,
},
valSet,
true,
vals: map[int64]*types.ValidatorSet{
1: vals,
3: vals,
},
err: false,
},
{
headerSet,
map[int64]*types.ValidatorSet{
headers: map[int64]*types.SignedHeader{
1: h1,
},
vals: map[int64]*types.ValidatorSet{
1: vals,
},
errorToThrow: provider.ErrBadLightBlock{Reason: errors.New("nil header or vals")},
errorHeight: 3,
err: true,
},
{
headers: map[int64]*types.SignedHeader{
1: h1,
},
errorToThrow: provider.ErrBadLightBlock{Reason: errors.New("nil header or vals")},
errorHeight: 3,
vals: valSet,
err: true,
},
{
headers: map[int64]*types.SignedHeader{
1: h1,
3: h3,
},
vals: map[int64]*types.ValidatorSet{
1: vals,
2: vals,
3: emptyValSet,
},
true,
err: true,
},
}
for _, tc := range testCases {
badNode := mockp.New(
chainID,
tc.headers,
tc.vals,
)
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
badNode,
[]provider.Provider{badNode, badNode},
dbs.New(dbm.NewMemDB()),
)
require.NoError(t, err)
for i, tc := range testCases {
testCase := tc
t.Run(fmt.Sprintf("case: %d", i), func(t *testing.T) {
mockBadNode := mockNodeFromHeadersAndVals(testCase.headers, testCase.vals)
if testCase.errorToThrow != nil {
mockBadNode.On("LightBlock", mock.Anything, testCase.errorHeight).Return(nil, testCase.errorToThrow)
}
_, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(2*time.Hour))
if tc.err {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
c, err := light.NewClient(
ctx,
chainID,
trustOptions,
mockBadNode,
[]provider.Provider{mockBadNode, mockBadNode},
dbs.New(dbm.NewMemDB()),
)
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(ctx, 3, bTime.Add(2*time.Hour))
if testCase.err {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
mockBadNode.AssertExpectations(t)
})
}
}
func TestClientHandlesContexts(t *testing.T) {
p := mockp.New(genMockNode(chainID, 100, 10, 1, bTime))
genBlock, err := p.LightBlock(ctx, 1)
require.NoError(t, err)
mockNode := &provider_mocks.Provider{}
mockNode.On("LightBlock",
mock.MatchedBy(func(ctx context.Context) bool { return ctx.Err() == nil }),
int64(1)).Return(l1, nil)
mockNode.On("LightBlock",
mock.MatchedBy(func(ctx context.Context) bool { return ctx.Err() == context.DeadlineExceeded }),
mock.Anything).Return(nil, context.DeadlineExceeded)
mockNode.On("LightBlock",
mock.MatchedBy(func(ctx context.Context) bool { return ctx.Err() == context.Canceled }),
mock.Anything).Return(nil, context.Canceled)
// instantiate the light client with a timeout
ctxTimeOut, cancel := context.WithTimeout(ctx, 10*time.Millisecond)
ctxTimeOut, cancel := context.WithTimeout(ctx, 1*time.Nanosecond)
defer cancel()
_, err = light.NewClient(
_, err := light.NewClient(
ctxTimeOut,
chainID,
light.TrustOptions{
Period: 24 * time.Hour,
Height: 1,
Hash: genBlock.Hash(),
},
p,
[]provider.Provider{p, p},
trustOptions,
mockNode,
[]provider.Provider{mockNode, mockNode},
dbs.New(dbm.NewMemDB()),
)
require.Error(t, ctxTimeOut.Err())
@@ -989,19 +1059,15 @@ func TestClientHandlesContexts(t *testing.T) {
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 24 * time.Hour,
Height: 1,
Hash: genBlock.Hash(),
},
p,
[]provider.Provider{p, p},
trustOptions,
mockNode,
[]provider.Provider{mockNode, mockNode},
dbs.New(dbm.NewMemDB()),
)
require.NoError(t, err)
// verify a block with a timeout
ctxTimeOutBlock, cancel := context.WithTimeout(ctx, 10*time.Millisecond)
ctxTimeOutBlock, cancel := context.WithTimeout(ctx, 1*time.Nanosecond)
defer cancel()
_, err = c.VerifyLightBlockAtHeight(ctxTimeOutBlock, 100, bTime.Add(100*time.Minute))
require.Error(t, ctxTimeOutBlock.Err())
@@ -1010,11 +1076,11 @@ func TestClientHandlesContexts(t *testing.T) {
// verify a block with a cancel
ctxCancel, cancel := context.WithCancel(ctx)
defer cancel()
time.AfterFunc(10*time.Millisecond, cancel)
cancel()
_, err = c.VerifyLightBlockAtHeight(ctxCancel, 100, bTime.Add(100*time.Minute))
require.Error(t, ctxCancel.Err())
require.Error(t, err)
require.True(t, errors.Is(err, context.Canceled))
mockNode.AssertExpectations(t)
}

View File

@@ -111,6 +111,7 @@ func (c *Client) compareNewHeaderWithWitness(ctx context.Context, errc chan erro
witness provider.Provider, witnessIndex int) {
lightBlock, err := witness.LightBlock(ctx, h.Height)
// fmt.Println(lightBlock.SignedHeader)
switch err {
// no error means we move on to checking the hash of the two headers
case nil:

View File

@@ -1,10 +1,12 @@
package light_test
import (
"bytes"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
dbm "github.com/tendermint/tm-db"
@@ -12,7 +14,7 @@ import (
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/light"
"github.com/tendermint/tendermint/light/provider"
mockp "github.com/tendermint/tendermint/light/provider/mock"
provider_mocks "github.com/tendermint/tendermint/light/provider/mocks"
dbs "github.com/tendermint/tendermint/light/store/db"
"github.com/tendermint/tendermint/types"
)
@@ -20,15 +22,15 @@ import (
func TestLightClientAttackEvidence_Lunatic(t *testing.T) {
// primary performs a lunatic attack
var (
latestHeight = int64(10)
latestHeight = int64(3)
valSize = 5
divergenceHeight = int64(6)
divergenceHeight = int64(2)
primaryHeaders = make(map[int64]*types.SignedHeader, latestHeight)
primaryValidators = make(map[int64]*types.ValidatorSet, latestHeight)
)
witnessHeaders, witnessValidators, chainKeys := genMockNodeWithKeys(chainID, latestHeight, valSize, 2, bTime)
witness := mockp.New(chainID, witnessHeaders, witnessValidators)
witnessHeaders, witnessValidators, chainKeys := genLightBlocksWithKeys(chainID, latestHeight, valSize, 2, bTime)
forgedKeys := chainKeys[divergenceHeight-1].ChangeKeys(3) // we change 3 out of the 5 validators (still 2/5 remain)
forgedVals := forgedKeys.ToValidators(2, 0)
@@ -42,7 +44,38 @@ func TestLightClientAttackEvidence_Lunatic(t *testing.T) {
nil, forgedVals, forgedVals, hash("app_hash"), hash("cons_hash"), hash("results_hash"), 0, len(forgedKeys))
primaryValidators[height] = forgedVals
}
primary := mockp.New(chainID, primaryHeaders, primaryValidators)
// never called, delete it to make mockery asserts pass
delete(witnessHeaders, 2)
delete(primaryHeaders, 2)
mockWitness := mockNodeFromHeadersAndVals(witnessHeaders, witnessValidators)
mockPrimary := mockNodeFromHeadersAndVals(primaryHeaders, primaryValidators)
mockWitness.On("ReportEvidence", mock.Anything, mock.MatchedBy(func(evidence types.Evidence) bool {
evAgainstPrimary := &types.LightClientAttackEvidence{
// after the divergence height the valset doesn't change so we expect the evidence to be for the latest height
ConflictingBlock: &types.LightBlock{
SignedHeader: primaryHeaders[latestHeight],
ValidatorSet: primaryValidators[latestHeight],
},
CommonHeight: 1,
}
return bytes.Equal(evidence.Hash(), evAgainstPrimary.Hash())
})).Return(nil)
mockPrimary.On("ReportEvidence", mock.Anything, mock.MatchedBy(func(evidence types.Evidence) bool {
evAgainstWitness := &types.LightClientAttackEvidence{
// when forming evidence against witness we learn that the canonical chain continued to change validator sets
// hence the conflicting block is at 7
ConflictingBlock: &types.LightBlock{
SignedHeader: witnessHeaders[divergenceHeight+1],
ValidatorSet: witnessValidators[divergenceHeight+1],
},
CommonHeight: divergenceHeight - 1,
}
return bytes.Equal(evidence.Hash(), evAgainstWitness.Hash())
})).Return(nil)
c, err := light.NewClient(
ctx,
@@ -52,121 +85,134 @@ func TestLightClientAttackEvidence_Lunatic(t *testing.T) {
Height: 1,
Hash: primaryHeaders[1].Hash(),
},
primary,
[]provider.Provider{witness},
mockPrimary,
[]provider.Provider{mockWitness},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err)
// Check verification returns an error.
_, err = c.VerifyLightBlockAtHeight(ctx, 10, bTime.Add(1*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, latestHeight, bTime.Add(1*time.Hour))
if assert.Error(t, err) {
assert.Equal(t, light.ErrLightClientAttack, err)
}
// Check evidence was sent to both full nodes.
evAgainstPrimary := &types.LightClientAttackEvidence{
// after the divergence height the valset doesn't change so we expect the evidence to be for height 10
ConflictingBlock: &types.LightBlock{
SignedHeader: primaryHeaders[10],
ValidatorSet: primaryValidators[10],
},
CommonHeight: 4,
}
assert.True(t, witness.HasEvidence(evAgainstPrimary))
evAgainstWitness := &types.LightClientAttackEvidence{
// when forming evidence against witness we learn that the canonical chain continued to change validator sets
// hence the conflicting block is at 7
ConflictingBlock: &types.LightBlock{
SignedHeader: witnessHeaders[7],
ValidatorSet: witnessValidators[7],
},
CommonHeight: 4,
}
assert.True(t, primary.HasEvidence(evAgainstWitness))
mockWitness.AssertExpectations(t)
mockPrimary.AssertExpectations(t)
}
func TestLightClientAttackEvidence_Equivocation(t *testing.T) {
verificationOptions := map[string]light.Option{
"sequential": light.SequentialVerification(),
"skipping": light.SkippingVerification(light.DefaultTrustLevel),
cases := []struct {
name string
lightOption light.Option
unusedWitnessBlockHeights []int64
unusedPrimaryBlockHeights []int64
latestHeight int64
divergenceHeight int64
}{
{
name: "sequential",
lightOption: light.SequentialVerification(),
unusedWitnessBlockHeights: []int64{4, 6},
latestHeight: int64(5),
divergenceHeight: int64(3),
},
{
name: "skipping",
lightOption: light.SkippingVerification(light.DefaultTrustLevel),
unusedWitnessBlockHeights: []int64{2, 4, 6},
unusedPrimaryBlockHeights: []int64{2, 4, 6},
latestHeight: int64(5),
divergenceHeight: int64(3),
},
}
for s, verificationOption := range verificationOptions {
t.Log("==> verification", s)
// primary performs an equivocation attack
var (
latestHeight = int64(10)
valSize = 5
divergenceHeight = int64(6)
primaryHeaders = make(map[int64]*types.SignedHeader, latestHeight)
primaryValidators = make(map[int64]*types.ValidatorSet, latestHeight)
)
// validators don't change in this network (however we still use a map just for convenience)
witnessHeaders, witnessValidators, chainKeys := genMockNodeWithKeys(chainID, latestHeight+2, valSize, 2, bTime)
witness := mockp.New(chainID, witnessHeaders, witnessValidators)
for height := int64(1); height <= latestHeight; height++ {
if height < divergenceHeight {
primaryHeaders[height] = witnessHeaders[height]
for _, tc := range cases {
testCase := tc
t.Run(testCase.name, func(t *testing.T) {
// primary performs an equivocation attack
var (
valSize = 5
primaryHeaders = make(map[int64]*types.SignedHeader, testCase.latestHeight)
// validators don't change in this network (however we still use a map just for convenience)
primaryValidators = make(map[int64]*types.ValidatorSet, testCase.latestHeight)
)
witnessHeaders, witnessValidators, chainKeys := genLightBlocksWithKeys(chainID,
testCase.latestHeight+1, valSize, 2, bTime)
for height := int64(1); height <= testCase.latestHeight; height++ {
if height < testCase.divergenceHeight {
primaryHeaders[height] = witnessHeaders[height]
primaryValidators[height] = witnessValidators[height]
continue
}
// we don't have a network partition so we will make 4/5 (greater than 2/3) malicious and vote again for
// a different block (which we do by adding txs)
primaryHeaders[height] = chainKeys[height].GenSignedHeader(chainID, height,
bTime.Add(time.Duration(height)*time.Minute), []types.Tx{[]byte("abcd")},
witnessValidators[height], witnessValidators[height+1], hash("app_hash"),
hash("cons_hash"), hash("results_hash"), 0, len(chainKeys[height])-1)
primaryValidators[height] = witnessValidators[height]
continue
}
// we don't have a network partition so we will make 4/5 (greater than 2/3) malicious and vote again for
// a different block (which we do by adding txs)
primaryHeaders[height] = chainKeys[height].GenSignedHeader(chainID, height,
bTime.Add(time.Duration(height)*time.Minute), []types.Tx{[]byte("abcd")},
witnessValidators[height], witnessValidators[height+1], hash("app_hash"),
hash("cons_hash"), hash("results_hash"), 0, len(chainKeys[height])-1)
primaryValidators[height] = witnessValidators[height]
}
primary := mockp.New(chainID, primaryHeaders, primaryValidators)
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Hour,
Height: 1,
Hash: primaryHeaders[1].Hash(),
},
primary,
[]provider.Provider{witness},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
verificationOption,
)
require.NoError(t, err)
for _, height := range testCase.unusedWitnessBlockHeights {
delete(witnessHeaders, height)
}
mockWitness := mockNodeFromHeadersAndVals(witnessHeaders, witnessValidators)
for _, height := range testCase.unusedPrimaryBlockHeights {
delete(primaryHeaders, height)
}
mockPrimary := mockNodeFromHeadersAndVals(primaryHeaders, primaryValidators)
// Check verification returns an error.
_, err = c.VerifyLightBlockAtHeight(ctx, 10, bTime.Add(1*time.Hour))
if assert.Error(t, err) {
assert.Equal(t, light.ErrLightClientAttack, err)
}
// Check evidence was sent to both full nodes.
// Common height should be set to the height of the divergent header in the instance
// of an equivocation attack and the validator sets are the same as what the witness has
mockWitness.On("ReportEvidence", mock.Anything, mock.MatchedBy(func(evidence types.Evidence) bool {
evAgainstPrimary := &types.LightClientAttackEvidence{
ConflictingBlock: &types.LightBlock{
SignedHeader: primaryHeaders[testCase.divergenceHeight],
ValidatorSet: primaryValidators[testCase.divergenceHeight],
},
CommonHeight: testCase.divergenceHeight,
}
return bytes.Equal(evidence.Hash(), evAgainstPrimary.Hash())
})).Return(nil)
mockPrimary.On("ReportEvidence", mock.Anything, mock.MatchedBy(func(evidence types.Evidence) bool {
evAgainstWitness := &types.LightClientAttackEvidence{
ConflictingBlock: &types.LightBlock{
SignedHeader: witnessHeaders[testCase.divergenceHeight],
ValidatorSet: witnessValidators[testCase.divergenceHeight],
},
CommonHeight: testCase.divergenceHeight,
}
return bytes.Equal(evidence.Hash(), evAgainstWitness.Hash())
})).Return(nil)
// Check evidence was sent to both full nodes.
// Common height should be set to the height of the divergent header in the instance
// of an equivocation attack and the validator sets are the same as what the witness has
evAgainstPrimary := &types.LightClientAttackEvidence{
ConflictingBlock: &types.LightBlock{
SignedHeader: primaryHeaders[divergenceHeight],
ValidatorSet: primaryValidators[divergenceHeight],
},
CommonHeight: divergenceHeight,
}
assert.True(t, witness.HasEvidence(evAgainstPrimary))
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 4 * time.Hour,
Height: 1,
Hash: primaryHeaders[1].Hash(),
},
mockPrimary,
[]provider.Provider{mockWitness},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
testCase.lightOption,
)
require.NoError(t, err)
evAgainstWitness := &types.LightClientAttackEvidence{
ConflictingBlock: &types.LightBlock{
SignedHeader: witnessHeaders[divergenceHeight],
ValidatorSet: witnessValidators[divergenceHeight],
},
CommonHeight: divergenceHeight,
}
assert.True(t, primary.HasEvidence(evAgainstWitness))
// Check verification returns an error.
_, err = c.VerifyLightBlockAtHeight(ctx, testCase.latestHeight, bTime.Add(300*time.Second))
if assert.Error(t, err) {
assert.Equal(t, light.ErrLightClientAttack, err)
}
mockWitness.AssertExpectations(t)
mockPrimary.AssertExpectations(t)
})
}
}
@@ -182,7 +228,10 @@ func TestLightClientAttackEvidence_ForwardLunatic(t *testing.T) {
primaryValidators = make(map[int64]*types.ValidatorSet, forgedHeight)
)
witnessHeaders, witnessValidators, chainKeys := genMockNodeWithKeys(chainID, latestHeight, valSize, 2, bTime)
witnessHeaders, witnessValidators, chainKeys := genLightBlocksWithKeys(chainID, latestHeight, valSize, 2, bTime)
for _, unusedHeader := range []int64{3, 5, 6, 8} {
delete(witnessHeaders, unusedHeader)
}
// primary has the exact same headers except it forges one extra header in the future using keys from 2/5ths of
// the validators
@@ -190,6 +239,9 @@ func TestLightClientAttackEvidence_ForwardLunatic(t *testing.T) {
primaryHeaders[h] = witnessHeaders[h]
primaryValidators[h] = witnessValidators[h]
}
for _, unusedHeader := range []int64{3, 5, 6, 8} {
delete(primaryHeaders, unusedHeader)
}
forgedKeys := chainKeys[latestHeight].ChangeKeys(3) // we change 3 out of the 5 validators (still 2/5 remain)
primaryValidators[forgedHeight] = forgedKeys.ToValidators(2, 0)
primaryHeaders[forgedHeight] = forgedKeys.GenSignedHeader(
@@ -204,15 +256,36 @@ func TestLightClientAttackEvidence_ForwardLunatic(t *testing.T) {
hash("results_hash"),
0, len(forgedKeys),
)
mockPrimary := mockNodeFromHeadersAndVals(primaryHeaders, primaryValidators)
lastBlock, _ := mockPrimary.LightBlock(ctx, forgedHeight)
mockPrimary.On("LightBlock", mock.Anything, int64(0)).Return(lastBlock, nil)
mockPrimary.On("LightBlock", mock.Anything, mock.Anything).Return(nil, provider.ErrLightBlockNotFound)
witness := mockp.New(chainID, witnessHeaders, witnessValidators)
primary := mockp.New(chainID, primaryHeaders, primaryValidators)
/*
for _, unusedHeader := range []int64{3, 5, 6, 8} {
delete(witnessHeaders, unusedHeader)
}
*/
mockWitness := mockNodeFromHeadersAndVals(witnessHeaders, witnessValidators)
lastBlock, _ = mockWitness.LightBlock(ctx, latestHeight)
mockWitness.On("LightBlock", mock.Anything, int64(0)).Return(lastBlock, nil).Once()
mockWitness.On("LightBlock", mock.Anything, int64(12)).Return(nil, provider.ErrHeightTooHigh)
laggingWitness := witness.Copy("laggingWitness")
mockWitness.On("ReportEvidence", mock.Anything, mock.MatchedBy(func(evidence types.Evidence) bool {
// Check evidence was sent to the witness against the full node
evAgainstPrimary := &types.LightClientAttackEvidence{
ConflictingBlock: &types.LightBlock{
SignedHeader: primaryHeaders[forgedHeight],
ValidatorSet: primaryValidators[forgedHeight],
},
CommonHeight: latestHeight,
}
return bytes.Equal(evidence.Hash(), evAgainstPrimary.Hash())
})).Return(nil).Twice()
// In order to perform the attack, the primary needs at least one accomplice as a witness to also
// send the forged block
accomplice := primary
accomplice := mockPrimary
c, err := light.NewClient(
ctx,
@@ -222,8 +295,8 @@ func TestLightClientAttackEvidence_ForwardLunatic(t *testing.T) {
Height: 1,
Hash: primaryHeaders[1].Hash(),
},
primary,
[]provider.Provider{witness, accomplice},
mockPrimary,
[]provider.Provider{mockWitness, accomplice},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxClockDrift(1*time.Second),
@@ -251,7 +324,7 @@ func TestLightClientAttackEvidence_ForwardLunatic(t *testing.T) {
}
go func() {
time.Sleep(2 * time.Second)
witness.AddLightBlock(newLb)
mockWitness.On("LightBlock", mock.Anything, int64(0)).Return(newLb, nil)
}()
// Now assert that verification returns an error. We craft the light clients time to be a little ahead of the chain
@@ -261,26 +334,19 @@ func TestLightClientAttackEvidence_ForwardLunatic(t *testing.T) {
assert.Equal(t, light.ErrLightClientAttack, err)
}
// Check evidence was sent to the witness against the full node
evAgainstPrimary := &types.LightClientAttackEvidence{
ConflictingBlock: &types.LightBlock{
SignedHeader: primaryHeaders[forgedHeight],
ValidatorSet: primaryValidators[forgedHeight],
},
CommonHeight: latestHeight,
}
assert.True(t, witness.HasEvidence(evAgainstPrimary))
// We attempt the same call but now the supporting witness has a block which should
// immediately conflict in time with the primary
_, err = c.VerifyLightBlockAtHeight(ctx, forgedHeight, bTime.Add(time.Duration(forgedHeight)*time.Minute))
if assert.Error(t, err) {
assert.Equal(t, light.ErrLightClientAttack, err)
}
assert.True(t, witness.HasEvidence(evAgainstPrimary))
// Lastly we test the unfortunate case where the light clients supporting witness doesn't update
// in enough time
mockLaggingWitness := mockNodeFromHeadersAndVals(witnessHeaders, witnessValidators)
mockLaggingWitness.On("LightBlock", mock.Anything, int64(12)).Return(nil, provider.ErrHeightTooHigh)
lastBlock, _ = mockLaggingWitness.LightBlock(ctx, latestHeight)
mockLaggingWitness.On("LightBlock", mock.Anything, int64(0)).Return(lastBlock, nil)
c, err = light.NewClient(
ctx,
chainID,
@@ -289,8 +355,8 @@ func TestLightClientAttackEvidence_ForwardLunatic(t *testing.T) {
Height: 1,
Hash: primaryHeaders[1].Hash(),
},
primary,
[]provider.Provider{laggingWitness, accomplice},
mockPrimary,
[]provider.Provider{mockLaggingWitness, accomplice},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
light.MaxClockDrift(1*time.Second),
@@ -300,17 +366,20 @@ func TestLightClientAttackEvidence_ForwardLunatic(t *testing.T) {
_, err = c.Update(ctx, bTime.Add(time.Duration(forgedHeight)*time.Minute))
assert.NoError(t, err)
mockPrimary.AssertExpectations(t)
mockWitness.AssertExpectations(t)
}
// 1. Different nodes therefore a divergent header is produced.
// => light client returns an error upon creation because primary and witness
// have a different view.
func TestClientDivergentTraces1(t *testing.T) {
primary := mockp.New(genMockNode(chainID, 10, 5, 2, bTime))
firstBlock, err := primary.LightBlock(ctx, 1)
headers, vals, _ := genLightBlocksWithKeys(chainID, 1, 5, 2, bTime)
mockPrimary := mockNodeFromHeadersAndVals(headers, vals)
firstBlock, err := mockPrimary.LightBlock(ctx, 1)
require.NoError(t, err)
witness := mockp.New(genMockNode(chainID, 10, 5, 2, bTime))
headers, vals, _ = genLightBlocksWithKeys(chainID, 1, 5, 2, bTime)
mockWitness := mockNodeFromHeadersAndVals(headers, vals)
_, err = light.NewClient(
ctx,
@@ -320,20 +389,25 @@ func TestClientDivergentTraces1(t *testing.T) {
Hash: firstBlock.Hash(),
Period: 4 * time.Hour,
},
primary,
[]provider.Provider{witness},
mockPrimary,
[]provider.Provider{mockWitness},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.Error(t, err)
assert.Contains(t, err.Error(), "does not match primary")
mockWitness.AssertExpectations(t)
mockPrimary.AssertExpectations(t)
}
// 2. Two out of three nodes don't respond but the third has a header that matches
// => verification should be successful and all the witnesses should remain
func TestClientDivergentTraces2(t *testing.T) {
primary := mockp.New(genMockNode(chainID, 10, 5, 2, bTime))
firstBlock, err := primary.LightBlock(ctx, 1)
headers, vals, _ := genLightBlocksWithKeys(chainID, 2, 5, 2, bTime)
mockPrimaryNode := mockNodeFromHeadersAndVals(headers, vals)
mockDeadNode := &provider_mocks.Provider{}
mockDeadNode.On("LightBlock", mock.Anything, mock.Anything).Return(nil, provider.ErrNoResponse)
firstBlock, err := mockPrimaryNode.LightBlock(ctx, 1)
require.NoError(t, err)
c, err := light.NewClient(
ctx,
@@ -343,31 +417,35 @@ func TestClientDivergentTraces2(t *testing.T) {
Hash: firstBlock.Hash(),
Period: 4 * time.Hour,
},
primary,
[]provider.Provider{deadNode, deadNode, primary},
mockPrimaryNode,
[]provider.Provider{mockDeadNode, mockDeadNode, mockPrimaryNode},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(ctx, 10, bTime.Add(1*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(1*time.Hour))
assert.NoError(t, err)
assert.Equal(t, 3, len(c.Witnesses()))
mockDeadNode.AssertExpectations(t)
mockPrimaryNode.AssertExpectations(t)
}
// 3. witness has the same first header, but different second header
// => creation should succeed, but the verification should fail
//nolint: dupl
func TestClientDivergentTraces3(t *testing.T) {
_, primaryHeaders, primaryVals := genMockNode(chainID, 10, 5, 2, bTime)
primary := mockp.New(chainID, primaryHeaders, primaryVals)
//
primaryHeaders, primaryVals, _ := genLightBlocksWithKeys(chainID, 2, 5, 2, bTime)
mockPrimary := mockNodeFromHeadersAndVals(primaryHeaders, primaryVals)
firstBlock, err := primary.LightBlock(ctx, 1)
firstBlock, err := mockPrimary.LightBlock(ctx, 1)
require.NoError(t, err)
_, mockHeaders, mockVals := genMockNode(chainID, 10, 5, 2, bTime)
mockHeaders, mockVals, _ := genLightBlocksWithKeys(chainID, 2, 5, 2, bTime)
mockHeaders[1] = primaryHeaders[1]
mockVals[1] = primaryVals[1]
witness := mockp.New(chainID, mockHeaders, mockVals)
mockWitness := mockNodeFromHeadersAndVals(mockHeaders, mockVals)
c, err := light.NewClient(
ctx,
@@ -377,33 +455,35 @@ func TestClientDivergentTraces3(t *testing.T) {
Hash: firstBlock.Hash(),
Period: 4 * time.Hour,
},
primary,
[]provider.Provider{witness},
mockPrimary,
[]provider.Provider{mockWitness},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(ctx, 10, bTime.Add(1*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(1*time.Hour))
assert.Error(t, err)
assert.Equal(t, 1, len(c.Witnesses()))
mockWitness.AssertExpectations(t)
mockPrimary.AssertExpectations(t)
}
// 4. Witness has a divergent header but can not produce a valid trace to back it up.
// It should be ignored
//nolint: dupl
func TestClientDivergentTraces4(t *testing.T) {
_, primaryHeaders, primaryVals := genMockNode(chainID, 10, 5, 2, bTime)
primary := mockp.New(chainID, primaryHeaders, primaryVals)
//
primaryHeaders, primaryVals, _ := genLightBlocksWithKeys(chainID, 2, 5, 2, bTime)
mockPrimary := mockNodeFromHeadersAndVals(primaryHeaders, primaryVals)
firstBlock, err := primary.LightBlock(ctx, 1)
firstBlock, err := mockPrimary.LightBlock(ctx, 1)
require.NoError(t, err)
_, mockHeaders, mockVals := genMockNode(chainID, 10, 5, 2, bTime)
witness := primary.Copy("witness")
witness.AddLightBlock(&types.LightBlock{
SignedHeader: mockHeaders[10],
ValidatorSet: mockVals[10],
})
witnessHeaders, witnessVals, _ := genLightBlocksWithKeys(chainID, 2, 5, 2, bTime)
primaryHeaders[2] = witnessHeaders[2]
primaryVals[2] = witnessVals[2]
mockWitness := mockNodeFromHeadersAndVals(primaryHeaders, primaryVals)
c, err := light.NewClient(
ctx,
@@ -413,14 +493,16 @@ func TestClientDivergentTraces4(t *testing.T) {
Hash: firstBlock.Hash(),
Period: 4 * time.Hour,
},
primary,
[]provider.Provider{witness},
mockPrimary,
[]provider.Provider{mockWitness},
dbs.New(dbm.NewMemDB()),
light.Logger(log.TestingLogger()),
)
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(ctx, 10, bTime.Add(1*time.Hour))
_, err = c.VerifyLightBlockAtHeight(ctx, 2, bTime.Add(1*time.Hour))
assert.Error(t, err)
assert.Equal(t, 1, len(c.Witnesses()))
mockWitness.AssertExpectations(t)
mockPrimary.AssertExpectations(t)
}

View File

@@ -2,7 +2,6 @@ package light_test
import (
"context"
"fmt"
"io/ioutil"
stdlog "log"
"os"
@@ -19,87 +18,12 @@ import (
rpctest "github.com/tendermint/tendermint/rpc/test"
)
// Automatically getting new headers and verifying them.
func ExampleClient_Update() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
conf := rpctest.CreateConfig("ExampleClient_Update")
// Start a test application
app := kvstore.NewApplication()
_, closer, err := rpctest.StartTendermint(ctx, conf, app, rpctest.SuppressStdout)
if err != nil {
stdlog.Fatal(err)
}
defer func() { _ = closer(ctx) }()
// give Tendermint time to generate some blocks
time.Sleep(5 * time.Second)
dbDir, err := ioutil.TempDir("", "light-client-example")
if err != nil {
stdlog.Fatal(err)
}
defer os.RemoveAll(dbDir)
chainID := conf.ChainID()
primary, err := httpp.New(chainID, conf.RPC.ListenAddress)
if err != nil {
stdlog.Fatal(err)
}
block, err := primary.LightBlock(ctx, 2)
if err != nil {
stdlog.Fatal(err)
}
db, err := dbm.NewGoLevelDB("light-client-db", dbDir)
if err != nil {
stdlog.Fatal(err)
}
c, err := light.NewClient(
ctx,
chainID,
light.TrustOptions{
Period: 504 * time.Hour, // 21 days
Height: 2,
Hash: block.Hash(),
},
primary,
[]provider.Provider{primary}, // NOTE: primary should not be used here
dbs.New(db),
light.Logger(log.TestingLogger()),
)
if err != nil {
stdlog.Fatal(err)
}
defer func() {
if err := c.Cleanup(); err != nil {
stdlog.Fatal(err)
}
}()
time.Sleep(2 * time.Second)
h, err := c.Update(ctx, time.Now())
if err != nil {
stdlog.Fatal(err)
}
if h != nil && h.Height > 2 {
fmt.Println("successful update")
} else {
fmt.Println("update failed")
}
}
// Manually getting light blocks and verifying them.
func ExampleClient_VerifyLightBlockAtHeight() {
func ExampleClient() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
conf := rpctest.CreateConfig("ExampleClient_VerifyLightBlockAtHeight")
logger := log.TestingLogger()
// Start a test application
app := kvstore.NewApplication()
@@ -110,9 +34,6 @@ func ExampleClient_VerifyLightBlockAtHeight() {
}
defer func() { _ = closer(ctx) }()
// give Tendermint time to generate some blocks
time.Sleep(5 * time.Second)
dbDir, err := ioutil.TempDir("", "light-client-example")
if err != nil {
stdlog.Fatal(err)
@@ -126,6 +47,9 @@ func ExampleClient_VerifyLightBlockAtHeight() {
stdlog.Fatal(err)
}
// give Tendermint time to generate some blocks
time.Sleep(5 * time.Second)
block, err := primary.LightBlock(ctx, 2)
if err != nil {
stdlog.Fatal(err)
@@ -146,7 +70,7 @@ func ExampleClient_VerifyLightBlockAtHeight() {
primary,
[]provider.Provider{primary}, // NOTE: primary should not be used here
dbs.New(db),
light.Logger(log.TestingLogger()),
light.Logger(logger),
)
if err != nil {
stdlog.Fatal(err)
@@ -157,15 +81,26 @@ func ExampleClient_VerifyLightBlockAtHeight() {
}
}()
// wait for a few more blocks to be produced
time.Sleep(2 * time.Second)
// veify the block at height 3
_, err = c.VerifyLightBlockAtHeight(context.Background(), 3, time.Now())
if err != nil {
stdlog.Fatal(err)
}
h, err := c.TrustedLightBlock(3)
// retrieve light block at height 3
_, err = c.TrustedLightBlock(3)
if err != nil {
stdlog.Fatal(err)
}
fmt.Println("got header", h.Height)
// update to the latest height
lb, err := c.Update(ctx, time.Now())
if err != nil {
stdlog.Fatal(err)
}
logger.Info("verified light block", "light-block", lb)
}

View File

@@ -3,10 +3,12 @@ package light_test
import (
"time"
"github.com/stretchr/testify/mock"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/tmhash"
tmtime "github.com/tendermint/tendermint/libs/time"
provider_mocks "github.com/tendermint/tendermint/light/provider/mocks"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/tendermint/tendermint/types"
"github.com/tendermint/tendermint/version"
@@ -169,12 +171,12 @@ func (pkz privKeys) ChangeKeys(delta int) privKeys {
return newKeys.Extend(delta)
}
// Generates the header and validator set to create a full entire mock node with blocks to height (
// blockSize) and with variation in validator sets. BlockIntervals are in per minute.
// genLightBlocksWithKeys generates the header and validator set to create
// blocks to height. BlockIntervals are in per minute.
// NOTE: Expected to have a large validator set size ~ 100 validators.
func genMockNodeWithKeys(
func genLightBlocksWithKeys(
chainID string,
blockSize int64,
numBlocks int64,
valSize int,
valVariation float32,
bTime time.Time) (
@@ -183,9 +185,9 @@ func genMockNodeWithKeys(
map[int64]privKeys) {
var (
headers = make(map[int64]*types.SignedHeader, blockSize)
valset = make(map[int64]*types.ValidatorSet, blockSize+1)
keymap = make(map[int64]privKeys, blockSize+1)
headers = make(map[int64]*types.SignedHeader, numBlocks)
valset = make(map[int64]*types.ValidatorSet, numBlocks+1)
keymap = make(map[int64]privKeys, numBlocks+1)
keys = genPrivKeys(valSize)
totalVariation = valVariation
valVariationInt int
@@ -207,7 +209,7 @@ func genMockNodeWithKeys(
valset[1] = keys.ToValidators(2, 0)
keys = newKeys
for height := int64(2); height <= blockSize; height++ {
for height := int64(2); height <= numBlocks; height++ {
totalVariation += valVariation
valVariationInt = int(totalVariation)
totalVariation = -float32(valVariationInt)
@@ -226,17 +228,14 @@ func genMockNodeWithKeys(
return headers, valset, keymap
}
func genMockNode(
chainID string,
blockSize int64,
valSize int,
valVariation float32,
bTime time.Time) (
string,
map[int64]*types.SignedHeader,
map[int64]*types.ValidatorSet) {
headers, valset, _ := genMockNodeWithKeys(chainID, blockSize, valSize, valVariation, bTime)
return chainID, headers, valset
func mockNodeFromHeadersAndVals(headers map[int64]*types.SignedHeader,
vals map[int64]*types.ValidatorSet) *provider_mocks.Provider {
mockNode := &provider_mocks.Provider{}
for i, header := range headers {
lb := &types.LightBlock{SignedHeader: header, ValidatorSet: vals[i]}
mockNode.On("LightBlock", mock.Anything, i).Return(lb, nil)
}
return mockNode
}
func hash(s string) []byte {

View File

@@ -2,6 +2,7 @@ package light_test
import (
"context"
"fmt"
"io/ioutil"
"os"
"testing"
@@ -17,6 +18,7 @@ import (
httpp "github.com/tendermint/tendermint/light/provider/http"
dbs "github.com/tendermint/tendermint/light/store/db"
rpctest "github.com/tendermint/tendermint/rpc/test"
"github.com/tendermint/tendermint/types"
)
// NOTE: these are ports of the tests from example_test.go but
@@ -34,10 +36,11 @@ func TestClientIntegration_Update(t *testing.T) {
app := kvstore.NewApplication()
_, closer, err := rpctest.StartTendermint(ctx, conf, app, rpctest.SuppressStdout)
require.NoError(t, err)
defer func() { require.NoError(t, closer(ctx)) }()
defer func() {
fmt.Println("close")
// give Tendermint time to generate some blocks
time.Sleep(5 * time.Second)
require.NoError(t, closer(ctx))
}()
dbDir, err := ioutil.TempDir("", "light-client-test-update-example")
require.NoError(t, err)
@@ -48,7 +51,8 @@ func TestClientIntegration_Update(t *testing.T) {
primary, err := httpp.New(chainID, conf.RPC.ListenAddress)
require.NoError(t, err)
block, err := primary.LightBlock(ctx, 2)
// give Tendermint time to generate some blocks
block, err := waitForBlock(ctx, primary, 2)
require.NoError(t, err)
db, err := dbm.NewGoLevelDB("light-client-db", dbDir)
@@ -71,8 +75,17 @@ func TestClientIntegration_Update(t *testing.T) {
defer func() { require.NoError(t, c.Cleanup()) }()
time.Sleep(2 * time.Second)
// ensure Tendermint is at height 3 or higher
_, err = waitForBlock(ctx, primary, 3)
require.NoError(t, err)
for i := 0; i < 100; i++ {
b, pErr := primary.LightBlock(ctx, 0)
if pErr != nil {
fmt.Println("err")
fmt.Println(pErr)
fmt.Println(b)
}
}
h, err := c.Update(ctx, time.Now())
require.NoError(t, err)
require.NotNil(t, h)
@@ -94,9 +107,6 @@ func TestClientIntegration_VerifyLightBlockAtHeight(t *testing.T) {
require.NoError(t, err)
defer func() { require.NoError(t, closer(ctx)) }()
// give Tendermint time to generate some blocks
time.Sleep(5 * time.Second)
dbDir, err := ioutil.TempDir("", "light-client-test-verify-example")
require.NoError(t, err)
defer os.RemoveAll(dbDir)
@@ -106,7 +116,8 @@ func TestClientIntegration_VerifyLightBlockAtHeight(t *testing.T) {
primary, err := httpp.New(chainID, conf.RPC.ListenAddress)
require.NoError(t, err)
block, err := primary.LightBlock(ctx, 2)
// give Tendermint time to generate some blocks
block, err := waitForBlock(ctx, primary, 2)
require.NoError(t, err)
db, err := dbm.NewGoLevelDB("light-client-db", dbDir)
@@ -128,6 +139,10 @@ func TestClientIntegration_VerifyLightBlockAtHeight(t *testing.T) {
defer func() { require.NoError(t, c.Cleanup()) }()
// ensure Tendermint is at height 3 or higher
_, err = waitForBlock(ctx, primary, 3)
require.NoError(t, err)
_, err = c.VerifyLightBlockAtHeight(ctx, 3, time.Now())
require.NoError(t, err)
@@ -136,3 +151,23 @@ func TestClientIntegration_VerifyLightBlockAtHeight(t *testing.T) {
require.EqualValues(t, 3, h.Height)
}
func waitForBlock(ctx context.Context, p provider.Provider, height int64) (*types.LightBlock, error) {
for {
block, err := p.LightBlock(ctx, height)
switch err {
case nil:
return block, nil
// node isn't running yet, wait 1 second and repeat
case provider.ErrNoResponse, provider.ErrHeightTooHigh:
timer := time.NewTimer(1 * time.Second)
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-timer.C:
}
default:
return nil, err
}
}
}

View File

@@ -114,6 +114,7 @@ func (p *http) LightBlock(ctx context.Context, height int64) (*types.LightBlock,
sh, err := p.signedHeader(ctx, h)
if err != nil {
fmt.Println("nil signed header")
return nil, err
}
@@ -138,6 +139,9 @@ func (p *http) LightBlock(ctx context.Context, height int64) (*types.LightBlock,
SignedHeader: sh,
ValidatorSet: vs,
}
if lb.SignedHeader.Commit == nil {
fmt.Println(lb)
}
err = lb.ValidateBasic(p.chainID)
if err != nil {

View File

@@ -1,30 +0,0 @@
package mock
import (
"context"
"fmt"
"github.com/tendermint/tendermint/light/provider"
"github.com/tendermint/tendermint/types"
)
type deadMock struct {
id string
}
// NewDeadMock creates a mock provider that always errors. id is used in case of multiple providers.
func NewDeadMock(id string) provider.Provider {
return &deadMock{id: id}
}
func (p *deadMock) String() string {
return fmt.Sprintf("DeadMock-%s", p.id)
}
func (p *deadMock) LightBlock(_ context.Context, height int64) (*types.LightBlock, error) {
return nil, provider.ErrNoResponse
}
func (p *deadMock) ReportEvidence(_ context.Context, ev types.Evidence) error {
return provider.ErrNoResponse
}

View File

@@ -1,125 +0,0 @@
package mock
import (
"context"
"errors"
"fmt"
"strings"
"sync"
"time"
"github.com/tendermint/tendermint/light/provider"
"github.com/tendermint/tendermint/types"
)
type Mock struct {
id string
mtx sync.Mutex
headers map[int64]*types.SignedHeader
vals map[int64]*types.ValidatorSet
evidenceToReport map[string]types.Evidence // hash => evidence
latestHeight int64
}
var _ provider.Provider = (*Mock)(nil)
// New creates a mock provider with the given set of headers and validator
// sets.
func New(id string, headers map[int64]*types.SignedHeader, vals map[int64]*types.ValidatorSet) *Mock {
height := int64(0)
for h := range headers {
if h > height {
height = h
}
}
return &Mock{
id: id,
headers: headers,
vals: vals,
evidenceToReport: make(map[string]types.Evidence),
latestHeight: height,
}
}
func (p *Mock) String() string {
var headers strings.Builder
for _, h := range p.headers {
fmt.Fprintf(&headers, " %d:%X", h.Height, h.Hash())
}
var vals strings.Builder
for _, v := range p.vals {
fmt.Fprintf(&vals, " %X", v.Hash())
}
return fmt.Sprintf("Mock{id: %s, headers: %s, vals: %v}", p.id, headers.String(), vals.String())
}
func (p *Mock) LightBlock(ctx context.Context, height int64) (*types.LightBlock, error) {
p.mtx.Lock()
defer p.mtx.Unlock()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(10 * time.Millisecond):
}
var lb *types.LightBlock
if height > p.latestHeight {
return nil, provider.ErrHeightTooHigh
}
if height == 0 && len(p.headers) > 0 {
height = p.latestHeight
}
if _, ok := p.headers[height]; ok {
sh := p.headers[height]
vals := p.vals[height]
lb = &types.LightBlock{
SignedHeader: sh,
ValidatorSet: vals,
}
}
if lb == nil {
return nil, provider.ErrLightBlockNotFound
}
if lb.SignedHeader == nil || lb.ValidatorSet == nil {
return nil, provider.ErrBadLightBlock{Reason: errors.New("nil header or vals")}
}
if err := lb.ValidateBasic(lb.ChainID); err != nil {
return nil, provider.ErrBadLightBlock{Reason: err}
}
return lb, nil
}
func (p *Mock) ReportEvidence(_ context.Context, ev types.Evidence) error {
p.evidenceToReport[string(ev.Hash())] = ev
return nil
}
func (p *Mock) HasEvidence(ev types.Evidence) bool {
_, ok := p.evidenceToReport[string(ev.Hash())]
return ok
}
func (p *Mock) AddLightBlock(lb *types.LightBlock) {
p.mtx.Lock()
defer p.mtx.Unlock()
if err := lb.ValidateBasic(lb.ChainID); err != nil {
panic(fmt.Sprintf("unable to add light block, err: %v", err))
}
p.headers[lb.Height] = lb.SignedHeader
p.vals[lb.Height] = lb.ValidatorSet
if lb.Height > p.latestHeight {
p.latestHeight = lb.Height
}
}
func (p *Mock) Copy(id string) *Mock {
return New(id, p.headers, p.vals)
}

View File

@@ -0,0 +1,53 @@
// Code generated by mockery v0.0.0-dev. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
types "github.com/tendermint/tendermint/types"
)
// Provider is an autogenerated mock type for the Provider type
type Provider struct {
mock.Mock
}
// LightBlock provides a mock function with given fields: ctx, height
func (_m *Provider) LightBlock(ctx context.Context, height int64) (*types.LightBlock, error) {
ret := _m.Called(ctx, height)
var r0 *types.LightBlock
if rf, ok := ret.Get(0).(func(context.Context, int64) *types.LightBlock); ok {
r0 = rf(ctx, height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.LightBlock)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, int64) error); ok {
r1 = rf(ctx, height)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ReportEvidence provides a mock function with given fields: _a0, _a1
func (_m *Provider) ReportEvidence(_a0 context.Context, _a1 types.Evidence) error {
ret := _m.Called(_a0, _a1)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, types.Evidence) error); ok {
r0 = rf(_a0, _a1)
} else {
r0 = ret.Error(0)
}
return r0
}

View File

@@ -66,7 +66,7 @@ type nodeImpl struct {
eventBus *types.EventBus // pub/sub for services
stateStore sm.Store
blockStore *store.BlockStore // store the blockchain to disk
bcReactor service.Service // for fast-syncing
bcReactor service.Service // for block-syncing
mempoolReactor service.Service // for gossipping transactions
mempool mempool.Mempool
stateSync bool // whether the node should state sync on startup
@@ -225,9 +225,9 @@ func makeNode(config *cfg.Config,
}
}
// Determine whether we should do fast sync. This must happen after the handshake, since the
// Determine whether we should do block sync. This must happen after the handshake, since the
// app may modify the validator set, specifying ourself as the only validator.
fastSync := config.FastSyncMode && !onlyValidatorIsUs(state, pubKey)
blockSync := config.FastSyncMode && !onlyValidatorIsUs(state, pubKey)
logNodeStartupInfo(state, pubKey, logger, consensusLogger, config.Mode)
@@ -281,15 +281,15 @@ func makeNode(config *cfg.Config,
csReactorShim, csReactor, csState := createConsensusReactor(
config, state, blockExec, blockStore, mp, evPool,
privValidator, csMetrics, stateSync || fastSync, eventBus,
privValidator, csMetrics, stateSync || blockSync, eventBus,
peerManager, router, consensusLogger,
)
// Create the blockchain reactor. Note, we do not start fast sync if we're
// Create the blockchain reactor. Note, we do not start block sync if we're
// doing a state sync first.
bcReactorShim, bcReactor, err := createBlockchainReactor(
logger, config, state, blockExec, blockStore, csReactor,
peerManager, router, fastSync && !stateSync, csMetrics,
peerManager, router, blockSync && !stateSync, csMetrics,
)
if err != nil {
return nil, fmt.Errorf("could not create blockchain reactor: %w", err)
@@ -303,16 +303,16 @@ func makeNode(config *cfg.Config,
bcReactorForSwitch = bcReactor.(p2p.Reactor)
}
// Make ConsensusReactor. Don't enable fully if doing a state sync and/or fast sync first.
// Make ConsensusReactor. Don't enable fully if doing a state sync and/or block sync first.
// FIXME We need to update metrics here, since other reactors don't have access to them.
if stateSync {
csMetrics.StateSyncing.Set(1)
} else if fastSync {
csMetrics.FastSyncing.Set(1)
} else if blockSync {
csMetrics.BlockSyncing.Set(1)
}
// Set up state sync reactor, and schedule a sync if requested.
// FIXME The way we do phased startups (e.g. replay -> fast sync -> consensus) is very messy,
// FIXME The way we do phased startups (e.g. replay -> block sync -> consensus) is very messy,
// we should clean this whole thing up. See:
// https://github.com/tendermint/tendermint/issues/4644
var (
@@ -380,35 +380,37 @@ func makeNode(config *cfg.Config,
pexCh := pex.ChannelDescriptor()
transport.AddChannelDescriptors([]*p2p.ChannelDescriptor{&pexCh})
if config.P2P.DisableLegacy {
addrBook = nil
pexReactorV2, err = createPEXReactorV2(config, logger, peerManager, router)
if err != nil {
return nil, err
}
} else {
// setup Transport and Switch
sw = createSwitch(
config, transport, p2pMetrics, mpReactorShim, bcReactorForSwitch,
stateSyncReactorShim, csReactorShim, evReactorShim, proxyApp, nodeInfo, nodeKey, p2pLogger,
)
if config.P2P.PexReactor {
if config.P2P.DisableLegacy {
addrBook = nil
pexReactorV2, err = createPEXReactorV2(config, logger, peerManager, router)
if err != nil {
return nil, err
}
} else {
// setup Transport and Switch
sw = createSwitch(
config, transport, p2pMetrics, mpReactorShim, bcReactorForSwitch,
stateSyncReactorShim, csReactorShim, evReactorShim, proxyApp, nodeInfo, nodeKey, p2pLogger,
)
err = sw.AddPersistentPeers(strings.SplitAndTrimEmpty(config.P2P.PersistentPeers, ",", " "))
if err != nil {
return nil, fmt.Errorf("could not add peers from persistent-peers field: %w", err)
}
err = sw.AddPersistentPeers(strings.SplitAndTrimEmpty(config.P2P.PersistentPeers, ",", " "))
if err != nil {
return nil, fmt.Errorf("could not add peers from persistent-peers field: %w", err)
}
err = sw.AddUnconditionalPeerIDs(strings.SplitAndTrimEmpty(config.P2P.UnconditionalPeerIDs, ",", " "))
if err != nil {
return nil, fmt.Errorf("could not add peer ids from unconditional_peer_ids field: %w", err)
}
err = sw.AddUnconditionalPeerIDs(strings.SplitAndTrimEmpty(config.P2P.UnconditionalPeerIDs, ",", " "))
if err != nil {
return nil, fmt.Errorf("could not add peer ids from unconditional_peer_ids field: %w", err)
}
addrBook, err = createAddrBookAndSetOnSwitch(config, sw, p2pLogger, nodeKey)
if err != nil {
return nil, fmt.Errorf("could not create addrbook: %w", err)
}
addrBook, err = createAddrBookAndSetOnSwitch(config, sw, p2pLogger, nodeKey)
if err != nil {
return nil, fmt.Errorf("could not create addrbook: %w", err)
}
pexReactor = createPEXReactorAndAddToSwitch(addrBook, config, sw, logger)
pexReactor = createPEXReactorAndAddToSwitch(addrBook, config, sw, logger)
}
}
if config.RPC.PprofListenAddress != "" {
@@ -608,7 +610,7 @@ func (n *nodeImpl) OnStart() error {
}
if n.config.Mode != cfg.ModeSeed {
if n.config.FastSync.Version == cfg.BlockchainV0 {
if n.config.BlockSync.Version == cfg.BlockSyncV0 {
// Start the real blockchain reactor separately since the switch uses the shim.
if err := n.bcReactor.Start(); err != nil {
return err
@@ -651,7 +653,7 @@ func (n *nodeImpl) OnStart() error {
// Run state sync
if n.stateSync {
bcR, ok := n.bcReactor.(cs.FastSyncReactor)
bcR, ok := n.bcReactor.(cs.BlockSyncReactor)
if !ok {
return fmt.Errorf("this blockchain reactor does not support switching from state sync")
}
@@ -693,7 +695,7 @@ func (n *nodeImpl) OnStop() {
if n.config.Mode != cfg.ModeSeed {
// now stop the reactors
if n.config.FastSync.Version == cfg.BlockchainV0 {
if n.config.BlockSync.Version == cfg.BlockSyncV0 {
// Stop the real blockchain reactor separately since the switch uses the shim.
if err := n.bcReactor.Stop(); err != nil {
n.Logger.Error("failed to stop the blockchain reactor", "err", err)
@@ -786,8 +788,8 @@ func (n *nodeImpl) ConfigureRPC() (*rpccore.Environment, error) {
Logger: n.Logger.With("module", "rpc"),
Config: *n.config.RPC,
FastSyncReactor: n.bcReactor.(cs.FastSyncReactor),
Config: *n.config.RPC,
BlockSyncReactor: n.bcReactor.(cs.BlockSyncReactor),
}
if n.config.Mode == cfg.ModeValidator {
pubKey, err := n.privValidator.GetPubKey(context.TODO())
@@ -1031,14 +1033,14 @@ func (n *nodeImpl) NodeInfo() types.NodeInfo {
return n.nodeInfo
}
// startStateSync starts an asynchronous state sync process, then switches to fast sync mode.
// startStateSync starts an asynchronous state sync process, then switches to block sync mode.
func startStateSync(
ssR statesync.SyncReactor,
bcR cs.FastSyncReactor,
bcR cs.BlockSyncReactor,
conR cs.ConsSyncReactor,
sp statesync.StateProvider,
config *cfg.StateSyncConfig,
fastSync bool,
blockSync bool,
stateInitHeight int64,
eb *types.EventBus,
) error {
@@ -1072,17 +1074,17 @@ func startStateSync(
stateSyncLogger.Error("failed to emit the statesync start event", "err", err)
}
if fastSync {
if blockSync {
// FIXME Very ugly to have these metrics bleed through here.
conR.SetFastSyncingMetrics(1)
if err := bcR.SwitchToFastSync(state); err != nil {
stateSyncLogger.Error("failed to switch to fast sync", "err", err)
conR.SetBlockSyncingMetrics(1)
if err := bcR.SwitchToBlockSync(state); err != nil {
stateSyncLogger.Error("failed to switch to block sync", "err", err)
return
}
d := types.EventDataFastSyncStatus{Complete: false, Height: state.LastBlockHeight}
if err := eb.PublishEventFastSyncStatus(d); err != nil {
stateSyncLogger.Error("failed to emit the fastsync starting event", "err", err)
d := types.EventDataBlockSyncStatus{Complete: false, Height: state.LastBlockHeight}
if err := eb.PublishEventBlockSyncStatus(d); err != nil {
stateSyncLogger.Error("failed to emit the block sync starting event", "err", err)
}
} else {

View File

@@ -659,7 +659,7 @@ func loadStatefromGenesis(t *testing.T) sm.State {
func TestNodeStartStateSync(t *testing.T) {
mockSSR := &statesync.MockSyncReactor{}
mockFSR := &consmocks.FastSyncReactor{}
mockFSR := &consmocks.BlockSyncReactor{}
mockCSR := &consmocks.ConsSyncReactor{}
mockSP := &ssmocks.StateProvider{}
state := loadStatefromGenesis(t)

View File

@@ -16,8 +16,8 @@ import (
abci "github.com/tendermint/tendermint/abci/types"
cfg "github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/crypto"
bcv0 "github.com/tendermint/tendermint/internal/blockchain/v0"
bcv2 "github.com/tendermint/tendermint/internal/blockchain/v2"
bcv0 "github.com/tendermint/tendermint/internal/blocksync/v0"
bcv2 "github.com/tendermint/tendermint/internal/blocksync/v2"
cs "github.com/tendermint/tendermint/internal/consensus"
"github.com/tendermint/tendermint/internal/evidence"
"github.com/tendermint/tendermint/internal/mempool"
@@ -337,14 +337,14 @@ func createBlockchainReactor(
csReactor *cs.Reactor,
peerManager *p2p.PeerManager,
router *p2p.Router,
fastSync bool,
blockSync bool,
metrics *cs.Metrics,
) (*p2p.ReactorShim, service.Service, error) {
logger = logger.With("module", "blockchain")
switch config.FastSync.Version {
case cfg.BlockchainV0:
switch config.BlockSync.Version {
case cfg.BlockSyncV0:
reactorShim := p2p.NewReactorShim(logger, "BlockchainShim", bcv0.ChannelShims)
var (
@@ -362,7 +362,7 @@ func createBlockchainReactor(
reactor, err := bcv0.NewReactor(
logger, state.Copy(), blockExec, blockStore, csReactor,
channels[bcv0.BlockchainChannel], peerUpdates, fastSync,
channels[bcv0.BlockchainChannel], peerUpdates, blockSync,
metrics,
)
if err != nil {
@@ -371,11 +371,11 @@ func createBlockchainReactor(
return reactorShim, reactor, nil
case cfg.BlockchainV2:
return nil, nil, errors.New("fastsync version v2 is no longer supported. Please use v0")
case cfg.BlockSyncV2:
return nil, nil, errors.New("block sync version v2 is no longer supported. Please use v0")
default:
return nil, nil, fmt.Errorf("unknown fastsync version %s", config.FastSync.Version)
return nil, nil, fmt.Errorf("unknown block sync version %s", config.BlockSync.Version)
}
}
@@ -725,15 +725,15 @@ func makeNodeInfo(
}
var bcChannel byte
switch config.FastSync.Version {
case cfg.BlockchainV0:
switch config.BlockSync.Version {
case cfg.BlockSyncV0:
bcChannel = byte(bcv0.BlockchainChannel)
case cfg.BlockchainV2:
case cfg.BlockSyncV2:
bcChannel = bcv2.BlockchainChannel
default:
return types.NodeInfo{}, fmt.Errorf("unknown fastsync version %s", config.FastSync.Version)
return types.NodeInfo{}, fmt.Errorf("unknown blocksync version %s", config.BlockSync.Version)
}
nodeInfo := types.NodeInfo{

View File

@@ -186,17 +186,20 @@ message ResponseBeginBlock {
}
message ResponseCheckTx {
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"];
string codespace = 8;
string sender = 9;
int64 priority = 10;
string mempool_error = 11;
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"];
string codespace = 8;
string sender = 9;
int64 priority = 10;
// mempool_error is set by Tendermint.
// ABCI applictions creating a ResponseCheckTX should not set mempool_error.
string mempool_error = 11;
}
message ResponseDeliverTx {

View File

@@ -1,4 +1,4 @@
package blockchain
package blocksync
import (
"errors"

View File

@@ -1,4 +1,4 @@
package blockchain_test
package blocksync_test
import (
"encoding/hex"
@@ -8,7 +8,7 @@ import (
proto "github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/require"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
bcproto "github.com/tendermint/tendermint/proto/tendermint/blocksync"
"github.com/tendermint/tendermint/types"
)

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: tendermint/blockchain/types.proto
// source: tendermint/blocksync/types.proto
package blockchain
package blocksync
import (
fmt "fmt"
@@ -32,7 +32,7 @@ func (m *BlockRequest) Reset() { *m = BlockRequest{} }
func (m *BlockRequest) String() string { return proto.CompactTextString(m) }
func (*BlockRequest) ProtoMessage() {}
func (*BlockRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_2927480384e78499, []int{0}
return fileDescriptor_19b397c236e0fa07, []int{0}
}
func (m *BlockRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -77,7 +77,7 @@ func (m *NoBlockResponse) Reset() { *m = NoBlockResponse{} }
func (m *NoBlockResponse) String() string { return proto.CompactTextString(m) }
func (*NoBlockResponse) ProtoMessage() {}
func (*NoBlockResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_2927480384e78499, []int{1}
return fileDescriptor_19b397c236e0fa07, []int{1}
}
func (m *NoBlockResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -122,7 +122,7 @@ func (m *BlockResponse) Reset() { *m = BlockResponse{} }
func (m *BlockResponse) String() string { return proto.CompactTextString(m) }
func (*BlockResponse) ProtoMessage() {}
func (*BlockResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_2927480384e78499, []int{2}
return fileDescriptor_19b397c236e0fa07, []int{2}
}
func (m *BlockResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -166,7 +166,7 @@ func (m *StatusRequest) Reset() { *m = StatusRequest{} }
func (m *StatusRequest) String() string { return proto.CompactTextString(m) }
func (*StatusRequest) ProtoMessage() {}
func (*StatusRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_2927480384e78499, []int{3}
return fileDescriptor_19b397c236e0fa07, []int{3}
}
func (m *StatusRequest) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -205,7 +205,7 @@ func (m *StatusResponse) Reset() { *m = StatusResponse{} }
func (m *StatusResponse) String() string { return proto.CompactTextString(m) }
func (*StatusResponse) ProtoMessage() {}
func (*StatusResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_2927480384e78499, []int{4}
return fileDescriptor_19b397c236e0fa07, []int{4}
}
func (m *StatusResponse) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -262,7 +262,7 @@ func (m *Message) Reset() { *m = Message{} }
func (m *Message) String() string { return proto.CompactTextString(m) }
func (*Message) ProtoMessage() {}
func (*Message) Descriptor() ([]byte, []int) {
return fileDescriptor_2927480384e78499, []int{5}
return fileDescriptor_19b397c236e0fa07, []int{5}
}
func (m *Message) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -373,42 +373,41 @@ func (*Message) XXX_OneofWrappers() []interface{} {
}
func init() {
proto.RegisterType((*BlockRequest)(nil), "tendermint.blockchain.BlockRequest")
proto.RegisterType((*NoBlockResponse)(nil), "tendermint.blockchain.NoBlockResponse")
proto.RegisterType((*BlockResponse)(nil), "tendermint.blockchain.BlockResponse")
proto.RegisterType((*StatusRequest)(nil), "tendermint.blockchain.StatusRequest")
proto.RegisterType((*StatusResponse)(nil), "tendermint.blockchain.StatusResponse")
proto.RegisterType((*Message)(nil), "tendermint.blockchain.Message")
proto.RegisterType((*BlockRequest)(nil), "tendermint.blocksync.BlockRequest")
proto.RegisterType((*NoBlockResponse)(nil), "tendermint.blocksync.NoBlockResponse")
proto.RegisterType((*BlockResponse)(nil), "tendermint.blocksync.BlockResponse")
proto.RegisterType((*StatusRequest)(nil), "tendermint.blocksync.StatusRequest")
proto.RegisterType((*StatusResponse)(nil), "tendermint.blocksync.StatusResponse")
proto.RegisterType((*Message)(nil), "tendermint.blocksync.Message")
}
func init() { proto.RegisterFile("tendermint/blockchain/types.proto", fileDescriptor_2927480384e78499) }
func init() { proto.RegisterFile("tendermint/blocksync/types.proto", fileDescriptor_19b397c236e0fa07) }
var fileDescriptor_2927480384e78499 = []byte{
// 370 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x93, 0xc1, 0x4e, 0xfa, 0x40,
0x10, 0xc6, 0xdb, 0x7f, 0x81, 0x7f, 0x32, 0x50, 0x1a, 0x9b, 0xa8, 0xc4, 0x98, 0x46, 0xab, 0x12,
0x3d, 0xd8, 0x26, 0x78, 0x25, 0x1e, 0x38, 0x11, 0x13, 0x8c, 0xa9, 0xc6, 0x83, 0x17, 0xd2, 0xe2,
0x86, 0x36, 0x4a, 0x17, 0xd9, 0xed, 0xc1, 0xb7, 0xf0, 0x19, 0x7c, 0x1a, 0x8f, 0x1c, 0x3d, 0x1a,
0x78, 0x11, 0xc3, 0x6c, 0x29, 0x4b, 0x03, 0xf5, 0xb6, 0x3b, 0xfd, 0xe6, 0x37, 0xdf, 0x7e, 0x99,
0xc2, 0x31, 0x27, 0xf1, 0x33, 0x99, 0x8c, 0xa2, 0x98, 0xbb, 0xc1, 0x2b, 0x1d, 0xbc, 0x0c, 0x42,
0x3f, 0x8a, 0x5d, 0xfe, 0x3e, 0x26, 0xcc, 0x19, 0x4f, 0x28, 0xa7, 0xe6, 0xee, 0x4a, 0xe2, 0xac,
0x24, 0x07, 0x87, 0x52, 0x27, 0xca, 0x45, 0xbf, 0x68, 0xb2, 0x9b, 0x50, 0xeb, 0x2c, 0xae, 0x1e,
0x79, 0x4b, 0x08, 0xe3, 0xe6, 0x1e, 0x54, 0x42, 0x12, 0x0d, 0x43, 0xde, 0x50, 0x8f, 0xd4, 0x73,
0xcd, 0x4b, 0x6f, 0xf6, 0x05, 0x18, 0xb7, 0x34, 0x55, 0xb2, 0x31, 0x8d, 0x19, 0xd9, 0x2a, 0xbd,
0x06, 0x7d, 0x5d, 0x78, 0x09, 0x65, 0x1c, 0x89, 0xba, 0x6a, 0x6b, 0xdf, 0x91, 0x8c, 0x8a, 0x07,
0x08, 0xbd, 0x50, 0xd9, 0x06, 0xe8, 0xf7, 0xdc, 0xe7, 0x09, 0x4b, 0x3d, 0xd9, 0x6d, 0xa8, 0x2f,
0x0b, 0xc5, 0xa3, 0x4d, 0x13, 0x4a, 0x81, 0xcf, 0x48, 0xe3, 0x1f, 0x56, 0xf1, 0x6c, 0x7f, 0x6a,
0xf0, 0xbf, 0x47, 0x18, 0xf3, 0x87, 0xc4, 0xbc, 0x01, 0x1d, 0x67, 0xf4, 0x27, 0x02, 0x9d, 0x3a,
0x3a, 0x71, 0x36, 0x46, 0xe7, 0xc8, 0xc9, 0x74, 0x15, 0xaf, 0x16, 0xc8, 0x49, 0x3d, 0xc0, 0x4e,
0x4c, 0xfb, 0x4b, 0x9c, 0x30, 0x86, 0x83, 0xab, 0xad, 0xe6, 0x16, 0x5e, 0x2e, 0xc1, 0xae, 0xe2,
0x19, 0x71, 0x2e, 0xd4, 0x1e, 0xd4, 0x73, 0x48, 0x0d, 0x91, 0xa7, 0xc5, 0x16, 0x33, 0xa0, 0x1e,
0xe4, 0x71, 0x0c, 0xa3, 0xcb, 0x5e, 0x5c, 0x2a, 0xc4, 0xad, 0x05, 0xbf, 0xc0, 0x31, 0xb9, 0x60,
0xde, 0x81, 0x91, 0xe1, 0x52, 0x7b, 0x65, 0xe4, 0x9d, 0xfd, 0xc1, 0xcb, 0xfc, 0xd5, 0xd9, 0x5a,
0xa5, 0x53, 0x06, 0x8d, 0x25, 0xa3, 0xce, 0xe3, 0xd7, 0xcc, 0x52, 0xa7, 0x33, 0x4b, 0xfd, 0x99,
0x59, 0xea, 0xc7, 0xdc, 0x52, 0xa6, 0x73, 0x4b, 0xf9, 0x9e, 0x5b, 0xca, 0x53, 0x7b, 0x18, 0xf1,
0x30, 0x09, 0x9c, 0x01, 0x1d, 0xb9, 0xf2, 0x26, 0xaf, 0x8e, 0xb8, 0xc8, 0xee, 0xc6, 0xff, 0x23,
0xa8, 0xe0, 0xc7, 0xab, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5e, 0x59, 0x07, 0xbd, 0x3f, 0x03,
0x00, 0x00,
var fileDescriptor_19b397c236e0fa07 = []byte{
// 368 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0x4d, 0x4f, 0xfa, 0x40,
0x10, 0xc6, 0xdb, 0x7f, 0x81, 0x7f, 0x32, 0x50, 0x1a, 0x1b, 0xa3, 0xc4, 0x98, 0x86, 0xd4, 0x97,
0xe8, 0xc1, 0x36, 0xc1, 0xa3, 0xc6, 0x03, 0x27, 0x4c, 0x7c, 0x49, 0x4a, 0xbc, 0x78, 0x21, 0x14,
0x37, 0x40, 0x94, 0x2e, 0x32, 0xdb, 0x03, 0xdf, 0xc2, 0x2f, 0xe0, 0xf7, 0xf1, 0xc8, 0xd1, 0xa3,
0x81, 0x2f, 0x62, 0x98, 0x2d, 0x65, 0x69, 0xb0, 0xb7, 0xdd, 0xe9, 0x33, 0xbf, 0x79, 0xfa, 0x64,
0x16, 0xea, 0x82, 0x45, 0x2f, 0x6c, 0x32, 0x1a, 0x46, 0xc2, 0x0f, 0xdf, 0x78, 0xef, 0x15, 0xa7,
0x51, 0xcf, 0x17, 0xd3, 0x31, 0x43, 0x6f, 0x3c, 0xe1, 0x82, 0xdb, 0xbb, 0x6b, 0x85, 0x97, 0x2a,
0x0e, 0x0e, 0x95, 0x3e, 0x52, 0xcb, 0x6e, 0xd9, 0xe3, 0x9e, 0x42, 0xa5, 0xb9, 0xbc, 0x06, 0xec,
0x3d, 0x66, 0x28, 0xec, 0x3d, 0x28, 0x0d, 0xd8, 0xb0, 0x3f, 0x10, 0x35, 0xbd, 0xae, 0x9f, 0x19,
0x41, 0x72, 0x73, 0xcf, 0xc1, 0x7a, 0xe0, 0x89, 0x12, 0xc7, 0x3c, 0x42, 0xf6, 0xa7, 0xf4, 0x06,
0xcc, 0x4d, 0xe1, 0x05, 0x14, 0x69, 0x24, 0xe9, 0xca, 0x8d, 0x7d, 0x4f, 0xf1, 0x29, 0xfd, 0x4b,
0xbd, 0x54, 0xb9, 0x16, 0x98, 0x6d, 0xd1, 0x15, 0x31, 0x26, 0x9e, 0xdc, 0x6b, 0xa8, 0xae, 0x0a,
0xf9, 0xa3, 0x6d, 0x1b, 0x0a, 0x61, 0x17, 0x59, 0xed, 0x1f, 0x55, 0xe9, 0xec, 0x7e, 0x1a, 0xf0,
0xff, 0x9e, 0x21, 0x76, 0xfb, 0xcc, 0xbe, 0x05, 0x93, 0x66, 0x74, 0x26, 0x12, 0x9d, 0x38, 0x72,
0xbd, 0x6d, 0xc9, 0x79, 0x6a, 0x30, 0x2d, 0x2d, 0xa8, 0x84, 0x6a, 0x50, 0x6d, 0xd8, 0x89, 0x78,
0x67, 0x45, 0x93, 0xbe, 0x68, 0x6e, 0xb9, 0x71, 0xb2, 0x1d, 0x97, 0xc9, 0xaf, 0xa5, 0x05, 0x56,
0x94, 0x89, 0xf4, 0x0e, 0xaa, 0x19, 0xa2, 0x41, 0xc4, 0xa3, 0x5c, 0x83, 0x29, 0xcf, 0x0c, 0xb3,
0x34, 0xa4, 0xdc, 0xd2, 0xdf, 0x2d, 0xe4, 0xd1, 0x36, 0x42, 0x5f, 0xd2, 0x50, 0x2d, 0xd8, 0x8f,
0x60, 0xa5, 0xb4, 0xc4, 0x5c, 0x91, 0x70, 0xc7, 0xf9, 0xb8, 0xd4, 0x5d, 0x15, 0x37, 0x2a, 0xcd,
0x22, 0x18, 0x18, 0x8f, 0x9a, 0x4f, 0x5f, 0x73, 0x47, 0x9f, 0xcd, 0x1d, 0xfd, 0x67, 0xee, 0xe8,
0x1f, 0x0b, 0x47, 0x9b, 0x2d, 0x1c, 0xed, 0x7b, 0xe1, 0x68, 0xcf, 0x57, 0xfd, 0xa1, 0x18, 0xc4,
0xa1, 0xd7, 0xe3, 0x23, 0x5f, 0x5d, 0xe2, 0xf5, 0x91, 0x76, 0xd8, 0xdf, 0xf6, 0x30, 0xc2, 0x12,
0x7d, 0xbb, 0xfc, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xf5, 0x1c, 0xa3, 0x45, 0x37, 0x03, 0x00, 0x00,
}
func (m *BlockRequest) Marshal() (dAtA []byte, err error) {

View File

@@ -1,7 +1,7 @@
syntax = "proto3";
package tendermint.blockchain;
package tendermint.blocksync;
option go_package = "github.com/tendermint/tendermint/proto/tendermint/blockchain";
option go_package = "github.com/tendermint/tendermint/proto/tendermint/blocksync";
import "tendermint/types/block.proto";

View File

@@ -136,6 +136,8 @@ func (env *Environment) Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes
// use a non-canonical commit
if height == env.BlockStore.Height() {
commit := env.BlockStore.LoadSeenCommit(height)
fmt.Println("not commited height")
fmt.Println(commit)
return ctypes.NewResultCommit(&header, commit, false), nil
}

View File

@@ -81,7 +81,7 @@ type Environment struct {
ConsensusReactor *consensus.Reactor
EventBus *types.EventBus // thread safe
Mempool mempl.Mempool
FastSyncReactor consensus.FastSyncReactor
BlockSyncReactor consensus.BlockSyncReactor
Logger log.Logger

View File

@@ -69,10 +69,10 @@ func (env *Environment) Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, err
EarliestAppHash: earliestAppHash,
EarliestBlockHeight: earliestBlockHeight,
EarliestBlockTime: time.Unix(0, earliestBlockTimeNano),
MaxPeerBlockHeight: env.FastSyncReactor.GetMaxPeerBlockHeight(),
MaxPeerBlockHeight: env.BlockSyncReactor.GetMaxPeerBlockHeight(),
CatchingUp: env.ConsensusReactor.WaitSync(),
TotalSyncedTime: env.FastSyncReactor.GetTotalSyncedTime(),
RemainingTime: env.FastSyncReactor.GetRemainingSyncTime(),
TotalSyncedTime: env.BlockSyncReactor.GetTotalSyncedTime(),
RemainingTime: env.BlockSyncReactor.GetRemainingSyncTime(),
},
ValidatorInfo: validatorInfo,
}

View File

@@ -16,6 +16,8 @@ const (
PSQL EventSinkType = "psql"
)
//go:generate mockery --case underscore --name EventSink
// EventSink interface is defined the APIs for the IndexerService to interact with the data store,
// including the block/transaction indexing and the search functions.
//

194
state/mocks/block_store.go Normal file
View File

@@ -0,0 +1,194 @@
// Code generated by mockery 2.7.5. DO NOT EDIT.
package mocks
import (
mock "github.com/stretchr/testify/mock"
types "github.com/tendermint/tendermint/types"
)
// BlockStore is an autogenerated mock type for the BlockStore type
type BlockStore struct {
mock.Mock
}
// Base provides a mock function with given fields:
func (_m *BlockStore) Base() int64 {
ret := _m.Called()
var r0 int64
if rf, ok := ret.Get(0).(func() int64); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(int64)
}
return r0
}
// Height provides a mock function with given fields:
func (_m *BlockStore) Height() int64 {
ret := _m.Called()
var r0 int64
if rf, ok := ret.Get(0).(func() int64); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(int64)
}
return r0
}
// LoadBaseMeta provides a mock function with given fields:
func (_m *BlockStore) LoadBaseMeta() *types.BlockMeta {
ret := _m.Called()
var r0 *types.BlockMeta
if rf, ok := ret.Get(0).(func() *types.BlockMeta); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.BlockMeta)
}
}
return r0
}
// LoadBlock provides a mock function with given fields: height
func (_m *BlockStore) LoadBlock(height int64) *types.Block {
ret := _m.Called(height)
var r0 *types.Block
if rf, ok := ret.Get(0).(func(int64) *types.Block); ok {
r0 = rf(height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.Block)
}
}
return r0
}
// LoadBlockByHash provides a mock function with given fields: hash
func (_m *BlockStore) LoadBlockByHash(hash []byte) *types.Block {
ret := _m.Called(hash)
var r0 *types.Block
if rf, ok := ret.Get(0).(func([]byte) *types.Block); ok {
r0 = rf(hash)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.Block)
}
}
return r0
}
// LoadBlockCommit provides a mock function with given fields: height
func (_m *BlockStore) LoadBlockCommit(height int64) *types.Commit {
ret := _m.Called(height)
var r0 *types.Commit
if rf, ok := ret.Get(0).(func(int64) *types.Commit); ok {
r0 = rf(height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.Commit)
}
}
return r0
}
// LoadBlockMeta provides a mock function with given fields: height
func (_m *BlockStore) LoadBlockMeta(height int64) *types.BlockMeta {
ret := _m.Called(height)
var r0 *types.BlockMeta
if rf, ok := ret.Get(0).(func(int64) *types.BlockMeta); ok {
r0 = rf(height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.BlockMeta)
}
}
return r0
}
// LoadBlockPart provides a mock function with given fields: height, index
func (_m *BlockStore) LoadBlockPart(height int64, index int) *types.Part {
ret := _m.Called(height, index)
var r0 *types.Part
if rf, ok := ret.Get(0).(func(int64, int) *types.Part); ok {
r0 = rf(height, index)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.Part)
}
}
return r0
}
// LoadSeenCommit provides a mock function with given fields: height
func (_m *BlockStore) LoadSeenCommit(height int64) *types.Commit {
ret := _m.Called(height)
var r0 *types.Commit
if rf, ok := ret.Get(0).(func(int64) *types.Commit); ok {
r0 = rf(height)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.Commit)
}
}
return r0
}
// PruneBlocks provides a mock function with given fields: height
func (_m *BlockStore) PruneBlocks(height int64) (uint64, error) {
ret := _m.Called(height)
var r0 uint64
if rf, ok := ret.Get(0).(func(int64) uint64); ok {
r0 = rf(height)
} else {
r0 = ret.Get(0).(uint64)
}
var r1 error
if rf, ok := ret.Get(1).(func(int64) error); ok {
r1 = rf(height)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SaveBlock provides a mock function with given fields: block, blockParts, seenCommit
func (_m *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) {
_m.Called(block, blockParts, seenCommit)
}
// Size provides a mock function with given fields:
func (_m *BlockStore) Size() int64 {
ret := _m.Called()
var r0 int64
if rf, ok := ret.Get(0).(func() int64); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(int64)
}
return r0
}

167
state/mocks/event_sink.go Normal file
View File

@@ -0,0 +1,167 @@
// Code generated by mockery 2.7.5. DO NOT EDIT.
package mocks
import (
context "context"
mock "github.com/stretchr/testify/mock"
indexer "github.com/tendermint/tendermint/state/indexer"
query "github.com/tendermint/tendermint/libs/pubsub/query"
tenderminttypes "github.com/tendermint/tendermint/types"
types "github.com/tendermint/tendermint/abci/types"
)
// EventSink is an autogenerated mock type for the EventSink type
type EventSink struct {
mock.Mock
}
// GetTxByHash provides a mock function with given fields: _a0
func (_m *EventSink) GetTxByHash(_a0 []byte) (*types.TxResult, error) {
ret := _m.Called(_a0)
var r0 *types.TxResult
if rf, ok := ret.Get(0).(func([]byte) *types.TxResult); ok {
r0 = rf(_a0)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*types.TxResult)
}
}
var r1 error
if rf, ok := ret.Get(1).(func([]byte) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// HasBlock provides a mock function with given fields: _a0
func (_m *EventSink) HasBlock(_a0 int64) (bool, error) {
ret := _m.Called(_a0)
var r0 bool
if rf, ok := ret.Get(0).(func(int64) bool); ok {
r0 = rf(_a0)
} else {
r0 = ret.Get(0).(bool)
}
var r1 error
if rf, ok := ret.Get(1).(func(int64) error); ok {
r1 = rf(_a0)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// IndexBlockEvents provides a mock function with given fields: _a0
func (_m *EventSink) IndexBlockEvents(_a0 tenderminttypes.EventDataNewBlockHeader) error {
ret := _m.Called(_a0)
var r0 error
if rf, ok := ret.Get(0).(func(tenderminttypes.EventDataNewBlockHeader) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// IndexTxEvents provides a mock function with given fields: _a0
func (_m *EventSink) IndexTxEvents(_a0 []*types.TxResult) error {
ret := _m.Called(_a0)
var r0 error
if rf, ok := ret.Get(0).(func([]*types.TxResult) error); ok {
r0 = rf(_a0)
} else {
r0 = ret.Error(0)
}
return r0
}
// SearchBlockEvents provides a mock function with given fields: _a0, _a1
func (_m *EventSink) SearchBlockEvents(_a0 context.Context, _a1 *query.Query) ([]int64, error) {
ret := _m.Called(_a0, _a1)
var r0 []int64
if rf, ok := ret.Get(0).(func(context.Context, *query.Query) []int64); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]int64)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *query.Query) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SearchTxEvents provides a mock function with given fields: _a0, _a1
func (_m *EventSink) SearchTxEvents(_a0 context.Context, _a1 *query.Query) ([]*types.TxResult, error) {
ret := _m.Called(_a0, _a1)
var r0 []*types.TxResult
if rf, ok := ret.Get(0).(func(context.Context, *query.Query) []*types.TxResult); ok {
r0 = rf(_a0, _a1)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*types.TxResult)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *query.Query) error); ok {
r1 = rf(_a0, _a1)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// Stop provides a mock function with given fields:
func (_m *EventSink) Stop() 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
}
// Type provides a mock function with given fields:
func (_m *EventSink) Type() indexer.EventSinkType {
ret := _m.Called()
var r0 indexer.EventSinkType
if rf, ok := ret.Get(0).(func() indexer.EventSinkType); ok {
r0 = rf()
} else {
r0 = ret.Get(0).(indexer.EventSinkType)
}
return r0
}

View File

@@ -9,6 +9,8 @@ import (
// NOTE: Interfaces used by RPC must be thread safe!
//------------------------------------------------------
//go:generate mockery --case underscore --name BlockStore
//------------------------------------------------------
// blockstore

View File

@@ -268,6 +268,7 @@ func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit {
panic(err)
}
if len(bz) == 0 {
fmt.Println("nil bytes")
return nil
}
err = proto.Unmarshal(bz, pbc)

View File

@@ -23,7 +23,7 @@ The above should hold for any arbitrary, valid network configuration, and that c
A testnet configuration is specified as a TOML testnet manifest (see below). The testnet runner uses the manifest to configure a set of Docker containers and start them in some order. The manifests can be written manually (to test specific configurations) or generated randomly by the testnet generator (to test a wide range of configuration permutations).
When running a testnet, the runner will first start the Docker nodes in some sequence, submit random transactions, and wait for the nodes to come online and the first blocks to be produced. This may involve e.g. waiting for nodes to fast sync and/or state sync. If specified, it will then run any misbehaviors (e.g. double-signing) and perturbations (e.g. killing or disconnecting nodes). It then waits for the testnet to stabilize, with all nodes online and having reached the latest height.
When running a testnet, the runner will first start the Docker nodes in some sequence, submit random transactions, and wait for the nodes to come online and the first blocks to be produced. This may involve e.g. waiting for nodes to block sync and/or state sync. If specified, it will then run any misbehaviors (e.g. double-signing) and perturbations (e.g. killing or disconnecting nodes). It then waits for the testnet to stabilize, with all nodes online and having reached the latest height.
Once the testnet stabilizes, a set of Go end-to-end tests are run against the live testnet to verify network invariants (for example that blocks are identical across nodes). These use the RPC client to interact with the network, and should consider the entire network as a black box (i.e. it should not test any network or node internals, only externally visible behavior via RPC). The tests may use the `testNode()` helper to run parallel tests against each individual testnet node, and/or inspect the full blockchain history via `fetchBlockChain()`.

View File

@@ -30,7 +30,7 @@ var (
nodeABCIProtocols = uniformChoice{"unix", "tcp", "builtin", "grpc"}
nodePrivvalProtocols = uniformChoice{"file", "unix", "tcp", "grpc"}
// FIXME: v2 disabled due to flake
nodeFastSyncs = uniformChoice{"v0"} // "v2"
nodeBlockSyncs = uniformChoice{"v0"} // "v2"
nodeMempools = uniformChoice{"v0", "v1"}
nodeStateSyncs = uniformChoice{false, true}
nodePersistIntervals = uniformChoice{0, 1, 5}
@@ -273,7 +273,7 @@ func generateNode(
Database: nodeDatabases.Choose(r).(string),
ABCIProtocol: nodeABCIProtocols.Choose(r).(string),
PrivvalProtocol: nodePrivvalProtocols.Choose(r).(string),
FastSync: nodeFastSyncs.Choose(r).(string),
BlockSync: nodeBlockSyncs.Choose(r).(string),
Mempool: nodeMempools.Choose(r).(string),
StateSync: nodeStateSyncs.Choose(r).(bool) && startAt > 0,
PersistInterval: ptrUint64(uint64(nodePersistIntervals.Choose(r).(int))),
@@ -311,7 +311,7 @@ func generateNode(
}
if node.StateSync {
node.FastSync = "v0"
node.BlockSync = "v0"
}
return &node

View File

@@ -30,11 +30,6 @@ validator05 = 50
[node.seed01]
mode = "seed"
perturb = ["restart"]
seeds = ["seed02"]
[node.seed02]
mode = "seed"
seeds = ["seed01"]
[node.validator01]
perturb = ["disconnect"]
@@ -47,7 +42,7 @@ database = "boltdb"
persist_interval = 0
perturb = ["restart"]
privval_protocol = "tcp"
seeds = ["seed02"]
seeds = ["seed01"]
[node.validator03]
database = "badgerdb"
@@ -66,29 +61,21 @@ perturb = ["pause"]
[node.validator05]
database = "cleveldb"
fast_sync = "v0"
seeds = ["seed02"]
block_sync = "v0"
seeds = ["seed01"]
start_at = 1005 # Becomes part of the validator set at 1010
abci_protocol = "grpc"
perturb = ["kill", "pause", "disconnect", "restart"]
perturb = ["pause", "disconnect", "restart"]
privval_protocol = "tcp"
[node.full01]
mode = "full"
start_at = 1010
# FIXME: should be v2, disabled due to flake
fast_sync = "v0"
block_sync = "v0"
persistent_peers = ["validator01", "validator02", "validator03", "validator04", "validator05"]
perturb = ["restart"]
retain_blocks = 7
[node.full02]
mode = "full"
start_at = 1015
# FIXME: should be v2, disabled due to flake
fast_sync = "v0"
perturb = ["restart"]
seeds = ["seed01"]
state_sync = true
[node.light01]

View File

@@ -106,9 +106,9 @@ type ManifestNode struct {
// runner will wait for the network to reach at least this block height.
StartAt int64 `toml:"start_at"`
// FastSync specifies the fast sync mode: "" (disable), "v0" or "v2".
// BlockSync specifies the block sync mode: "" (disable), "v0" or "v2".
// Defaults to disabled.
FastSync string `toml:"fast_sync"`
BlockSync string `toml:"block_sync"`
// Mempool specifies which version of mempool to use. Either "v0" or "v1"
Mempool string `toml:"mempool_version"`

View File

@@ -79,7 +79,7 @@ type Node struct {
IP net.IP
ProxyPort uint32
StartAt int64
FastSync string
BlockSync string
Mempool string
StateSync bool
Database string
@@ -168,7 +168,7 @@ func LoadTestnet(file string) (*Testnet, error) {
ABCIProtocol: ProtocolBuiltin,
PrivvalProtocol: ProtocolFile,
StartAt: nodeManifest.StartAt,
FastSync: nodeManifest.FastSync,
BlockSync: nodeManifest.BlockSync,
Mempool: nodeManifest.Mempool,
StateSync: nodeManifest.StateSync,
PersistInterval: 1,
@@ -328,10 +328,10 @@ func (n Node) Validate(testnet Testnet) error {
}
}
}
switch n.FastSync {
switch n.BlockSync {
case "", "v0", "v2":
default:
return fmt.Errorf("invalid fast sync setting %q", n.FastSync)
return fmt.Errorf("invalid block sync setting %q", n.BlockSync)
}
switch n.Mempool {
case "", "v0", "v1":

View File

@@ -296,10 +296,10 @@ func MakeConfig(node *e2e.Node) (*config.Config, error) {
cfg.Mempool.Version = node.Mempool
}
if node.FastSync == "" {
if node.BlockSync == "" {
cfg.FastSyncMode = false
} else {
cfg.FastSync.Version = node.FastSync
cfg.BlockSync.Version = node.BlockSync
}
if node.StateSync {

View File

@@ -1,8 +1,15 @@
#!/usr/bin/make -f
.PHONY: fuzz-mempool
fuzz-mempool:
cd mempool && \
.PHONY: fuzz-mempool-v1
fuzz-mempool-v1:
cd mempool/v1 && \
rm -f *-fuzz.zip && \
go-fuzz-build && \
go-fuzz
.PHONY: fuzz-mempool-v0
fuzz-mempool-v0:
cd mempool/v0 && \
rm -f *-fuzz.zip && \
go-fuzz-build && \
go-fuzz
@@ -37,3 +44,9 @@ fuzz-rpc-server:
rm -f *-fuzz.zip && \
go-fuzz-build && \
go-fuzz
clean:
find . -name corpus -type d -exec rm -rf {} +;
find . -name crashers -type d -exec rm -rf {} +;
find . -name suppressions -type d -exec rm -rf {} +;
find . -name *\.zip -type f -delete

View File

@@ -1,4 +1,4 @@
package checktx
package v0
import (
"context"

View File

@@ -0,0 +1,33 @@
package v0_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
mempoolv0 "github.com/tendermint/tendermint/test/fuzz/mempool/v0"
)
const testdataCasesDir = "testdata/cases"
func TestMempoolTestdataCases(t *testing.T) {
entries, err := os.ReadDir(testdataCasesDir)
require.NoError(t, err)
for _, e := range entries {
entry := e
t.Run(entry.Name(), func(t *testing.T) {
defer func() {
r := recover()
require.Nilf(t, r, "testdata/cases test panic")
}()
f, err := os.Open(filepath.Join(testdataCasesDir, entry.Name()))
require.NoError(t, err)
input, err := ioutil.ReadAll(f)
require.NoError(t, err)
mempoolv0.Fuzz(input)
})
}
}

View File

View File

@@ -0,0 +1,37 @@
package v1
import (
"context"
"github.com/tendermint/tendermint/abci/example/kvstore"
"github.com/tendermint/tendermint/config"
"github.com/tendermint/tendermint/internal/mempool"
mempoolv1 "github.com/tendermint/tendermint/internal/mempool/v0"
"github.com/tendermint/tendermint/proxy"
)
var mp mempool.Mempool
func init() {
app := kvstore.NewApplication()
cc := proxy.NewLocalClientCreator(app)
appConnMem, _ := cc.NewABCIClient()
err := appConnMem.Start()
if err != nil {
panic(err)
}
cfg := config.DefaultMempoolConfig()
cfg.Broadcast = false
mp = mempoolv1.NewCListMempool(cfg, appConnMem, 0)
}
func Fuzz(data []byte) int {
err := mp.CheckTx(context.Background(), data, nil, mempool.TxInfo{})
if err != nil {
return 0
}
return 1
}

View File

@@ -0,0 +1,33 @@
package v1_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
mempoolv1 "github.com/tendermint/tendermint/test/fuzz/mempool/v1"
)
const testdataCasesDir = "testdata/cases"
func TestMempoolTestdataCases(t *testing.T) {
entries, err := os.ReadDir(testdataCasesDir)
require.NoError(t, err)
for _, e := range entries {
entry := e
t.Run(entry.Name(), func(t *testing.T) {
defer func() {
r := recover()
require.Nilf(t, r, "testdata/cases test panic")
}()
f, err := os.Open(filepath.Join(testdataCasesDir, entry.Name()))
require.NoError(t, err)
input, err := ioutil.ReadAll(f)
require.NoError(t, err)
mempoolv1.Fuzz(input)
})
}
}

View File

View File

@@ -1,5 +1,5 @@
// nolint: gosec
package addr
package addrbook
import (
"encoding/json"

View File

@@ -0,0 +1,33 @@
package addrbook_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/test/fuzz/p2p/addrbook"
)
const testdataCasesDir = "testdata/cases"
func TestAddrbookTestdataCases(t *testing.T) {
entries, err := os.ReadDir(testdataCasesDir)
require.NoError(t, err)
for _, e := range entries {
entry := e
t.Run(entry.Name(), func(t *testing.T) {
defer func() {
r := recover()
require.Nilf(t, r, "testdata/cases test panic")
}()
f, err := os.Open(filepath.Join(testdataCasesDir, entry.Name()))
require.NoError(t, err)
input, err := ioutil.ReadAll(f)
require.NoError(t, err)
addrbook.Fuzz(input)
})
}
}

View File

View File

@@ -0,0 +1,33 @@
package pex_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/test/fuzz/p2p/pex"
)
const testdataCasesDir = "testdata/cases"
func TestPexTestdataCases(t *testing.T) {
entries, err := os.ReadDir(testdataCasesDir)
require.NoError(t, err)
for _, e := range entries {
entry := e
t.Run(entry.Name(), func(t *testing.T) {
defer func() {
r := recover()
require.Nilf(t, r, "testdata/cases test panic")
}()
f, err := os.Open(filepath.Join(testdataCasesDir, entry.Name()))
require.NoError(t, err)
input, err := ioutil.ReadAll(f)
require.NoError(t, err)
pex.Fuzz(input)
})
}
}

View File

View File

@@ -0,0 +1,33 @@
package secretconnection_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/test/fuzz/p2p/secretconnection"
)
const testdataCasesDir = "testdata/cases"
func TestSecretConnectionTestdataCases(t *testing.T) {
entries, err := os.ReadDir(testdataCasesDir)
require.NoError(t, err)
for _, e := range entries {
entry := e
t.Run(entry.Name(), func(t *testing.T) {
defer func() {
r := recover()
require.Nilf(t, r, "testdata/cases test panic")
}()
f, err := os.Open(filepath.Join(testdataCasesDir, entry.Name()))
require.NoError(t, err)
input, err := ioutil.ReadAll(f)
require.NoError(t, err)
secretconnection.Fuzz(input)
})
}
}

View File

View File

@@ -0,0 +1,33 @@
package server_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/test/fuzz/rpc/jsonrpc/server"
)
const testdataCasesDir = "testdata/cases"
func TestServerTestdataCases(t *testing.T) {
entries, err := os.ReadDir(testdataCasesDir)
require.NoError(t, err)
for _, e := range entries {
entry := e
t.Run(entry.Name(), func(t *testing.T) {
defer func() {
r := recover()
require.Nilf(t, r, "testdata/cases test panic")
}()
f, err := os.Open(filepath.Join(testdataCasesDir, entry.Name()))
require.NoError(t, err)
input, err := ioutil.ReadAll(f)
require.NoError(t, err)
server.Fuzz(input)
})
}
}

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