mirror of
https://github.com/tendermint/tendermint.git
synced 2026-08-22 07:06:14 +00:00
Merge remote-tracking branch 'origin/master' into proxy-remove-triforcated-client
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
+3
-20
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user