From b302d7e85febcb15249a3c9a21f1902c5f253edf Mon Sep 17 00:00:00 2001 From: Teja2045 <106052623+Teja2045@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:25:49 +0530 Subject: [PATCH] feat: add expiration interval and improve voting power calculation (#52) * feat: add expiration interval * feat: update voting power calculation * chore: remove todo * feat: fix voting sum logic --- keeper/abci.go | 24 ++++++++++++++++++++---- keeper/blob_status.go | 3 ++- keeper/store.go | 21 +++++++++++++++++++-- keeper/store_test.go | 4 ++-- types/avail_config.go | 15 +++++++++++---- types/keys.go | 3 +++ 6 files changed, 57 insertions(+), 13 deletions(-) diff --git a/keeper/abci.go b/keeper/abci.go index 90b0c87..b49dca8 100644 --- a/keeper/abci.go +++ b/keeper/abci.go @@ -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 @@ -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 @@ -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 @@ -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 { @@ -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 } @@ -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 { @@ -210,5 +226,5 @@ func (h *ProofOfBlobProposalHandler) aggregateVotes(ctx sdk.Context, ci abci.Ext } } - return votes, nil + return votes, int64(totalVoting), nil } diff --git a/keeper/blob_status.go b/keeper/blob_status.go index 8ac2e33..fc00e02 100644 --- a/keeper/blob_status.go +++ b/keeper/blob_status.go @@ -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 } diff --git a/keeper/store.go b/keeper/store.go index 172a71a..558613d 100644 --- a/keeper/store.go +++ b/keeper/store.go @@ -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 "" @@ -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. @@ -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) @@ -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) diff --git a/keeper/store_test.go b/keeper/store_test.go index 079496a..cf6fa75 100644 --- a/keeper/store_test.go +++ b/keeper/store_test.go @@ -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) } }) diff --git a/types/avail_config.go b/types/avail_config.go index 21b5b72..f351c23 100644 --- a/types/avail_config.go +++ b/types/avail_config.go @@ -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"` } @@ -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 = ` @@ -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 @@ -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" ` @@ -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", @@ -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)), } } diff --git a/types/keys.go b/types/keys.go index 75d227c..6c03608 100644 --- a/types/keys.go +++ b/types/keys.go @@ -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 (