Skip to content

Commit

Permalink
Retrieve wallet states concurrently to significantly reduce redemptio…
Browse files Browse the repository at this point in the history
…n processing time
  • Loading branch information
evandrosaturnino committed Dec 7, 2024
1 parent 96bab76 commit db6dc15
Showing 1 changed file with 83 additions and 69 deletions.
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

0 comments on commit db6dc15

Please sign in to comment.