diff --git a/docs/tendermint-core/consensus/proposer-based-timestamps.md b/docs/tendermint-core/consensus/proposer-based-timestamps.md new file mode 100644 index 000000000..def42dc20 --- /dev/null +++ b/docs/tendermint-core/consensus/proposer-based-timestamps.md @@ -0,0 +1,94 @@ +--- order: 3 --- + +# PBTS + + This document provides an overview of the Proposer-Based Timestamp (PBTS) + algorithm added to Tendermint in the v0.36 release. It outlines the core + functionality as well as the parameters and constraints of the this algorithm. + +## Algorithm Overview + +The PBTS algorithm defines a way for a Tendermint blockchain to create block +timestamps that are within a reasonable bound of the clocks of the validators on +the network. This replaces the original BFTTime algorithm for timestamp +assignment that relied on the timestamps included in precommit messages. + +## Algorithm Parameters + +The functionality of the PBTS algorithm is governed by two parameters within +Tendermint. These two parameters are [consensus +parameters](https://github.com/tendermint/tendermint/blob/master/spec/abci/apps.md#L291), +meaning they are configured by the ABCI application and are expected to be the +same across all nodes on the network. + +### `Precision` + +The `Precision` parameter configures the acceptable upper-bound of clock drift +among all of the nodes on a Tendermint network. Any two nodes on a Tendermint +network are expected to have clocks that differ by at most `Precision` +milliseconds any given instant. + +### `MessageDelay` + +The `MessageDelay` parameter configures the acceptable upper-bound for +transmitting a `Proposal` message from the proposer to _all_ of the validators +on the network. + +Networks should choose as small a value for `MessageDelay` as is practical, +provided it is large enough that messages can reach all participants with high +probability given the number of participants and latency of their connections. + +## Algorithm Concepts + +### Block timestamps + +Each block produced by the Tendermint consensus engine contains a timestamp. +The timestamp produced in each block is a meaningful representation of time that is +useful for the protocols and applications built on top of Tendermint. + +The following protocols and application features require a reliable source of time: + +* Tendermint Light Clients [rely on correspondence between their known time](https://github.com/tendermint/tendermint/blob/master/spec/light-client/verification/README.md#definitions-1) and the block time for block verification. +* Tendermint Evidence validity is determined [either in terms of heights or in terms of time](https://github.com/tendermint/tendermint/blob/master/spec/consensus/evidence.md#verification). +* Unbonding of staked assets in the Cosmos Hub [occurs after a period of 21 + days](https://github.com/cosmos/governance/blob/master/params-change/Staking.md#unbondingtime). +* IBC packets can use either a [timestamp or a height to timeout packet + delivery](https://docs.cosmos.network/v0.44/ibc/overview.html#acknowledgements) + +### Proposer Selects a Block Timestamp + +When the proposer node creates a new block proposal, the node reads the time +from its local clock and uses this reading as the timestamp for the proposed +block. + +### Timeliness + +When each validator on a Tendermint network receives a proposed block, it +performs a series of checks to ensure that the block can be considered valid as +a candidate to be the next block in the chain. + +The PBTS algorithm performs a validity check on the timestamp of proposed +blocks. When a validator receives a proposal it ensures that the timestamp in +the proposal is within a bound of the validator's local clock. Specifically, the +algorithm checks that the timestamp is no more than `Precision` greater than the +node's local clock and no less than `Precision` + `MessageDelay` behind than the +node's local clock. This creates range of acceptable timestamps around the +node's local time. If the timestamp is within this range, the PBTS algorithm +considers the block **timely**. If a block is not **timely**, the node will +issue a `nil` `prevote` for this block, signaling to the rest of the network +that the node does not consider the block to be valid. + +### Clock Synchronization + +The PBTS algorithm requires the clocks of the validators on a Tendermint network +are within `Precision` of each other. In practice, this means that validators +should periodically synchronize to a reliable NTP server. Validators that drift +too far away from the rest of the network will no longer propose blocks with +valid timestamps. Additionally they will not view the timestamps of blocks +proposed by their peers to be valid either. + +## See Also + +* [The PBTS specification](https://github.com/tendermint/tendermint/blob/master/spec/consensus/proposer-based-timestamp/README.md) + contains all of the details of the algorithm. + diff --git a/internal/eventbus/event_bus.go b/internal/eventbus/event_bus.go index 2a7c032b3..1d2d510e3 100644 --- a/internal/eventbus/event_bus.go +++ b/internal/eventbus/event_bus.go @@ -50,13 +50,6 @@ func (b *EventBus) NumClientSubscriptions(clientID string) int { return b.pubsub.NumClientSubscriptions(clientID) } -// Deprecated: Use SubscribeWithArgs instead. -func (b *EventBus) Subscribe(ctx context.Context, - clientID string, query *tmquery.Query, capacities ...int) (Subscription, error) { - - return b.pubsub.Subscribe(ctx, clientID, query, capacities...) -} - func (b *EventBus) SubscribeWithArgs(ctx context.Context, args tmpubsub.SubscribeArgs) (Subscription, error) { return b.pubsub.SubscribeWithArgs(ctx, args) } diff --git a/internal/pubsub/pubsub.go b/internal/pubsub/pubsub.go index 707f9cb13..df2dd90e3 100644 --- a/internal/pubsub/pubsub.go +++ b/internal/pubsub/pubsub.go @@ -153,26 +153,6 @@ func BufferCapacity(cap int) Option { // BufferCapacity returns capacity of the publication queue. func (s *Server) BufferCapacity() int { return cap(s.queue) } -// Subscribe creates a subscription for the given client ID and query. -// If len(capacities) > 0, its first value is used as the queue capacity. -// -// Deprecated: Use SubscribeWithArgs. This method will be removed in v0.36. -func (s *Server) Subscribe(ctx context.Context, clientID string, query *query.Query, capacities ...int) (*Subscription, error) { - args := SubscribeArgs{ - ClientID: clientID, - Query: query, - Limit: 1, - } - if len(capacities) > 0 { - args.Limit = capacities[0] - if len(capacities) > 1 { - args.Quota = capacities[1] - } - // bounds are checked below - } - return s.SubscribeWithArgs(ctx, args) -} - // Observe registers an observer function that will be called synchronously // with each published message matching any of the given queries, prior to it // being forwarded to any subscriber. If no queries are specified, all diff --git a/internal/rpc/core/env.go b/internal/rpc/core/env.go index 348ed0867..24f43a4a7 100644 --- a/internal/rpc/core/env.go +++ b/internal/rpc/core/env.go @@ -57,12 +57,6 @@ type consensusState interface { GetRoundStateSimpleJSON() ([]byte, error) } -type transport interface { - Listeners() []string - IsListening() bool - NodeInfo() types.NodeInfo -} - type peerManager interface { Peers() []types.NodeID Addresses(types.NodeID) []p2p.NodeAddress @@ -83,8 +77,9 @@ type Environment struct { ConsensusReactor *consensus.Reactor BlockSyncReactor *blocksync.Reactor - // Legacy p2p stack - P2PTransport transport + IsListening bool + Listeners []string + NodeInfo types.NodeInfo // interfaces for new p2p interfaces PeerManager peerManager @@ -225,6 +220,10 @@ func (env *Environment) StartService(ctx context.Context, conf *config.Config) ( return nil, err } + env.Listeners = []string{ + fmt.Sprintf("Listener(@%v)", conf.P2P.ExternalAddress), + } + listenAddrs := strings.SplitAndTrimEmpty(conf.RPC.ListenAddress, ",", " ") routes := NewRoutesMap(env, &RouteOptions{ Unsafe: conf.RPC.Unsafe, diff --git a/internal/rpc/core/net.go b/internal/rpc/core/net.go index 3cead393c..5444b77b7 100644 --- a/internal/rpc/core/net.go +++ b/internal/rpc/core/net.go @@ -27,8 +27,8 @@ func (env *Environment) NetInfo(ctx context.Context) (*coretypes.ResultNetInfo, } return &coretypes.ResultNetInfo{ - Listening: env.P2PTransport.IsListening(), - Listeners: env.P2PTransport.Listeners(), + Listening: env.IsListening, + Listeners: env.Listeners, NPeers: len(peers), Peers: peers, }, nil diff --git a/internal/rpc/core/status.go b/internal/rpc/core/status.go index 2f648978a..46b8a6fcd 100644 --- a/internal/rpc/core/status.go +++ b/internal/rpc/core/status.go @@ -66,7 +66,7 @@ func (env *Environment) Status(ctx context.Context) (*coretypes.ResultStatus, er } result := &coretypes.ResultStatus{ - NodeInfo: env.P2PTransport.NodeInfo(), + NodeInfo: env.NodeInfo, ApplicationInfo: applicationInfo, SyncInfo: coretypes.SyncInfo{ LatestBlockHash: latestBlockHash, diff --git a/node/node.go b/node/node.go index 1a549c7d5..f68435de6 100644 --- a/node/node.go +++ b/node/node.go @@ -58,7 +58,6 @@ type nodeImpl struct { router *p2p.Router nodeInfo types.NodeInfo nodeKey types.NodeKey // our node privkey - isListening bool // services eventSinks []indexer.EventSink @@ -416,8 +415,6 @@ func makeNode( node.rpcEnv.PubKey = pubKey } - node.rpcEnv.P2PTransport = node - node.BaseService = *service.NewBaseService(logger, "Node", node) return node, nil @@ -462,6 +459,7 @@ func (n *nodeImpl) OnStart(ctx context.Context) error { } } + n.rpcEnv.NodeInfo = n.nodeInfo // Start the RPC server before the P2P server // so we can eg. receive txs for the first block if n.config.RPC.ListenAddress != "" { @@ -480,7 +478,7 @@ func (n *nodeImpl) OnStart(ctx context.Context) error { if err := n.router.Start(ctx); err != nil { return err } - n.isListening = true + n.rpcEnv.IsListening = true for _, reactor := range n.services { if err := reactor.Start(ctx); err != nil { @@ -575,7 +573,7 @@ func (n *nodeImpl) OnStop() { n.stateSyncReactor.Wait() n.router.Wait() - n.isListening = false + n.rpcEnv.IsListening = false // finally stop the listeners / external services for _, l := range n.rpcListeners { @@ -664,21 +662,6 @@ func (n *nodeImpl) RPCEnvironment() *rpccore.Environment { //------------------------------------------------------------------------------ -func (n *nodeImpl) Listeners() []string { - return []string{ - fmt.Sprintf("Listener(@%v)", n.config.P2P.ExternalAddress), - } -} - -func (n *nodeImpl) IsListening() bool { - return n.isListening -} - -// NodeInfo returns the Node's Info from the Switch. -func (n *nodeImpl) NodeInfo() types.NodeInfo { - return n.nodeInfo -} - // genesisDocProvider returns a GenesisDoc. // It allows the GenesisDoc to be pulled from sources other than the // filesystem, for instance from a distributed key-value store cluster. diff --git a/spec/abci/apps.md b/spec/abci/apps.md index 030a3d3c3..d6ec19832 100644 --- a/spec/abci/apps.md +++ b/spec/abci/apps.md @@ -346,6 +346,19 @@ a block minus it's overhead ( ~ `MaxBytes`). Must have `MaxNum > 0`. +### SynchronyParams.Precision + +`SynchronyParams.Precision` is a parameter of the Proposer-Based Timestamps algorithm. +that configures the acceptable upper-bound of clock drift among +all of the nodes on a Tendermint network. Any two nodes on a Tendermint network +are expected to have clocks that differ by at most `Precision`. + +### SynchronyParams.MessageDelay + +`SynchronyParams.MessageDelay` is a parameter of the Proposer-Based Timestamps +algorithm that configures the acceptable upper-bound for transmitting a `Proposal` +message from the proposer to all of the validators on the network. + ### Updates The application may set the ConsensusParams during InitChain, and update them during