Skip to content

Commit

Permalink
Merge pull request #74 from Lorenzo-Protocol/btcstaking_cross_chain_main
Browse files Browse the repository at this point in the history
feat(btcstaking): mint to bridge addr when non-local chainid specified
  • Loading branch information
473n committed Jul 22, 2024
1 parent d8c3d78 commit 91e040c
Show file tree
Hide file tree
Showing 13 changed files with 365 additions and 187 deletions.
3 changes: 2 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,8 @@ func NewLorenzoApp(
keys[feetypes.StoreKey],
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
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())

var transferStack ibcporttypes.IBCModule
transferStack = ibctransfer.NewIBCModule(app.TransferKeeper)
Expand Down
2 changes: 2 additions & 0 deletions proto/lorenzo/btcstaking/v1/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ 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;
}
9 changes: 5 additions & 4 deletions proto/lorenzo/btcstaking/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ message QueryParamsResponse {
}

message StakingRecordDisplay {
string txId = 1;
string mintToAddress = 2;
string tx_id = 1;
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;
}
20 changes: 11 additions & 9 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 @@ -64,17 +64,19 @@ func CmdGetBTCStakingRecord() *cobra.Command {
return err
}
res, err := queryClient.StakingRecord(cmd.Context(), &types.QueryStakingRecordRequest{TxHash: txHashBytes[:]})
if err != nil {
return err
}
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
if err != nil {
return err
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,
}
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
90 changes: 61 additions & 29 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
const Dep1Amount = 2e6
Expand Down Expand Up @@ -114,15 +118,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)
var stakingTxHash = stakingMsgTx.TxHash()
staking_record := ms.getBTCStakingRecord(ctx, stakingTxHash)
if staking_record != nil {
stakingTxHash := stakingMsgTx.TxHash()
if ms.getBTCStakingRecord(ctx, stakingTxHash) != nil {
return nil, types.ErrDupBTCTx
}

Expand All @@ -148,40 +171,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, btc_receiving_addr)
} else {
btcAmount, mintToAddr, err = extractPaymentToWithOpReturnId(stakingMsgTx, btc_receiving_addr)
var opReturnMsg []byte
btcAmount, opReturnMsg, err = extractPaymentToWithOpReturnId(stakingMsgTx, btc_receiving_addr)
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 @@ -199,11 +230,12 @@ func (ms msgServer) CreateBTCStaking(goCtx context.Context, req *types.MsgCreate
return nil, types.ErrTransferToAddr.Wrap(err.Error())
}
stakingRecord := types.BTCStakingRecord{
TxHash: stakingTxHash[:],
Amount: btcAmount,
MintToAddr: mintToAddr,
BtcReceiverName: receiver.Name,
BtcReceiverAddr: receiver.Addr,
TxHash: stakingTxHash[:],
Amount: btcAmount,
ReceiverAddr: receiverAddr,
AgentName: receiver.Name,
AgentBtcAddr: receiver.Addr,
ChainId: chainId,
}
err = ms.addBTCStakingRecord(ctx, &stakingRecord)
if err != nil {
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 @@ -66,7 +66,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 91e040c

Please sign in to comment.