diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d3a6a8a71..862ef598a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,11 +27,13 @@ jobs: version: latest args: build --skip-validate # skip validate skips initial sanity checks in order to be able to fully run + - run: echo https://github.com/tendermint/tendermint/blob/${GITHUB_REF#refs/tags/}/CHANGELOG.md#${GITHUB_REF#refs/tags/} > ../release_notes.md + - name: Release uses: goreleaser/goreleaser-action@v2 if: startsWith(github.ref, 'refs/tags/') with: version: latest - args: release --rm-dist + args: release --rm-dist --release-notes=../release_notes.md env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 07d049cf2..73fee3289 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ Friendly reminder: We have a [bug bounty program](https://hackerone.com/cosmos). +## v0.35.3 + +April 8, 2022 + +### FEATURES + +- [cli] [\#8081](https://github.com/tendermint/tendermint/pull/8081) add a safer-to-use `reset-state` command. (@marbar3778) + +### IMPROVEMENTS + +- [consensus] [\#8138](https://github.com/tendermint/tendermint/pull/8138) change lock handling in reactor and handleMsg for RoundState. (@williambanfield) + +### BUG FIXES + +- [cli] [\#8276](https://github.com/tendermint/tendermint/pull/8276) scmigrate: ensure target key is correctly renamed. (@creachadair) + ## v0.35.2 February 28, 2022 diff --git a/internal/consensus/reactor.go b/internal/consensus/reactor.go index c73f998d8..18589c93a 100644 --- a/internal/consensus/reactor.go +++ b/internal/consensus/reactor.go @@ -211,7 +211,6 @@ func (r *Reactor) OnStart(ctx context.Context) error { go r.peerStatsRoutine(ctx, peerUpdates) r.subscribeToBroadcastEvents(chBundle.state) - go r.updateRoundStateRoutine() if !r.WaitSync() { if err := r.state.Start(ctx); err != nil { @@ -219,6 +218,8 @@ func (r *Reactor) OnStart(ctx context.Context) error { } } + go r.updateRoundStateRoutine(ctx) + go r.processStateCh(ctx, chBundle) go r.processDataCh(ctx, chBundle) go r.processVoteCh(ctx, chBundle) @@ -260,6 +261,15 @@ func (r *Reactor) SwitchToConsensus(ctx context.Context, state sm.State, skipWAL // NOTE: The line below causes broadcastNewRoundStepRoutine() to broadcast a // NewRoundStepMessage. r.state.updateToState(ctx, state) + if err := r.state.Start(ctx); err != nil { + panic(fmt.Sprintf(`failed to start consensus state: %v + +conS: +%+v + +conR: +%+v`, err, r.state, r)) + } r.mtx.Lock() r.waitSync = false @@ -273,16 +283,6 @@ func (r *Reactor) SwitchToConsensus(ctx context.Context, state sm.State, skipWAL r.state.doWALCatchup = false } - if err := r.state.Start(ctx); err != nil { - panic(fmt.Sprintf(`failed to start consensus state: %v - -conS: -%+v - -conR: -%+v`, err, r.state, r)) - } - d := types.EventDataBlockSyncStatus{Complete: true, Height: state.LastBlockHeight} if err := r.eventBus.PublishEventBlockSyncStatus(ctx, d); err != nil { r.logger.Error("failed to emit the blocksync complete event", "err", err) @@ -408,17 +408,20 @@ func (r *Reactor) sendNewRoundStepMessage(ctx context.Context, peerID types.Node }) } -func (r *Reactor) updateRoundStateRoutine() { +func (r *Reactor) updateRoundStateRoutine(ctx context.Context) { t := time.NewTicker(100 * time.Microsecond) defer t.Stop() - for range t.C { - if !r.IsRunning() { + + for { + select { + case <-ctx.Done(): return + case <-t.C: + rs := r.state.GetRoundState() + r.mtx.Lock() + r.rs = rs + r.mtx.Unlock() } - rs := r.state.GetRoundState() - r.mtx.Lock() - r.rs = rs - r.mtx.Unlock() } } @@ -836,10 +839,10 @@ func (r *Reactor) queryMaj23Routine(ctx context.Context, ps *PeerState, stateCh return } + // TODO create more reliable copies of these + // structures so the following go routines don't race rs := r.getRoundState() prs := ps.GetRoundState() - // TODO create more reliable coppies of these - // structures so the following go routines don't race wg := &sync.WaitGroup{} diff --git a/internal/rpc/core/status.go b/internal/rpc/core/status.go index 46b8a6fcd..870c13424 100644 --- a/internal/rpc/core/status.go +++ b/internal/rpc/core/status.go @@ -77,14 +77,25 @@ func (env *Environment) Status(ctx context.Context) (*coretypes.ResultStatus, er EarliestAppHash: earliestAppHash, EarliestBlockHeight: earliestBlockHeight, EarliestBlockTime: time.Unix(0, earliestBlockTimeNano), - MaxPeerBlockHeight: env.BlockSyncReactor.GetMaxPeerBlockHeight(), - CatchingUp: env.ConsensusReactor.WaitSync(), - TotalSyncedTime: env.BlockSyncReactor.GetTotalSyncedTime(), - RemainingTime: env.BlockSyncReactor.GetRemainingSyncTime(), + // this should start as true, if consensus + // hasn't started yet, and then flip to false + // (or true,) depending on what's actually + // happening. + CatchingUp: true, }, ValidatorInfo: validatorInfo, } + if env.ConsensusReactor != nil { + result.SyncInfo.CatchingUp = env.ConsensusReactor.WaitSync() + } + + if env.BlockSyncReactor != nil { + result.SyncInfo.MaxPeerBlockHeight = env.BlockSyncReactor.GetMaxPeerBlockHeight() + result.SyncInfo.TotalSyncedTime = env.BlockSyncReactor.GetTotalSyncedTime() + result.SyncInfo.RemainingTime = env.BlockSyncReactor.GetRemainingSyncTime() + } + if env.StateSyncMetricer != nil { result.SyncInfo.TotalSnapshots = env.StateSyncMetricer.TotalSnapshots() result.SyncInfo.ChunkProcessAvgTime = env.StateSyncMetricer.ChunkProcessAvgTime() @@ -103,6 +114,9 @@ func (env *Environment) validatorAtHeight(h int64) *types.Validator { if err != nil { return nil } + if env.ConsensusState == nil { + return nil + } if env.PubKey == nil { return nil } diff --git a/internal/statesync/reactor.go b/internal/statesync/reactor.go index 5ca1d0798..ea2cac4f4 100644 --- a/internal/statesync/reactor.go +++ b/internal/statesync/reactor.go @@ -143,6 +143,12 @@ type Reactor struct { peerEvents p2p.PeerEventSubscriber chCreator p2p.ChannelCreator sendBlockError func(context.Context, p2p.PeerError) error + postSyncHook func(context.Context, sm.State) error + + // when true, the reactor will, during startup perform a + // statesync for this node, and otherwise just provide + // snapshots to other nodes. + needsStateSync bool // Dispatcher is used to multiplex light block requests and responses over multiple // peers used by the p2p state provider and in reverse sync. @@ -171,7 +177,6 @@ type Reactor struct { // and querying, references to p2p Channels and a channel to listen for peer // updates on. Note, the reactor will close all p2p Channels when stopping. func NewReactor( - ctx context.Context, chainID string, initialHeight int64, cfg config.StateSyncConfig, @@ -184,23 +189,26 @@ func NewReactor( tempDir string, ssMetrics *Metrics, eventBus *eventbus.EventBus, + postSyncHook func(context.Context, sm.State) error, + needsStateSync bool, ) *Reactor { - r := &Reactor{ - logger: logger, - chainID: chainID, - initialHeight: initialHeight, - cfg: cfg, - conn: conn, - chCreator: channelCreator, - peerEvents: peerEvents, - tempDir: tempDir, - stateStore: stateStore, - blockStore: blockStore, - peers: newPeerList(), - providers: make(map[types.NodeID]*BlockProvider), - metrics: ssMetrics, - eventBus: eventBus, + logger: logger, + chainID: chainID, + initialHeight: initialHeight, + cfg: cfg, + conn: conn, + chCreator: channelCreator, + peerEvents: peerEvents, + tempDir: tempDir, + stateStore: stateStore, + blockStore: blockStore, + peers: newPeerList(), + providers: make(map[types.NodeID]*BlockProvider), + metrics: ssMetrics, + eventBus: eventBus, + postSyncHook: postSyncHook, + needsStateSync: needsStateSync, } r.BaseService = *service.NewBaseService(logger, "StateSync", r) @@ -300,6 +308,14 @@ func (r *Reactor) OnStart(ctx context.Context) error { go r.processChannels(ctx, snapshotCh, chunkCh, blockCh, paramsCh) go r.processPeerUpdates(ctx, r.peerEvents(ctx)) + if r.needsStateSync { + r.logger.Info("starting state sync") + if _, err := r.Sync(ctx); err != nil { + r.logger.Error("state sync failed; shutting down this node", "err", err) + return err + } + } + return nil } @@ -379,6 +395,12 @@ func (r *Reactor) Sync(ctx context.Context) (sm.State, error) { } } + if r.postSyncHook != nil { + if err := r.postSyncHook(ctx, state); err != nil { + return sm.State{}, err + } + } + return state, nil } diff --git a/internal/statesync/reactor_test.go b/internal/statesync/reactor_test.go index cef0735f2..86c84a9ed 100644 --- a/internal/statesync/reactor_test.go +++ b/internal/statesync/reactor_test.go @@ -155,7 +155,6 @@ func setup( logger := log.NewNopLogger() rts.reactor = NewReactor( - ctx, factory.DefaultTestChainID, 1, *cfg, @@ -167,7 +166,9 @@ func setup( rts.blockStore, "", m, - nil, // eventbus can be nil + nil, // eventbus can be nil + nil, // post-sync-hook + false, // run Sync during Start() ) rts.syncer = &syncer{ diff --git a/node/node.go b/node/node.go index 37b66c697..8a4da29f2 100644 --- a/node/node.go +++ b/node/node.go @@ -60,19 +60,17 @@ type nodeImpl struct { nodeKey types.NodeKey // our node privkey // services - eventSinks []indexer.EventSink - initialState sm.State - stateStore sm.Store - blockStore *store.BlockStore // store the blockchain to disk - evPool *evidence.Pool - stateSync bool // whether the node should state sync on startup - stateSyncReactor *statesync.Reactor // for hosting and restoring state sync snapshots - indexerService *indexer.Service - services []service.Service - rpcListeners []net.Listener // rpc servers - shutdownOps closer - rpcEnv *rpccore.Environment - prometheusSrv *http.Server + eventSinks []indexer.EventSink + initialState sm.State + stateStore sm.Store + blockStore *store.BlockStore // store the blockchain to disk + evPool *evidence.Pool + indexerService *indexer.Service + services []service.Service + rpcListeners []net.Listener // rpc servers + shutdownOps closer + rpcEnv *rpccore.Environment + prometheusSrv *http.Server } // newDefaultNode returns a Tendermint node with default settings for the @@ -331,13 +329,15 @@ func makeNode( nodeMetrics.consensus.BlockSyncing.Set(1) } + if cfg.P2P.PexReactor { + node.services = append(node.services, pex.NewReactor(logger, peerManager, node.router.OpenChannel, peerManager.Subscribe)) + } + // Set up state sync reactor, and schedule a sync if requested. // 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 - node.stateSync = stateSync - node.stateSyncReactor = statesync.NewReactor( - ctx, + node.services = append(node.services, statesync.NewReactor( genDoc.ChainID, genDoc.InitialHeight, *cfg.StateSync, @@ -350,11 +350,24 @@ func makeNode( cfg.StateSync.TempDir, nodeMetrics.statesync, eventBus, - ) + // the post-sync operation + func(ctx context.Context, state sm.State) error { + csReactor.SetStateSyncingMetrics(0) - if cfg.P2P.PexReactor { - node.services = append(node.services, pex.NewReactor(logger, peerManager, node.router.OpenChannel, peerManager.Subscribe)) - } + // TODO: Some form of orchestrator is needed here between the state + // advancing reactors to be able to control which one of the three + // is running + // FIXME Very ugly to have these metrics bleed through here. + csReactor.SetBlockSyncingMetrics(1) + if err := bcReactor.SwitchToBlockSync(ctx, state); err != nil { + logger.Error("failed to switch to block sync", "err", err) + return err + } + + return nil + }, + stateSync, + )) if cfg.Mode == config.ModeValidator { node.rpcEnv.PubKey = pubKey @@ -481,40 +494,6 @@ func (n *nodeImpl) OnStart(ctx context.Context) error { } } - if err := n.stateSyncReactor.Start(ctx); err != nil { - return err - } - - // Run state sync - // TODO: We shouldn't run state sync if we already have state that has a - // LastBlockHeight that is not InitialHeight - if n.stateSync { - // RUN STATE SYNC NOW: - // - // TODO: Eventually this should run as part of some - // separate orchestrator - n.logger.Info("starting state sync") - ssState, err := n.stateSyncReactor.Sync(ctx) - if err != nil { - n.logger.Error("state sync failed; shutting down this node", "err", err) - // stop the node - n.Stop() - return err - } - - n.rpcEnv.ConsensusReactor.SetStateSyncingMetrics(0) - - // TODO: Some form of orchestrator is needed here between the state - // advancing reactors to be able to control which one of the three - // is running - // FIXME Very ugly to have these metrics bleed through here. - n.rpcEnv.ConsensusReactor.SetBlockSyncingMetrics(1) - if err := n.rpcEnv.BlockSyncReactor.SwitchToBlockSync(ctx, ssState); err != nil { - n.logger.Error("failed to switch to block sync", "err", err) - return err - } - } - return nil } @@ -531,7 +510,6 @@ func (n *nodeImpl) OnStop() { reactor.Wait() } - n.stateSyncReactor.Wait() n.router.Wait() n.rpcEnv.IsListening = false