Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 4 additions & 14 deletions abci/extend_vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,6 @@ func (h *VoteExtHandler) constructVoteExtBody(ctx sdk.Context, req *abci.Request
return voteexthandler.Body{}, errors.Wrap(types.ErrFailedToComputeInitialValidatorSetRoot, err.Error())
}*/

prevStateRoot := h.stateRoots[req.GetHeight()-1]
initStateRoot := h.stateRoots[req.GetHeight()]

var extBody voteexthandler.Body
/*if len(validatorUpdates) != 0 {

// Apply validator set updates to the initial validator set and create merkle tree from the new validator set
Expand Down Expand Up @@ -157,20 +153,14 @@ func (h *VoteExtHandler) constructVoteExtBody(ctx sdk.Context, req *abci.Request
}
}*/

extBody = voteexthandler.Body{
InitialValidatorSetRoot: hardcoded[:],
InitialBlockHeight: req.GetHeight() - 1,
InitialStateRoot: prevStateRoot,
NewValidatorSetRoot: hardcoded[:],
NewBlockHeight: req.GetHeight(),
NewStateRoot: initStateRoot,
}

return extBody, nil
return h.buildExpectedVoteExtBody(req.GetHeight())
}

func (h *VoteExtHandler) ExtendVoteHandler() sdk.ExtendVoteHandler {
return func(ctx sdk.Context, req *abci.RequestExtendVote) (*abci.ResponseExtendVote, error) {
if req.GetHeight() < 3 {
return &abci.ResponseExtendVote{VoteExtension: []byte{}}, nil
}

// Initialize poseidon hash
poseidonHash := poseidon.CreatePoseidon(*field.Fp, constants.PoseidonParamsKimchiFp)
Expand Down
85 changes: 60 additions & 25 deletions abci/helpers.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package vote_ext

import (
"encoding/json"
/*
"math/big"
"sort"
*/
"strconv"
"sync"

"cosmossdk.io/errors"
Expand Down Expand Up @@ -61,7 +61,7 @@ func verifySchnorr(voteExt MinaSignatureVoteExt, pubKey keys.PublicKey, ctx sdk.
if err := sig.UnmarshalBytes(voteExt.Signature); err != nil {
return errors.Wrap(types.ErrInvalidSigEncoding, "")
}
// Verify signature; if ok, keep the vote in memory.
// Verify the vote-extension signature against the reconstructed body hash.
if !pubKey.Verify(sig, extBodyHashInput, types.DevnetNetworkID) {
return errors.Wrap(types.ErrInvalidSignature, "")
}
Expand Down Expand Up @@ -195,6 +195,62 @@ func (h *VoteExtHandler) computeValidatorSetMerkleRoot(validators []ValidatorInf

*/

func cloneBytes(b []byte) []byte {
if b == nil {
return nil
}

return append([]byte(nil), b...)
}

func (h *VoteExtHandler) storeStateRoot(height int64, root []byte) {
h.mu.Lock()
defer h.mu.Unlock()

if h.stateRoots == nil {
h.stateRoots = make(map[int64][]byte)
}

h.stateRoots[height] = cloneBytes(root)
}

func (h *VoteExtHandler) getStateRoot(height int64) ([]byte, bool) {
h.mu.RLock()
defer h.mu.RUnlock()

root, ok := h.stateRoots[height]
if !ok {
return nil, false
}

return cloneBytes(root), true
}

func (h *VoteExtHandler) buildExpectedVoteExtBody(height int64) (voteexthandler.Body, error) {
if height < 3 {
return voteexthandler.Body{}, errors.Wrap(types.ErrFailedToGetVoteExtBody, "vote extensions start at height 3")
}

initialStateRoot, ok := h.getStateRoot(height - 2)
if !ok {
return voteexthandler.Body{}, errors.Wrap(types.ErrFailedToGetVoteExtBody, "missing state root for committed height "+strconv.FormatInt(height-2, 10))
}

newStateRoot, ok := h.getStateRoot(height - 1)
if !ok {
return voteexthandler.Body{}, errors.Wrap(types.ErrFailedToGetVoteExtBody, "missing state root for committed height "+strconv.FormatInt(height-1, 10))
}

return voteexthandler.Body{
InitialValidatorSetRoot: hardcoded[:],
InitialBlockHeight: height - 2,
InitialStateRoot: initialStateRoot,
NewValidatorSetRoot: hardcoded[:],
NewBlockHeight: height - 1,
NewStateRoot: newStateRoot,
}, nil
}

// storeVote saves the extension in-memory for later proposal processing.
func (h *VoteExtHandler) storeVote(height uint64, minaAddress string, ext []byte) {
h.mu.Lock()
Expand All @@ -205,7 +261,7 @@ func (h *VoteExtHandler) storeVote(height uint64, minaAddress string, ext []byte
if _, ok := h.votes[height]; !ok {
h.votes[height] = make(map[string][]byte)
}
h.votes[height][minaAddress] = ext
h.votes[height][minaAddress] = cloneBytes(ext)
}

// fetchVotes returns a COPY of the map for the given height.
Expand All @@ -215,7 +271,7 @@ func (h *VoteExtHandler) fetchVotes(height uint64) map[string][]byte {
res := make(map[string][]byte)
if m, ok := h.votes[height]; ok {
for k, v := range m {
res[k] = v
res[k] = cloneBytes(v)
}
}
return res
Expand All @@ -227,24 +283,3 @@ func (h *VoteExtHandler) deleteVotes(height uint64) {
defer h.mu.Unlock()
delete(h.votes, height)
}

func (h *VoteExtHandler) getVoteExtBody(height uint64) (types.Body, error) {
h.mu.Lock()
defer h.mu.Unlock()

ownMinaAddress, err := h.MinaPrivateKey.PublicKey.ToAddress()
if err != nil {
return types.Body{}, errors.Wrap(types.ErrFailedToConvertPubKeyToAddr, "nodes own secondary pubkey")
}

for _, vote := range h.votes[height] {
var ve MinaSignatureVoteExt
if err := json.Unmarshal(vote, &ve); err != nil {
continue // skip malformed entry
}
if ve.MinaAddress == ownMinaAddress {
return ve.VoteExtBody, nil
}
}
return types.Body{}, errors.Wrap(types.ErrMissingVoteExt, "")
}
1 change: 0 additions & 1 deletion abci/pre_blocker.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ func (h *VoteExtHandler) PreBlocker() sdk.PreBlocker {

// If height is 1, we won't have any votes thus skip the proposal
if req.GetHeight() == 1 {
h.stateRoots[req.GetHeight()] = ctx.BlockHeader().AppHash
return &sdk.ResponsePreBlock{}, nil
}

Expand Down
7 changes: 2 additions & 5 deletions abci/prepare_proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (
// PrepareProposalHandler injects the collected vote-extensions (for height-1)
// as the very first transaction of the proposal block.
// A simple JSON payload prefixed by "VOTEEXT:" is used; this is *not* part of
// consensus state and will be verified by ProcessProposal on peers.
// consensus state and will be verified by ProcessProposal on peers. The state
// root cache is seeded in ProcessProposal, not in PrepareProposal.
func (h *VoteExtHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {

return func(ctx sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) {
Expand All @@ -21,8 +22,6 @@ func (h *VoteExtHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
targetHeight := uint64(req.GetHeight() - 1)
votes := h.fetchVotes(targetHeight)
if len(votes) == 0 {
h.stateRoots[req.GetHeight()] = ctx.BlockHeader().AppHash

return &abci.ResponsePrepareProposal{Txs: req.Txs}, nil
}

Expand All @@ -40,8 +39,6 @@ func (h *VoteExtHandler) PrepareProposalHandler() sdk.PrepareProposalHandler {
txs = append(txs, extTx)
txs = append(txs, req.Txs...)

h.stateRoots[req.GetHeight()] = ctx.BlockHeader().AppHash

return &abci.ResponsePrepareProposal{Txs: txs}, nil
}
}
8 changes: 7 additions & 1 deletion abci/process_proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (h *VoteExtHandler) reconstructVoteExtBody(req *abci.RequestProcessProposal
if err := json.Unmarshal(voteBytes, &ve); err != nil {
continue // skip malformed entry
}
h.storeVote(uint64(req.GetHeight()), ve.MinaAddress, voteBytes)
h.storeVote(data.Height, ve.MinaAddress, voteBytes)
}

// Create our Mina address from our local Mina public key.
Expand Down Expand Up @@ -67,9 +67,15 @@ func (h *VoteExtHandler) reconstructVoteExtBody(req *abci.RequestProcessProposal
// is rejected. This shifts the ≥⅔ voting-power requirement to CometBFT itself:
// a block that does not include ≥⅔ of the network's vote-extensions will be
// rejected automatically because fewer than ⅔ of validators will `ACCEPT` it.
// It also seeds the state-root cache with committed root(H-1) before the vote
// extension stage starts for height H.
func (h *VoteExtHandler) ProcessProposalHandler() sdk.ProcessProposalHandler {

return func(ctx sdk.Context, req *abci.RequestProcessProposal) (*abci.ResponseProcessProposal, error) {
if req.GetHeight() >= 2 {
h.storeStateRoot(req.GetHeight()-1, ctx.BlockHeader().AppHash)
}

// If height is 1, we won't have any votes thus skip the proposal
if req.GetHeight() == 1 {
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
Expand Down
16 changes: 13 additions & 3 deletions abci/verify_extend_vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import (

func (h *VoteExtHandler) VerifyVoteExtensionHandler() sdk.VerifyVoteExtensionHandler {
return func(ctx sdk.Context, req *abci.RequestVerifyVoteExtension) (*abci.ResponseVerifyVoteExtension, error) {
if req.GetHeight() < 3 {
if len(req.VoteExtension) == 0 {
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT}, nil
}

return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_REJECT}, errors.Wrap(types.ErrMalformedVoteExtPayload, "vote extensions start at height 3")
}

// Unmarshal the extension payload
var voteExt MinaSignatureVoteExt
if err := json.Unmarshal(req.VoteExtension, &voteExt); err != nil {
Expand All @@ -35,6 +43,8 @@ func (h *VoteExtHandler) VerifyVoteExtensionHandler() sdk.VerifyVoteExtensionHan
return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_REJECT}, err
}

h.storeVote(uint64(req.GetHeight()), voteExt.MinaAddress, req.VoteExtension)

return &abci.ResponseVerifyVoteExtension{Status: abci.ResponseVerifyVoteExtension_ACCEPT}, nil
}
}
Expand Down Expand Up @@ -97,13 +107,13 @@ func (h *VoteExtHandler) verifyExtensionSig(ctx sdk.Context, req *abci.RequestVe
return err
}

h.storeVote(uint64(req.GetHeight()), voteExt.MinaAddress, req.VoteExtension)
return nil
}

// checks the validity of extension body for VerifyVoteExtensionHandler
// checkValidityOfVoteExtBody reconstructs the expected vote body from the
// shared committed-root cache instead of reading this node's local vote cache.
func (h *VoteExtHandler) checkValidityOfVoteExtBody(ctx sdk.Context, req *abci.RequestVerifyVoteExtension, voteExt MinaSignatureVoteExt) error {
extBody, err := h.getVoteExtBody(uint64(req.GetHeight()))
extBody, err := h.buildExpectedVoteExtBody(req.GetHeight())
if err != nil {
return errors.Wrap(types.ErrFailedToGetVoteExtBody, "failed to get vote extension body for height "+strconv.FormatUint(uint64(req.GetHeight()), 10))
}
Expand Down
3 changes: 1 addition & 2 deletions x/voteexthandler/types/vote_ext_body.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading