mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-07 13:55:17 +00:00
Proposer-Based Timestamps Merge (#7605)
This pull request merges in the changes for implementing Proposer-based timestamps into `master`. The power was primarily being done in the `wb/proposer-based-timestamps` branch, with changes being merged into that branch during development. This pull request represents an amalgamation of the changes made into that development branch. All of the changes that were placed into that branch have been cleanly rebased on top of the latest `master`. The changes compile and the tests pass insofar as our tests in general pass. ### Note To Reviewers These changes have been extensively reviewed during development. There is not much new here. In the interest of making effective use of time, I would recommend against trying to perform a complete audit of the changes presented and instead examine for mistakes that may have occurred during the process of rebasing the changes. I gave the complete change set a first pass for any issues, but additional eyes would be very appreciated. In sum, this change set does the following: closes #6942 merges in #6849
This commit is contained in:
@@ -883,7 +883,7 @@ func (commit *Commit) ValidateBasic() error {
|
||||
}
|
||||
|
||||
if commit.Height >= 1 {
|
||||
if commit.BlockID.IsZero() {
|
||||
if commit.BlockID.IsNil() {
|
||||
return errors.New("commit cannot be for nil block")
|
||||
}
|
||||
|
||||
@@ -1204,8 +1204,8 @@ func (blockID BlockID) ValidateBasic() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsZero returns true if this is the BlockID of a nil block.
|
||||
func (blockID BlockID) IsZero() bool {
|
||||
// IsNil returns true if this is the BlockID of a nil block.
|
||||
func (blockID BlockID) IsNil() bool {
|
||||
return len(blockID.Hash) == 0 &&
|
||||
blockID.PartSetHeader.IsZero()
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func CanonicalizeBlockID(bid tmproto.BlockID) *tmproto.CanonicalBlockID {
|
||||
panic(err)
|
||||
}
|
||||
var cbid *tmproto.CanonicalBlockID
|
||||
if rbid == nil || rbid.IsZero() {
|
||||
if rbid == nil || rbid.IsNil() {
|
||||
cbid = nil
|
||||
} else {
|
||||
cbid = &tmproto.CanonicalBlockID{
|
||||
|
||||
@@ -38,7 +38,6 @@ const (
|
||||
EventStateSyncStatusValue = "StateSyncStatus"
|
||||
EventTimeoutProposeValue = "TimeoutPropose"
|
||||
EventTimeoutWaitValue = "TimeoutWait"
|
||||
EventUnlockValue = "Unlock"
|
||||
EventValidBlockValue = "ValidBlock"
|
||||
EventVoteValue = "Vote"
|
||||
)
|
||||
@@ -259,7 +258,6 @@ var (
|
||||
EventQueryTimeoutPropose = QueryForEvent(EventTimeoutProposeValue)
|
||||
EventQueryTimeoutWait = QueryForEvent(EventTimeoutWaitValue)
|
||||
EventQueryTx = QueryForEvent(EventTxValue)
|
||||
EventQueryUnlock = QueryForEvent(EventUnlockValue)
|
||||
EventQueryValidatorSetUpdates = QueryForEvent(EventValidatorSetUpdatesValue)
|
||||
EventQueryValidBlock = QueryForEvent(EventValidBlockValue)
|
||||
EventQueryVote = QueryForEvent(EventVoteValue)
|
||||
|
||||
@@ -56,21 +56,26 @@ func TestGenesisBad(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenesisGood(t *testing.T) {
|
||||
func TestBasicGenesisDoc(t *testing.T) {
|
||||
// test a good one by raw json
|
||||
genDocBytes := []byte(
|
||||
`{
|
||||
"genesis_time": "0001-01-01T00:00:00Z",
|
||||
"chain_id": "test-chain-QDKdJr",
|
||||
"initial_height": "1000",
|
||||
"consensus_params": null,
|
||||
"validators": [{
|
||||
"pub_key":{"type":"tendermint/PubKeyEd25519","value":"AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="},
|
||||
"power":"10",
|
||||
"name":""
|
||||
}],
|
||||
"app_hash":"",
|
||||
"app_state":{"account_owner": "Bob"}
|
||||
"app_state":{"account_owner": "Bob"},
|
||||
"consensus_params": {
|
||||
"synchrony": {"precision": "1", "message_delay": "10"},
|
||||
"validator": {"pub_key_types":["ed25519"]},
|
||||
"block": {"max_bytes": "100"},
|
||||
"evidence": {"max_age_num_blocks": "100", "max_age_duration": "10"}
|
||||
}
|
||||
}`,
|
||||
)
|
||||
_, err := GenesisDocFromJSON(genDocBytes)
|
||||
@@ -97,7 +102,7 @@ func TestGenesisGood(t *testing.T) {
|
||||
genDocBytes, err = json.Marshal(genDoc)
|
||||
assert.NoError(t, err, "error marshaling genDoc")
|
||||
genDoc, err = GenesisDocFromJSON(genDocBytes)
|
||||
assert.NoError(t, err, "expected no error for valid genDoc json")
|
||||
require.NoError(t, err, "expected no error for valid genDoc json")
|
||||
|
||||
// test with invalid consensus params
|
||||
genDoc.ConsensusParams.Block.MaxBytes = 0
|
||||
|
||||
@@ -41,6 +41,7 @@ type ConsensusParams struct {
|
||||
Evidence EvidenceParams `json:"evidence"`
|
||||
Validator ValidatorParams `json:"validator"`
|
||||
Version VersionParams `json:"version"`
|
||||
Synchrony SynchronyParams `json:"synchrony"`
|
||||
}
|
||||
|
||||
// HashedParams is a subset of ConsensusParams.
|
||||
@@ -75,6 +76,15 @@ type VersionParams struct {
|
||||
AppVersion uint64 `json:"app_version,string"`
|
||||
}
|
||||
|
||||
// SynchronyParams influence the validity of block timestamps.
|
||||
// For more information on the relationship of the synchrony parameters to
|
||||
// block validity, see the Proposer-Based Timestamps specification:
|
||||
// https://github.com/tendermint/spec/blob/master/spec/consensus/proposer-based-timestamp/README.md
|
||||
type SynchronyParams struct {
|
||||
Precision time.Duration `json:"precision,string"`
|
||||
MessageDelay time.Duration `json:"message_delay,string"`
|
||||
}
|
||||
|
||||
// DefaultConsensusParams returns a default ConsensusParams.
|
||||
func DefaultConsensusParams() *ConsensusParams {
|
||||
return &ConsensusParams{
|
||||
@@ -82,6 +92,7 @@ func DefaultConsensusParams() *ConsensusParams {
|
||||
Evidence: DefaultEvidenceParams(),
|
||||
Validator: DefaultValidatorParams(),
|
||||
Version: DefaultVersionParams(),
|
||||
Synchrony: DefaultSynchronyParams(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +127,15 @@ func DefaultVersionParams() VersionParams {
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultSynchronyParams() SynchronyParams {
|
||||
// TODO(@wbanfield): Determine experimental values for these defaults
|
||||
// https://github.com/tendermint/tendermint/issues/7202
|
||||
return SynchronyParams{
|
||||
Precision: 500 * time.Millisecond,
|
||||
MessageDelay: 2 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func (val *ValidatorParams) IsValidPubkeyType(pubkeyType string) bool {
|
||||
for i := 0; i < len(val.PubKeyTypes); i++ {
|
||||
if val.PubKeyTypes[i] == pubkeyType {
|
||||
@@ -148,7 +168,7 @@ func (params ConsensusParams) ValidateConsensusParams() error {
|
||||
}
|
||||
|
||||
if params.Evidence.MaxAgeDuration <= 0 {
|
||||
return fmt.Errorf("evidence.MaxAgeDuration must be grater than 0 if provided, Got %v",
|
||||
return fmt.Errorf("evidence.MaxAgeDuration must be greater than 0 if provided, Got %v",
|
||||
params.Evidence.MaxAgeDuration)
|
||||
}
|
||||
|
||||
@@ -162,6 +182,16 @@ func (params ConsensusParams) ValidateConsensusParams() error {
|
||||
params.Evidence.MaxBytes)
|
||||
}
|
||||
|
||||
if params.Synchrony.MessageDelay <= 0 {
|
||||
return fmt.Errorf("synchrony.MessageDelay must be greater than 0. Got: %d",
|
||||
params.Synchrony.MessageDelay)
|
||||
}
|
||||
|
||||
if params.Synchrony.Precision <= 0 {
|
||||
return fmt.Errorf("synchrony.Precision must be greater than 0. Got: %d",
|
||||
params.Synchrony.Precision)
|
||||
}
|
||||
|
||||
if len(params.Validator.PubKeyTypes) == 0 {
|
||||
return errors.New("len(Validator.PubKeyTypes) must be greater than 0")
|
||||
}
|
||||
@@ -205,6 +235,8 @@ func (params ConsensusParams) HashConsensusParams() []byte {
|
||||
func (params *ConsensusParams) Equals(params2 *ConsensusParams) bool {
|
||||
return params.Block == params2.Block &&
|
||||
params.Evidence == params2.Evidence &&
|
||||
params.Version == params2.Version &&
|
||||
params.Synchrony == params2.Synchrony &&
|
||||
tmstrings.StringSliceEqual(params.Validator.PubKeyTypes, params2.Validator.PubKeyTypes)
|
||||
}
|
||||
|
||||
@@ -235,6 +267,10 @@ func (params ConsensusParams) UpdateConsensusParams(params2 *tmproto.ConsensusPa
|
||||
if params2.Version != nil {
|
||||
res.Version.AppVersion = params2.Version.AppVersion
|
||||
}
|
||||
if params2.Synchrony != nil {
|
||||
res.Synchrony.Precision = params2.Synchrony.Precision
|
||||
res.Synchrony.MessageDelay = params2.Synchrony.MessageDelay
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -255,6 +291,10 @@ func (params *ConsensusParams) ToProto() tmproto.ConsensusParams {
|
||||
Version: &tmproto.VersionParams{
|
||||
AppVersion: params.Version.AppVersion,
|
||||
},
|
||||
Synchrony: &tmproto.SynchronyParams{
|
||||
MessageDelay: params.Synchrony.MessageDelay,
|
||||
Precision: params.Synchrony.Precision,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,5 +315,9 @@ func ConsensusParamsFromProto(pbParams tmproto.ConsensusParams) ConsensusParams
|
||||
Version: VersionParams{
|
||||
AppVersion: pbParams.Version.AppVersion,
|
||||
},
|
||||
Synchrony: SynchronyParams{
|
||||
MessageDelay: pbParams.Synchrony.MessageDelay,
|
||||
Precision: pbParams.Synchrony.Precision,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,23 +23,140 @@ func TestConsensusParamsValidation(t *testing.T) {
|
||||
valid bool
|
||||
}{
|
||||
// test block params
|
||||
0: {makeParams(1, 0, 2, 0, valEd25519), true},
|
||||
1: {makeParams(0, 0, 2, 0, valEd25519), false},
|
||||
2: {makeParams(47*1024*1024, 0, 2, 0, valEd25519), true},
|
||||
3: {makeParams(10, 0, 2, 0, valEd25519), true},
|
||||
4: {makeParams(100*1024*1024, 0, 2, 0, valEd25519), true},
|
||||
5: {makeParams(101*1024*1024, 0, 2, 0, valEd25519), false},
|
||||
6: {makeParams(1024*1024*1024, 0, 2, 0, valEd25519), false},
|
||||
7: {makeParams(1024*1024*1024, 0, -1, 0, valEd25519), false},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 1,
|
||||
evidenceAge: 2,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 0,
|
||||
evidenceAge: 2,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 47 * 1024 * 1024,
|
||||
evidenceAge: 2,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 10,
|
||||
evidenceAge: 2,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 100 * 1024 * 1024,
|
||||
evidenceAge: 2,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 101 * 1024 * 1024,
|
||||
evidenceAge: 2,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 1024 * 1024 * 1024,
|
||||
evidenceAge: 2,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 1024 * 1024 * 1024,
|
||||
evidenceAge: 2,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: false,
|
||||
},
|
||||
// test evidence params
|
||||
8: {makeParams(1, 0, 0, 0, valEd25519), false},
|
||||
9: {makeParams(1, 0, 2, 2, valEd25519), false},
|
||||
10: {makeParams(1000, 0, 2, 1, valEd25519), true},
|
||||
11: {makeParams(1, 0, -1, 0, valEd25519), false},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 1,
|
||||
evidenceAge: 0,
|
||||
maxEvidenceBytes: 0,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 1,
|
||||
evidenceAge: 2,
|
||||
maxEvidenceBytes: 2,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 1000,
|
||||
evidenceAge: 2,
|
||||
maxEvidenceBytes: 1,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
blockBytes: 1,
|
||||
evidenceAge: -1,
|
||||
maxEvidenceBytes: 0,
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: false,
|
||||
},
|
||||
// test no pubkey type provided
|
||||
12: {makeParams(1, 0, 2, 0, []string{}), false},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
evidenceAge: 2,
|
||||
pubkeyTypes: []string{},
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: false,
|
||||
},
|
||||
// test invalid pubkey type provided
|
||||
13: {makeParams(1, 0, 2, 0, []string{"potatoes make good pubkeys"}), false},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
evidenceAge: 2,
|
||||
pubkeyTypes: []string{"potatoes make good pubkeys"},
|
||||
precision: 1,
|
||||
messageDelay: 1}),
|
||||
valid: false,
|
||||
},
|
||||
// test invalid pubkey type provided
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
evidenceAge: 2,
|
||||
precision: 1,
|
||||
messageDelay: -1}),
|
||||
valid: false,
|
||||
},
|
||||
{
|
||||
params: makeParams(makeParamsArgs{
|
||||
evidenceAge: 2,
|
||||
precision: -1,
|
||||
messageDelay: 1}),
|
||||
valid: false,
|
||||
},
|
||||
}
|
||||
for i, tc := range testCases {
|
||||
if tc.valid {
|
||||
@@ -50,38 +167,51 @@ func TestConsensusParamsValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func makeParams(
|
||||
blockBytes, blockGas int64,
|
||||
evidenceAge int64,
|
||||
maxEvidenceBytes int64,
|
||||
pubkeyTypes []string,
|
||||
) ConsensusParams {
|
||||
type makeParamsArgs struct {
|
||||
blockBytes int64
|
||||
blockGas int64
|
||||
evidenceAge int64
|
||||
maxEvidenceBytes int64
|
||||
pubkeyTypes []string
|
||||
precision time.Duration
|
||||
messageDelay time.Duration
|
||||
}
|
||||
|
||||
func makeParams(args makeParamsArgs) ConsensusParams {
|
||||
if args.pubkeyTypes == nil {
|
||||
args.pubkeyTypes = valEd25519
|
||||
}
|
||||
return ConsensusParams{
|
||||
Block: BlockParams{
|
||||
MaxBytes: blockBytes,
|
||||
MaxGas: blockGas,
|
||||
MaxBytes: args.blockBytes,
|
||||
MaxGas: args.blockGas,
|
||||
},
|
||||
Evidence: EvidenceParams{
|
||||
MaxAgeNumBlocks: evidenceAge,
|
||||
MaxAgeDuration: time.Duration(evidenceAge),
|
||||
MaxBytes: maxEvidenceBytes,
|
||||
MaxAgeNumBlocks: args.evidenceAge,
|
||||
MaxAgeDuration: time.Duration(args.evidenceAge),
|
||||
MaxBytes: args.maxEvidenceBytes,
|
||||
},
|
||||
Validator: ValidatorParams{
|
||||
PubKeyTypes: pubkeyTypes,
|
||||
PubKeyTypes: args.pubkeyTypes,
|
||||
},
|
||||
Synchrony: SynchronyParams{
|
||||
Precision: args.precision,
|
||||
MessageDelay: args.messageDelay,
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestConsensusParamsHash(t *testing.T) {
|
||||
params := []ConsensusParams{
|
||||
makeParams(4, 2, 3, 1, valEd25519),
|
||||
makeParams(1, 4, 3, 1, valEd25519),
|
||||
makeParams(1, 2, 4, 1, valEd25519),
|
||||
makeParams(2, 5, 7, 1, valEd25519),
|
||||
makeParams(1, 7, 6, 1, valEd25519),
|
||||
makeParams(9, 5, 4, 1, valEd25519),
|
||||
makeParams(7, 8, 9, 1, valEd25519),
|
||||
makeParams(4, 6, 5, 1, valEd25519),
|
||||
makeParams(makeParamsArgs{blockBytes: 4, blockGas: 2, evidenceAge: 3, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 1, blockGas: 4, evidenceAge: 3, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 1, blockGas: 2, evidenceAge: 4, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 2, blockGas: 5, evidenceAge: 7, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 1, blockGas: 7, evidenceAge: 6, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 9, blockGas: 5, evidenceAge: 4, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 7, blockGas: 8, evidenceAge: 9, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 4, blockGas: 6, evidenceAge: 5, maxEvidenceBytes: 1}),
|
||||
}
|
||||
|
||||
hashes := make([][]byte, len(params))
|
||||
@@ -101,20 +231,31 @@ func TestConsensusParamsHash(t *testing.T) {
|
||||
|
||||
func TestConsensusParamsUpdate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
params ConsensusParams
|
||||
intialParams ConsensusParams
|
||||
updates *tmproto.ConsensusParams
|
||||
updatedParams ConsensusParams
|
||||
}{
|
||||
// empty updates
|
||||
{
|
||||
makeParams(1, 2, 3, 0, valEd25519),
|
||||
&tmproto.ConsensusParams{},
|
||||
makeParams(1, 2, 3, 0, valEd25519),
|
||||
intialParams: makeParams(makeParamsArgs{blockBytes: 1, blockGas: 2, evidenceAge: 3}),
|
||||
updates: &tmproto.ConsensusParams{},
|
||||
updatedParams: makeParams(makeParamsArgs{blockBytes: 1, blockGas: 2, evidenceAge: 3}),
|
||||
},
|
||||
{
|
||||
// update synchrony params
|
||||
intialParams: makeParams(makeParamsArgs{evidenceAge: 3, precision: time.Second, messageDelay: 3 * time.Second}),
|
||||
updates: &tmproto.ConsensusParams{
|
||||
Synchrony: &tmproto.SynchronyParams{
|
||||
Precision: time.Second * 2,
|
||||
MessageDelay: time.Second * 4,
|
||||
},
|
||||
},
|
||||
updatedParams: makeParams(makeParamsArgs{evidenceAge: 3, precision: 2 * time.Second, messageDelay: 4 * time.Second}),
|
||||
},
|
||||
// fine updates
|
||||
{
|
||||
makeParams(1, 2, 3, 0, valEd25519),
|
||||
&tmproto.ConsensusParams{
|
||||
intialParams: makeParams(makeParamsArgs{blockBytes: 1, blockGas: 2, evidenceAge: 3}),
|
||||
updates: &tmproto.ConsensusParams{
|
||||
Block: &tmproto.BlockParams{
|
||||
MaxBytes: 100,
|
||||
MaxGas: 200,
|
||||
@@ -128,11 +269,15 @@ func TestConsensusParamsUpdate(t *testing.T) {
|
||||
PubKeyTypes: valSecp256k1,
|
||||
},
|
||||
},
|
||||
makeParams(100, 200, 300, 50, valSecp256k1),
|
||||
updatedParams: makeParams(makeParamsArgs{
|
||||
blockBytes: 100, blockGas: 200,
|
||||
evidenceAge: 300,
|
||||
maxEvidenceBytes: 50,
|
||||
pubkeyTypes: valSecp256k1}),
|
||||
},
|
||||
{
|
||||
makeParams(1, 2, 3, 0, valEd25519),
|
||||
&tmproto.ConsensusParams{
|
||||
intialParams: makeParams(makeParamsArgs{blockBytes: 1, blockGas: 2, evidenceAge: 3}),
|
||||
updates: &tmproto.ConsensusParams{
|
||||
Block: &tmproto.BlockParams{
|
||||
MaxBytes: 100,
|
||||
MaxGas: 200,
|
||||
@@ -145,17 +290,23 @@ func TestConsensusParamsUpdate(t *testing.T) {
|
||||
Validator: &tmproto.ValidatorParams{
|
||||
PubKeyTypes: valSr25519,
|
||||
},
|
||||
}, makeParams(100, 200, 300, 50, valSr25519),
|
||||
},
|
||||
updatedParams: makeParams(makeParamsArgs{
|
||||
blockBytes: 100,
|
||||
blockGas: 200,
|
||||
evidenceAge: 300,
|
||||
maxEvidenceBytes: 50,
|
||||
pubkeyTypes: valSr25519}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
assert.Equal(t, tc.updatedParams, tc.params.UpdateConsensusParams(tc.updates))
|
||||
assert.Equal(t, tc.updatedParams, tc.intialParams.UpdateConsensusParams(tc.updates))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConsensusParamsUpdate_AppVersion(t *testing.T) {
|
||||
params := makeParams(1, 2, 3, 0, valEd25519)
|
||||
params := makeParams(makeParamsArgs{blockBytes: 1, blockGas: 2, evidenceAge: 3})
|
||||
|
||||
assert.EqualValues(t, 0, params.Version.AppVersion)
|
||||
|
||||
@@ -167,14 +318,16 @@ func TestConsensusParamsUpdate_AppVersion(t *testing.T) {
|
||||
|
||||
func TestProto(t *testing.T) {
|
||||
params := []ConsensusParams{
|
||||
makeParams(4, 2, 3, 1, valEd25519),
|
||||
makeParams(1, 4, 3, 1, valEd25519),
|
||||
makeParams(1, 2, 4, 1, valEd25519),
|
||||
makeParams(2, 5, 7, 1, valEd25519),
|
||||
makeParams(1, 7, 6, 1, valEd25519),
|
||||
makeParams(9, 5, 4, 1, valEd25519),
|
||||
makeParams(7, 8, 9, 1, valEd25519),
|
||||
makeParams(4, 6, 5, 1, valEd25519),
|
||||
makeParams(makeParamsArgs{blockBytes: 4, blockGas: 2, evidenceAge: 3, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 1, blockGas: 4, evidenceAge: 3, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 1, blockGas: 2, evidenceAge: 4, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 2, blockGas: 5, evidenceAge: 7, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 1, blockGas: 7, evidenceAge: 6, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 9, blockGas: 5, evidenceAge: 4, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 7, blockGas: 8, evidenceAge: 9, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{blockBytes: 4, blockGas: 6, evidenceAge: 5, maxEvidenceBytes: 1}),
|
||||
makeParams(makeParamsArgs{precision: time.Second, messageDelay: time.Minute}),
|
||||
makeParams(makeParamsArgs{precision: time.Nanosecond, messageDelay: time.Millisecond}),
|
||||
}
|
||||
|
||||
for i := range params {
|
||||
|
||||
@@ -34,14 +34,14 @@ type Proposal struct {
|
||||
|
||||
// NewProposal returns a new Proposal.
|
||||
// If there is no POLRound, polRound should be -1.
|
||||
func NewProposal(height int64, round int32, polRound int32, blockID BlockID) *Proposal {
|
||||
func NewProposal(height int64, round int32, polRound int32, blockID BlockID, ts time.Time) *Proposal {
|
||||
return &Proposal{
|
||||
Type: tmproto.ProposalType,
|
||||
Height: height,
|
||||
Round: round,
|
||||
BlockID: blockID,
|
||||
POLRound: polRound,
|
||||
Timestamp: tmtime.Now(),
|
||||
Timestamp: tmtime.Canonical(ts),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,31 @@ func (p *Proposal) ValidateBasic() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsTimely validates that the block timestamp is 'timely' according to the proposer-based timestamp algorithm.
|
||||
// To evaluate if a block is timely, its timestamp is compared to the local time of the validator along with the
|
||||
// configured Precision and MsgDelay parameters.
|
||||
// Specifically, a proposed block timestamp is considered timely if it is satisfies the following inequalities:
|
||||
//
|
||||
// localtime >= proposedBlockTime - Precision
|
||||
// localtime <= proposedBlockTime + MsgDelay + Precision
|
||||
//
|
||||
// Note: If the proposal is for the `initialHeight` the second inequality is not checked. This is because
|
||||
// the timestamp in this case is set to the preconfigured genesis time.
|
||||
// For more information on the meaning of 'timely', see the proposer-based timestamp specification:
|
||||
// https://github.com/tendermint/spec/tree/master/spec/consensus/proposer-based-timestamp
|
||||
func (p *Proposal) IsTimely(recvTime time.Time, sp SynchronyParams, initialHeight int64) bool {
|
||||
// lhs is `proposedBlockTime - Precision` in the first inequality
|
||||
lhs := p.Timestamp.Add(-sp.Precision)
|
||||
// rhs is `proposedBlockTime + MsgDelay + Precision` in the second inequality
|
||||
rhs := p.Timestamp.Add(sp.MessageDelay).Add(sp.Precision)
|
||||
|
||||
if recvTime.Before(lhs) || (p.Height != initialHeight && recvTime.After(rhs)) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// String returns a string representation of the Proposal.
|
||||
//
|
||||
// 1. height
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"github.com/tendermint/tendermint/internal/libs/protoio"
|
||||
tmrand "github.com/tendermint/tendermint/libs/rand"
|
||||
tmtime "github.com/tendermint/tendermint/libs/time"
|
||||
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -60,7 +61,7 @@ func TestProposalVerifySignature(t *testing.T) {
|
||||
|
||||
prop := NewProposal(
|
||||
4, 2, 2,
|
||||
BlockID{tmrand.Bytes(tmhash.Size), PartSetHeader{777, tmrand.Bytes(tmhash.Size)}})
|
||||
BlockID{tmrand.Bytes(tmhash.Size), PartSetHeader{777, tmrand.Bytes(tmhash.Size)}}, tmtime.Now())
|
||||
p := prop.ToProto()
|
||||
signBytes := ProposalSignBytes("test_chain_id", p)
|
||||
|
||||
@@ -172,7 +173,7 @@ func TestProposalValidateBasic(t *testing.T) {
|
||||
|
||||
prop := NewProposal(
|
||||
4, 2, 2,
|
||||
blockID)
|
||||
blockID, tmtime.Now())
|
||||
p := prop.ToProto()
|
||||
err := privVal.SignProposal(ctx, "test_chain_id", p)
|
||||
prop.Signature = p.Signature
|
||||
@@ -184,9 +185,9 @@ func TestProposalValidateBasic(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestProposalProtoBuf(t *testing.T) {
|
||||
proposal := NewProposal(1, 2, 3, makeBlockID([]byte("hash"), 2, []byte("part_set_hash")))
|
||||
proposal := NewProposal(1, 2, 3, makeBlockID([]byte("hash"), 2, []byte("part_set_hash")), tmtime.Now())
|
||||
proposal.Signature = []byte("sig")
|
||||
proposal2 := NewProposal(1, 2, 3, BlockID{})
|
||||
proposal2 := NewProposal(1, 2, 3, BlockID{}, tmtime.Now())
|
||||
|
||||
testCases := []struct {
|
||||
msg string
|
||||
@@ -210,3 +211,99 @@ func TestProposalProtoBuf(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsTimely(t *testing.T) {
|
||||
genesisTime, err := time.Parse(time.RFC3339, "2019-03-13T23:00:00Z")
|
||||
require.NoError(t, err)
|
||||
testCases := []struct {
|
||||
name string
|
||||
genesisHeight int64
|
||||
proposalHeight int64
|
||||
proposalTime time.Time
|
||||
recvTime time.Time
|
||||
precision time.Duration
|
||||
msgDelay time.Duration
|
||||
expectTimely bool
|
||||
}{
|
||||
// proposalTime - precision <= localTime <= proposalTime + msgDelay + precision
|
||||
{
|
||||
// Checking that the following inequality evaluates to true:
|
||||
// 0 - 2 <= 1 <= 0 + 1 + 2
|
||||
name: "basic timely",
|
||||
genesisHeight: 1,
|
||||
proposalHeight: 2,
|
||||
proposalTime: genesisTime,
|
||||
recvTime: genesisTime.Add(1 * time.Nanosecond),
|
||||
precision: time.Nanosecond * 2,
|
||||
msgDelay: time.Nanosecond,
|
||||
expectTimely: true,
|
||||
},
|
||||
{
|
||||
// Checking that the following inequality evaluates to false:
|
||||
// 0 - 2 <= 4 <= 0 + 1 + 2
|
||||
name: "local time too large",
|
||||
genesisHeight: 1,
|
||||
proposalHeight: 2,
|
||||
proposalTime: genesisTime,
|
||||
recvTime: genesisTime.Add(4 * time.Nanosecond),
|
||||
precision: time.Nanosecond * 2,
|
||||
msgDelay: time.Nanosecond,
|
||||
expectTimely: false,
|
||||
},
|
||||
{
|
||||
// Checking that the following inequality evaluates to false:
|
||||
// 4 - 2 <= 0 <= 4 + 2 + 1
|
||||
name: "proposal time too large",
|
||||
genesisHeight: 1,
|
||||
proposalHeight: 2,
|
||||
proposalTime: genesisTime.Add(4 * time.Nanosecond),
|
||||
recvTime: genesisTime,
|
||||
precision: time.Nanosecond * 2,
|
||||
msgDelay: time.Nanosecond,
|
||||
expectTimely: false,
|
||||
},
|
||||
{
|
||||
// Checking that the following inequality evaluates to true:
|
||||
// 0 - 2 <= 4
|
||||
// and the following check is skipped
|
||||
// 4 <= 0 + 1 + 2
|
||||
name: "local time too large but proposal is for genesis",
|
||||
genesisHeight: 1,
|
||||
proposalHeight: 1,
|
||||
proposalTime: genesisTime,
|
||||
recvTime: genesisTime.Add(4 * time.Nanosecond),
|
||||
precision: time.Nanosecond * 2,
|
||||
msgDelay: time.Nanosecond,
|
||||
expectTimely: true,
|
||||
},
|
||||
{
|
||||
// Checking that the following inequality evaluates to false:
|
||||
// 4 - 2 <= 0
|
||||
name: "proposal time too large for genesis block proposal",
|
||||
genesisHeight: 1,
|
||||
proposalHeight: 1,
|
||||
proposalTime: genesisTime.Add(4 * time.Nanosecond),
|
||||
recvTime: genesisTime,
|
||||
precision: time.Nanosecond * 2,
|
||||
msgDelay: time.Nanosecond,
|
||||
expectTimely: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
p := Proposal{
|
||||
Height: testCase.proposalHeight,
|
||||
Timestamp: testCase.proposalTime,
|
||||
}
|
||||
|
||||
sp := SynchronyParams{
|
||||
Precision: testCase.precision,
|
||||
MessageDelay: testCase.msgDelay,
|
||||
}
|
||||
|
||||
ti := p.IsTimely(testCase.recvTime, sp, testCase.genesisHeight)
|
||||
assert.Equal(t, testCase.expectTimely, ti)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (vote *Vote) CommitSig() CommitSig {
|
||||
switch {
|
||||
case vote.BlockID.IsComplete():
|
||||
blockIDFlag = BlockIDFlagCommit
|
||||
case vote.BlockID.IsZero():
|
||||
case vote.BlockID.IsNil():
|
||||
blockIDFlag = BlockIDFlagNil
|
||||
default:
|
||||
panic(fmt.Sprintf("Invalid vote %v - expected BlockID to be either empty or complete", vote))
|
||||
@@ -177,7 +177,7 @@ func (vote *Vote) ValidateBasic() error {
|
||||
|
||||
// BlockID.ValidateBasic would not err if we for instance have an empty hash but a
|
||||
// non-empty PartsSetHeader:
|
||||
if !vote.BlockID.IsZero() && !vote.BlockID.IsComplete() {
|
||||
if !vote.BlockID.IsNil() && !vote.BlockID.IsComplete() {
|
||||
return fmt.Errorf("blockID must be either empty or complete, got: %v", vote.BlockID)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestVoteSet_AddVote_Good(t *testing.T) {
|
||||
assert.Nil(t, voteSet.GetByAddress(val0Addr))
|
||||
assert.False(t, voteSet.BitArray().GetIndex(0))
|
||||
blockID, ok := voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(), "there should be no 2/3 majority")
|
||||
assert.False(t, ok || !blockID.IsNil(), "there should be no 2/3 majority")
|
||||
|
||||
vote := &Vote{
|
||||
ValidatorAddress: val0Addr,
|
||||
@@ -48,7 +48,7 @@ func TestVoteSet_AddVote_Good(t *testing.T) {
|
||||
assert.NotNil(t, voteSet.GetByAddress(val0Addr))
|
||||
assert.True(t, voteSet.BitArray().GetIndex(0))
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(), "there should be no 2/3 majority")
|
||||
assert.False(t, ok || !blockID.IsNil(), "there should be no 2/3 majority")
|
||||
}
|
||||
|
||||
func TestVoteSet_AddVote_Bad(t *testing.T) {
|
||||
@@ -155,7 +155,7 @@ func TestVoteSet_2_3Majority(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
blockID, ok := voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(), "there should be no 2/3 majority")
|
||||
assert.False(t, ok || !blockID.IsNil(), "there should be no 2/3 majority")
|
||||
|
||||
// 7th validator voted for some blockhash
|
||||
{
|
||||
@@ -166,7 +166,7 @@ func TestVoteSet_2_3Majority(t *testing.T) {
|
||||
_, err = signAddVote(ctx, privValidators[6], withBlockHash(vote, tmrand.Bytes(32)), voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(), "there should be no 2/3 majority")
|
||||
assert.False(t, ok || !blockID.IsNil(), "there should be no 2/3 majority")
|
||||
}
|
||||
|
||||
// 8th validator voted for nil.
|
||||
@@ -178,7 +178,7 @@ func TestVoteSet_2_3Majority(t *testing.T) {
|
||||
_, err = signAddVote(ctx, privValidators[7], vote, voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.True(t, ok || blockID.IsZero(), "there should be 2/3 majority for nil")
|
||||
assert.True(t, ok || blockID.IsNil(), "there should be 2/3 majority for nil")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
blockID, ok := voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(),
|
||||
assert.False(t, ok || !blockID.IsNil(),
|
||||
"there should be no 2/3 majority")
|
||||
|
||||
// 67th validator voted for nil
|
||||
@@ -225,7 +225,7 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
_, err = signAddVote(ctx, privValidators[66], withBlockHash(vote, nil), voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(),
|
||||
assert.False(t, ok || !blockID.IsNil(),
|
||||
"there should be no 2/3 majority: last vote added was nil")
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
_, err = signAddVote(ctx, privValidators[67], withBlockPartSetHeader(vote, blockPartsHeader), voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(),
|
||||
assert.False(t, ok || !blockID.IsNil(),
|
||||
"there should be no 2/3 majority: last vote added had different PartSetHeader Hash")
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
_, err = signAddVote(ctx, privValidators[68], withBlockPartSetHeader(vote, blockPartsHeader), voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(),
|
||||
assert.False(t, ok || !blockID.IsNil(),
|
||||
"there should be no 2/3 majority: last vote added had different PartSetHeader Total")
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ func TestVoteSet_2_3MajorityRedux(t *testing.T) {
|
||||
_, err = signAddVote(ctx, privValidators[69], withBlockHash(vote, tmrand.Bytes(32)), voteSet)
|
||||
require.NoError(t, err)
|
||||
blockID, ok = voteSet.TwoThirdsMajority()
|
||||
assert.False(t, ok || !blockID.IsZero(),
|
||||
assert.False(t, ok || !blockID.IsNil(),
|
||||
"there should be no 2/3 majority: last vote added had different BlockHash")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user