Skip to content

Commit

Permalink
Merge pull request #42 from Lorenzo-Protocol/minter_allow_list
Browse files Browse the repository at this point in the history
feat(btcstaking): add allow list for receivers with eth addr
  • Loading branch information
sheldonleedev authored Jun 5, 2024
2 parents de8c775 + 8c3427b commit 95aa988
Show file tree
Hide file tree
Showing 8 changed files with 267 additions and 628 deletions.
2 changes: 2 additions & 0 deletions proto/lorenzo/btcstaking/v1/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ message Params {
repeated Receiver receivers = 1;
// deprecated
uint32 btc_confirmations_depth = 2;
// allow list to mint for receiver with eth_addr
repeated string minter_allow_list = 3;
}
23 changes: 4 additions & 19 deletions proto/lorenzo/btcstaking/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,14 @@ import "lorenzo/btcstaking/v1/params.proto";
option go_package = "github.com/Lorenzo-Protocol/lorenzo/x/btcstaking/types";

// Msg defines the Msg service.
// TODO: handle unbonding tx with full witness
service Msg {
option (cosmos.msg.v1.service) = true;

// CreateBTCDelegation creates a new BTC delegation
rpc CreateBTCStaking(MsgCreateBTCStaking) returns (MsgCreateBTCStakingResponse);

rpc Burn(MsgBurnRequest) returns (MsgBurnResponse);

rpc AddReceiver(MsgAddReceiver) returns (MsgAddReceiverResponse);

rpc RemoveReceiver(MsgRemoveReceiver) returns (MsgRemoveReceiverResponse);
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
}

