Skip to content

Commit

Permalink
Implemented move funds task
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaszslabon committed Jan 3, 2024
1 parent f1c8275 commit 590e84d
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 268 deletions.
5 changes: 4 additions & 1 deletion pkg/tbtc/moving_funds.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ func newMovingFundsAction(
}

func (mfa *movingFundsAction) execute() error {
// TODO: Implement
// TODO: Before proceeding with creation of the Bitcoin transaction, wait
// 32 blocks to ensure the commitment transaction has accumulated
// enough confirmations in the Ethereum chain and will not be reverted
// even if a reorg occurs.
return nil
}

Expand Down
267 changes: 0 additions & 267 deletions pkg/tbtc/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"encoding/hex"
"fmt"
"math/big"
"sort"
"sync"

"github.com/keep-network/keep-core/pkg/bitcoin"
Expand Down Expand Up @@ -708,272 +707,6 @@ func (n *node) handleMovingFundsProposal(
walletActionLogger.Infof("wallet action dispatched successfully")
}

// TODO: Move the logic from this function into tbtcpg.MovingFundsTask:
func (n *node) HandleMovingFundsProposal(sourceWalletPublicKeyHash [20]byte) {
go func() {
logger.Info(
"moving funds proposal initiated for wallet with PKH [0x%x]",
sourceWalletPublicKeyHash,
)

// Make sure the wallet meets the criteria for moving funds proposal.
sourceWallet, found := n.walletRegistry.getWalletByPublicKeyHash(
sourceWalletPublicKeyHash,
)
if !found {
logger.Errorf(
"skipping moving funds proposal for wallet with PKH "+
"[0x%x] as the node does not control it",
sourceWalletPublicKeyHash,
)
return
}

sourceWalletChainData, err := n.chain.GetWallet(sourceWalletPublicKeyHash)
if err != nil {
logger.Errorf(
"failed to get wallet data for source wallet with PKH "+
"[0x%x]: [%v]",
sourceWalletPublicKeyHash,
err,
)
return
}

walletMainUtxo, err := DetermineWalletMainUtxo(
sourceWalletPublicKeyHash,
n.chain,
n.btcChain,
)
if err != nil {
logger.Errorf(
"skipping moving funds proposal for wallet with PKH "+
"[0x%x] due to error determining wallet main UTXO: [%v]",
sourceWalletPublicKeyHash,
err,
)
return
}

walletBalance := int64(0)
if walletMainUtxo != nil {
walletBalance = walletMainUtxo.Value
}

if sourceWalletChainData.State != StateMovingFunds {
logger.Infof(
"ignoring moving funds proposal for wallet with PKH [0x%x]; "+
"wallet not in MovingFunds state",
sourceWalletPublicKeyHash,
)
return
}

if sourceWalletChainData.PendingRedemptionsValue > 0 {
logger.Infof(
"ignoring moving funds proposal for wallet with PKH [0x%x]; "+
"wallet has pending redemptions",
sourceWalletPublicKeyHash,
)
return
}

if sourceWalletChainData.PendingMovedFundsSweepRequestsCount > 0 {
logger.Infof(
"ignoring moving funds proposal for wallet with PKH [0x%x]; "+
"wallet has pending moved funds sweep requests",
sourceWalletPublicKeyHash,
)
return
}

if sourceWalletChainData.MovingFundsTargetWalletsCommitmentHash != [32]byte{} {
logger.Infof(
"ignoring moving funds proposal for wallet with PKH [0x%x]; "+
"wallet has already submitted commitment",
sourceWalletPublicKeyHash,
)
return
}

if walletBalance <= 0 {
// The wallet's balance cannot be `0`. Since we are dealing with
// a signed integer we also check it's not negative just in case.
logger.Infof(
"ignoring moving funds proposal for wallet with PKH [0x%x]; "+
"wallet does not have a positive balance",
sourceWalletPublicKeyHash,
)
return
}

logger.Infof(
"proceeding with moving funds commitment for wallet with "+
"PKH [0x%x]",
sourceWalletPublicKeyHash,
)

// Prepare the list of target wallets.
liveWalletsCount, err := n.chain.GetLiveWalletsCount()
if err != nil {
logger.Errorf("failed to get live wallets count: [%v]", err)
return
}

if liveWalletsCount == 0 {
logger.Infof(
"skipping moving funds proposal for wallet with PKH [0x%x] due"+
"to lack of live wallets",
sourceWalletPublicKeyHash,
)
return
}

_, _, _, _, _, walletMaxBtcTransfer, _, err := n.chain.GetWalletParameters()
if err != nil {
logger.Errorf("failed to get wallet parameters: [%v]", err)
return
}

if walletMaxBtcTransfer == 0 {
logger.Errorf("wallet max BTC transfer parameter must be positive")
return
}

ceilingDivide := func(x, y uint64) uint64 {
// The divisor must be positive, but we do not need to check it as
// this function will be executed with wallet max BTC transfer as
// the divisor and we already ensured it is positive.
return (x + y - 1) / y
}
min := func(x, y uint64) uint64 {
if x < y {
return x
}
return y
}

targetWalletsCount := min(
uint64(liveWalletsCount),
ceilingDivide(uint64(walletBalance), walletMaxBtcTransfer),
)

// Prepare a list of target wallets using the new wallets registration
// events. Retrieve only the necessary number of live wallets.
// The iteration is started from the end of the
events, err := n.chain.PastNewWalletRegisteredEvents(nil)
if err != nil {
logger.Errorf(
"failed to get past new wallet registered events: [%v]",
err,
)
return
}

targetWallets := make([][20]byte, 0)

for i := len(events) - 1; i >= 0; i-- {
walletPubKeyHash := events[i].WalletPublicKeyHash
if walletPubKeyHash == sourceWalletPublicKeyHash {
// Just in case make sure not to include the source wallet
// itself.
continue
}
wallet, err := n.chain.GetWallet(walletPubKeyHash)
if err != nil {
logger.Errorf(
"failed to get wallet data for wallet with PKH [0x%x]: [%v]",
walletPubKeyHash,
err,
)
continue
}
if wallet.State == StateLive {
targetWallets = append(targetWallets, walletPubKeyHash)
}
if len(targetWallets) == int(targetWalletsCount) {
// Stop the iteration if enough live wallets have been gathered.
break
}
}

if len(targetWallets) != int(targetWalletsCount) {
logger.Errorf(
"failed to get enough target wallets: required [%v]; "+
"gathered [%v]",
targetWalletsCount,
len(targetWallets),
)
return
}

// Sort the target wallets according to their numerical representation
// as the on-chain contract expects.
sort.Slice(targetWallets, func(i, j int) bool {
bigIntI := new(big.Int).SetBytes(targetWallets[i][:])
bigIntJ := new(big.Int).SetBytes(targetWallets[j][:])
return bigIntI.Cmp(bigIntJ) < 0
})

logger.Infof("gathered [%v] target wallets", len(targetWallets))

walletMemberIDs := make([]uint32, 0)
for _, operatorAddress := range sourceWallet.signingGroupOperators {
operatorID, err := n.getSigningGroupOperatorID(operatorAddress)
if err != nil {
logger.Errorf(
"failed to get operator ID belonging to wallet with "+
"PKH [0x%x]: [%v]",
sourceWalletPublicKeyHash,
err,
)
return
}
walletMemberIDs = append(walletMemberIDs, operatorID)
}

latestBlockHeight, err := n.btcChain.GetLatestBlockHeight()
if err != nil {
logger.Errorf(
"failed to get latest Bitcoin block height: [%v]",
err,
)
return
}

// Use the latest Bitcoin block height to determine the wallet member
// index. Increase the result of the modulo operation by one since the
// wallet member index must be within range [1, len(walletMemberIDs)].
walletMemberIndex := (int(latestBlockHeight) % len(walletMemberIDs)) + 1

err = n.chain.SubmitMovingFundsCommitment(
sourceWalletPublicKeyHash,
*walletMainUtxo,
walletMemberIDs,
uint32(walletMemberIndex),
targetWallets,
)
if err != nil {
logger.Errorf(
"failed to submit moving funds commitment for wallet wit PKH "+
"[0x%x]: [%v]",
sourceWalletPublicKeyHash,
err,
)
return
}

logger.Infof(
"finished moving funds commitment for wallet with PKH [0x%x]",
sourceWalletPublicKeyHash,
)

// TODO: Add construction of the move funds Bitcoin transaction.
// Before proceeding with the Bitcoin transaction, check if the
// commitment was successfully submitted.
}()
}

// coordinationLayerSettings represents settings for the coordination layer.
type coordinationLayerSettings struct {
// executeCoordinationProcedureFn is a function executing the coordination
Expand Down
Loading

0 comments on commit 590e84d

Please sign in to comment.