[cherry-picked] ABCI Vote Extension 2 (#6885)

* add proto, add boilerplates

* add canonical

* fix tests

* add vote signing test

* Update internal/consensus/msgs_test.go

* modify state execution in progress

* add extension signing

* add extension signing

* VoteExtension -> ExtendVote

* modify state execution in progress

* add extension signing

* verify in progress

* modify CommitSig

* fix test

* apply review

* update data structures

* Apply suggestions from code review

* Add comments

* fix test

* VoteExtensionSigned => VoteExtensionToSigned

* Apply suggestions from code review

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>

* *Signed -> *ToSign

* add Vote to RequestExtendVote

* add example VoteExtension

* apply reviews

* fix vote

* Apply suggestions from code review

Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>

* fix typo, modify proto

* add abcipp_kvstore.go

* add extension test

* fix test

* fix test

* fix test

* fit lint

* uncomment test

* refactor test in progress

* gofmt

* apply review

* fix lint

Co-authored-by: Aleksandr Bezobchuk <alexanderbez@users.noreply.github.com>
Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
This commit is contained in:
mconcat
2021-09-23 00:19:09 +09:00
committed by Sergio Mena
parent 10588ec66b
commit 2f2986b15a
8 changed files with 103 additions and 16 deletions

View File

@@ -20,7 +20,9 @@ type Application interface {
ProcessProposal(context.Context, *RequestProcessProposal) (*ResponseProcessProposal, error)
// Deliver the decided block with its txs to the Application
FinalizeBlock(context.Context, *RequestFinalizeBlock) (*ResponseFinalizeBlock, error)
// Create application specific vote extension
ExtendVote(context.Context, *RequestExtendVote) (*ResponseExtendVote, error)
// Verify application's vote extension data
VerifyVoteExtension(context.Context, *RequestVerifyVoteExtension) (*ResponseVerifyVoteExtension, error)
// Commit the state and return the application Merkle root hash
Commit(context.Context, *RequestCommit) (*ResponseCommit, error)

View File

@@ -5,6 +5,8 @@ import (
"encoding/json"
"github.com/cosmos/gogoproto/jsonpb"
types "github.com/tendermint/tendermint/proto/tendermint/types"
)
const (
@@ -51,6 +53,16 @@ func (r ResponseProcessProposal) IsStatusUnknown() bool {
return r.Status == ResponseProcessProposal_UNKNOWN
}
// IsOK returns true if Code is OK
func (r ResponseVerifyVoteExtension) IsOK() bool {
return r.Result <= ResponseVerifyVoteExtension_ACCEPT
}
// IsErr returns true if Code is something other than OK.
func (r ResponseVerifyVoteExtension) IsErr() bool {
return r.Result > ResponseVerifyVoteExtension_ACCEPT
}
//---------------------------------------------------------------------------
// override JSON marshaling so we emit defaults (ie. disable omitempty)
@@ -156,3 +168,25 @@ func MarshalTxResults(r []*ExecTxResult) ([][]byte, error) {
}
return s, nil
}
// -----------------------------------------------
// construct Result data
func RespondExtendVote(appDataToSign, appDataSelfAuthenticating []byte) ResponseExtendVote {
return ResponseExtendVote{
VoteExtension: &types.VoteExtension{
AppDataToSign: appDataToSign,
AppDataSelfAuthenticating: appDataSelfAuthenticating,
},
}
}
func RespondVerifyVoteExtension(ok bool) ResponseVerifyVoteExtension {
result := ResponseVerifyVoteExtension_REJECT
if ok {
result = ResponseVerifyVoteExtension_ACCEPT
}
return ResponseVerifyVoteExtension{
Result: result,
}
}

View File

@@ -108,6 +108,7 @@ func (vs *validatorStub) signVote(
Timestamp: tmtime.Now(),
Type: voteType,
BlockID: types.BlockID{Hash: hash, PartSetHeader: header},
VoteExtension: types.VoteExtensionFromProto(kvstore.ConstructVoteExtension(pubKey.Address())),
}
v := vote.ToProto()
if err := vs.PrivValidator.SignVote(test.DefaultTestChainID, v); err != nil {
@@ -133,6 +134,10 @@ func signVote(vs *validatorStub, voteType tmproto.SignedMsgType, hash []byte, he
panic(fmt.Errorf("failed to sign vote: %v", err))
}
// TODO: remove hardcoded vote extension.
// currently set for abci/examples/kvstore/persistent_kvstore.go
v.VoteExtension = types.VoteExtensionFromProto(kvstore.ConstructVoteExtension(v.ValidatorAddress))
vs.lastVote = v
return v

View File

@@ -1971,6 +1971,7 @@ func (cs *State) handleCompleteProposal(blockHeight int64) {
// Attempt to add the vote. if its a duplicate signature, dupeout the validator
func (cs *State) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) {
added, err := cs.addVote(vote, peerID)
if err != nil {
// If the vote height is off, we'll just ignore it,
// But if it's a conflicting sig, add it to the cs.evpool.
@@ -2068,6 +2069,13 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error
return
}
// Verify VoteExtension if precommit
if vote.Type == tmproto.PrecommitType {
if err = cs.blockExec.VerifyVoteExtension(vote); err != nil {
return false, err
}
}
height := cs.Height
added, err = cs.Votes.AddVote(vote, peerID)
if !added {
@@ -2229,9 +2237,6 @@ func (cs *State) signVote(
BlockID: types.BlockID{Hash: hash, PartSetHeader: header},
}
v := vote.ToProto()
err := cs.privValidator.SignVote(cs.state.ChainID, v)
switch msgType {
case tmproto.PrecommitType:
// if the signedMessage type is for a precommit, add VoteExtension
@@ -2241,6 +2246,8 @@ func (cs *State) signVote(
}
vote.VoteExtension = ext
}
v := vote.ToProto()
err := cs.privValidator.SignVote(cs.state.ChainID, v)
vote.Signature = v.Signature
vote.Timestamp = v.Timestamp

View File

@@ -306,6 +306,23 @@ func (blockExec *BlockExecutor) ExtendVote(vote *types.Vote) (types.VoteExtensio
return types.VoteExtensionFromProto(resp.VoteExtension), nil
}
func (blockExec *BlockExecutor) VerifyVoteExtension(vote *types.Vote) error {
req := abci.RequestVerifyVoteExtension{
Vote: vote.ToProto(),
}
resp, err := blockExec.proxyApp.VerifyVoteExtensionSync(req)
if err != nil {
return err
}
if resp.IsErr() {
return types.ErrVoteInvalidExtension
}
return nil
}
// Commit locks the mempool, runs the ABCI Commit message, and updates the
// mempool.
// It returns the result of calling abci.Commit (the AppHash) and the height to retain (if any).

View File

@@ -179,14 +179,14 @@ func TestFinalizeBlockValidators(t *testing.T) {
[]byte("Signature1"),
state.Validators.Validators[0].Address,
now,
types.VoteExtensionToSign{},
)
types.VoteExtensionToSign{},
)
commitSig1 = types.NewCommitSigForBlock(
[]byte("Signature2"),
state.Validators.Validators[1].Address,
now,
types.VoteExtensionToSign{},
)
types.VoteExtensionToSign{},
)
absentSig = types.NewCommitSigAbsent()
)

View File

@@ -720,6 +720,7 @@ func (cs *CommitSig) ToProto() *tmproto.CommitSig {
ValidatorAddress: cs.ValidatorAddress,
Timestamp: cs.Timestamp,
Signature: cs.Signature,
VoteExtension: cs.VoteExtension.ToProto(),
}
}
@@ -731,6 +732,7 @@ func (cs *CommitSig) FromProto(csp tmproto.CommitSig) error {
cs.ValidatorAddress = csp.ValidatorAddress
cs.Timestamp = csp.Timestamp
cs.Signature = csp.Signature
cs.VoteExtension = VoteExtensionToSignFromProto(csp.VoteExtension)
return cs.ValidateBasic()
}

View File

@@ -52,12 +52,34 @@ type VoteExtensionToSign struct {
AppDataToSign []byte `json:"app_data_to_sign"`
}
func (ext VoteExtensionToSign) ToProto() *tmproto.VoteExtensionToSign {
if ext.IsEmpty() {
return nil
}
return &tmproto.VoteExtensionToSign{
AppDataToSign: ext.AppDataToSign,
}
}
func VoteExtensionToSignFromProto(pext *tmproto.VoteExtensionToSign) VoteExtensionToSign {
if pext == nil {
return VoteExtensionToSign{}
}
return VoteExtensionToSign{
AppDataToSign: pext.AppDataToSign,
}
}
func (ext VoteExtensionToSign) IsEmpty() bool {
return len(ext.AppDataToSign) == 0
}
// BytesPacked returns a bytes-packed representation for
// debugging and human identification. This function should
// not be used for any logical operations.
func (ext VoteExtensionToSign) BytesPacked() []byte {
res := make([]byte, len(ext.AppDataToSign))
copy(res, ext.AppDataToSign)
res := []byte{}
res = append(res, ext.AppDataToSign...)
return res
}
@@ -86,9 +108,9 @@ func (ext VoteExtension) ToSign() VoteExtensionToSign {
// debugging and human identification. This function should
// not be used for any logical operations.
func (ext VoteExtension) BytesPacked() []byte {
res := make([]byte, len(ext.AppDataToSign)+len(ext.AppDataSelfAuthenticating))
copy(res[:len(ext.AppDataToSign)], ext.AppDataToSign)
copy(res[len(ext.AppDataToSign):], ext.AppDataSelfAuthenticating)
res := []byte{}
res = append(res, ext.AppDataToSign...)
res = append(res, ext.AppDataSelfAuthenticating...)
return res
}
@@ -257,11 +279,9 @@ func (vote *Vote) ValidateBasic() error {
func (ext VoteExtension) Copy() VoteExtension {
res := VoteExtension{
AppDataToSign: make([]byte, len(ext.AppDataToSign)),
AppDataSelfAuthenticating: make([]byte, len(ext.AppDataSelfAuthenticating)),
AppDataToSign: ext.AppDataToSign,
AppDataSelfAuthenticating: ext.AppDataSelfAuthenticating,
}
copy(res.AppDataToSign, ext.AppDataToSign)
copy(res.AppDataSelfAuthenticating, ext.AppDataSelfAuthenticating)
return res
}