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

Prune EthereumSignatures and EthereumEventVoteRecords #517

Merged
merged 85 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
203b02f
Prune EthereumSignatures and EthereumEventVoteRecords
cbrit Apr 26, 2023
3b7e663
Prune signatures for all OutgoingTx types on tx deletion
cbrit Apr 27, 2023
9e7219b
Update event claim unit test to expect pruning
cbrit Apr 27, 2023
6e6d225
Change recordEventVote to only record for new events. Still updates s…
cbrit Apr 27, 2023
9576296
Review changes
cbrit May 9, 2023
5f290f5
Delete signature before outgoing tx
cbrit May 11, 2023
61c2f84
Addition of CompletedOutgoingTx, and windows for persisting event vot…
cbrit May 16, 2023
798bb28
Add queries for ethereum events and signatures, adjust pruning
cbrit May 18, 2023
8eac7c9
Remove TxHistoryWindow param recently added
cbrit May 18, 2023
3384780
Change stale signer set pruning to pattern used for other OutgoingTx
cbrit May 18, 2023
d879f93
Use completed outgoing tx store data for slashing, add pruning after …
cbrit May 18, 2023
016b9d8
Undo that signer set pruning rework because it broke everything
cbrit May 19, 2023
b59b95f
Set completed outgoing tx for observed signer set updates
cbrit May 19, 2023
18297d3
Fix slashing and pruning, conditionally add signer txs for to Complet…
cbrit May 22, 2023
97a7cfc
Ignore unit tests in /migrations in test-cov recipe
cbrit May 29, 2023
f11ae2f
Merge branch 'main' into collin/pruning
cbrit May 29, 2023
9b2fa69
Fix bugs found while getting validator_out working, test tweaks
cbrit May 30, 2023
7774350
Add completed signer set txs to CompletedOutgoingTxs query
cbrit May 30, 2023
93898a6
Tweaks around pruning. Comment for important param context
cbrit May 30, 2023
cee01a5
Happy path improvements
cbrit May 30, 2023
ad2e1ed
Bump target tx timeout so that transaction stress can pass
cbrit May 30, 2023
1a60aa4
Check pruning in batch/contract call slashing unit tests
cbrit May 30, 2023
c9e7b46
Check if validator is jailed before jailing to avoid panic
cbrit May 30, 2023
d094333
Fix double jailing, combine batch/contract call slashing and pruning …
cbrit May 30, 2023
10ba8e8
Merge branch 'main' into collin/pruning
cbrit May 30, 2023
62052b0
Remove coverage.txt from index
cbrit May 31, 2023
8aec8a4
Include CompletedOutgoingTxs in unsigned tx queries, + unit tests
cbrit May 31, 2023
e612af8
Tweak comment for pruning method
cbrit Jun 1, 2023
944db59
Refactor validator_out test jailing check
cbrit Jun 2, 2023
fa739d7
Review items - queries and signature -> confirmation term consistency
cbrit Jun 5, 2023
2b4f813
Refactor slashing
cbrit Jun 5, 2023
bddc200
Slash based on OutgoingTx (not completed), and new slashing window param
cbrit Jun 5, 2023
45f3b66
Slash for both Outgoing and Completed, make the Outgoing->Completed s…
cbrit Jun 6, 2023
71176eb
Move GetUnconfirmed*Txs methods into keeper, add tests
cbrit Jun 6, 2023
cfb424d
Refactor signature deletion method,tweak unit test
cbrit Jun 6, 2023
751b4f8
Revert "Review items - queries and signature -> confirmation term con…
cbrit Jun 9, 2023
fdfee5f
Update rust proto bindings
cbrit Jun 9, 2023
aabb8de
Correct mistakes in git revert
cbrit Jun 12, 2023
32d2a87
Rename Complete -> CompleteOutgoingTx, and use it for batch timeouts
cbrit Jun 12, 2023
1b53075
Fix condition ordering for slashing
cbrit Jun 12, 2023
bfe6f10
Add clarity to event votes command name
cbrit Jun 12, 2023
71bacf4
Remove unneeded comments
cbrit Jun 12, 2023
1bbf5c0
Fix thing in test_runner preventing rust tests from finishing
cbrit Jun 14, 2023
bb2df29
Fix minor proto inconsistency
cbrit Jun 29, 2023
2259b01
Add arg to cli command usage string
cbrit Jun 29, 2023
7160287
Correct function name in abci comment
cbrit Jun 29, 2023
4f10dd4
Err check on casting in executed tx handlers
cbrit Jun 29, 2023
6fd891b
Remove remaining references to unused param
cbrit Jun 29, 2023
49fe53c
Slashing reason event key/value
cbrit Jun 29, 2023
92b7c88
Only prune signer sets in abci
cbrit Jun 29, 2023
8f974ee
Ordering of outgoing txs returned by unslashed/unsigned methods
cbrit Jun 29, 2023
386d151
Move general outgoing tx methods to keeper.go
cbrit Jun 29, 2023
43e7eb9
Remove top level module, make integration-tests module
cbrit Jun 30, 2023
f525d8a
Make integration-tests module replacements consistent with previous m…
cbrit Jun 30, 2023
5ee3eee
Conditional delete in CompleteOutgoingTx
cbrit Jul 6, 2023
5fa34d1
Fix unit tests broken by review changes
cbrit Jul 10, 2023
cb3a8f2
Fix test file name
cbrit Jul 10, 2023
8bfdcdd
Update workflow file with test file fix
cbrit Jul 10, 2023
3b5532f
Only prune accepted event votes
cbrit Aug 2, 2023
c063451
Fix key name typo
cbrit Aug 2, 2023
dea66cd
Get height outside of loop in slashing
cbrit Aug 2, 2023
e1fb6b3
Fix efficiency issues in slashing refactor
cbrit Aug 3, 2023
a3e1a77
Fix signer set pruning bug
cbrit Aug 3, 2023
daa37bb
Add casting check for signer set
cbrit Aug 3, 2023
a1a73c8
Improve event vote cli command names
cbrit Aug 3, 2023
3255a88
Rename param to clarify it's an Ethereum height
cbrit Aug 3, 2023
2dc35c4
Update signerSetTxExecuted unit test
cbrit Aug 3, 2023
54f8996
Unit test fixes
cbrit Aug 3, 2023
fee0d33
Guarantee ascening batch nonce order when querying for unsigned
cbrit Aug 4, 2023
8fbe3de
Sign signer sets in ascending order
cbrit Aug 4, 2023
c2d78eb
Relay batches in ascending order
cbrit Aug 4, 2023
5ccfc53
Order contract call signing
cbrit Aug 4, 2023
9e4ef50
Clean up unsigned batches test
cbrit Aug 9, 2023
18d89aa
Remove unneeded ordering by scope for contract calls
cbrit Aug 9, 2023
1db56ab
Clarify comment forr param
cbrit Aug 9, 2023
9d0c767
Remove unnecessary contract call sorting from orchestrator
cbrit Aug 9, 2023
f8618ee
Revert "Fix key name typo"
cbrit Aug 9, 2023
c2a34f9
Remove duplicate height extraction from slashing loop
cbrit Aug 9, 2023
39c8888
Format slashing.go
cbrit Aug 9, 2023
3258760
Refactor GetUnbondingValidators method
cbrit Aug 9, 2023
5402aa8
Remove GetUnbondingValidators keeper method
cbrit Aug 9, 2023
dbb3f9c
Move relayer batch sorting into latest batches query
cbrit Aug 9, 2023
5b2cb3e
Remove unnecessary sort in BatchTxs query
cbrit Aug 10, 2023
b98ada6
Cover signer set tx pruning/slashing in unit test
cbrit Aug 10, 2023
64f09b2
Undo mistaken comment change
cbrit Aug 10, 2023
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
53 changes: 40 additions & 13 deletions module/x/gravity/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ func BeginBlocker(ctx sdk.Context, k keeper.Keeper) {
// EndBlocker is called at the end of every block
func EndBlocker(ctx sdk.Context, k keeper.Keeper) {
outgoingTxSlashing(ctx, k)
// Do we need to concern ourselves with future slashing windows for this pruning?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on this question?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a window of blocks for which the chain waits to prune batch votes so that validators have time to sign them before getting slashed. At the time when I commented this I didn't fully understand how vote records work for events as distinct from signatures for outgoing TXs. There's no reason to keep event vote records around after the last observed event nonce has incremented so I'll remove this comment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that you have me thinking about it, I think there's certainly an argument to be made for a slashing window for the oracle function as well. It's as necessary to complete the bridge transaction lifecycle as signatures are. From this perspective, we may want to keep a sliding window's worth of history for both.

pruneEventVoteRecords(ctx, k)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move this to BeginBlocker with the other pruning functions?

eventVoteRecordTally(ctx, k)
updateObservedEthereumHeight(ctx, k)
}
Expand Down Expand Up @@ -100,12 +102,30 @@ func pruneSignerSetTxs(ctx sdk.Context, k keeper.Keeper) {
earliestToPrune := currentBlock - params.SignedSignerSetTxsWindow
for _, set := range k.GetSignerSetTxs(ctx) {
if set.Nonce < lastObserved.Nonce && set.Height < earliestToPrune {
k.DeleteEthereumSignatures(ctx, set.GetStoreIndex())
EricBolten marked this conversation as resolved.
Show resolved Hide resolved
k.DeleteOutgoingTx(ctx, set.GetStoreIndex())
}
}
}
}

