Skip to content

Commit

Permalink
New external staking events (#131)
Browse files Browse the repository at this point in the history
  • Loading branch information
KonradStaniec authored Oct 7, 2024
1 parent 64b6897 commit efeb745
Show file tree
Hide file tree
Showing 8 changed files with 2,274 additions and 309 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ refund transaction fee for certain transactions from protocol stakeholders
* [#138](https://github.com/babylonlabs-io/babylon/pull/138) Intercept staking module
messages inside `authz.MsgExec`

### Misc Improvements
### Improvements

* #[131](https://github.com/babylonlabs-io/babylon/pull/131) Add new staking
events

* [#113](https://github.com/babylonlabs-io/babylon/pull/113) Add multibuild binary
for upgrade handler `testnet` and `mainnet`.
Expand Down
88 changes: 88 additions & 0 deletions proto/babylon/btcstaking/v1/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,91 @@ message EventPowerDistUpdate {
EventBTCDelegationStateUpdate btc_del_state_update = 4;
}
}

// EventBTCDelegationCreated is the event emitted when a BTC delegation is created
// on the Babylon chain
message EventBTCDelegationCreated {
// staking_tx_hash is the hash of the staking tx.
// It uniquely identifies a BTC delegation
string staking_tx_hash = 1;
// version of the params used to validate the delegation
uint32 params_version = 2;
// finality_provider_btc_pks_hex is the list of hex str of Bitcoin secp256k1 PK of
// the finality providers that this BTC delegation delegates to
// the PK follows encoding in BIP-340 spec
repeated string finality_provider_btc_pks_hex = 3;
// staker_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the staker that
// creates this BTC delegation the PK follows encoding in BIP-340 spec
string staker_btc_pk_hex = 4;
// staking_time is the timelock of the staking tx specified in the BTC script
uint32 staking_time = 5;
// staking_amount is the total amount of BTC stake in this delegation
// quantified in satoshi
uint64 staking_amount = 6;
// unbonding_time is the time is timelock on unbonding tx chosen by the staker
uint32 unbonding_time = 7;
// unbonding_tx is hex encoded bytes of the unsigned unbonding tx
string unbonding_tx = 8;
// state of the BTC delegation
BTCDelegationStatus state = 9;
}

// EventCovenantSignatureRecevied is the event emitted when a covenant committee
// sends valid covenant signatures for a BTC delegation
message EventCovenantSignatureRecevied{
// staking_tx_hash is the hash of the staking identifing the BTC delegation
// that this covenant signature is for
string staking_tx_hash = 1;
// covenant_btc_pk_hex is the hex str of Bitcoin secp256k1 PK of the
// covnenat committee that send the signature
string covenant_btc_pk_hex = 2;
// covenant_unbonding_signature_hex is the hex str of the BIP340 Schnorr
// signature of the covenant committee on the unbonding tx
string covenant_unbonding_signature_hex = 3;
}

// EventCovenantQuroumReached is the event emitted quorum of covenant committee
// is reached for a BTC delegation
message EventCovenantQuroumReached {
// staking_tx_hash is the hash of the staking identifing the BTC delegation
// that this covenant signature is for
string staking_tx_hash = 1;
// state of the BTC delegation
BTCDelegationStatus state = 2;
}

// EventBTCDelegationInclusionProofReceived is the event emitted when a BTC delegation
// inclusion proof is received
message EventBTCDelegationInclusionProofReceived {
// staking_tx_hash is the hash of the staking tx.
// It uniquely identifies a BTC delegation
string staking_tx_hash = 1;
// start_height is the start BTC height of the BTC delegation
// it is the start BTC height of the timelock
uint64 start_height = 2;
// end_height is the end height of the BTC delegation
// it is calculated by end_height = start_height + staking_time
uint64 end_height = 3;
// state of the BTC delegation
BTCDelegationStatus state = 4;
}

// EventBTCDelgationUnbondedEarly is the event emitted when a BTC delegation
// is unbonded by staker sending unbonding tx to BTC
message EventBTCDelgationUnbondedEarly {
// staking_tx_hash is the hash of the staking tx.
// It uniquely identifies a BTC delegation
string staking_tx_hash = 1;
// state of the BTC delegation
BTCDelegationStatus state = 2;
}

// EventBTCDelegationExpired is the event emitted when a BTC delegation
// is unbonded by expiration of the staking tx timelock
message EventBTCDelegationExpired {
// staking_tx_hash is the hash of the staking tx.
// It uniquely identifies a BTC delegation
string staking_tx_hash = 1;
// state of the BTC delegation
BTCDelegationStatus state = 2;
}
86 changes: 57 additions & 29 deletions x/btcstaking/keeper/btc_delegations.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,29 @@ func (k Keeper) AddBTCDelegation(ctx sdk.Context, btcDel *types.BTCDelegation) e
// save this BTC delegation
k.setBTCDelegation(ctx, btcDel)

// notify subscriber
event := &types.EventBTCDelegationStateUpdate{
StakingTxHash: stakingTxHash.String(),
NewState: types.BTCDelegationStatus_PENDING,
}
if err := ctx.EventManager().EmitTypedEvent(event); err != nil {
panic(fmt.Errorf("failed to emit EventBTCDelegationStateUpdate for the new pending BTC delegation: %w", err))
if err := ctx.EventManager().EmitTypedEvents(types.NewBtcDelCreationEvent(
stakingTxHash.String(),
btcDel,
)); err != nil {
panic(fmt.Errorf("failed to emit events for the new pending BTC delegation: %w", err))
}

// NOTE: we don't need to record events for pending BTC delegations since these
// do not affect voting power distribution
// NOTE: we only insert unbonded event if the delegation already has inclusion proof
if btcDel.HasInclusionProof() {
if err := ctx.EventManager().EmitTypedEvent(types.NewInclusionProofEvent(
stakingTxHash.String(),
btcDel.StartHeight,
btcDel.EndHeight,
types.BTCDelegationStatus_PENDING,
)); err != nil {
panic(fmt.Errorf("failed to emit EventBTCDelegationInclusionProofReceived for the new pending BTC delegation: %w", err))
}

// record event that the BTC delegation will become unbonded at endHeight-w
// This event will be generated to subscribers as block event, when the
// btc light client block height will reach btcDel.EndHeight-wValue
unbondedEvent := types.NewEventPowerDistUpdateWithBTCDel(&types.EventBTCDelegationStateUpdate{
StakingTxHash: stakingTxHash.String(),
NewState: types.BTCDelegationStatus_UNBONDED,
Expand All @@ -85,6 +94,7 @@ func (k Keeper) addCovenantSigsToBTCDelegation(
parsedUnbondingSlashingAdaptorSignatures []asig.AdaptorSignature,
params *types.Params,
) {

// All is fine add received signatures to the BTC delegation and BtcUndelegation
btcDel.AddCovenantSigs(
covPK,
Expand All @@ -95,22 +105,46 @@ func (k Keeper) addCovenantSigsToBTCDelegation(

k.setBTCDelegation(ctx, btcDel)

if err := ctx.EventManager().EmitTypedEvent(types.NewCovenantSignatureReceivedEvent(
btcDel,
covPK,
unbondingTxSig,
)); err != nil {
panic(fmt.Errorf("failed to emit EventCovenantSignatureRecevied for the new active BTC delegation: %w", err))
}

// If reaching the covenant quorum after this msg, the BTC delegation becomes
// active. Then, record and emit this event
if btcDel.HasCovenantQuorums(params.CovenantQuorum) && btcDel.HasInclusionProof() {
// notify subscriber
event := &types.EventBTCDelegationStateUpdate{
StakingTxHash: btcDel.MustGetStakingTxHash().String(),
NewState: types.BTCDelegationStatus_ACTIVE,
}
if err := ctx.EventManager().EmitTypedEvent(event); err != nil {
panic(fmt.Errorf("failed to emit EventBTCDelegationStateUpdate for the new active BTC delegation: %w", err))
if btcDel.HasCovenantQuorums(params.CovenantQuorum) {
if btcDel.HasInclusionProof() {
quorumReachedEvent := types.NewCovenantQuorumReachedEvent(
btcDel,
types.BTCDelegationStatus_ACTIVE,
)
if err := ctx.EventManager().EmitTypedEvent(quorumReachedEvent); err != nil {
panic(fmt.Errorf("failed to emit emit for the new verified BTC delegation: %w", err))
}

// record event that the BTC delegation becomes active at this height
activeEvent := types.NewEventPowerDistUpdateWithBTCDel(
&types.EventBTCDelegationStateUpdate{
StakingTxHash: btcDel.MustGetStakingTxHash().String(),
NewState: types.BTCDelegationStatus_ACTIVE,
},
)
btcTip := k.btclcKeeper.GetTipInfo(ctx)
k.addPowerDistUpdateEvent(ctx, btcTip.Height, activeEvent)
} else {
quorumReachedEvent := types.NewCovenantQuorumReachedEvent(
btcDel,
types.BTCDelegationStatus_VERIFIED,
)

if err := ctx.EventManager().EmitTypedEvent(quorumReachedEvent); err != nil {
panic(fmt.Errorf("failed to emit emit for the new verified BTC delegation: %w", err))
}
}

// record event that the BTC delegation becomes active at this height
activeEvent := types.NewEventPowerDistUpdateWithBTCDel(event)
btcTip := k.btclcKeeper.GetTipInfo(ctx)
k.addPowerDistUpdateEvent(ctx, btcTip.Height, activeEvent)
}
}

Expand All @@ -124,20 +158,14 @@ func (k Keeper) btcUndelegate(
btcDel.BtcUndelegation.DelegatorUnbondingSig = unbondingTxSig
k.setBTCDelegation(ctx, btcDel)

// notify subscriber about this unbonded BTC delegation
event := &types.EventBTCDelegationStateUpdate{
StakingTxHash: btcDel.MustGetStakingTxHash().String(),
NewState: types.BTCDelegationStatus_UNBONDED,
}

if !btcDel.HasInclusionProof() {
return
}

// NOTE: do not generate voting power change event if the delegation
// does not have inclusion proof
if err := ctx.EventManager().EmitTypedEvent(event); err != nil {
panic(fmt.Errorf("failed to emit EventBTCDelegationStateUpdate for the new unbonded BTC delegation: %w", err))
// notify subscriber about this unbonded BTC delegation
event := &types.EventBTCDelegationStateUpdate{
StakingTxHash: btcDel.MustGetStakingTxHash().String(),
NewState: types.BTCDelegationStatus_UNBONDED,
}

// record event that the BTC delegation becomes unbonded at this height
Expand Down
29 changes: 19 additions & 10 deletions x/btcstaking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,17 +284,26 @@ func (ms msgServer) AddBTCDelegationInclusionProof(
btcDel.EndHeight = btcDel.StartHeight + uint64(btcDel.StakingTime)
ms.setBTCDelegation(ctx, btcDel)

// 7. emit activation and expiry event
// record event that the BTC delegation becomes active at this height
// notify subscriber
event := &types.EventBTCDelegationStateUpdate{
StakingTxHash: btcDel.MustGetStakingTxHash().String(),
NewState: types.BTCDelegationStatus_ACTIVE,
}
if err := ctx.EventManager().EmitTypedEvent(event); err != nil {
panic(fmt.Errorf("failed to emit EventBTCDelegationStateUpdate for the new active BTC delegation: %w", err))
// 7. emit events
stakingTxHash := btcDel.MustGetStakingTxHash()

newInclusionProofEvent := types.NewInclusionProofEvent(
stakingTxHash.String(),
btcDel.StartHeight,
btcDel.EndHeight,
types.BTCDelegationStatus_ACTIVE,
)

if err := ctx.EventManager().EmitTypedEvents(newInclusionProofEvent); err != nil {
panic(fmt.Errorf("failed to emit events for the new active BTC delegation: %w", err))
}
activeEvent := types.NewEventPowerDistUpdateWithBTCDel(event)

activeEvent := types.NewEventPowerDistUpdateWithBTCDel(
&types.EventBTCDelegationStateUpdate{
StakingTxHash: stakingTxHash.String(),
NewState: types.BTCDelegationStatus_ACTIVE,
},
)
btcTip := ms.btclcKeeper.GetTipInfo(ctx)
ms.addPowerDistUpdateEvent(ctx, btcTip.Height, activeEvent)

Expand Down
28 changes: 23 additions & 5 deletions x/btcstaking/keeper/power_dist_change.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,19 +170,37 @@ func (k Keeper) ProcessAllPowerDistUpdateEvents(
switch typedEvent := event.Ev.(type) {
case *types.EventPowerDistUpdate_BtcDelStateUpdate:
delEvent := typedEvent.BtcDelStateUpdate
btcDel, err := k.GetBTCDelegation(ctx, delEvent.StakingTxHash)
if err != nil {
panic(err) // only programming error
}
if delEvent.NewState == types.BTCDelegationStatus_ACTIVE {
// newly active BTC delegation
btcDel, err := k.GetBTCDelegation(ctx, delEvent.StakingTxHash)
if err != nil {
panic(err) // only programming error
}

// add the BTC delegation to each restaked finality provider
for _, fpBTCPK := range btcDel.FpBtcPkList {
fpBTCPKHex := fpBTCPK.MarshalHex()
activeBTCDels[fpBTCPKHex] = append(activeBTCDels[fpBTCPKHex], btcDel)
}
} else if delEvent.NewState == types.BTCDelegationStatus_UNBONDED {
// add the expired BTC delegation to the map
sdkCtx := sdk.UnwrapSDKContext(ctx)
// delegation expired and become unbonded emit block event about this
// information
if btcDel.IsUnbondedEarly() {
unbondedEarlyEvent := types.NewDelegationUnbondedEarlyEvent(delEvent.StakingTxHash)

if err := sdkCtx.EventManager().EmitTypedEvent(unbondedEarlyEvent); err != nil {
panic(fmt.Errorf("failed to emit event the new unbonded BTC delegation: %w", err))
}
} else {
expiredEvent := types.NewExpiredDelegationEvent(delEvent.StakingTxHash)

if err := sdkCtx.EventManager().EmitTypedEvent(expiredEvent); err != nil {
panic(fmt.Errorf("failed to emit event for the new expired BTC delegation: %w", err))
}
}

// add the unbonded BTC delegation to the map
unbondedBTCDels[delEvent.StakingTxHash] = struct{}{}
}
case *types.EventPowerDistUpdate_SlashedFp:
Expand Down
10 changes: 10 additions & 0 deletions x/btcstaking/types/btc_delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ func (d *BTCDelegation) IsUnbondedEarly() bool {
return d.BtcUndelegation.DelegatorUnbondingSig != nil
}

func (d *BTCDelegation) FinalityProviderKeys() []string {
var fpPks = make([]string, len(d.FpBtcPkList))

for i, fpPk := range d.FpBtcPkList {
fpPks[i] = fpPk.MarshalHex()
}

return fpPks
}

// GetStatus returns the status of the BTC Delegation based on BTC height, w value, and covenant quorum
// Pending: the BTC height is in the range of d's [startHeight, endHeight-w] and the delegation does not have covenant signatures
// Active: the BTC height is in the range of d's [startHeight, endHeight-w] and the delegation has quorum number of signatures over slashing tx, unbonding tx, and slashing unbonding tx from covenant committee
Expand Down
Loading

0 comments on commit efeb745

Please sign in to comment.