In this section you will learn how a node can ExtendVote
and VerifyVote
on the forum application.
The ExtendVote
method allows applications to extend the pre-commit vote with arbitrary data. This allows applications
to force their validators to do more than just validate blocks within consensus.
When a validator is preparing to send a pre-commit
vote, it first calls ExtendVote.
The application then returns a
blob of data called a vote extension.
This data is opaque to the consensus algorithm but can contain application-specific
information.
The validator then sends both the pre-commit
vote and the vote extension
together to other validators. Other validators
also call ExtendVote
to generate their own vote extensions.
When a validator receives a pre-commit
vote with an attached vote extension
, it calls VerifyVoteExtension
to validate
the vote extension.
If valid, the validator includes the vote in its tally.
The proposer of the next block will receive all vote extensions
in PrepareProposalRequest
.
This allows validators to have access to all vote extensions at the next height. They can then use the data in the
vote extensions
to inform the transactions that make it into the next block.
Following is the code for the ExtendVote
function:
// ExtendVote returns curse words as vote extensions
func (app *ForumApp) ExtendVote(_ context.Context, _ *abci.ExtendVoteRequest) (*abci.ExtendVoteResponse, error) {
app.logger.Info("Executing Application ExtendVote")
return &abci.ExtendVoteResponse{VoteExtension: []byte(app.CurseWords)}, nil
}
Explanation of code:
ExtendVote
function takes two parameters: a context.Context
and a pointer to an abci.ExtendVoteRequest
struct.
It returns a pointer to an ExtendVoteResponse
struct and an error.
The method implementation simply returns a new instance of ExtendVoteResponse
with the VoteExtension
field set to
the value of app.CurseWords.
The app.CurseWords
is expected to be a byte array containing the vote extension data.
The ExtendVoteResponse
struct is used to encapsulate the response data for the ExtendVote
method. By setting
the VoteExtension
field, the method includes the application-specific vote extension data in the response.
In this implementation, the ExtendVote
method in the ForumApp application returns the application-specific vote extension
data stored in the app.CurseWords
variable.
Tip: The vote extensions
are opaque to the consensus algorithm but visible to the application, allowing for a variety
of use cases like price oracles, encrypted mempools, and threshold cryptography.
The VerifyVoteExtension
method allows applications to verify the VoteExtension
data attached to each pre-commit
message.
When a validator is preparing to send a pre-commit
vote, it first calls ExtendVote
to generate a VoteExtension.
This VoteExtension
is broadcast along with the pre-commit
vote.
Other validators also call ExtendVote
to generate their own vote extensions.
However, not all validators will generate
the same vote extension.
When a validator receives a pre-commit
vote with an attached vote extension
, it calls VerifyVoteExtension
to
validate the vote extension.
If the vote extension is successfully verified, the pre-commit
vote is included in the tally. If validation fails,
the entire pre-commit
message is ignored.
Following is the blurb of code for the VerifyVoteExtension
function:
// VerifyVoteExtension verifies the vote extensions and ensure they include the curse words
// It will not be called for extensions generated by this validator.
func (app *ForumApp) VerifyVoteExtension(_ context.Context, req *abci.VerifyVoteExtensionRequest) (*abci.VerifyVoteExtensionResponse, error) {
app.logger.Info("Executing Application VerifyVoteExtension")
if _, ok := app.valAddrToPubKeyMap[string(req.ValidatorAddress)]; !ok {
// we do not have a validator with this address mapped; this should never happen
return nil, errors.New("unknown validator")
}
curseWords := strings.Split(string(req.VoteExtension), "|")
if hasDuplicateWords(curseWords) {
return &abci.VerifyVoteExtensionResponse{Status: abci.VERIFY_VOTE_EXTENSION_STATUS_REJECT}, nil
}
// ensure vote extension curse words limit has not been exceeded
if len(curseWords) > CurseWordsLimitVE {
return &abci.VerifyVoteExtensionResponse{Status: abci.VERIFY_VOTE_EXTENSION_STATUS_REJECT}, nil
}
return &abci.VerifyVoteExtensionResponse{Status: abci.VERIFY_VOTE_EXTENSION_STATUS_ACCEPT}, nil
}
Explanation of code:
VerifyVoteExtension
function takes two parameters: a context.Context
and a pointer to an VerifyVoteExtensionRequest
object.
It returns a pointer to an VerifyVoteExtensionResponse
object and an error.
The implementation checks if the validator address provided in the request (req.ValidatorAddress
) is mapped to a public
key in the app.valAddrToPubKeyMap.
If the validator address is not found in the map, it returns an error indicating
an “unknown validator”. This check ensures that the validator making the request is recognized by the application.
The method splits the VoteExtension
field of the request (req.VoteExtension
) into individual words using the
strings.Split
function. The separator used is the pipe character (|). The resulting words are stored in the curseWords
slice.
The implementation creates a temporary map called tmpCurseWordMap
to verify that there are no duplicate words in the
curseWords
slice and to check if the validator is trying to cheat by including the same word multiple times.
If the length of the tmpCurseWordMap
is less than the length of the curseWords
slice, it means that there are
duplicate words in the extension. In this case, the method returns a response with a status of abci.VERIFY_VOTE_EXTENSION_STATUS_REJECT
,
indicating that the vote extension
is rejected.
If the length of the curseWords
slic is greater than the maximum number of cursor words allowed in vote extensions (CurseWordsLimitVE = 10
),
it is not permitted. In this case, the method returns a response with a status of abci.VERIFY_VOTE_EXTENSION_STATUS_REJECT
,
indicating that the vote extension
is rejected.
If there are no duplicate words in the extension, the method returns a response with a status of abci.VERIFY_VOTE_EXTENSION_STATUS_ACCEPT
,
indicating that the vote extension
is accepted.
Tip: Verified vote extensions
can be persisted by the application. For example, the application could store data
derived from the vote extensions.
In the next session, you will find the entire implementation of the Forum Application in the app.go file.