Skip to content

Commit

Permalink
feat(btcstaking): mint to bridge addr when non-local chainid specified
Browse files Browse the repository at this point in the history
  • Loading branch information
473n committed Jul 19, 2024
1 parent 70c4ce3 commit bf53f93
Show file tree
Hide file tree
Showing 13 changed files with 370 additions and 191 deletions.
2 changes: 1 addition & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ func NewLorenzoApp(
app.BTCLightClientKeeper,
)

app.BTCStakingKeeper = btcstakingkeeper.NewKeeper(appCodec, keys[btcstakingtypes.StoreKey], app.BTCLightClientKeeper, app.BankKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String())
app.BTCStakingKeeper = btcstakingkeeper.NewKeeper(appCodec, keys[btcstakingtypes.StoreKey], app.BTCLightClientKeeper, app.BankKeeper, app.EvmKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String())

// self keeper
app.PlanKeeper = plankeeper.NewKeeper(
Expand Down
4 changes: 3 additions & 1 deletion proto/lorenzo/btcstaking/v1/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ message Params {
uint32 btc_confirmations_depth = 2;
// allow list to mint for receiver with eth_addr
repeated string minter_allow_list = 3;
// cross chain bridge contract address
string bridge_addr = 4;
//minimum satoshi per txout
int64 txout_dust_amount = 4;
int64 txout_dust_amount = 5;
}
7 changes: 4 additions & 3 deletions proto/lorenzo/btcstaking/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ message QueryParamsResponse {

message StakingRecordDisplay {
string tx_id = 1;
string mint_to_address = 2;
string receiver_address = 2;
string amount = 3;
string btc_receiver_name = 4;
string btc_receiver_addr = 5;
string agent_name = 4;
string agent_btc_addr = 5;
uint32 chain_id = 6;
}

message QueryStakingRecordRequest {
Expand Down
7 changes: 4 additions & 3 deletions proto/lorenzo/btcstaking/v1/staking_record.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ option go_package = "github.com/Lorenzo-Protocol/lorenzo/x/btcstaking/types";
message BTCStakingRecord {
bytes tx_hash = 1;
uint64 amount = 2;
bytes mint_to_addr = 3;
string btc_receiver_name = 4;
string btc_receiver_addr = 5;
bytes receiver_addr = 3;
string agent_name = 4;
string agent_btc_addr = 5;
uint32 chain_id = 6;
}
19 changes: 12 additions & 7 deletions x/btcstaking/client/cli/query.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package cli

import (
"encoding/hex"
"fmt"

sdkmath "cosmossdk.io/math"
"github.com/Lorenzo-Protocol/lorenzo/x/btcstaking/types"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/ethereum/go-ethereum/common"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -70,12 +70,17 @@ func CmdGetBTCStakingRecord() *cobra.Command {
if res.Record == nil {
return fmt.Errorf("record not found")
}
resDisp := types.StakingRecordDisplay{}
resDisp.TxId = (chainhash.Hash)(res.Record.TxHash).String()
resDisp.Amount = sdkmath.NewIntFromUint64(res.Record.Amount).Mul(sdkmath.NewIntFromUint64(1e10)).String()
resDisp.MintToAddress = "0x" + hex.EncodeToString(res.Record.MintToAddr)
resDisp.BtcReceiverName = res.Record.BtcReceiverName
resDisp.BtcReceiverAddr = res.Record.BtcReceiverAddr
resDisp := types.StakingRecordDisplay{
TxId: (chainhash.Hash)(res.Record.TxHash).String(),
Amount: sdkmath.NewIntFromUint64(res.Record.Amount).Mul(sdkmath.NewIntFromUint64(1e10)).String(),
ReceiverAddress: common.BytesToAddress(res.Record.ReceiverAddr).String(),
AgentName: res.Record.AgentName,
AgentBtcAddr: res.Record.AgentBtcAddr,
ChainId: res.Record.ChainId,
}
if err != nil {
return err
}
return clientCtx.PrintProto(&resDisp)
},
}
Expand Down
4 changes: 4 additions & 0 deletions x/btcstaking/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type (

btclcKeeper types.BTCLightClientKeeper

evmKeeper types.EvmKeeper

// the address capable of executing a MsgUpdateParams message. Typically, this
// should be the x/gov module account.
authority string
Expand All @@ -33,6 +35,7 @@ func NewKeeper(

btclcKeeper types.BTCLightClientKeeper,
bankKeeper bankkeeper.Keeper,
evmKeeper types.EvmKeeper,

authority string,
) Keeper {
Expand All @@ -42,6 +45,7 @@ func NewKeeper(

btclcKeeper: btclcKeeper,
bankKeeper: bankKeeper,
evmKeeper: evmKeeper,

authority: authority,
}
Expand Down
94 changes: 63 additions & 31 deletions x/btcstaking/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package keeper
import (
"bytes"
"context"
"encoding/hex"
"encoding/binary"
"fmt"

errorsmod "cosmossdk.io/errors"
Expand All @@ -18,7 +18,11 @@ import (
"github.com/ethereum/go-ethereum/common"
)

const EthAddrLen = 42
const (
EthAddrLen = 20
ChainIDLen = 4
SatoshiToStBTCMul = 1e10
)

const (
Dep0Amount = 4e5
Expand Down Expand Up @@ -120,15 +124,34 @@ func canPerformMint(signer sdk.AccAddress, p types.Params) bool {
return false
}

func checkBTCTxDepth(stakingTxDepth uint64, btcAmount uint64) error {
if btcAmount < Dep0Amount { // no depth check required
} else if btcAmount < Dep1Amount { // at least 1 depth
if stakingTxDepth < 1 {
return types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=1; depth=%d", stakingTxDepth)
}
} else if btcAmount < Dep2Amount {
if stakingTxDepth < 2 {
return types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=2; depth=%d", stakingTxDepth)
}
} else if btcAmount < Dep3Amount {
if stakingTxDepth < 3 {
return types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=3; depth=%d", stakingTxDepth)
}
} else if stakingTxDepth < 4 {
return types.ErrBlkHdrNotConfirmed.Wrapf("not k-deep: k=4; depth=%d", stakingTxDepth)
}
return nil
}

func (ms msgServer) CreateBTCStaking(goCtx context.Context, req *types.MsgCreateBTCStaking) (*types.MsgCreateBTCStakingResponse, error) {
stakingMsgTx, err := NewBTCTxFromBytes(req.StakingTx.Transaction)
if err != nil {
return nil, types.ErrParseBTCTx.Wrap(err.Error())
}
ctx := sdk.UnwrapSDKContext(goCtx)
stakingTxHash := stakingMsgTx.TxHash()
stakingRecord := ms.getBTCStakingRecord(ctx, stakingTxHash)
if stakingRecord != nil {
if ms.getBTCStakingRecord(ctx, stakingTxHash) != nil {
return nil, types.ErrDupBTCTx
}

Expand All @@ -154,40 +177,48 @@ func (ms msgServer) CreateBTCStaking(goCtx context.Context, req *types.MsgCreate
return nil, types.ErrInvalidReceivingAddr.Wrap(err.Error())
}
var mintToAddr []byte
var receiverAddr []byte
var btcAmount uint64
lrzChainId := uint32(ms.evmKeeper.ChainID().Uint64())
var chainId uint32 = lrzChainId
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()
receiverAddr = mintToAddr
btcAmount, err = ExtractPaymentTo(stakingMsgTx, btcReceivingAddr)
} else {
btcAmount, mintToAddr, err = ExtractPaymentToWithOpReturnIdAndDust(stakingMsgTx, btcReceivingAddr, p.TxoutDustAmount)
var opReturnMsg []byte
btcAmount, opReturnMsg, err = ExtractPaymentToWithOpReturnIdAndDust(stakingMsgTx, btcReceivingAddr, p.TxoutDustAmount)
if err != nil {
return nil, types.ErrInvalidTransaction.Wrap(err.Error())
}
if len(opReturnMsg) == EthAddrLen {
mintToAddr = opReturnMsg
receiverAddr = mintToAddr
} else if len(opReturnMsg) == EthAddrLen+ChainIDLen {
receiverAddr = opReturnMsg[:EthAddrLen]
chainId = binary.BigEndian.Uint32(opReturnMsg[EthAddrLen:])
if chainId != lrzChainId {
mintToAddr = common.HexToAddress(p.BridgeAddr).Bytes()
} else {
mintToAddr = receiverAddr
}
} else {
return nil, types.ErrMintToAddr
}
}
if err != nil || btcAmount == 0 {
return nil, types.ErrInvalidTransaction
} 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=1; depth=%d", stakingTxDepth)
}
} else if btcAmount < Dep2Amount {
if stakingTxDepth < 2 {
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 < 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))
err = checkBTCTxDepth(stakingTxDepth, btcAmount)
if err != nil {
return nil, err
}

toMintAmount := sdkmath.NewIntFromUint64(btcAmount).Mul(sdkmath.NewIntFromUint64(1e10))
toMintAmount := sdkmath.NewIntFromUint64(btcAmount).Mul(sdkmath.NewIntFromUint64(SatoshiToStBTCMul))

coins := []sdk.Coin{
{
Expand All @@ -203,18 +234,19 @@ func (ms msgServer) CreateBTCStaking(goCtx context.Context, req *types.MsgCreate
if err != nil {
return nil, types.ErrTransferToAddr.Wrap(err.Error())
}
bctStakingRecord := types.BTCStakingRecord{
TxHash: stakingTxHash[:],
Amount: btcAmount,
MintToAddr: mintToAddr,
BtcReceiverName: receiver.Name,
BtcReceiverAddr: receiver.Addr,
stakingRecord := types.BTCStakingRecord{
TxHash: stakingTxHash[:],
Amount: btcAmount,
ReceiverAddr: receiverAddr,
AgentName: receiver.Name,
AgentBtcAddr: receiver.Addr,
ChainId: chainId,
}
err = ms.addBTCStakingRecord(ctx, &bctStakingRecord)
err = ms.addBTCStakingRecord(ctx, &stakingRecord)
if err != nil {
return nil, types.ErrRecordStaking.Wrap(err.Error())
}
err = ctx.EventManager().EmitTypedEvent(types.NewEventBTCStakingCreated(&bctStakingRecord))
err = ctx.EventManager().EmitTypedEvent(types.NewEventBTCStakingCreated(&stakingRecord))
if err != nil {
panic(fmt.Errorf("fail to emit EventBTCStakingCreated : %w", err))
}
Expand Down
2 changes: 1 addition & 1 deletion x/btcstaking/keeper/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func traverseMerkleBlock(msg *wire.MsgMerkleBlock, hei uint32, pos uint32, bit_u
}
}

// XXX: skip some checks
// XXX: missing some checks, not a safe function to use on chain.
func ParseBTCProof(msgMerkleBlk *wire.MsgMerkleBlock) (uint32, []byte, error) {
hei := calcHeight(int(msgMerkleBlk.Transactions))
bit_used, hash_used := 0, 0
Expand Down
6 changes: 6 additions & 0 deletions x/btcstaking/types/expected_keepers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package types

import (
big "math/big"

sdk "github.com/cosmos/cosmos-sdk/types"

lrz "github.com/Lorenzo-Protocol/lorenzo/types"
Expand All @@ -15,3 +17,7 @@ type BTCLightClientKeeper interface {
GetBTCNet() *chaincfg.Params
GetFeeRate(ctx sdk.Context) uint64
}

type EvmKeeper interface {
ChainID() *big.Int
}
3 changes: 3 additions & 0 deletions x/btcstaking/types/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ func (params Params) Validate() error {
if err := ValidateAddressList(params.MinterAllowList); err != nil {
return fmt.Errorf("invalid minter allow list")
}
if !common.IsHexAddress(params.BridgeAddr) {
return fmt.Errorf("invalid cross chain mint address")
}
return nil
}

Expand Down
Loading

0 comments on commit bf53f93

Please sign in to comment.