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

fix: escrow finish and cancel integration #2382

Merged
merged 3 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
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
16 changes: 5 additions & 11 deletions packages/xrpl/test/integration/transactions/escrowCancel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
type XrplIntegrationTestContext,
} from '../setup'
import {
calculateWaitTimeForTransaction,
waitForAndForceProgressLedgerTime,
generateFundedWallet,
getXRPBalance,
testTransaction,
Expand Down Expand Up @@ -40,8 +40,6 @@ describe('EscrowCancel', function () {
})
).result.ledger.close_time

const waitTimeInMs = calculateWaitTimeForTransaction(CLOSE_TIME)

const createTx: EscrowCreate = {
Account: testContext.wallet.classicAddress,
TransactionType: 'EscrowCreate',
Expand Down Expand Up @@ -86,14 +84,10 @@ describe('EscrowCancel', function () {
OfferSequence: sequence,
}

// We set the CancelAfter timer to be 3 seconds after the last ledger close_time. We need to wait this long
// before we can cancel the escrow.
const cancelAfterTimerPromise = new Promise((resolve) => {
setTimeout(resolve, waitTimeInMs)
})

// Make sure we wait long enough before canceling the escrow.
await cancelAfterTimerPromise
await waitForAndForceProgressLedgerTime(
testContext.client,
CLOSE_TIME + 3,
)

// rippled uses the close time of the previous ledger
await sendLedgerAccept(testContext.client)
Expand Down
22 changes: 8 additions & 14 deletions packages/xrpl/test/integration/transactions/escrowFinish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import {
type XrplIntegrationTestContext,
} from '../setup'
import {
calculateWaitTimeForTransaction,
generateFundedWallet,
getXRPBalance,
sendLedgerAccept,
testTransaction,
getLedgerCloseTime,
waitForAndForceProgressLedgerTime,
} from '../utils'

// how long before each test case times out
Expand All @@ -32,14 +33,7 @@ describe('EscrowFinish', function () {
const wallet1 = await generateFundedWallet(testContext.client)

// get the most recent close_time from the standalone container for cancel & finish after.
const CLOSE_TIME: number = (
await testContext.client.request({
command: 'ledger',
ledger_index: 'validated',
})
).result.ledger.close_time

const waitTimeInMs = calculateWaitTimeForTransaction(CLOSE_TIME)
const CLOSE_TIME = await getLedgerCloseTime(testContext.client)

const AMOUNT = 10000

Expand All @@ -51,10 +45,6 @@ describe('EscrowFinish', function () {
FinishAfter: CLOSE_TIME + 2,
}

const finishAfterPromise = new Promise((resolve) => {
setTimeout(resolve, waitTimeInMs)
})

await testTransaction(testContext.client, createTx, testContext.wallet)

const initialBalance = await getXRPBalance(testContext.client, wallet1)
Expand Down Expand Up @@ -83,7 +73,11 @@ describe('EscrowFinish', function () {
OfferSequence: sequence!,
}

await finishAfterPromise
// wait for the escrow to be ready to finish
await waitForAndForceProgressLedgerTime(
testContext.client,
CLOSE_TIME + 2,
)

// rippled uses the close time of the previous ledger
await sendLedgerAccept(testContext.client)
Expand Down
96 changes: 67 additions & 29 deletions packages/xrpl/test/integration/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
type SubmitResponse,
TimeoutError,
NotConnectedError,
unixTimeToRippleTime,
} from '../../src'
import { Payment, Transaction } from '../../src/models/transactions'
import { hashSignedTx } from '../../src/utils/hashes'
Expand Down Expand Up @@ -76,34 +75,6 @@ export async function ledgerAccept(
})
}

/**
* Attempt to get the time after which we can check for the escrow to be finished.
* Sometimes the ledger close_time is in the future, so we need to wait for it to catch up.
*
* @param targetTime - The target wait time, before accounting for current ledger time.
* @param minimumWaitTimeMs - The minimum wait time in milliseconds.
* @param maximumWaitTimeMs - The maximum wait time in milliseconds.
* @returns The wait time in milliseconds.
*/
export function calculateWaitTimeForTransaction(
targetTime: number,
minimumWaitTimeMs = 5000,
maximumWaitTimeMs = 20000,
): number {
const currentTimeUnixMs = Math.floor(new Date().getTime())
const currentTimeRippleSeconds = unixTimeToRippleTime(currentTimeUnixMs)
const closeTimeCurrentTimeDiffSeconds = currentTimeRippleSeconds - targetTime
const closeTimeCurrentTimeDiffMs = closeTimeCurrentTimeDiffSeconds * 1000
return Math.max(
minimumWaitTimeMs,
Math.min(
Math.abs(closeTimeCurrentTimeDiffMs) + minimumWaitTimeMs,
// Maximum wait time of 20 seconds
maximumWaitTimeMs,
),
)
}

export function subscribeDone(client: Client): void {
client.removeAllListeners()
}
Expand Down Expand Up @@ -299,3 +270,70 @@ export async function getXRPBalance(
}
return (await client.request(request)).result.account_data.Balance
}

/**
* Retrieves the close time of the ledger.
*
* @param client - The client object.
* @returns - A promise that resolves to the close time of the ledger.
*
* @example
* const closeTime = await getLedgerCloseTime(client);
* console.log(closeTime); // Output: 1626424978
*/
export async function getLedgerCloseTime(client: Client): Promise<number> {
const CLOSE_TIME: number = (
await client.request({
command: 'ledger',
ledger_index: 'validated',
})
).result.ledger.close_time

return CLOSE_TIME
}

/**
* Waits for the ledger time to reach a specific value and forces ledger progress if necessary.
*
* @param client - The client object.
* @param ledgerTime - The target ledger time.
* @param [retries=20] - The number of retries before throwing an error.
* @returns - A promise that resolves when the ledger time reaches the target value.
*
* @example
* try {
* await waitForAndForceProgressLedgerTime(client, 1626424978, 10);
* console.log('Ledger time reached.'); // Output: Ledger time reached.
* } catch (error) {
* console.error(error);
* }
*/
export async function waitForAndForceProgressLedgerTime(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is only used in the escrow tests, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes @mvadari

client: Client,
ledgerTime: number,
retries = 20,
): Promise<void> {
async function getCloseTime(): Promise<boolean> {
const CLOSE_TIME: number = await getLedgerCloseTime(client)
if (CLOSE_TIME >= ledgerTime) {
return true
}

return false
}

let retryCounter = retries || 0

while (retryCounter > 0) {
// eslint-disable-next-line no-await-in-loop -- Necessary for retries
if (await getCloseTime()) {
return
}

// eslint-disable-next-line no-await-in-loop -- Necessary for retries
await ledgerAccept(client)
retryCounter -= 1
}

throw new Error(`Ledger time not reached after ${retries} retries.`)
}