Add design proposal and update validator-timestamp-oracle
This commit is contained in:
parent
3a1e125ce3
commit
a3912bc084
|
@ -174,6 +174,7 @@ module.exports = {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
"implemented-proposals/abi-management",
|
"implemented-proposals/abi-management",
|
||||||
|
"implemented-proposals/bank-timestamp-correction",
|
||||||
"implemented-proposals/commitment",
|
"implemented-proposals/commitment",
|
||||||
"implemented-proposals/cross-program-invocation",
|
"implemented-proposals/cross-program-invocation",
|
||||||
"implemented-proposals/durable-tx-nonces",
|
"implemented-proposals/durable-tx-nonces",
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
---
|
||||||
|
title: Bank Timestamp Correction
|
||||||
|
---
|
||||||
|
|
||||||
|
Each Bank has a timestamp that is stashed in the Clock sysvar and used to assess
|
||||||
|
time-based stake account lockups. However, since genesis, this value has been
|
||||||
|
based on a theoretical slots-per-second instead of reality, so it's quite
|
||||||
|
inaccurate. This poses a problem for lockups, since the accounts will not
|
||||||
|
register as lockup-free on (or anytime near) the date the lockup is set to
|
||||||
|
expire.
|
||||||
|
|
||||||
|
Block times are already being estimated to cache in Blockstore and long-term
|
||||||
|
storage using a [validator timestamp oracle](validator-timestamp-oracle.md);
|
||||||
|
this data provides an opportunity to align the bank timestamp more closely with
|
||||||
|
real-world time.
|
||||||
|
|
||||||
|
The general outline of the proposed implementation is as follows:
|
||||||
|
|
||||||
|
- Correct each Bank timestamp using the validator-provided timestamp.
|
||||||
|
- Update the validator-provided timestamp calculation to use a stake-weighted
|
||||||
|
median, rather than a stake-weighted mean.
|
||||||
|
- Bound the timestamp correction so that it cannot deviate too far from the
|
||||||
|
expected theoretical estimate
|
||||||
|
|
||||||
|
## Timestamp Correction
|
||||||
|
|
||||||
|
On every new Bank, the runtime calculates a realistic timestamp estimate using
|
||||||
|
validator timestamp-oracle data. The Bank timestamp is corrected to this value
|
||||||
|
if it is greater than or equal to the previous Bank's timestamp. That is, time
|
||||||
|
should not ever go backward, so that locked up accounts may be released by the
|
||||||
|
correction, but once released, accounts can never be relocked by a time
|
||||||
|
correction.
|
||||||
|
|
||||||
|
### Calculating Stake-Weighted Median Timestamp
|
||||||
|
|
||||||
|
In order to calculate the estimated timestamp for a particular Bank, the runtime
|
||||||
|
first needs to get the most recent vote timestamps from the active validator
|
||||||
|
set. The `Bank::vote_accounts()` method provides the vote accounts state, and
|
||||||
|
these can be filtered to all accounts whose most recent timestamp is for an
|
||||||
|
ancestor slot back to the current root. This should guarantee 2/3+ of the
|
||||||
|
current cluster stake is represented, since by definition, roots must be
|
||||||
|
confirmed by 2/3+ stake.
|
||||||
|
|
||||||
|
From each vote timestamp, an estimate for the current Bank is calculated using
|
||||||
|
the epoch's target ns_per_slot for any delta between the Bank slot and the
|
||||||
|
timestamp slot. Each timestamp estimate is is associated with the stake
|
||||||
|
delegated to that vote account, and all the timestamps are collected to create a
|
||||||
|
stake-weighted timestamp distribution.
|
||||||
|
|
||||||
|
From this set, the stake-weighted median timestamp -- that is, the timestamp at
|
||||||
|
which 50% of the stake estimates a greater-or-equal timestamp and 50% of the
|
||||||
|
stake estimates a lesser-or-equal timestamp -- is selected as the potential
|
||||||
|
corrected timestamp.
|
||||||
|
|
||||||
|
This stake-weighted median timestamp is preferred over the stake-weighted mean
|
||||||
|
because the multiplication of stake by proposed timestamp in the mean
|
||||||
|
calculation allows a node with very small stake to still have a large effect on
|
||||||
|
the resulting timestamp by proposing a timestamp that is very large or very
|
||||||
|
small. For example, using the previous `calculate_stake_weighted_timestamp()`
|
||||||
|
method, a node with 0.00003% of the stake proposing a timestamp of `i64::MAX`
|
||||||
|
can shift the timestamp forward 97k years!
|
||||||
|
|
||||||
|
### Bounding Timestamps
|
||||||
|
|
||||||
|
In addition to preventing time moving backward, we can prevent malicious
|
||||||
|
activity by bounding the corrected timestamp to an acceptable level of deviation
|
||||||
|
from the theoretical expected time.
|
||||||
|
|
||||||
|
This proposal suggests that each timestamp be allowed to deviate up to 25% from
|
||||||
|
the expected time since the start of the epoch.
|
||||||
|
|
||||||
|
In order to calculate the timestamp deviation, each Bank needs to log the
|
||||||
|
`epoch_start_timestamp` in the Clock sysvar. This value is set to the
|
||||||
|
`Clock::unix_timestamp` on the first slot of each epoch.
|
||||||
|
|
||||||
|
Then, the runtime compares the expected elapsed time since the start of the
|
||||||
|
epoch with the proposed elapsed time based on the corrected timestamp. If the
|
||||||
|
corrected elaped time is within +/- 25% of expected, the corrected timestamp is
|
||||||
|
accepted. Otherwise, it is bounded to the acceptable deviation.
|
|
@ -48,19 +48,11 @@ Vote vector (`Vote::slots.iter().max()`). It is signed by the validator's
|
||||||
identity keypair as a usual Vote. In order to enable this reporting, the Vote
|
identity keypair as a usual Vote. In order to enable this reporting, the Vote
|
||||||
struct needs to be extended to include a timestamp field, `timestamp: Option<UnixTimestamp>`, which will be set to `None` in most Votes.
|
struct needs to be extended to include a timestamp field, `timestamp: Option<UnixTimestamp>`, which will be set to `None` in most Votes.
|
||||||
|
|
||||||
This proposal suggests that Vote instructions with `Some(timestamp)` be issued
|
As of https://github.com/solana-labs/solana/pull/10630, validators submit a
|
||||||
every 30min, which should be short enough to prevent block times drifting very
|
timestamp every vote. This enables implementation of a block time caching
|
||||||
much, without adding too much transaction overhead to the cluster. Validators
|
service that allows nodes to calculate the estimated timestamp immediately after
|
||||||
can convert this time to a slot interval using the `slots_per_year` value that
|
the block is rooted, and cache that value in Blockstore. This provides
|
||||||
is stored in each bank.
|
persistent data and quick queries, while still meeting requirement 1) above.
|
||||||
|
|
||||||
```text
|
|
||||||
let seconds_in_30min = 1800;
|
|
||||||
let timestamp_interval = (slots_per_year / SECONDS_PER_YEAR) * seconds_in_30min;
|
|
||||||
```
|
|
||||||
|
|
||||||
Votes with `Some(timestamp)` should be triggered in `replay_stage::handle_votable_bank()`
|
|
||||||
when `bank.slot() % timestamp_interval == 0`.
|
|
||||||
|
|
||||||
### Vote Accounts
|
### Vote Accounts
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue