Skip to content

Commit

Permalink
incentive: access control of withdrawing rewards (#77)
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastianElvis authored Sep 11, 2023
1 parent 9f40981 commit adca841
Show file tree
Hide file tree
Showing 11 changed files with 89 additions and 128 deletions.
7 changes: 3 additions & 4 deletions proto/babylon/incentive/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ service Msg {

// MsgWithdrawReward defines a message for withdrawing reward of a stakeholder.
message MsgWithdrawReward {
string signer = 1;

// {submitter, reporter, btc_validator, btc_delegation}
string type = 2;
string type = 1;
// address is the address of the stakeholder in bech32 string
string address = 3;
// signer of this msg has to be this address
string address = 2;
}

// MsgWithdrawRewardResponse is the response to the MsgWithdrawReward message
Expand Down
30 changes: 18 additions & 12 deletions test/e2e/btc_staking_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,10 @@ var (
r = rand.New(rand.NewSource(time.Now().Unix()))
net = &chaincfg.SimNetParams
// BTC validator
valSK, _, _ = datagen.GenRandomBTCKeyPair(r)
btcVal, _ = datagen.GenRandomBTCValidatorWithBTCSK(r, valSK)
btcValBabylonAddr = sdk.AccAddress(btcVal.BabylonPk.Address())
valBTCSK, _, _ = datagen.GenRandomBTCKeyPair(r)
btcVal *bstypes.BTCValidator
// BTC delegation
delBabylonSK, delBabylonPK, _ = datagen.GenRandomSecp256k1KeyPair(r)
delBabylonAddr = sdk.AccAddress(delBabylonPK.Address())
delBTCSK, delBTCPK, _ = datagen.GenRandomBTCKeyPair(r)
delBTCSK, delBTCPK, _ = datagen.GenRandomBTCKeyPair(r)
// jury
jurySK, _ = btcec.PrivKeyFromBytes(
[]byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
Expand Down Expand Up @@ -83,6 +80,9 @@ func (s *BTCStakingTestSuite) Test1CreateBTCValidatorAndDelegation() {
/*
create a random BTC validator on Babylon
*/
// NOTE: we use the node's secret key as Babylon secret key for the BTC validator
btcVal, err = datagen.GenRandomBTCValidatorWithBTCBabylonSKs(r, valBTCSK, nonValidatorNode.SecretKey)
s.NoError(err)
nonValidatorNode.CreateBTCValidator(btcVal.BabylonPk, btcVal.BtcPk, btcVal.Pop, btcVal.Description.Moniker, btcVal.Description.Identity, btcVal.Description.Website, btcVal.Description.SecurityContact, btcVal.Description.Details, btcVal.Commission)

// wait for a block so that above txs take effect
Expand All @@ -98,6 +98,8 @@ func (s *BTCStakingTestSuite) Test1CreateBTCValidatorAndDelegation() {
*/
// BTC staking params, BTC delegation key pairs and PoP
params := nonValidatorNode.QueryBTCStakingParams()
// NOTE: we use the node's secret key as Babylon secret key for the BTC delegation
delBabylonSK := nonValidatorNode.SecretKey
pop, err := bstypes.NewPoP(delBabylonSK, delBTCSK)
s.NoError(err)
// generate staking tx and slashing tx
Expand Down Expand Up @@ -136,7 +138,7 @@ func (s *BTCStakingTestSuite) Test1CreateBTCValidatorAndDelegation() {
stakingTxInfo := btcctypes.NewTransactionInfoFromSpvProof(blockWithStakingTx.SpvProof)

// submit the message for creating BTC delegation
nonValidatorNode.CreateBTCDelegation(delBabylonPK.(*secp256k1.PubKey), pop, stakingTx, stakingTxInfo, slashingTx, delegatorSig)
nonValidatorNode.CreateBTCDelegation(delBabylonSK.PubKey().(*secp256k1.PubKey), pop, stakingTx, stakingTxInfo, slashingTx, delegatorSig)

// wait for a block so that above txs take effect
nonValidatorNode.WaitForNextBlock()
Expand Down Expand Up @@ -229,7 +231,7 @@ func (s *BTCStakingTestSuite) Test3CommitPublicRandomnessAndSubmitFinalitySignat
commit a number of public randomness since activatedHeight
*/
// commit public randomness list
srList, msgCommitPubRandList, err := datagen.GenRandomMsgCommitPubRandList(r, valSK, activatedHeight, 100)
srList, msgCommitPubRandList, err := datagen.GenRandomMsgCommitPubRandList(r, valBTCSK, activatedHeight, 100)
s.NoError(err)
nonValidatorNode.CommitPubRandList(
msgCommitPubRandList.ValBtcPk,
Expand All @@ -248,8 +250,10 @@ func (s *BTCStakingTestSuite) Test3CommitPublicRandomnessAndSubmitFinalitySignat
s.Equal(pubRandMap[activatedHeight].MustMarshal(), msgCommitPubRandList.PubRandList[0].MustMarshal())

// no reward gauge for BTC validator and delegation yet
btcValBabylonAddr := sdk.AccAddress(nonValidatorNode.SecretKey.PubKey().Address().Bytes())
_, err = nonValidatorNode.QueryRewardGauge(btcValBabylonAddr)
s.Error(err)
delBabylonAddr := sdk.AccAddress(nonValidatorNode.SecretKey.PubKey().Address().Bytes())
_, err = nonValidatorNode.QueryRewardGauge(delBabylonAddr)
s.Error(err)

Expand All @@ -261,7 +265,7 @@ func (s *BTCStakingTestSuite) Test3CommitPublicRandomnessAndSubmitFinalitySignat
s.NoError(err)
msgToSign := append(sdk.Uint64ToBigEndian(activatedHeight), blockToVote.LastCommitHash...)
// generate EOTS signature
sig, err := eots.Sign(valSK, srList[0], msgToSign)
sig, err := eots.Sign(valBTCSK, srList[0], msgToSign)
s.NoError(err)
eotsSig := bbn.NewSchnorrEOTSSigFromModNScalar(sig)
// submit finality signature
Expand Down Expand Up @@ -303,6 +307,8 @@ func (s *BTCStakingTestSuite) Test4WithdrawReward() {
s.NoError(err)

// BTC validator balance before withdraw
btcValBabylonAddr := sdk.AccAddress(nonValidatorNode.SecretKey.PubKey().Address().Bytes())
delBabylonAddr := sdk.AccAddress(nonValidatorNode.SecretKey.PubKey().Address().Bytes())
btcValBalance, err := nonValidatorNode.QueryBalances(btcValBabylonAddr.String())
s.NoError(err)
// BTC validator reward gauge should not be fully withdrawn
Expand All @@ -313,7 +319,7 @@ func (s *BTCStakingTestSuite) Test4WithdrawReward() {
s.False(btcValRg.IsFullyWithdrawn())

// withdraw BTC validator reward
nonValidatorNode.WithdrawReward(itypes.BTCValidatorType.String(), btcValBabylonAddr.String(), initialization.ValidatorWalletName)
nonValidatorNode.WithdrawReward(itypes.BTCValidatorType.String(), initialization.ValidatorWalletName)
nonValidatorNode.WaitForNextBlock()

// balance after withdrawing BTC validator reward
Expand All @@ -339,7 +345,7 @@ func (s *BTCStakingTestSuite) Test4WithdrawReward() {
s.False(btcDelRg.IsFullyWithdrawn())

// withdraw BTC delegation reward
nonValidatorNode.WithdrawReward(itypes.BTCDelegationType.String(), delBabylonAddr.String(), initialization.ValidatorWalletName)
nonValidatorNode.WithdrawReward(itypes.BTCDelegationType.String(), initialization.ValidatorWalletName)
nonValidatorNode.WaitForNextBlock()

// balance after withdrawing BTC delegation reward
Expand Down Expand Up @@ -452,7 +458,7 @@ func (s *BTCStakingTestSuite) Test6SubmitUnbondingSignatures() {
validatorUnbondingSig, err := delegation.BtcUndelegation.UnbondingTx.Sign(
stakingTxMsg,
delegation.StakingTx.Script,
valSK,
valBTCSK,
net,
)
s.NoError(err)
Expand Down
6 changes: 4 additions & 2 deletions test/e2e/btc_timestamping_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ func (s *BTCTimestampingTestSuite) Test5WithdrawReward() {
nonValidatorNode, err := chainA.GetNodeAtIndex(2)
s.NoError(err)

// NOTE: nonValidatorNode.PublicAddress is the address associated with key name `val`
// and is both the submitter and reporter
submitterReporterAddr := sdk.MustAccAddressFromBech32(nonValidatorNode.PublicAddress)

// balance before withdraw
Expand All @@ -193,7 +195,7 @@ func (s *BTCTimestampingTestSuite) Test5WithdrawReward() {
s.False(reporterRg.IsFullyWithdrawn())

// withdraw submitter reward
nonValidatorNode.WithdrawReward(itypes.SubmitterType.String(), submitterReporterAddr.String(), initialization.ValidatorWalletName)
nonValidatorNode.WithdrawReward(itypes.SubmitterType.String(), initialization.ValidatorWalletName)
nonValidatorNode.WaitForNextBlock()

// balance after withdrawing submitter reward
Expand All @@ -210,7 +212,7 @@ func (s *BTCTimestampingTestSuite) Test5WithdrawReward() {
s.True(rgs2[itypes.SubmitterType.String()].IsFullyWithdrawn())

// withdraw reporter reward
nonValidatorNode.WithdrawReward(itypes.ReporterType.String(), submitterReporterAddr.String(), initialization.ValidatorWalletName)
nonValidatorNode.WithdrawReward(itypes.ReporterType.String(), initialization.ValidatorWalletName)
nonValidatorNode.WaitForNextBlock()

// balance after withdrawing reporter reward
Expand Down
11 changes: 6 additions & 5 deletions test/e2e/configurer/chain/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ func (n *NodeConfig) FinalizeSealedEpochs(startEpoch uint64, lastEpoch uint64) {
currentBtcTip, err := n.QueryTip()
require.NoError(n.t, err)

_, c, err := bech32.DecodeAndConvert(n.PublicAddress)
_, submitterAddr, err := bech32.DecodeAndConvert(n.PublicAddress)
require.NoError(n.t, err)

btcCheckpoint, err := cttypes.FromRawCkptToBTCCkpt(checkpoint.Ckpt, c)
btcCheckpoint, err := cttypes.FromRawCkptToBTCCkpt(checkpoint.Ckpt, submitterAddr)
require.NoError(n.t, err)

babylonTagBytes, err := hex.DecodeString(initialization.BabylonOpReturnTag)
Expand Down Expand Up @@ -191,9 +191,10 @@ func (n *NodeConfig) WasmExecute(contract, execMsg, from string) {
n.LogActionF("successfully executed")
}

func (n *NodeConfig) WithdrawReward(sType, sAddr, from string) {
n.LogActionF("withdraw reward of type %s address %s", sType, sAddr)
cmd := []string{"babylond", "tx", "incentive", "withdraw-reward", sType, sAddr, fmt.Sprintf("--from=%s", from)}
// NOTE: this command will withdraw the reward of the address associated with the tx signer `from`
func (n *NodeConfig) WithdrawReward(sType, from string) {
n.LogActionF("withdraw reward of type %s for tx signer %s", sType, from)
cmd := []string{"babylond", "tx", "incentive", "withdraw-reward", sType, fmt.Sprintf("--from=%s", from)}
n.LogActionF(strings.Join(cmd, " "))
_, _, err := n.containerManager.ExecTxCmd(n.t, n.chainId, n.Name, cmd)
require.NoError(n.t, err)
Expand Down
7 changes: 6 additions & 1 deletion test/e2e/initialization/export.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package initialization

import "fmt"
import (
"fmt"

cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
)

type ChainMeta struct {
DataDir string `json:"dataDir"`
Expand All @@ -12,6 +16,7 @@ type Node struct {
ConfigDir string `json:"configDir"`
Mnemonic string `json:"mnemonic"`
PublicAddress string `json:"publicAddress"`
SecretKey cryptotypes.PrivKey
PublicKey string `json:"publicKey"`
PeerId string `json:"peerId"`
IsValidator bool `json:"isValidator"`
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/initialization/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,6 @@ func (n *internalNode) export() *Node {
}

pub, err := n.keyInfo.GetPubKey()

if err != nil {
panic("pub key should be correct")
}
Expand All @@ -249,7 +248,8 @@ func (n *internalNode) export() *Node {
ConfigDir: n.configDir(),
Mnemonic: n.mnemonic,
PublicAddress: addr.String(),
PublicKey: pub.Address().String(),
SecretKey: n.privateKey,
PublicKey: pub.String(),
PeerId: n.peerId,
IsValidator: n.isValidator,
}
Expand Down
14 changes: 10 additions & 4 deletions testutil/datagen/btcstaking.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
secp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)
Expand All @@ -28,17 +29,22 @@ func GenRandomBTCValidator(r *rand.Rand) (*bstypes.BTCValidator, error) {
}

func GenRandomBTCValidatorWithBTCSK(r *rand.Rand, btcSK *btcec.PrivateKey) (*bstypes.BTCValidator, error) {
bbnSK, _, err := GenRandomSecp256k1KeyPair(r)
if err != nil {
return nil, err
}
return GenRandomBTCValidatorWithBTCBabylonSKs(r, btcSK, bbnSK)
}

func GenRandomBTCValidatorWithBTCBabylonSKs(r *rand.Rand, btcSK *btcec.PrivateKey, bbnSK cryptotypes.PrivKey) (*bstypes.BTCValidator, error) {
// commission
commission := sdk.NewDecWithPrec(int64(RandomInt(r, 49)+1), 2) // [1/100, 50/100]
// description
description := stakingtypes.Description{}
// key pairs
btcPK := btcSK.PubKey()
bip340PK := bbn.NewBIP340PubKeyFromBTCPK(btcPK)
bbnSK, bbnPK, err := GenRandomSecp256k1KeyPair(r)
if err != nil {
return nil, err
}
bbnPK := bbnSK.PubKey()
secp256k1PK, ok := bbnPK.(*secp256k1.PubKey)
if !ok {
return nil, fmt.Errorf("failed to assert bbnPK to *secp256k1.PubKey")
Expand Down
9 changes: 4 additions & 5 deletions x/incentive/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,18 @@ func GetTxCmd() *cobra.Command {

func NewWithdrawRewardCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "withdraw-reward [type] [address]",
Short: "withdraw reward of a given stakeholder in a given type (one of {submitter, reporter, btc_validator, btc_delegation})",
Args: cobra.ExactArgs(2),
Use: "withdraw-reward [type]",
Short: "withdraw reward of the stakeholder behind the transaction submitter in a given type (one of {submitter, reporter, btc_validator, btc_delegation})",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

msg := types.MsgWithdrawReward{
Signer: clientCtx.FromAddress.String(),
Type: args[0],
Address: args[1],
Address: clientCtx.FromAddress.String(),
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
Expand Down
1 change: 0 additions & 1 deletion x/incentive/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ func FuzzWithdrawReward(f *testing.F) {

// invoke withdraw and assert consistency
resp, err := ms.WithdrawReward(ctx, &types.MsgWithdrawReward{
Signer: datagen.GenRandomAccount().Address,
Type: sType.String(),
Address: sAddr.String(),
})
Expand Down
7 changes: 1 addition & 6 deletions x/incentive/types/msg.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package types

import (
"fmt"

errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
)
Expand All @@ -15,15 +13,12 @@ var (

// GetSigners returns the expected signers for a MsgUpdateParams message.
func (m *MsgWithdrawReward) GetSigners() []sdk.AccAddress {
addr, _ := sdk.AccAddressFromBech32(m.Signer)
addr, _ := sdk.AccAddressFromBech32(m.Address)
return []sdk.AccAddress{addr}
}

// ValidateBasic does a sanity check on the provided data.
func (m *MsgWithdrawReward) ValidateBasic() error {
if len(m.Signer) == 0 {
return fmt.Errorf("empty signer")
}
if _, err := NewStakeHolderTypeFromString(m.Type); err != nil {
return err
}
Expand Down
Loading

0 comments on commit adca841

Please sign in to comment.