Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add expiration interval and improve voting power calculation #52

Merged
merged 4 commits into from
Sep 24, 2024
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
24 changes: 20 additions & 4 deletions keeper/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ type StakeWeightedVotes struct {
// ExtendedCommitInfo Contains additional information about the commit phase, including
// vote extensions and details about the current consensus round.
ExtendedCommitInfo abci.ExtendedCommitInfo

// TotalVotingPower is the sum of all validators' voting power
TotalVotingPower int64
}

// ProofOfBlobProposalHandler manages the proposal and vote extension logic related to
Expand Down Expand Up @@ -62,7 +65,7 @@ func (h *ProofOfBlobProposalHandler) PrepareProposal(ctx sdk.Context, req *abci.
h.keeper.ProposerAddress = req.ProposerAddress
proposalTxs := req.Txs

votes, err := h.aggregateVotes(ctx, req.LocalLastCommit)
votes, totalVotingPower, err := h.aggregateVotes(ctx, req.LocalLastCommit)
if err != nil {
fmt.Println("error while aggregating votes", err)
return nil, err
Expand All @@ -71,6 +74,7 @@ func (h *ProofOfBlobProposalHandler) PrepareProposal(ctx sdk.Context, req *abci.
injectedVoteExtTx := StakeWeightedVotes{
Votes: votes,
ExtendedCommitInfo: req.LocalLastCommit,
TotalVotingPower: totalVotingPower,
}

// if there is any another tx, it might give any marshelling error, so ignoring this err
Expand Down Expand Up @@ -102,6 +106,14 @@ func (h *ProofOfBlobProposalHandler) ProcessProposal(_ sdk.Context, req *abci.Re
return &abci.ResponseProcessProposal{Status: abci.ResponseProcessProposal_ACCEPT}, nil
}

// 66% of voting power is needed. Change the percentage if required
func isEnoughVoting(voting, totalVoting int64) bool {
// division can be inaccurate due to decimal roundups
// voting / totalVoting * 100 > 66
// voting * 100 > 66 * totalVoting
return voting*100 > 66*totalVoting
}

// PreBlocker runs before finalizing each block, responsible for handling vote extensions
// and managing the posting of blocks to the Avail light client.
func (k *Keeper) PreBlocker(ctx sdk.Context, req *abci.RequestFinalizeBlock) error {
Expand All @@ -119,8 +131,9 @@ func (k *Keeper) PreBlocker(ctx sdk.Context, req *abci.RequestFinalizeBlock) err
votingPower := injectedVoteExtTx.Votes[pendingRangeKey]

state := FailureState
totalVotingPower := injectedVoteExtTx.TotalVotingPower

if votingPower > 0 { // TODO: calculate voting power properly
if isEnoughVoting(votingPower, totalVotingPower) {
state = ReadyState
}

Expand Down Expand Up @@ -177,14 +190,17 @@ func (k *Keeper) IsValidBlockToPostToDA(height uint64) bool {
// specific block ranges.
// If the vote extension contains a vote for the pending range, it sums the voting power
// of validators.
func (h *ProofOfBlobProposalHandler) aggregateVotes(ctx sdk.Context, ci abci.ExtendedCommitInfo) (map[string]int64, error) {
func (h *ProofOfBlobProposalHandler) aggregateVotes(ctx sdk.Context, ci abci.ExtendedCommitInfo) (map[string]int64, int64, error) {
from := h.keeper.GetStartHeightFromStore(ctx)
to := h.keeper.GetEndHeightFromStore(ctx)

pendingRangeKey := Key(from, to)
votes := make(map[string]int64, 1)
totalVoting := 0

for _, v := range ci.Votes {
totalVoting += int(v.Validator.Power)

// Process only votes with BlockIDFlagCommit, indicating the validator committed to the block.
// Skip votes with other flags (e.g., BlockIDFlagUnknown, BlockIDFlagNil).
if v.BlockIdFlag != cmtproto.BlockIDFlagCommit {
Expand All @@ -210,5 +226,5 @@ func (h *ProofOfBlobProposalHandler) aggregateVotes(ctx sdk.Context, ci abci.Ext
}

}
return votes, nil
return votes, int64(totalVoting), nil
}
3 changes: 2 additions & 1 deletion keeper/blob_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ import (
func (k *Keeper) SetBlobStatusPending(ctx sdk.Context, startHeight, endHeight uint64) bool {
store := ctx.KVStore(k.storeKey)

if !CanUpdateStatusToPending(store) { // Todo: we should check for expiration too (what if the status was pending for too long)
if !k.CanUpdateStatusToPending(ctx, store) {
return false
}

UpdateBlobStatus(ctx, store, PendingState)
UpdateStartHeight(ctx, store, startHeight)
UpdatePendingHeight(ctx, store, uint64(ctx.BlockHeight()))
UpdateEndHeight(ctx, store, endHeight)
return true
}
Expand Down
21 changes: 19 additions & 2 deletions keeper/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ const (
FailureState uint32 = 3
)

// if the previous status has been pending for too long
func (k *Keeper) isExpired(currentHeight, pendingStartHeight uint64, status uint32) bool {
return status == PendingState && currentHeight-pendingStartHeight >= k.relayer.AvailConfig.ExpirationInterval
}

func ParseStatus(status uint32, startHeight, endHeight uint64) string {
if startHeight == 0 && endHeight == 0 {
return ""
Expand Down Expand Up @@ -46,15 +51,17 @@ func ParseVotingEndHeight(height uint64) string {

// CanUpdateStatusToPending checks if the blob status can be updated to "pending".
// This function verifies whether the current status allows transitioning to the "pending" state.
func CanUpdateStatusToPending(store storetypes2.KVStore) bool {
func (k *Keeper) CanUpdateStatusToPending(ctx sdk.Context, store storetypes2.KVStore) bool {
statusBytes := store.Get(types.BlobStatusKey)
if len(statusBytes) == 0 {
return true
}

pendingStartHeight := k.GetPendingHeightFromStore(ctx)

status := binary.BigEndian.Uint32(statusBytes)

return status == ReadyState || status == FailureState
return status == ReadyState || status == FailureState || k.isExpired(uint64(ctx.BlockHeight()), pendingStartHeight, status)
}

// GetStatusFromStore retrieves the current status of the blob from the store.
Expand Down Expand Up @@ -85,6 +92,11 @@ func UpdateStartHeight(_ sdk.Context, store storetypes2.KVStore, startHeight uin
return updateHeight(store, types.PrevHeightKey, startHeight)
}

// UpdatePendingHeight updates the height at which the status is changed to pending in the KV store
func UpdatePendingHeight(_ sdk.Context, store storetypes2.KVStore, startHeight uint64) error {
return updateHeight(store, types.PendingHeightKey, startHeight)
}

// UpdateEndHeight updates the end height in the KV store.
func UpdateEndHeight(_ sdk.Context, store storetypes2.KVStore, endHeight uint64) error {
return updateHeight(store, types.NextHeightKey, endHeight)
Expand Down Expand Up @@ -124,6 +136,11 @@ func (k *Keeper) GetProvenHeightFromStore(ctx sdk.Context) uint64 {
return k.getHeight(ctx, types.ProvenHeightKey)
}

// GetPendingHeightFromStore retrieves the pending start height from the KV store.
func (k *Keeper) GetPendingHeightFromStore(ctx sdk.Context) uint64 {
return k.getHeight(ctx, types.PendingHeightKey)
}

// GetAvailHeightFromStore retrieves the avail height from the KV store.
func (k *Keeper) GetAvailHeightFromStore(ctx sdk.Context) uint64 {
return k.getHeight(ctx, types.AvailHeightKey)
Expand Down
4 changes: 2 additions & 2 deletions keeper/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ func (s *TestSuite) TestCanUpdateStatusToPending() {

for _, tc := range testCases {
s.Run(tc.name, func() {
res := store.CanUpdateStatusToPending(s.store)
res := s.keeper.CanUpdateStatusToPending(s.ctx, s.store)
s.True(res)
if tc.updateStatus {
err := store.UpdateBlobStatus(s.ctx, s.store, tc.status)
s.Require().NoError(err)

res := store.CanUpdateStatusToPending(s.store)
res := s.keeper.CanUpdateStatusToPending(s.ctx, s.store)
s.False(res)
}
})
Expand Down
15 changes: 11 additions & 4 deletions types/avail_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ type AvailConfiguration struct {

PublishBlobInterval uint64 `json:"publish-blob-interval"`

VoteInterval uint64 `json:"vote-interval"`
ValidatorKey string `json:"validator-key"`
VoteInterval uint64 `json:"vote-interval"`
ValidatorKey string `json:"validator-key"`
ExpirationInterval uint64 `json:"expiration-interval"`
// CosmosNodeDir string `json:"cosmos-node-dir"`
}

Expand All @@ -43,6 +44,7 @@ const (
FlagPublishBlobInterval = "avail.publish-blob-interval"
FlagVoteInterval = "avail.vote-interval"
FlagValidatorKey = "avail.validator-key"
FlagExpirationInterval = "avail.expiration-interval"
FlagCosmosNodeDir = "avail.cosmos-node-dir"

DefaultConfigTemplate = `
Expand All @@ -65,7 +67,7 @@ const (
cosmos-node-rpc = "http://127.0.0.1:26657"

# Maximum number of blocks over which blobs can be processed
max-blob-blocks = 10
max-blob-blocks = 20

# The frequency at which block data is posted to the Avail Network
publish-blob-interval = 5
Expand All @@ -74,6 +76,9 @@ const (
# Avail and confirm it with the network using vote extension
vote-interval = 5

# If the previous blocks status remains pending beyond the expiration interval, it should be marked as expired
expiration-interval = 30

# It is the keyname of the cosmos validator account to sign the transactions
validator-key = "alice"
`
Expand All @@ -85,8 +90,9 @@ var DefaultAvailConfig = AvailConfiguration{
AppID: 1,
CosmosNodeRPC: "http://127.0.0.1:26657",
MaxBlobBlocks: 20,
PublishBlobInterval: 10,
PublishBlobInterval: 5,
VoteInterval: 5,
ExpirationInterval: 30,
LightClientURL: "http://127.0.0.1:8000",
ValidatorKey: "alice",
// CosmosNodeDir: ".simapp",
Expand All @@ -103,6 +109,7 @@ func AvailConfigFromAppOpts(appOpts servertypes.AppOptions) AvailConfiguration {
PublishBlobInterval: cast.ToUint64(appOpts.Get(FlagPublishBlobInterval)),
VoteInterval: cast.ToUint64(appOpts.Get(FlagVoteInterval)),
ValidatorKey: cast.ToString(appOpts.Get(FlagValidatorKey)),
ExpirationInterval: cast.ToUint64(appOpts.Get(FlagExpirationInterval)),
// CosmosNodeDir: cast.ToString(appOpts.Get(FlagCosmosNodeDir)),
}
}
3 changes: 3 additions & 0 deletions types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ var (
LastVotingEndHeightKey = collections.NewPrefix(8)

AvailHeightKey = collections.NewPrefix(9)

// PendingHeightKey saves the height at which the state is changed to pending
PendingHeightKey = collections.NewPrefix(10)
)

const (
Expand Down
Loading