From 2a1d0542f35f4a3cf6c9f9fa9d4c39ba95b5b5a5 Mon Sep 17 00:00:00 2001 From: folex <0xdxdy@gmail.com> Date: Fri, 6 Sep 2019 11:20:28 +0200 Subject: [PATCH] Add full vote to BeginBlock --- abci/types/types.proto | 16 +++++ abci/types/typespb_test.go | 124 ++++++++++++++++++++++++++++++++ docs/app-dev/app-development.md | 3 +- docs/spec/abci/abci.md | 3 +- state/execution.go | 2 + state/execution_test.go | 3 + types/protobuf.go | 15 ++++ 7 files changed, 163 insertions(+), 3 deletions(-) diff --git a/abci/types/types.proto b/abci/types/types.proto index 8f9dda83..f731e5ad 100644 --- a/abci/types/types.proto +++ b/abci/types/types.proto @@ -311,6 +311,22 @@ message ValidatorUpdate { message VoteInfo { Validator validator = 1 [(gogoproto.nullable)=false]; bool signed_last_block = 2; + Vote full_vote = 3; +} + +// Vote from LastCommit +message Vote { + int32 type = 1; + // TODO: maybe remove height cause it could be derived from BeginBlock? + int64 height = 2; + int32 round = 3; + // TODO: maybe remove block id cause it's already in the header as last_block_id? + BlockID block_id = 4 [json_name = "block_id"]; + google.protobuf.Timestamp timestamp = 5 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true]; + // TODO: maybe remove validator address cause it's already in VoteInfo.validator? + bytes validator_address = 6 [json_name = "validator_address"]; + int32 validator_index = 7 [json_name = "validator_index"]; + bytes signature = 8; } message PubKey { diff --git a/abci/types/typespb_test.go b/abci/types/typespb_test.go index e900d384..96206502 100644 --- a/abci/types/typespb_test.go +++ b/abci/types/typespb_test.go @@ -2153,6 +2153,62 @@ func TestVoteInfoMarshalTo(t *testing.T) { } } +func TestVoteProto(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedVote(popr, false) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) + if err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + msg := &Vote{} + if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + littlefuzz := make([]byte, len(dAtA)) + copy(littlefuzz, dAtA) + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) + } + if !p.Equal(msg) { + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) + } + if len(littlefuzz) > 0 { + fuzzamount := 100 + for i := 0; i < fuzzamount; i++ { + littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256)) + littlefuzz = append(littlefuzz, byte(popr.Intn(256))) + } + // shouldn't panic + _ = github_com_gogo_protobuf_proto.Unmarshal(littlefuzz, msg) + } +} + +func TestVoteMarshalTo(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedVote(popr, false) + size := p.Size() + dAtA := make([]byte, size) + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) + } + _, err := p.MarshalTo(dAtA) + if err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + msg := &Vote{} + if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) + } + if !p.Equal(msg) { + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) + } +} + func TestPubKeyProto(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) @@ -2949,6 +3005,24 @@ func TestVoteInfoJSON(t *testing.T) { t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p) } } +func TestVoteJSON(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedVote(popr, true) + marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{} + jsondata, err := marshaler.MarshalToString(p) + if err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + msg := &Vote{} + err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg) + if err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + if !p.Equal(msg) { + t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p) + } +} func TestPubKeyJSON(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) @@ -4049,6 +4123,34 @@ func TestVoteInfoProtoCompactText(t *testing.T) { } } +func TestVoteProtoText(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedVote(popr, true) + dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p) + msg := &Vote{} + if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + if !p.Equal(msg) { + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) + } +} + +func TestVoteProtoCompactText(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedVote(popr, true) + dAtA := github_com_gogo_protobuf_proto.CompactTextString(p) + msg := &Vote{} + if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + if !p.Equal(msg) { + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) + } +} + func TestPubKeyProtoText(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) @@ -4941,6 +5043,28 @@ func TestVoteInfoSize(t *testing.T) { } } +func TestVoteSize(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedVote(popr, true) + size2 := github_com_gogo_protobuf_proto.Size(p) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) + if err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + size := p.Size() + if len(dAtA) != size { + t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(dAtA)) + } + if size2 != size { + t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2) + } + size3 := github_com_gogo_protobuf_proto.Size(p) + if size3 != size { + t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3) + } +} + func TestPubKeySize(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) diff --git a/docs/app-dev/app-development.md b/docs/app-dev/app-development.md index ba21d3a3..28f70250 100644 --- a/docs/app-dev/app-development.md +++ b/docs/app-dev/app-development.md @@ -265,8 +265,7 @@ ResponseCommit requestCommit(RequestCommit requestCommit) { ### BeginBlock The BeginBlock request can be used to run some code at the beginning of -every block. It also allows Tendermint to send the current block hash -and header to the application, before it sends any of the transactions. +every block. It also allows Tendermint to send the current block hash, header and commit of the last block to the application, before it sends any of the transactions. The app should remember the latest height and header (ie. from which it has run a successful Commit) so that it can tell Tendermint where to diff --git a/docs/spec/abci/abci.md b/docs/spec/abci/abci.md index 275c4dcd..340ed9dd 100644 --- a/docs/spec/abci/abci.md +++ b/docs/spec/abci/abci.md @@ -449,6 +449,7 @@ Commit are included in the header of the next block. - `Validator (Validator)`: A validator - `SignedLastBlock (bool)`: Indicates whether or not the validator signed the last block + - `FullVote (Vote)`: Validator's vote - **Usage**: - Indicates whether a validator signed the last block, allowing for rewards based on validator availability @@ -482,7 +483,7 @@ Commit are included in the header of the next block. - **Fields**: - `Round (int32)`: Commit round. - `Votes ([]VoteInfo)`: List of validators addresses in the last validator set - with their voting power and whether or not they signed a vote. + with their voting power, votes and whether or not they signed a vote. ### ConsensusParams diff --git a/state/execution.go b/state/execution.go index 2ac2e968..5673fe5b 100644 --- a/state/execution.go +++ b/state/execution.go @@ -334,6 +334,8 @@ func getBeginBlockValidatorInfo(block *types.Block, stateDB dbm.DB) (abci.LastCo voteInfo := abci.VoteInfo{ Validator: types.TM2PB.Validator(val), SignedLastBlock: vote != nil, + // TODO: maybe make it optional, set by config, disabled by default? + FullVote: types.TM2PB.Vote(vote), } voteInfos[i] = voteInfo } diff --git a/state/execution_test.go b/state/execution_test.go index 02d13b35..f58e1501 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -92,9 +92,12 @@ func TestBeginBlockValidators(t *testing.T) { tc.expectedAbsentValidators[ctr] == i { assert.False(t, v.SignedLastBlock) + assert.Nil(t, v.FullVote) ctr++ } else { assert.True(t, v.SignedLastBlock) + assert.NotNil(t, v.FullVote) + assert.Equal(t, *v.FullVote, types.Vote(*tc.lastCommitPrecommits[i])) } } } diff --git a/types/protobuf.go b/types/protobuf.go index c87e82c0..f4dc526a 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -220,3 +220,18 @@ func (pb2tm) ValidatorUpdates(vals []abci.ValidatorUpdate) ([]*Validator, error) } return tmVals, nil } + +func (tm2pb) Vote(vote *CommitSig) *abci.Vote { + blockID := TM2PB.BlockID(vote.BlockID) + + return &abci.Vote{ + Type: int32(vote.Type), + Height: vote.Height, + Round: int32(vote.Round), + BlockId: &blockID, + Timestamp: vote.Timestamp, + ValidatorAddress: vote.ValidatorAddress, + ValidatorIndex: int32(vote.ValidatorIndex), + Signature: vote.Signature, + } +}