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

refactor: Adding improvements from the official Gravity Bridge #351

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
949c763
https://github.com/Gravity-Bridge/Gravity-Bridge/commit/4adc8d10e5814…
facundomedica Dec 30, 2021
70f62d1
don't slash jailidators
facundomedica Jan 3, 2022
c2ef8d1
don't jail jailidators https://github.com/Gravity-Bridge/Gravity-Brid…
facundomedica Jan 3, 2022
4f8a32d
Merge branch 'facu/fix-slashing-logic' of https://github.com/umee-net…
facundomedica Jan 3, 2022
f1c5cfb
finish use hashmap for slashing confirms (https://github.com/Gravity-…
facundomedica Jan 3, 2022
69b2b83
Merge branch 'main' into facu/fix-slashing-logic
facundomedica Jan 3, 2022
f125a52
added events (https://github.com/Gravity-Bridge/Gravity-Bridge/commit…
facundomedica Jan 3, 2022
97dd68d
added events (https://github.com/Gravity-Bridge/Gravity-Bridge/commit…
facundomedica Jan 3, 2022
22dfc31
Apply suggestions from code review
facundomedica Jan 3, 2022
a99c4ba
fixes
facundomedica Jan 3, 2022
7dcb09a
Update x/peggy/abci.go
facundomedica Jan 4, 2022
4aaf0fd
fix events in attestation_handler
facundomedica Jan 4, 2022
2c820de
Merge branch 'main' into facu/fix-slashing-logic
facundomedica Jan 4, 2022
8028ba5
fix events in abci.go
facundomedica Jan 4, 2022
506245b
Update x/peggy/abci.go
facundomedica Jan 4, 2022
38b0d92
Merge branch 'facu/fix-slashing-logic' of https://github.com/umee-net…
facundomedica Jan 4, 2022
7290ce5
Merge branch 'main' into facu/fix-slashing-logic
facundomedica Jan 4, 2022
43e2e2f
fix
facundomedica Jan 4, 2022
dec3173
Merge branch 'facu/fix-slashing-logic' of https://github.com/umee-net…
facundomedica Jan 4, 2022
e35eb42
Merge branch 'main' of https://github.com/umee-network/umee into facu…
facundomedica Jan 5, 2022
cd9fccb
valset_slashing and batch_slashing events
facundomedica Jan 5, 2022
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
157 changes: 108 additions & 49 deletions x/peggy/abci.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package peggy

import (
"fmt"
"sort"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -157,58 +158,83 @@ func (h *BlockHandler) cleanupTimedOutBatches(ctx sdk.Context) {
}
}

// prepValsetConfirms loads all confirmations into a hashmap indexed by validatorAddr
// reducing the lookup time dramatically and separating out the task of looking up
// the orchestrator for each validator.
func prepValsetConfirms(ctx sdk.Context, k keeper.Keeper, nonce uint64) map[string]*types.MsgValsetConfirm {
confirms := k.GetValsetConfirms(ctx, nonce)
ret := make(map[string]*types.MsgValsetConfirm)
for _, confirm := range confirms {
// TODO this presents problems for delegate key rotation see issue #344
confVal, err := sdk.AccAddressFromBech32(confirm.Orchestrator)
if err != nil {
panic(fmt.Sprintf("invalid claim confirmation address: %s", confirm.Orchestrator))
}
val, foundValidator := k.GetOrchestratorValidator(ctx, confVal)
if !foundValidator {
panic(fmt.Sprintf("failed to find validator by orchestrator address: %s", confVal))
}
ret[val.String()] = confirm
}
return ret
}

func (h *BlockHandler) valsetSlashing(ctx sdk.Context, params *types.Params) {
maxHeight := uint64(0)

// don't slash in the beginning before there aren't even SignedValsetsWindow blocks yet
if uint64(ctx.BlockHeight()) > params.SignedValsetsWindow {
maxHeight = uint64(ctx.BlockHeight()) - params.SignedValsetsWindow
} else {
// we can't slash anyone if SignedValsetWindow blocks have not passed
if uint64(ctx.BlockHeight()) <= params.SignedValsetsWindow {
return
}

unslashedValsets := h.k.GetUnslashedValsets(ctx, maxHeight)
unslashedValsets := h.k.GetUnslashedValsets(ctx, params.SignedValsetsWindow)

facundomedica marked this conversation as resolved.
Show resolved Hide resolved
currentBondedSet := h.k.StakingKeeper.GetBondedValidatorsByPower(ctx)

// unslashedValsets are sorted by nonce in ASC order
for _, vs := range unslashedValsets {
confirms := h.k.GetValsetConfirms(ctx, vs.Nonce)
confirms := prepValsetConfirms(ctx, h.k, vs.Nonce)

// SLASH BONDED VALIDATORS who didn't attest valset request
currentBondedSet := h.k.StakingKeeper.GetBondedValidatorsByPower(ctx)

for _, val := range currentBondedSet {
consAddr, _ := val.GetConsAddr()
consAddr, err := val.GetConsAddr()
if err != nil {
panic("Failed to get validator consensus addr")
}
valSigningInfo, exist := h.k.SlashingKeeper.GetValidatorSigningInfo(ctx, consAddr)

// Slash validator ONLY if he joined after valset is created
if exist && valSigningInfo.StartHeight < int64(vs.Height) {
// slash validator ONLY if it joined before the valset was created
startedBeforeValsetCreated := valSigningInfo.StartHeight < int64(vs.Height)

if exist && startedBeforeValsetCreated {
// Check if validator has confirmed valset or not
found := false
for _, conf := range confirms {
ethAddress, exists := h.k.GetEthAddressByValidator(ctx, val.GetOperator())
// This may have an issue if the validator changes their eth address
// TODO this presents problems for delegate key rotation see issue #344
if exists && common.HexToAddress(conf.EthAddress) == ethAddress {
found = true
break
}
}
_, found := confirms[val.GetOperator().String()]

// slash validators for not confirming valsets
if !found {
cons, _ := val.GetConsAddr()
consPower := val.ConsensusPower(h.k.StakingKeeper.PowerReduction(ctx))

h.k.StakingKeeper.Slash(
ctx,
cons,
ctx.BlockHeight(),
consPower,
params.SlashFractionValset,
)
// refresh validator before slashing/jailing
val, foundVal := h.k.StakingKeeper.GetValidator(ctx, val.GetOperator())
if !foundVal {
// this should be impossible, we haven't even progressed a single block since we got the list
panic("Validator exited set during endblocker?")
}

if !val.IsJailed() {
h.k.StakingKeeper.Jail(ctx, cons)
consPower := val.ConsensusPower(h.k.StakingKeeper.PowerReduction(ctx))
h.k.StakingKeeper.Slash(
ctx,
consAddr,
ctx.BlockHeight(),
consPower,
params.SlashFractionValset,
)
ctx.EventManager().EmitEvent(
Copy link
Contributor

Choose a reason for hiding this comment

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

see comments below on events 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

This event is still wrong btw

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alexanderbez pls check now

Copy link
Contributor

Choose a reason for hiding this comment

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

Nice! Looks good. WDYT about valset_slashing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In that case we will need valset_slashing and batch_slashing. Idk how much an event type should englobe, or if it should be very narrow

Copy link
Contributor

Choose a reason for hiding this comment

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

it should be specific to the domain, in this case, valset slashing.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, we need to update events.md, which probably could use an entire overhaul, to reflect these new events.

I don't have a strong opinion on slashing vs valset_slashing, I just wanted to suggest it.

sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute("ValsetSignatureSlashing", consAddr.String()),
),
)

h.k.StakingKeeper.Jail(ctx, consAddr)
}
}
}
Expand Down Expand Up @@ -247,9 +273,21 @@ func (h *BlockHandler) valsetSlashing(ctx sdk.Context, params *types.Params) {

// slash validators for not confirming valsets
if !found {
consPower := validator.ConsensusPower(h.k.StakingKeeper.PowerReduction(ctx))
// refresh validator before slashing/jailing
validator, foundVal := h.k.StakingKeeper.GetValidator(ctx, validator.GetOperator())
if !foundVal {
// this should be impossible, we haven't even progressed a single block since we got the list
panic("Validator exited set during endblocker?")
}

consPower := validator.ConsensusPower(h.k.StakingKeeper.PowerReduction(ctx))
h.k.StakingKeeper.Slash(ctx, valConsAddr, ctx.BlockHeight(), consPower, params.SlashFractionValset)
ctx.EventManager().EmitEvent(
Copy link
Contributor

Choose a reason for hiding this comment

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

see comments below on events 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute("ValsetSignatureSlashing", valConsAddr.String()),
),
)

if !validator.IsJailed() {
h.k.StakingKeeper.Jail(ctx, valConsAddr)
Expand All @@ -264,6 +302,24 @@ func (h *BlockHandler) valsetSlashing(ctx sdk.Context, params *types.Params) {
}
}

// prepBatchConfirms loads all confirmations into a hashmap indexed by validatorAddr
// reducing the lookup time dramatically and separating out the task of looking up
// the orchestrator for each validator.
func prepBatchConfirms(ctx sdk.Context, k keeper.Keeper, batch *types.OutgoingTxBatch) map[string]*types.MsgConfirmBatch {
confirms := k.GetBatchConfirmByNonceAndTokenContract(ctx, batch.BatchNonce, common.HexToAddress(batch.TokenContract))
ret := make(map[string]*types.MsgConfirmBatch)
for _, confirm := range confirms {
// TODO this presents problems for delegate key rotation see issue #344
facundomedica marked this conversation as resolved.
Show resolved Hide resolved
confVal, _ := sdk.AccAddressFromBech32(confirm.Orchestrator)
val, foundValidator := k.GetOrchestratorValidator(ctx, confVal)
if !foundValidator {
panic("Confirm from validator we can't identify?")
}
ret[val.String()] = confirm
}
return ret
}

func (h *BlockHandler) batchSlashing(ctx sdk.Context, params *types.Params) {
// #2 condition
// We look through the full bonded set (not just the active set, include unbonding validators)
Expand All @@ -283,7 +339,8 @@ func (h *BlockHandler) batchSlashing(ctx sdk.Context, params *types.Params) {
for _, batch := range unslashedBatches {
// SLASH BONDED VALIDTORS who didn't attest batch requests
currentBondedSet := h.k.StakingKeeper.GetBondedValidatorsByPower(ctx)
confirms := h.k.GetBatchConfirmByNonceAndTokenContract(ctx, batch.BatchNonce, common.HexToAddress(batch.TokenContract))
confirms := prepBatchConfirms(ctx, h.k, batch)

for _, val := range currentBondedSet {
// Don't slash validators who joined after batch is created
consAddr, _ := val.GetConsAddr()
Expand All @@ -293,25 +350,27 @@ func (h *BlockHandler) batchSlashing(ctx sdk.Context, params *types.Params) {
continue
}

found := false
for _, batchConfirmation := range confirms {
// TODO this presents problems for delegate key rotation see issue #344
orchestratorAcc, _ := sdk.AccAddressFromBech32(batchConfirmation.Orchestrator)
delegatedOperator, delegatedFound := h.k.GetOrchestratorValidator(ctx, orchestratorAcc)
if delegatedFound && delegatedOperator.Equals(val.GetOperator()) {
found = true
break
}
}
_, found := confirms[val.GetOperator().String()]

if !found {
cons, _ := val.GetConsAddr()
consPower := val.ConsensusPower(h.k.StakingKeeper.PowerReduction(ctx))
// refresh validator before slashing/jailing
val, foundVal := h.k.StakingKeeper.GetValidator(ctx, val.GetOperator())
if !foundVal {
// this should be impossible, we haven't even progressed a single block since we got the list
panic("Validator exited set during endblocker?")
}

h.k.StakingKeeper.Slash(ctx, cons, ctx.BlockHeight(), consPower, params.SlashFractionBatch)
consPower := val.ConsensusPower(h.k.StakingKeeper.PowerReduction(ctx))

if !val.IsJailed() {
h.k.StakingKeeper.Jail(ctx, cons)
h.k.StakingKeeper.Slash(ctx, consAddr, ctx.BlockHeight(), consPower, params.SlashFractionBatch)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute("BatchSignatureSlashing", consAddr.String()),
),
)
h.k.StakingKeeper.Jail(ctx, consAddr)
}
}
}
Expand Down
26 changes: 12 additions & 14 deletions x/peggy/abci_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,27 +78,28 @@ func TestValsetSlashing_ValsetCreated_After_ValidatorBonded(t *testing.T) {
params := input.PeggyKeeper.GetParams(ctx)

ctx = ctx.WithBlockHeight(ctx.BlockHeight() + int64(params.SignedValsetsWindow) + 2)
val := input.StakingKeeper.Validator(ctx, testpeggy.ValAddrs[0])
vs := pk.GetCurrentValset(ctx)
height := uint64(ctx.BlockHeight()) - (params.SignedValsetsWindow + 1)
vs.Height = height
vs.Nonce = height
vs.Nonce = pk.GetLatestValsetNonce(ctx) + 1

pk.StoreValsetUnsafe(ctx, vs)

for i, val := range testpeggy.AccAddrs {
for i, orch := range testpeggy.OrchAddrs {
if i == 0 {
// don't sign with first validator
continue
}

valsetConfirm := types.NewMsgValsetConfirm(vs.Nonce, testpeggy.EthAddrs[i].String(), val, "dummysig")
valsetConfirm := types.NewMsgValsetConfirm(vs.Nonce, testpeggy.EthAddrs[i].String(), orch, "dummysig")
pk.SetValsetConfirm(ctx, valsetConfirm)
}

assert.Equal(t, uint64(1), pk.GetLatestValsetNonce(ctx))
NewBlockHandler(pk).EndBlocker(ctx)

// ensure that the validator who is bonded before valset is created is slashed
val = input.StakingKeeper.Validator(ctx, testpeggy.ValAddrs[0])
val := input.StakingKeeper.Validator(ctx, testpeggy.ValAddrs[0])
require.True(t, val.IsJailed())

// ensure that the validator who attested the valset is not slashed.
Expand Down Expand Up @@ -131,10 +132,7 @@ func TestValsetSlashing_UnbondingValidator_UnbondWindow_NotExpired(t *testing.T)

// Create Valset request
ctx = ctx.WithBlockHeight(valsetRequestHeight)
vs := pk.GetCurrentValset(ctx)
vs.Height = uint64(valsetRequestHeight)
vs.Nonce = uint64(valsetRequestHeight)
pk.StoreValsetUnsafe(ctx, vs)
vs := pk.SetValsetRequest(ctx)

// Start Unbonding validators
// Validator-1 Unbond slash window is not expired. if not attested, slash
Expand All @@ -146,12 +144,12 @@ func TestValsetSlashing_UnbondingValidator_UnbondWindow_NotExpired(t *testing.T)
undelegateMsg2 := testpeggy.NewTestMsgUnDelegateValidator(testpeggy.ValAddrs[1], testpeggy.StakingAmount)
sh(input.Context, undelegateMsg2)

for i, val := range testpeggy.AccAddrs {
for i, orch := range testpeggy.OrchAddrs {
if i == 0 {
// don't sign with first validator
continue
}
valsetConfirm := types.NewMsgValsetConfirm(vs.Nonce, testpeggy.EthAddrs[i].String(), val, "dummysig")
valsetConfirm := types.NewMsgValsetConfirm(vs.Nonce, testpeggy.EthAddrs[i].String(), orch, "dummysig")
pk.SetValsetConfirm(ctx, valsetConfirm)
}
staking.EndBlocker(input.Context, input.StakingKeeper)
Expand Down Expand Up @@ -187,7 +185,7 @@ func TestBatchSlashing(t *testing.T) {
}
pk.StoreBatchUnsafe(ctx, batch)

for i, val := range testpeggy.AccAddrs {
for i, orch := range testpeggy.OrchAddrs {
if i == 0 {
// don't sign with first validator
continue
Expand All @@ -204,7 +202,7 @@ func TestBatchSlashing(t *testing.T) {
Nonce: batch.BatchNonce,
TokenContract: testpeggy.TokenContractAddrs[0],
EthSigner: testpeggy.EthAddrs[i].String(),
Orchestrator: val.String(),
Orchestrator: orch.String(),
})
}

Expand Down Expand Up @@ -238,7 +236,7 @@ func TestValsetEmission(t *testing.T) {

// EndBlocker should set a new validator set
NewBlockHandler(pk).EndBlocker(ctx)
require.NotNil(t, pk.GetValset(ctx, uint64(ctx.BlockHeight())))
require.NotNil(t, pk.GetValset(ctx, uint64(pk.GetLatestValsetNonce(ctx))))
valsets := pk.GetValsets(ctx)
require.True(t, len(valsets) == 2)
}
Expand Down
32 changes: 32 additions & 0 deletions x/peggy/keeper/attestation_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper
import (
"fmt"
"math/big"
"strconv"

sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
Expand Down Expand Up @@ -74,10 +75,25 @@ func (a AttestationHandler) Handle(ctx sdk.Context, claim types.EthereumClaim) e
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

These events don't look right to me.

  1. We should put all attributes in const in types/events.go.
  2. These don't come from message processing, so the message.* doesn't make much sense. Instead, perhaps attestation.*
  3. Use snake_case, e.g. attestation.send_to_cosmos_amount

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, will work on this

ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeAttestation,
sdk.NewAttribute(types.AttributeKeySendToCosmosAmount, claim.Amount.String()),
sdk.NewAttribute(types.AttributeKeySendToCosmosNonce, strconv.Itoa(int(claim.GetEventNonce()))),
sdk.NewAttribute(types.AttributeKeySendToCosmosToken, claim.TokenContract),
),
)

// withdraw in this context means a withdraw from the Ethereum side of the bridge
case *types.MsgWithdrawClaim:
tokenContract := common.HexToAddress(claim.TokenContract)
a.keeper.OutgoingTxBatchExecuted(ctx, tokenContract, claim.BatchNonce)
ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeAttestation,
sdk.NewAttribute(types.AttributeKeyBatchSendToEthNonce, strconv.Itoa(int(claim.BatchNonce))),
),
)
return nil

case *types.MsgERC20DeployedClaim:
Expand All @@ -87,6 +103,15 @@ func (a AttestationHandler) Handle(ctx sdk.Context, claim types.EthereumClaim) e

// add to ERC20 mapping
a.keeper.SetCosmosOriginatedDenomToERC20(ctx, claim.CosmosDenom, common.HexToAddress(claim.TokenContract))

ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeAttestation,
sdk.NewAttribute(types.AttributeKeyERC20DeployedToken, claim.TokenContract),
sdk.NewAttribute(types.AttributeKeyERC20DeployedNonce, strconv.Itoa(int(claim.GetEventNonce()))),
),
)

return nil

case *types.MsgValsetUpdatedClaim:
Expand All @@ -100,6 +125,13 @@ func (a AttestationHandler) Handle(ctx sdk.Context, claim types.EthereumClaim) e
RewardToken: claim.RewardToken,
})

ctx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeAttestation,
sdk.NewAttribute(types.AttributeKeyValsetUpdatedNonce, strconv.Itoa(int(claim.GetEventNonce()))),
),
)

default:
panic(fmt.Sprintf("invalid event type for attestations %s", claim.GetType()))
}
Expand Down
9 changes: 4 additions & 5 deletions x/peggy/keeper/batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -282,13 +282,12 @@ func (k *Keeper) GetLastSlashedBatchBlock(ctx sdk.Context) uint64 {
// GetUnslashedBatches returns all the unslashed batches in state
func (k *Keeper) GetUnslashedBatches(ctx sdk.Context, maxHeight uint64) (out []*types.OutgoingTxBatch) {
lastSlashedBatchBlock := k.GetLastSlashedBatchBlock(ctx)
k.IterateBatchBySlashedBatchBlock(ctx, lastSlashedBatchBlock, maxHeight, func(_ []byte, batch *types.OutgoingTxBatch) bool {
if batch.Block > lastSlashedBatchBlock {
batches := k.GetOutgoingTxBatches(ctx)
for _, batch := range batches {
if batch.Block > lastSlashedBatchBlock && batch.Block < maxHeight {
out = append(out, batch)
}
return false
})

}
return
}

Expand Down
Loading