message TransactionKey {
Expand All @@ -46,9 +42,6 @@ message TransactionInfo {
TransactionKey key = 1;
// transaction is the full transaction in bytes
bytes transaction = 2;
// proof is the Merkle proof that this tx is included in the position in `key`
// TODO: maybe it could use here better format as we already processed and
// validated the proof?
bytes proof = 3;
}

Expand All @@ -70,20 +63,12 @@ message MsgBurnRequest {

message MsgBurnResponse{}

message MsgAddReceiver {
message MsgUpdateParams {
option (cosmos.msg.v1.signer) = "authority";

string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];

Receiver receiver = 2 [ (gogoproto.nullable) = false ];
Params params = 2 [ (gogoproto.nullable) = false ];
}

message MsgAddReceiverResponse {}
message MsgRemoveReceiver {
option (cosmos.msg.v1.signer) = "authority";

string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];

string receiver = 2 ;
}
message MsgRemoveReceiverResponse {}
message MsgUpdateParamsResponse {}
87 changes: 31 additions & 56 deletions x/btcstaking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import (

const EthAddrLen = 42

const Dep0Amount = 2e6
const Dep1Amount = 1e7
const Dep2Amount = 5e7
const Dep0Amount = 4e5
const Dep1Amount = 2e6
const Dep2Amount = 1e7
const Dep3Amount = 5e7

type msgServer struct {
Keeper
Expand All @@ -36,7 +37,6 @@ func NewMsgServerImpl(keeper Keeper) types.MsgServer {

var _ types.MsgServer = msgServer{}

// UpdateParams updates the params
func NewBTCTxFromBytes(txBytes []byte) (*wire.MsgTx, error) {
var msgTx wire.MsgTx
rbuf := bytes.NewReader(txBytes)
Expand Down Expand Up @@ -102,19 +102,17 @@ func extractPaymentToWithOpReturnId(tx *wire.MsgTx, addr btcutil.Address) (uint6
return amt, opReturnId, nil
}

/*func extractPaymentTo(tx *wire.MsgTx, addr btcutil.Address) (uint64, error) {
payToAddrScript, err := txscript.PayToAddrScript(addr)
if err != nil {
return 0, fmt.Errorf("invalid address")
}
var amt uint64 = 0
for _, out := range tx.TxOut {
if bytes.Equal(out.PkScript, payToAddrScript) {
amt += uint64(out.Value)
}
}
return amt, nil
}*/
func canPerformMint(signer sdk.AccAddress, p types.Params) bool {
if len(p.MinterAllowList) == 0 {
return true
}
for _, addr := range p.MinterAllowList {
if sdk.MustAccAddressFromBech32(addr).Equals(signer) {
return true
}
}
return false
}

func (ms msgServer) CreateBTCStaking(goCtx context.Context, req *types.MsgCreateBTCStaking) (*types.MsgCreateBTCStakingResponse, error) {
stakingMsgTx, err := NewBTCTxFromBytes(req.StakingTx.Transaction)
Expand Down Expand Up @@ -152,6 +150,10 @@ func (ms msgServer) CreateBTCStaking(goCtx context.Context, req *types.MsgCreate
var mintToAddr []byte
var btcAmount uint64
if common.IsHexAddress(receiver.EthAddr) {
signers := req.GetSigners()
if len(signers) == 0 || !canPerformMint(req.GetSigners()[0], *p) {
return nil, types.ErrNotInAllowList
}
mintToAddr = common.HexToAddress(receiver.EthAddr).Bytes()
btcAmount, err = extractPaymentTo(stakingMsgTx, btc_receiving_addr)
} else {
Expand All @@ -162,14 +164,18 @@ func (ms msgServer) CreateBTCStaking(goCtx context.Context, req *types.MsgCreate
} else if btcAmount < Dep0Amount { // no depth check required
} else if btcAmount < Dep1Amount { // at least 1 depth
if stakingTxDepth < 1 {
return nil, types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=%d; depth=%d", 1, stakingTxDepth)
return nil, types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=1; depth=%d", stakingTxDepth)
}
} else if btcAmount < Dep2Amount {
if stakingTxDepth < 2 {
return nil, types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=%d; depth=%d", 2, stakingTxDepth)
return nil, types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=2; depth=%d", stakingTxDepth)
}
} else if btcAmount < Dep3Amount {
if stakingTxDepth < 3 {
return nil, types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=3; depth=%d", stakingTxDepth)
}
} else if stakingTxDepth < 3 {
return nil, types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=%d; depth=%d", 3, stakingTxDepth)
} else if stakingTxDepth < 4 {
return nil, types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=4; depth=%d", stakingTxDepth)
}
if len(mintToAddr) != 20 {
return nil, types.ErrMintToAddr.Wrap(hex.EncodeToString(mintToAddr))
Expand Down Expand Up @@ -261,47 +267,16 @@ func findReceiver(receivers []*types.Receiver, name string) (int, *types.Receive
return idx, receiver
}

func (ms msgServer) AddReceiver(goCtx context.Context, req *types.MsgAddReceiver) (*types.MsgAddReceiverResponse, error) {
func (ms msgServer) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
if ms.authority != req.Authority {
return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.authority, req.Authority)
}
ctx := sdk.UnwrapSDKContext(goCtx)
params := ms.GetParams(ctx)
receiverIdx, receiver := findReceiver(params.Receivers, req.Receiver.Name)
if receiver != nil {
params.Receivers[receiverIdx] = &req.Receiver
} else {
params.Receivers = append(params.Receivers, &req.Receiver)
}
btclcParams := ms.btclcKeeper.GetBTCNet()
if _, err := btcutil.DecodeAddress(req.Receiver.Addr, btclcParams); err != nil {
return nil, types.ErrInvalidReceivingAddr.Wrap(err.Error())
}
if err := ms.SetParams(ctx, params); err != nil {
if err := req.Params.Validate(); err != nil {
return nil, err
}

return &types.MsgAddReceiverResponse{}, nil
}

func (ms msgServer) RemoveReceiver(goCtx context.Context, req *types.MsgRemoveReceiver) (*types.MsgRemoveReceiverResponse, error) {
if ms.authority != req.Authority {
return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", ms.authority, req.Authority)
}
ctx := sdk.UnwrapSDKContext(goCtx)
params := ms.GetParams(ctx)
receivers := make([]*types.Receiver, 0, len(params.Receivers))
for _, receiver := range params.Receivers {
if receiver.Name != req.Receiver {
receivers = append(receivers, receiver)
}
}
if len(receivers) == len(params.Receivers) {
return nil, govtypes.ErrInvalidProposalMsg.Wrap("Receiver not exists")
}
params.Receivers = receivers
if err := ms.SetParams(ctx, params); err != nil {
if err := ms.SetParams(ctx, &req.Params); err != nil {
return nil, err
}
return &types.MsgRemoveReceiverResponse{}, nil
return &types.MsgUpdateParamsResponse{}, nil
}
1 change: 1 addition & 0 deletions x/btcstaking/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ var (
ErrBurnInsufficientBalance = errorsmod.Register(ModuleName, 1115, "insufficient account balance")
ErrBurnInvalidSigner = errorsmod.Register(ModuleName, 1116, "invalid signer")
ErrInvalidEthAddr = errorsmod.Register(ModuleName, 1117, "invalid receiver eth addr")
ErrNotInAllowList = errorsmod.Register(ModuleName, 1118, "not in minter allow list")
)
33 changes: 27 additions & 6 deletions x/btcstaking/types/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package types
import (
"fmt"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
)

Expand All @@ -28,14 +29,19 @@ func (receiver Receiver) Validate() error {
return nil
}

// Validate performs basic genesis state validation returning an error upon any
// failure.
func (gs GenesisState) Validate() error {
if gs.Params == nil {
return fmt.Errorf("params cannot be nil")
func ValidateAddressList(allowList []string) error {
for _, a := range allowList {
if _, err := sdk.AccAddressFromBech32(a); err != nil {
return fmt.Errorf("invalid address")
}
}

return nil
}

func (params Params) Validate() error {
receivers := map[string]bool{}
for _, receiver := range gs.Params.Receivers {
for _, receiver := range params.Receivers {
if err := receiver.Validate(); err != nil {
return err
}
Expand All @@ -44,5 +50,20 @@ func (gs GenesisState) Validate() error {
}
receivers[receiver.Name] = true
}
if err := ValidateAddressList(params.MinterAllowList); err != nil {
return fmt.Errorf("invalid minter allow list")
}
return nil
}

// Validate performs basic genesis state validation returning an error upon any
// failure.
func (gs GenesisState) Validate() error {
if gs.Params == nil {
return fmt.Errorf("params cannot be nil")
}
if err := gs.Params.Validate(); err != nil {
return err
}
return nil
}
46 changes: 13 additions & 33 deletions x/btcstaking/types/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package types
import (
fmt "fmt"

errorsmod "cosmossdk.io/errors"
"cosmossdk.io/math"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
Expand All @@ -14,8 +13,7 @@ import (
var (
_ sdk.Msg = &MsgCreateBTCStaking{}
_ sdk.Msg = &MsgBurnRequest{}
_ sdk.Msg = &MsgRemoveReceiver{}
_ sdk.Msg = &MsgAddReceiver{}
_ sdk.Msg = &MsgUpdateParams{}
)

func (m *MsgCreateBTCStaking) ValidateBasic() error {
Expand All @@ -32,36 +30,6 @@ func (m *MsgCreateBTCStaking) ValidateBasic() error {
return nil
}

func (m *MsgAddReceiver) GetSigners() []sdk.AccAddress {
addr, _ := sdk.AccAddressFromBech32(m.Authority)
return []sdk.AccAddress{addr}
}

func (m *MsgAddReceiver) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil {
return errorsmod.Wrap(err, "invalid authority address")
}
if err := m.Receiver.Validate(); err != nil {
return err
}
return nil
}

func (m *MsgRemoveReceiver) GetSigners() []sdk.AccAddress {
addr, _ := sdk.AccAddressFromBech32(m.Authority)
return []sdk.AccAddress{addr}
}

func (m *MsgRemoveReceiver) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil {
return errorsmod.Wrap(err, "invalid authority address")
}
if len(m.Receiver) == 0 {
return fmt.Errorf("receiver name cannot be empty")
}
return nil
}

func (msg *MsgCreateBTCStaking) GetSigners() []sdk.AccAddress {
signer, err := sdk.AccAddressFromBech32(msg.Signer)
if err != nil {
Expand Down Expand Up @@ -99,3 +67,15 @@ func NewMsgBurnRequest(signer, btcTargetAddress string, amount math.Int) MsgBurn
Amount: amount,
}
}

func (m *MsgUpdateParams) GetSigners() []sdk.AccAddress {
addr, _ := sdk.AccAddressFromBech32(m.Authority)
return []sdk.AccAddress{addr}
}

func (m *MsgUpdateParams) ValidateBasic() error {
if err := m.Params.Validate(); err != nil {
return err
}
return nil
}
Loading

0 comments on commit 95aa988

Please sign in to comment.