mirror of
https://github.com/tendermint/tendermint.git
synced 2026-02-03 10:32:05 +00:00
[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:
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user