// pruneEventVoteRecords deletes all event vote records with nonces that are older than the last observed event nonce
func pruneEventVoteRecords(ctx sdk.Context, k keeper.Keeper) {
lastObservedEventNonce := k.GetLastObservedEventNonce(ctx)
EricBolten marked this conversation as resolved.
Show resolved Hide resolved
k.IterateEthereumEventVoteRecords(ctx, func(key []byte, eventVoteRecord *types.EthereumEventVoteRecord) bool {
event, err := types.UnpackEvent(eventVoteRecord.Event)
if err != nil {
panic(err)
}
eventNonce := event.GetEventNonce()
if eventNonce <= lastObservedEventNonce {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be < rather than <=?

Copy link
Member Author

@cbrit cbrit May 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the nonce has been canonically observed there is no need to keep new votes for it right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I have noticed while helping validators diagnose issues with their orchestrators is that it's valuable to have evidence they have or have not been submitting things correctly. If we prune everything every time, it's very hard to query for this sort of information. I think leaving some small bit of history before pruning might be worthwhile.

k.DeleteEthereumEventVoteRecord(ctx, eventNonce, event.Hash())
}

return false
})
}

// Iterate over all attestations currently being voted on in order of nonce and
// "Observe" those who have passed the threshold. Break the loop once we see
// an attestation that has not passed the threshold
Expand Down Expand Up @@ -155,11 +175,11 @@ func eventVoteRecordTally(ctx sdk.Context, k keeper.Keeper) {
// order to keep this information current regardless of the level of bridge activity.
//
// We determine if we should update the latest heights based on the following criteria:
// 1. A consensus of validators agrees that the proposed height is equal to or less than their
// last observed height, in order to reconcile the many different heights that will be submitted.
// The highest height that meets this criteria will be the proposed height.
// 2. The proposed consensus heights from this process are greater than the values stored from the last time
// we observed an Ethereum event from the bridge
// 1. A consensus of validators agrees that the proposed height is equal to or less than their
// last observed height, in order to reconcile the many different heights that will be submitted.
// The highest height that meets this criteria will be the proposed height.
// 2. The proposed consensus heights from this process are greater than the values stored from the last time
// we observed an Ethereum event from the bridge
func updateObservedEthereumHeight(ctx sdk.Context, k keeper.Keeper) {
// wait some minutes before checking the height votes
if ctx.BlockHeight()%50 != 0 {
Expand Down Expand Up @@ -228,12 +248,15 @@ func updateObservedEthereumHeight(ctx sdk.Context, k keeper.Keeper) {
// cleanupTimedOutBatchTxs deletes batches that have passed their expiration on Ethereum
// keep in mind several things when modifying this function
// A) unlike nonces timeouts are not monotonically increasing, meaning batch 5 can have a later timeout than batch 6
// this means that we MUST only cleanup a single batch at a time
//
// this means that we MUST only cleanup a single batch at a time
//
// B) it is possible for ethereumHeight to be zero if no events have ever occurred, make sure your code accounts for this
// C) When we compute the timeout we do our best to estimate the Ethereum block height at that very second. But what we work with
// here is the Ethereum block height at the time of the last Deposit or Withdraw to be observed. It's very important we do not
// project, if we do a slowdown on ethereum could cause a double spend. Instead timeouts will *only* occur after the timeout period
// AND any deposit or withdraw has occurred to update the Ethereum block height.
//
// here is the Ethereum block height at the time of the last Deposit or Withdraw to be observed. It's very important we do not
// project, if we do a slowdown on ethereum could cause a double spend. Instead timeouts will *only* occur after the timeout period
// AND any deposit or withdraw has occurred to update the Ethereum block height.
func cleanupTimedOutBatchTxs(ctx sdk.Context, k keeper.Keeper) {
ethereumHeight := k.GetLastObservedEthereumBlockHeight(ctx).EthereumHeight
k.IterateOutgoingTxsByType(ctx, types.BatchTxPrefixByte, func(key []byte, otx types.OutgoingTx) bool {
Expand All @@ -250,17 +273,21 @@ func cleanupTimedOutBatchTxs(ctx sdk.Context, k keeper.Keeper) {
// cleanupTimedOutContractCallTxs deletes logic calls that have passed their expiration on Ethereum
// keep in mind several things when modifying this function
// A) unlike nonces timeouts are not monotonically increasing, meaning call 5 can have a later timeout than batch 6
// this means that we MUST only cleanup a single call at a time
//
// this means that we MUST only cleanup a single call at a time
//
// B) it is possible for ethereumHeight to be zero if no events have ever occurred, make sure your code accounts for this
// C) When we compute the timeout we do our best to estimate the Ethereum block height at that very second. But what we work with
// here is the Ethereum block height at the time of the last Deposit or Withdraw to be observed. It's very important we do not
// project, if we do a slowdown on ethereum could cause a double spend. Instead timeouts will *only* occur after the timeout period
// AND any deposit or withdraw has occurred to update the Ethereum block height.
//
// here is the Ethereum block height at the time of the last Deposit or Withdraw to be observed. It's very important we do not
// project, if we do a slowdown on ethereum could cause a double spend. Instead timeouts will *only* occur after the timeout period
// AND any deposit or withdraw has occurred to update the Ethereum block height.
func cleanupTimedOutContractCallTxs(ctx sdk.Context, k keeper.Keeper) {
ethereumHeight := k.GetLastObservedEthereumBlockHeight(ctx).EthereumHeight
k.IterateOutgoingTxsByType(ctx, types.ContractCallTxPrefixByte, func(_ []byte, otx types.OutgoingTx) bool {
cctx, _ := otx.(*types.ContractCallTx)
if cctx.Timeout < ethereumHeight {
k.DeleteEthereumSignatures(ctx, cctx.GetStoreIndex())
k.DeleteOutgoingTx(ctx, cctx.GetStoreIndex())
}
return true
Expand Down
16 changes: 11 additions & 5 deletions module/x/gravity/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,18 +274,24 @@ func TestMsgSubmitEthreumEventSendToCosmosMultiValidator(t *testing.T) {
balance2 := input.BankKeeper.GetAllBalances(ctx, myCosmosAddr)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(denom, 12)}, balance2)

// and vouchers added to the account
// this happens when 2/3 of the voting power have submitted claims, so the third isn't required
balance3 := input.BankKeeper.GetAllBalances(ctx, myCosmosAddr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't balance3 just copy balance2 here if the value is needed for some other purpose? It's the exact same query and check and no mutations happen in between.

require.Equal(t, sdk.Coins{sdk.NewInt64Coin(denom, 12)}, balance3)

// when
ctx = ctx.WithBlockTime(myBlockTime)
_, err = h(ctx, ethClaim3Msg)
gravity.EndBlocker(ctx, input.GravityKeeper)
require.NoError(t, err)

// and attestation persisted
// and attestations pruned
a1 = input.GravityKeeper.GetEthereumEventVoteRecord(ctx, myNonce, ethClaim1.Hash())
a2 = input.GravityKeeper.GetEthereumEventVoteRecord(ctx, myNonce, ethClaim2.Hash())
a3 := input.GravityKeeper.GetEthereumEventVoteRecord(ctx, myNonce, ethClaim3.Hash())
require.NotNil(t, a3)
// and no additional added to the account
balance3 := input.BankKeeper.GetAllBalances(ctx, myCosmosAddr)
require.Equal(t, sdk.Coins{sdk.NewInt64Coin(denom, 12)}, balance3)
require.Nil(t, a1)
require.Nil(t, a2)
require.Nil(t, a3)
}

func TestMsgSetDelegateAddresses(t *testing.T) {
Expand Down
14 changes: 8 additions & 6 deletions module/x/gravity/keeper/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import (
const BatchTxSize = 100

// BuildBatchTx starts the following process chain:
// - find bridged denominator for given voucher type
// - determine if a an unexecuted batch is already waiting for this token type, if so confirm the new batch would
// have a higher total fees. If not exit withtout creating a batch
// - select available transactions from the outgoing transaction pool sorted by fee desc
// - persist an outgoing batch object with an incrementing ID = nonce
// - emit an event
// - find bridged denominator for given voucher type
// - determine if a an unexecuted batch is already waiting for this token type, if so confirm the new batch would
// have a higher total fees. If not exit withtout creating a batch
// - select available transactions from the outgoing transaction pool sorted by fee desc
// - persist an outgoing batch object with an incrementing ID = nonce
// - emit an event
func (k Keeper) BuildBatchTx(ctx sdk.Context, contractAddress common.Address, maxElements int) *types.BatchTx {
// if there is a more profitable batch for this token type do not create a new batch
if lastBatch := k.getLastOutgoingBatchByTokenType(ctx, contractAddress); lastBatch != nil {
Expand Down Expand Up @@ -81,6 +81,7 @@ func (k Keeper) batchTxExecuted(ctx sdk.Context, tokenContract common.Address, n
}
return false
})
k.DeleteEthereumSignatures(ctx, batchTx.GetStoreIndex())
k.DeleteOutgoingTx(ctx, batchTx.GetStoreIndex())
}

Expand Down Expand Up @@ -123,6 +124,7 @@ func (k Keeper) CancelBatchTx(ctx sdk.Context, batch *types.BatchTx) {
}

// Delete batch since it is finished
k.DeleteEthereumSignatures(ctx, batch.GetStoreIndex())
k.DeleteOutgoingTx(ctx, batch.GetStoreIndex())

ctx.EventManager().EmitEvent(
Expand Down
2 changes: 2 additions & 0 deletions module/x/gravity/keeper/contract_call.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ func (k Keeper) contractCallExecuted(ctx sdk.Context, invalidationScope []byte,
cctx, _ := otx.(*types.ContractCallTx)
if (cctx.InvalidationNonce < completedCallTx.InvalidationNonce) &&
bytes.Equal(cctx.InvalidationScope, completedCallTx.InvalidationScope) {
k.DeleteEthereumSignatures(ctx, cctx.GetStoreIndex())
EricBolten marked this conversation as resolved.
Show resolved Hide resolved
k.DeleteOutgoingTx(ctx, cctx.GetStoreIndex())
}
return false
})

k.DeleteEthereumSignatures(ctx, completedCallTx.GetStoreIndex())
k.DeleteOutgoingTx(ctx, completedCallTx.GetStoreIndex())
}
14 changes: 11 additions & 3 deletions module/x/gravity/keeper/ethereum_event_vote.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ func (k Keeper) recordEventVote(
// Add the validator's vote to this EthereumEventVoteRecord
eventVoteRecord.Votes = append(eventVoteRecord.Votes, val.String())

k.setEthereumEventVoteRecord(ctx, event.GetEventNonce(), event.Hash(), eventVoteRecord)
// ignore "old" event votes but still record the nonce for the submitting validator
if event.GetEventNonce() > k.GetLastObservedEventNonce(ctx) {
EricBolten marked this conversation as resolved.
Show resolved Hide resolved
k.setEthereumEventVoteRecord(ctx, event.GetEventNonce(), event.Hash(), eventVoteRecord)
}

EricBolten marked this conversation as resolved.
Show resolved Hide resolved
k.setLastEventNonceByValidator(ctx, val, event.GetEventNonce())

return eventVoteRecord, nil
Expand Down Expand Up @@ -140,6 +144,10 @@ func (k Keeper) setEthereumEventVoteRecord(ctx sdk.Context, eventNonce uint64, c
ctx.KVStore(k.storeKey).Set(types.MakeEthereumEventVoteRecordKey(eventNonce, claimHash), k.cdc.MustMarshal(eventVoteRecord))
}

func (k Keeper) DeleteEthereumEventVoteRecord(ctx sdk.Context, eventNonce uint64, claimHash []byte) {
ctx.KVStore(k.storeKey).Delete(types.MakeEthereumEventVoteRecordKey(eventNonce, claimHash))
}

// GetEthereumEventVoteRecord return a vote record given a nonce
func (k Keeper) GetEthereumEventVoteRecord(ctx sdk.Context, eventNonce uint64, claimHash []byte) *types.EthereumEventVoteRecord {
if bz := ctx.KVStore(k.storeKey).Get(types.MakeEthereumEventVoteRecordKey(eventNonce, claimHash)); bz == nil {
Expand All @@ -154,7 +162,7 @@ func (k Keeper) GetEthereumEventVoteRecord(ctx sdk.Context, eventNonce uint64, c
// GetEthereumEventVoteRecordMapping returns a mapping of eventnonce -> attestations at that nonce
func (k Keeper) GetEthereumEventVoteRecordMapping(ctx sdk.Context) (out map[uint64][]*types.EthereumEventVoteRecord) {
out = make(map[uint64][]*types.EthereumEventVoteRecord)
k.iterateEthereumEventVoteRecords(ctx, func(key []byte, eventVoteRecord *types.EthereumEventVoteRecord) bool {
k.IterateEthereumEventVoteRecords(ctx, func(key []byte, eventVoteRecord *types.EthereumEventVoteRecord) bool {
event, err := types.UnpackEvent(eventVoteRecord.Event)
if err != nil {
panic(err)
Expand All @@ -170,7 +178,7 @@ func (k Keeper) GetEthereumEventVoteRecordMapping(ctx sdk.Context) (out map[uint
}

// iterateEthereumEventVoteRecords iterates through all attestations
func (k Keeper) iterateEthereumEventVoteRecords(ctx sdk.Context, cb func([]byte, *types.EthereumEventVoteRecord) bool) {
func (k Keeper) IterateEthereumEventVoteRecords(ctx sdk.Context, cb func([]byte, *types.EthereumEventVoteRecord) bool) {
store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte{types.EthereumEventVoteRecordKey})
iter := store.Iterator(nil, nil)
defer iter.Close()
Expand Down
14 changes: 11 additions & 3 deletions module/x/gravity/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,12 @@ func (k Keeper) GetLastUnbondingBlockHeight(ctx sdk.Context) uint64 {
// ETHEREUM SIGNATURES //
///////////////////////////////

// getEthereumSignature returns a valset confirmation by a nonce and validator address
// getEthereumSignature returns an ethereum signature by a nonce and validator address
func (k Keeper) getEthereumSignature(ctx sdk.Context, storeIndex []byte, validator sdk.ValAddress) []byte {
return ctx.KVStore(k.storeKey).Get(types.MakeEthereumSignatureKey(storeIndex, validator))
}

// SetEthereumSignature sets a valset confirmation
// SetEthereumSignature sets an ethereum signature
func (k Keeper) SetEthereumSignature(ctx sdk.Context, sig types.EthereumTxConfirmation, val sdk.ValAddress) []byte {
key := types.MakeEthereumSignatureKey(sig.GetStoreIndex(), val)
ctx.KVStore(k.storeKey).Set(key, sig.GetSignature())
Expand All @@ -151,6 +151,14 @@ func (k Keeper) GetEthereumSignatures(ctx sdk.Context, storeIndex []byte) map[st
return signatures
}

// DeleteEthereumSignatures deletes all ethereum signatures for a given outgoing tx by store index
func (k Keeper) DeleteEthereumSignatures(ctx sdk.Context, storeIndex []byte) {
k.iterateEthereumSignatures(ctx, storeIndex, func(val sdk.ValAddress, h []byte) bool {
ctx.KVStore(k.storeKey).Delete(types.MakeEthereumSignatureKey(storeIndex, val))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a potential issue here deleting elements from something we are iterating over? Perhaps we should instead collect a slice of the keys and delete them after the iterator.

return false
})
}

// iterateEthereumSignatures iterates through all valset confirms by nonce in ASC order
func (k Keeper) iterateEthereumSignatures(ctx sdk.Context, storeIndex []byte, cb func(sdk.ValAddress, []byte) bool) {
prefixStore := prefix.NewStore(ctx.KVStore(k.storeKey), append([]byte{types.EthereumSignatureKey}, storeIndex...))
Expand Down Expand Up @@ -680,7 +688,7 @@ func (k Keeper) MigrateGravityContract(ctx sdk.Context, newBridgeAddress string,

// Reset all ethereum event nonces to zero
k.setLastObservedEventNonce(ctx, 0)
k.iterateEthereumEventVoteRecords(ctx, func(_ []byte, voteRecord *types.EthereumEventVoteRecord) bool {
k.IterateEthereumEventVoteRecords(ctx, func(_ []byte, voteRecord *types.EthereumEventVoteRecord) bool {
for _, vote := range voteRecord.Votes {
val, err := sdk.ValAddressFromBech32(vote)

Expand Down
2 changes: 1 addition & 1 deletion module/x/gravity/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func TestAttestationIterator(t *testing.T) {
input.GravityKeeper.setEthereumEventVoteRecord(ctx, dep2.EventNonce, dep2.Hash(), att2)

var atts []*types.EthereumEventVoteRecord
input.GravityKeeper.iterateEthereumEventVoteRecords(ctx, func(_ []byte, att *types.EthereumEventVoteRecord) bool {
input.GravityKeeper.IterateEthereumEventVoteRecords(ctx, func(_ []byte, att *types.EthereumEventVoteRecord) bool {
atts = append(atts, att)
return false
})
Expand Down