Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Retrieve wallet states concurrently in redemption service #828

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 83 additions & 69 deletions typescript/src/services/redemptions/redemptions-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
RedemptionRequest,
TBTCContracts,
Wallet,
WalletState,
} from "../../lib/contracts"
import {
Expand Down Expand Up @@ -190,105 +191,118 @@ export class RedemptionsService {

let walletData:
| {
walletPublicKey: Hex
mainUtxo: BitcoinUtxo
}
walletPublicKey: Hex
mainUtxo: BitcoinUtxo
}
| undefined = undefined
let maxAmount = BigNumber.from(0)
let liveWalletsCounter = 0

const bitcoinNetwork = await this.bitcoinClient.getNetwork()

for (const wallet of wallets) {
const { walletPublicKeyHash } = wallet
const { state, walletPublicKey, pendingRedemptionsValue } =
await this.tbtcContracts.bridge.wallets(walletPublicKeyHash)
const bridgeWalletsByWalletPublicKeyHash: Promise<Wallet>[] = []
for (let index = 0; index < wallets.length; index++) {
const { walletPublicKeyHash } = wallets[index]
bridgeWalletsByWalletPublicKeyHash.push(
this.tbtcContracts.bridge.wallets(walletPublicKeyHash)
)
}

let walletPublicKeyHashIndex = 0

// Wallet must be in Live state.
if (state !== WalletState.Live || !walletPublicKey) {
console.debug(
`Wallet is not in Live state ` +
// Using Promise.all, we retrieve all wallet states at once,
// significantly improving overall performance.
return Promise.all(bridgeWalletsByWalletPublicKeyHash).then(async (bridgeWallets) => {
let liveWalletsCounter = 0
for (const { state, walletPublicKey, pendingRedemptionsValue } of bridgeWallets) {
const { walletPublicKeyHash } = wallets[walletPublicKeyHashIndex]
walletPublicKeyHashIndex++

// Wallet must be in Live state.
if (state !== WalletState.Live || !walletPublicKey) {
console.debug(
`Wallet is not in Live state ` +
`(wallet public key hash: ${walletPublicKeyHash.toString()}). ` +
`Continue the loop execution to the next wallet...`
)
continue
}
liveWalletsCounter++
)
continue
}
liveWalletsCounter++

// Wallet must have a main UTXO that can be determined.
const mainUtxo = await this.determineWalletMainUtxo(
walletPublicKeyHash,
bitcoinNetwork
)
if (!mainUtxo) {
console.debug(
`Could not find matching UTXO on chains ` +
// Wallet must have a main UTXO that can be determined.
const mainUtxo = await this.determineWalletMainUtxo(
walletPublicKeyHash,
bitcoinNetwork
)
if (!mainUtxo) {
console.debug(
`Could not find matching UTXO on chains ` +
`for wallet public key hash (${walletPublicKeyHash.toString()}). ` +
`Continue the loop execution to the next wallet...`
)
continue
}
)
continue
}

const pendingRedemption =
await this.tbtcContracts.bridge.pendingRedemptions(
walletPublicKey,
redeemerOutputScript
)
const pendingRedemption =
await this.tbtcContracts.bridge.pendingRedemptions(
walletPublicKey,
redeemerOutputScript
)

if (pendingRedemption.requestedAt != 0) {
console.debug(
`There is a pending redemption request from this wallet to the ` +
if (pendingRedemption.requestedAt != 0) {
console.debug(
`There is a pending redemption request from this wallet to the ` +
`same Bitcoin address. Given wallet public key hash` +
`(${walletPublicKeyHash.toString()}) and redeemer output script ` +
`(${redeemerOutputScript.toString()}) pair can be used for only one ` +
`pending request at the same time. ` +
`Continue the loop execution to the next wallet...`
)
continue
}
)
continue
}

const walletBTCBalance = mainUtxo.value.sub(pendingRedemptionsValue)
const walletBTCBalance = mainUtxo.value.sub(pendingRedemptionsValue)

// Save the max possible redemption amount.
maxAmount = walletBTCBalance.gt(maxAmount) ? walletBTCBalance : maxAmount
// Save the max possible redemption amount.
maxAmount = walletBTCBalance.gt(maxAmount) ? walletBTCBalance : maxAmount

if (walletBTCBalance.gte(amount)) {
walletData = {
walletPublicKey,
mainUtxo,
}
if (walletBTCBalance.gte(amount)) {
walletData = {
walletPublicKey,
mainUtxo,
}

break
}
break
}

console.debug(
`The wallet (${walletPublicKeyHash.toString()})` +
console.debug(
`The wallet (${walletPublicKeyHash.toString()})` +
`cannot handle the redemption request. ` +
`Continue the loop execution to the next wallet...`
)
}
)
}

if (liveWalletsCounter === 0) {
throw new Error("Currently, there are no live wallets in the network.")
}
if (liveWalletsCounter === 0) {
throw new Error("Currently, there are no live wallets in the network.")
}

// Cover a corner case when the user requested redemption for all live wallets
// in the network using the same Bitcoin address.
if (!walletData && liveWalletsCounter > 0 && maxAmount.eq(0)) {
throw new Error(
"All live wallets in the network have the pending redemption for a given Bitcoin address. " +
// Cover a corner case when the user requested redemption for all live wallets
// in the network using the same Bitcoin address.
if (!walletData && liveWalletsCounter > 0 && maxAmount.eq(0)) {
throw new Error(
"All live wallets in the network have the pending redemption for a given Bitcoin address. " +
"Please use another Bitcoin address."
)
}
)
}

if (!walletData)
throw new Error(
`Could not find a wallet with enough funds. Maximum redemption amount is ${maxAmount} Satoshi ( ${maxAmount.div(
BigNumber.from(1e8)
)} BTC ) .`
)
if (!walletData)
throw new Error(
`Could not find a wallet with enough funds. Maximum redemption amount is ${maxAmount} Satoshi ( ${maxAmount.div(
BigNumber.from(1e8)
)} BTC ) .`
)

return walletData
return walletData
})
}

/**
Expand Down