This commit is contained in:
Sebastian 2022-09-16 14:27:24 -03:00
parent 4b72bbd017
commit 860f10e6f0
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
12 changed files with 821 additions and 664 deletions

View File

@ -20,8 +20,6 @@ import {
TalerCryptoInterfaceR, TalerCryptoInterfaceR,
} from "../cryptoImplementation.js"; } from "../cryptoImplementation.js";
const logger = new Logger("synchronousWorker.ts"); const logger = new Logger("synchronousWorker.ts");
/** /**

View File

@ -86,7 +86,12 @@ import {
checkDbInvariant, checkDbInvariant,
checkLogicInvariant, checkLogicInvariant,
} from "../../util/invariants.js"; } from "../../util/invariants.js";
import { OperationAttemptResult, OperationAttemptResultType, RetryTags, scheduleRetryInTx } from "../../util/retries.js"; import {
OperationAttemptResult,
OperationAttemptResultType,
RetryTags,
scheduleRetryInTx,
} from "../../util/retries.js";
import { import {
checkPaymentByProposalId, checkPaymentByProposalId,
confirmPay, confirmPay,

View File

@ -505,7 +505,7 @@ export async function createDepositGroup(
return { return {
depositGroupId: depositGroupId, depositGroupId: depositGroupId,
transactionId: makeEventId(TransactionType.Deposit, depositGroupId) transactionId: makeEventId(TransactionType.Deposit, depositGroupId),
}; };
} }

View File

@ -64,7 +64,12 @@ import {
readSuccessResponseTextOrThrow, readSuccessResponseTextOrThrow,
} from "../util/http.js"; } from "../util/http.js";
import { DbAccess, GetReadOnlyAccess } from "../util/query.js"; import { DbAccess, GetReadOnlyAccess } from "../util/query.js";
import { OperationAttemptResult, OperationAttemptResultType, RetryInfo, runOperationHandlerForResult } from "../util/retries.js"; import {
OperationAttemptResult,
OperationAttemptResultType,
RetryInfo,
runOperationHandlerForResult,
} from "../util/retries.js";
import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "../versions.js"; import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "../versions.js";
import { guardOperationException } from "./common.js"; import { guardOperationException } from "./common.js";

View File

@ -97,7 +97,13 @@ import {
throwUnexpectedRequestError, throwUnexpectedRequestError,
} from "../util/http.js"; } from "../util/http.js";
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
import { OperationAttemptResult, OperationAttemptResultType, RetryInfo, RetryTags, scheduleRetry } from "../util/retries.js"; import {
OperationAttemptResult,
OperationAttemptResultType,
RetryInfo,
RetryTags,
scheduleRetry,
} from "../util/retries.js";
import { spendCoins } from "../wallet.js"; import { spendCoins } from "../wallet.js";
import { getExchangeDetails } from "./exchanges.js"; import { getExchangeDetails } from "./exchanges.js";
import { getTotalRefreshCost } from "./refresh.js"; import { getTotalRefreshCost } from "./refresh.js";

View File

@ -52,7 +52,11 @@ import {
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
import { readSuccessResponseJsonOrThrow } from "../util/http.js"; import { readSuccessResponseJsonOrThrow } from "../util/http.js";
import { GetReadWriteAccess } from "../util/query.js"; import { GetReadWriteAccess } from "../util/query.js";
import { OperationAttemptResult, RetryInfo, runOperationHandlerForResult } from "../util/retries.js"; import {
OperationAttemptResult,
RetryInfo,
runOperationHandlerForResult,
} from "../util/retries.js";
import { guardOperationException } from "./common.js"; import { guardOperationException } from "./common.js";
import { createRefreshGroup, processRefreshGroup } from "./refresh.js"; import { createRefreshGroup, processRefreshGroup } from "./refresh.js";
import { internalCreateWithdrawalGroup } from "./withdraw.js"; import { internalCreateWithdrawalGroup } from "./withdraw.js";

View File

@ -52,7 +52,10 @@ import {
readSuccessResponseJsonOrThrow, readSuccessResponseJsonOrThrow,
} from "../util/http.js"; } from "../util/http.js";
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
import { OperationAttemptResult, OperationAttemptResultType } from "../util/retries.js"; import {
OperationAttemptResult,
OperationAttemptResultType,
} from "../util/retries.js";
import { makeCoinAvailable } from "../wallet.js"; import { makeCoinAvailable } from "../wallet.js";
import { updateExchangeFromUrl } from "./exchanges.js"; import { updateExchangeFromUrl } from "./exchanges.js";
import { makeEventId } from "./transactions.js"; import { makeEventId } from "./transactions.js";
@ -362,9 +365,6 @@ export async function acceptTip(
await processTip(ws, tipId); await processTip(ws, tipId);
} }
return { return {
transactionId: makeEventId( transactionId: makeEventId(TransactionType.Tip, tipId),
TransactionType.Tip, };
tipId
)
}
} }

View File

@ -93,7 +93,10 @@ import {
} from "../util/http.js"; } from "../util/http.js";
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js"; import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
import { DbAccess, GetReadOnlyAccess } from "../util/query.js"; import { DbAccess, GetReadOnlyAccess } from "../util/query.js";
import { OperationAttemptResult, OperationAttemptResultType } from "../util/retries.js"; import {
OperationAttemptResult,
OperationAttemptResultType,
} from "../util/retries.js";
import { import {
WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
WALLET_EXCHANGE_PROTOCOL_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION,
@ -900,7 +903,8 @@ export async function updateWithdrawalDenoms(
denom.verificationStatus === DenominationVerificationStatus.Unverified denom.verificationStatus === DenominationVerificationStatus.Unverified
) { ) {
logger.trace( logger.trace(
`Validating denomination (${current + 1}/${denominations.length `Validating denomination (${current + 1}/${
denominations.length
}) signature of ${denom.denomPubHash}`, }) signature of ${denom.denomPubHash}`,
); );
let valid = false; let valid = false;
@ -1804,7 +1808,7 @@ export async function acceptWithdrawalFromUri(
transactionId: makeEventId( transactionId: makeEventId(
TransactionType.Withdrawal, TransactionType.Withdrawal,
existingWithdrawalGroup.withdrawalGroupId, existingWithdrawalGroup.withdrawalGroupId,
) ),
}; };
} }
@ -1862,10 +1866,7 @@ export async function acceptWithdrawalFromUri(
return { return {
reservePub: withdrawalGroup.reservePub, reservePub: withdrawalGroup.reservePub,
confirmTransferUrl: withdrawInfo.confirmTransferUrl, confirmTransferUrl: withdrawInfo.confirmTransferUrl,
transactionId: makeEventId( transactionId: makeEventId(TransactionType.Withdrawal, withdrawalGroupId),
TransactionType.Withdrawal,
withdrawalGroupId,
)
}; };
} }
@ -1920,9 +1921,6 @@ export async function createManualWithdrawal(
return { return {
reservePub: withdrawalGroup.reservePub, reservePub: withdrawalGroup.reservePub,
exchangePaytoUris: exchangePaytoUris, exchangePaytoUris: exchangePaytoUris,
transactionId: makeEventId( transactionId: makeEventId(TransactionType.Withdrawal, withdrawalGroupId),
TransactionType.Withdrawal,
withdrawalGroupId,
)
}; };
} }

View File

@ -20,22 +20,30 @@
*/ */
import { import {
AbsoluteTime,FeeDescription, FeeDescriptionPair, AbsoluteTime,
Amounts, DenominationInfo FeeDescription,
FeeDescriptionPair,
Amounts,
DenominationInfo,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
// import { expect } from "chai"; // import { expect } from "chai";
import { createDenominationPairTimeline, createDenominationTimeline } from "./denominations.js"; import {
createDenominationPairTimeline,
createDenominationTimeline,
} from "./denominations.js";
import test, { ExecutionContext } from "ava"; import test, { ExecutionContext } from "ava";
/** /**
* Create some constants to be used as reference in the tests * Create some constants to be used as reference in the tests
*/ */
const VALUES = Array.from({ length: 10 }).map((undef, t) => Amounts.parseOrThrow(`USD:${t}`)) const VALUES = Array.from({ length: 10 }).map((undef, t) =>
const TIMESTAMPS = Array.from({ length: 20 }).map((undef, t_s) => ({ t_s })) Amounts.parseOrThrow(`USD:${t}`),
const ABS_TIME = TIMESTAMPS.map(m => AbsoluteTime.fromTimestamp(m)) );
const TIMESTAMPS = Array.from({ length: 20 }).map((undef, t_s) => ({ t_s }));
const ABS_TIME = TIMESTAMPS.map((m) => AbsoluteTime.fromTimestamp(m));
function normalize(list: DenominationInfo[]): DenominationInfo[] { function normalize(list: DenominationInfo[]): DenominationInfo[] {
return list.map((e, idx) => ({ ...e, denomPubHash: `id${idx}` })) return list.map((e, idx) => ({ ...e, denomPubHash: `id${idx}` }));
} }
//Avoiding to make an error-prone/time-consuming refactor //Avoiding to make an error-prone/time-consuming refactor
@ -45,299 +53,352 @@ function expect(t:ExecutionContext, thing: any):any {
deep: { deep: {
equal: (another: any) => t.deepEqual(thing, another), equal: (another: any) => t.deepEqual(thing, another),
equals: (another: any) => t.deepEqual(thing, another), equals: (another: any) => t.deepEqual(thing, another),
} },
} };
} }
// describe("Denomination timeline creation", (t) => { // describe("Denomination timeline creation", (t) => {
// describe("single value example", (t) => { // describe("single value example", (t) => {
test("should have one row with start and exp", (t) => { test("should have one row with start and exp", (t) => {
const timeline = createDenominationTimeline(
const timeline = createDenominationTimeline(normalize([ normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[2], stampExpireDeposit: TIMESTAMPS[2],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
"stampExpireDeposit",
"feeDeposit",
);
expect(t,timeline).deep.equal([{ expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[2], until: ABS_TIME[2],
fee: VALUES[1], fee: VALUES[1],
} as FeeDescription]) } as FeeDescription,
]);
}); });
test("should have two rows with the second denom in the middle if second is better", (t) => { test("should have two rows with the second denom in the middle if second is better", (t) => {
const timeline = createDenominationTimeline(normalize([ const timeline = createDenominationTimeline(
normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[3], stampExpireDeposit: TIMESTAMPS[3],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
"stampExpireDeposit",
"feeDeposit",
);
expect(t,timeline).deep.equal([{ expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
fee: VALUES[1], fee: VALUES[1],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[3], from: ABS_TIME[3],
until: ABS_TIME[4], until: ABS_TIME[4],
fee: VALUES[2], fee: VALUES[2],
}] as FeeDescription[]) },
] as FeeDescription[]);
}); });
test("should have two rows with the first denom in the middle if second is worse", (t) => { test("should have two rows with the first denom in the middle if second is worse", (t) => {
const timeline = createDenominationTimeline(normalize([ const timeline = createDenominationTimeline(
normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[3], stampExpireDeposit: TIMESTAMPS[3],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
"stampExpireDeposit",
"feeDeposit",
);
expect(t,timeline).deep.equal([{ expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[2], until: ABS_TIME[2],
fee: VALUES[2], fee: VALUES[2],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[4], until: ABS_TIME[4],
fee: VALUES[1], fee: VALUES[1],
}] as FeeDescription[]) },
] as FeeDescription[]);
}); });
test("should add a gap when there no fee", (t) => { test("should add a gap when there no fee", (t) => {
const timeline = createDenominationTimeline(normalize([ const timeline = createDenominationTimeline(
normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[2], stampExpireDeposit: TIMESTAMPS[2],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[3], stampStart: TIMESTAMPS[3],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
"stampExpireDeposit",
"feeDeposit",
);
expect(t,timeline).deep.equal([{ expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[2], until: ABS_TIME[2],
fee: VALUES[2], fee: VALUES[2],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[3], until: ABS_TIME[3],
},
}, { {
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[3], from: ABS_TIME[3],
until: ABS_TIME[4], until: ABS_TIME[4],
fee: VALUES[1], fee: VALUES[1],
}] as FeeDescription[]) },
] as FeeDescription[]);
}); });
test("should have three rows when first denom is between second and second is worse", (t) => { test("should have three rows when first denom is between second and second is worse", (t) => {
const timeline = createDenominationTimeline(normalize([ const timeline = createDenominationTimeline(
normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[3], stampExpireDeposit: TIMESTAMPS[3],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
expect(t,timeline).deep.equal([{ "stampExpireDeposit",
"feeDeposit",
);
expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[2], until: ABS_TIME[2],
fee: VALUES[2], fee: VALUES[2],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[3], until: ABS_TIME[3],
fee: VALUES[1], fee: VALUES[1],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[3], from: ABS_TIME[3],
until: ABS_TIME[4], until: ABS_TIME[4],
fee: VALUES[2], fee: VALUES[2],
}] as FeeDescription[]) },
] as FeeDescription[]);
}); });
test("should have one row when first denom is between second and second is better", (t) => { test("should have one row when first denom is between second and second is better", (t) => {
const timeline = createDenominationTimeline(normalize([ const timeline = createDenominationTimeline(
normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[3], stampExpireDeposit: TIMESTAMPS[3],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
"stampExpireDeposit",
"feeDeposit",
);
expect(t,timeline).deep.equal([{ expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[4], until: ABS_TIME[4],
fee: VALUES[1], fee: VALUES[1],
}] as FeeDescription[]) },
] as FeeDescription[]);
}); });
test("should only add the best1", (t) => { test("should only add the best1", (t) => {
const timeline = createDenominationTimeline(normalize([ const timeline = createDenominationTimeline(
normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[3], stampExpireDeposit: TIMESTAMPS[3],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
"stampExpireDeposit",
"feeDeposit",
);
expect(t,timeline).deep.equal([{ expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[2], until: ABS_TIME[2],
fee: VALUES[2], fee: VALUES[2],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[4], until: ABS_TIME[4],
fee: VALUES[1], fee: VALUES[1],
}] as FeeDescription[]) },
] as FeeDescription[]);
}); });
test("should only add the best2", (t) => { test("should only add the best2", (t) => {
const timeline = createDenominationTimeline(normalize([ const timeline = createDenominationTimeline(
normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[3], stampExpireDeposit: TIMESTAMPS[3],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[5], stampExpireDeposit: TIMESTAMPS[5],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[5], stampStart: TIMESTAMPS[5],
stampExpireDeposit: TIMESTAMPS[6], stampExpireDeposit: TIMESTAMPS[6],
feeDeposit: VALUES[3] feeDeposit: VALUES[3],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
"stampExpireDeposit",
"feeDeposit",
);
expect(t,timeline).deep.equal([{ expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[2], until: ABS_TIME[2],
fee: VALUES[2], fee: VALUES[2],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[5], until: ABS_TIME[5],
fee: VALUES[1], fee: VALUES[1],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[5], from: ABS_TIME[5],
until: ABS_TIME[6], until: ABS_TIME[6],
fee: VALUES[3], fee: VALUES[3],
}] as FeeDescription[]) },
] as FeeDescription[]);
}); });
test("should only add the best3", (t) => { test("should only add the best3", (t) => {
const timeline = createDenominationTimeline(normalize([ const timeline = createDenominationTimeline(
normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[5], stampExpireDeposit: TIMESTAMPS[5],
feeDeposit: VALUES[3] feeDeposit: VALUES[3],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[5], stampExpireDeposit: TIMESTAMPS[5],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[5], stampExpireDeposit: TIMESTAMPS[5],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
"stampExpireDeposit",
"feeDeposit",
);
expect(t,timeline).deep.equal([{ expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[5], until: ABS_TIME[5],
fee: VALUES[1], fee: VALUES[1],
}] as FeeDescription[]) },
] as FeeDescription[]);
}) });
// }) // })
// describe("multiple value example", (t) => { // describe("multiple value example", (t) => {
@ -345,75 +406,87 @@ function expect(t:ExecutionContext, thing: any):any {
//TODO: test the same start but different value //TODO: test the same start but different value
test("should not merge when there is different value", (t) => { test("should not merge when there is different value", (t) => {
const timeline = createDenominationTimeline(normalize([ const timeline = createDenominationTimeline(
normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[3], stampExpireDeposit: TIMESTAMPS[3],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[2], value: VALUES[2],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
"stampExpireDeposit",
"feeDeposit",
);
expect(t,timeline).deep.equal([{ expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
fee: VALUES[1], fee: VALUES[1],
}, { },
{
value: VALUES[2], value: VALUES[2],
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[4], until: ABS_TIME[4],
fee: VALUES[2], fee: VALUES[2],
}] as FeeDescription[]) },
] as FeeDescription[]);
}); });
test("should not merge when there is different value (with duplicates)", (t) => { test("should not merge when there is different value (with duplicates)", (t) => {
const timeline = createDenominationTimeline(normalize([ const timeline = createDenominationTimeline(
normalize([
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[3], stampExpireDeposit: TIMESTAMPS[3],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[2], value: VALUES[2],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[1], value: VALUES[1],
stampStart: TIMESTAMPS[1], stampStart: TIMESTAMPS[1],
stampExpireDeposit: TIMESTAMPS[3], stampExpireDeposit: TIMESTAMPS[3],
feeDeposit: VALUES[1] feeDeposit: VALUES[1],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
{ {
value: VALUES[2], value: VALUES[2],
stampStart: TIMESTAMPS[2], stampStart: TIMESTAMPS[2],
stampExpireDeposit: TIMESTAMPS[4], stampExpireDeposit: TIMESTAMPS[4],
feeDeposit: VALUES[2] feeDeposit: VALUES[2],
} as Partial<DenominationInfo> as DenominationInfo, } as Partial<DenominationInfo> as DenominationInfo,
]), "stampExpireDeposit", "feeDeposit"); ]),
"stampExpireDeposit",
"feeDeposit",
);
expect(t,timeline).deep.equal([{ expect(t, timeline).deep.equal([
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
fee: VALUES[1], fee: VALUES[1],
}, { },
{
value: VALUES[2], value: VALUES[2],
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[4], until: ABS_TIME[4],
fee: VALUES[2], fee: VALUES[2],
}] as FeeDescription[]) },
] as FeeDescription[]);
}); });
// it.skip("real world example: bitcoin exchange", (t) => { // it.skip("real world example: bitcoin exchange", (t) => {
@ -443,142 +516,160 @@ function expect(t:ExecutionContext, thing: any):any {
// describe("single value example", (t) => { // describe("single value example", (t) => {
test("should return empty", (t) => { test("should return empty", (t) => {
const left = [] as FeeDescription[]; const left = [] as FeeDescription[];
const right = [] as FeeDescription[]; const right = [] as FeeDescription[];
const pairs = createDenominationPairTimeline(left, right) const pairs = createDenominationPairTimeline(left, right);
expect(t,pairs).deep.equals([]) expect(t, pairs).deep.equals([]);
}); });
test("should return first element", (t) => { test("should return first element", (t) => {
const left = [
const left = [{ {
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
fee: VALUES[1], fee: VALUES[1],
}] as FeeDescription[]; },
] as FeeDescription[];
const right = [] as FeeDescription[]; const right = [] as FeeDescription[];
{ {
const pairs = createDenominationPairTimeline(left, right) const pairs = createDenominationPairTimeline(left, right);
expect(t,pairs).deep.equals([{ expect(t, pairs).deep.equals([
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
value: VALUES[1], value: VALUES[1],
left: VALUES[1], left: VALUES[1],
right: undefined, right: undefined,
}] as FeeDescriptionPair[]) },
] as FeeDescriptionPair[]);
} }
{ {
const pairs = createDenominationPairTimeline(right, left) const pairs = createDenominationPairTimeline(right, left);
expect(t,pairs).deep.equals([{ expect(t, pairs).deep.equals([
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
value: VALUES[1], value: VALUES[1],
right: VALUES[1], right: VALUES[1],
left: undefined, left: undefined,
}] as FeeDescriptionPair[]) },
] as FeeDescriptionPair[]);
} }
}); });
test("should add both to the same row", (t) => { test("should add both to the same row", (t) => {
const left = [
const left = [{ {
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
fee: VALUES[1], fee: VALUES[1],
}] as FeeDescription[]; },
] as FeeDescription[];
const right = [{ const right = [
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
fee: VALUES[2], fee: VALUES[2],
}] as FeeDescription[]; },
] as FeeDescription[];
{ {
const pairs = createDenominationPairTimeline(left, right) const pairs = createDenominationPairTimeline(left, right);
expect(t,pairs).deep.equals([{ expect(t, pairs).deep.equals([
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
value: VALUES[1], value: VALUES[1],
left: VALUES[1], left: VALUES[1],
right: VALUES[2], right: VALUES[2],
}] as FeeDescriptionPair[]) },
] as FeeDescriptionPair[]);
} }
{ {
const pairs = createDenominationPairTimeline(right, left) const pairs = createDenominationPairTimeline(right, left);
expect(t,pairs).deep.equals([{ expect(t, pairs).deep.equals([
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
value: VALUES[1], value: VALUES[1],
left: VALUES[2], left: VALUES[2],
right: VALUES[1], right: VALUES[1],
}] as FeeDescriptionPair[]) },
] as FeeDescriptionPair[]);
} }
}); });
test("should repeat the first and change the second", (t) => { test("should repeat the first and change the second", (t) => {
const left = [
const left = [{ {
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[5], until: ABS_TIME[5],
fee: VALUES[1], fee: VALUES[1],
}] as FeeDescription[]; },
] as FeeDescription[];
const right = [{ const right = [
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[2], until: ABS_TIME[2],
fee: VALUES[2], fee: VALUES[2],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[3], until: ABS_TIME[3],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[3], from: ABS_TIME[3],
until: ABS_TIME[4], until: ABS_TIME[4],
fee: VALUES[3], fee: VALUES[3],
}] as FeeDescription[]; },
] as FeeDescription[];
{ {
const pairs = createDenominationPairTimeline(left, right) const pairs = createDenominationPairTimeline(left, right);
expect(t,pairs).deep.equals([{ expect(t, pairs).deep.equals([
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[2], until: ABS_TIME[2],
value: VALUES[1], value: VALUES[1],
left: VALUES[1], left: VALUES[1],
right: VALUES[2], right: VALUES[2],
}, { },
{
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[3], until: ABS_TIME[3],
value: VALUES[1], value: VALUES[1],
left: VALUES[1], left: VALUES[1],
right: undefined, right: undefined,
}, { },
{
from: ABS_TIME[3], from: ABS_TIME[3],
until: ABS_TIME[4], until: ABS_TIME[4],
value: VALUES[1], value: VALUES[1],
left: VALUES[1], left: VALUES[1],
right: VALUES[3], right: VALUES[3],
}, { },
{
from: ABS_TIME[4], from: ABS_TIME[4],
until: ABS_TIME[5], until: ABS_TIME[5],
value: VALUES[1], value: VALUES[1],
left: VALUES[1], left: VALUES[1],
right: undefined, right: undefined,
}] as FeeDescriptionPair[]) },
] as FeeDescriptionPair[]);
} }
}); });
// }) // })
@ -586,97 +677,114 @@ function expect(t:ExecutionContext, thing: any):any {
// describe("multiple value example", (t) => { // describe("multiple value example", (t) => {
test("should separate denominations of different value", (t) => { test("should separate denominations of different value", (t) => {
const left = [
const left = [{ {
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
fee: VALUES[1], fee: VALUES[1],
}] as FeeDescription[]; },
] as FeeDescription[];
const right = [{ const right = [
{
value: VALUES[2], value: VALUES[2],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
fee: VALUES[2], fee: VALUES[2],
}] as FeeDescription[]; },
] as FeeDescription[];
{ {
const pairs = createDenominationPairTimeline(left, right) const pairs = createDenominationPairTimeline(left, right);
expect(t,pairs).deep.equals([{ expect(t, pairs).deep.equals([
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
value: VALUES[1], value: VALUES[1],
left: VALUES[1], left: VALUES[1],
right: undefined, right: undefined,
}, { },
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
value: VALUES[2], value: VALUES[2],
left: undefined, left: undefined,
right: VALUES[2], right: VALUES[2],
}] as FeeDescriptionPair[]) },
] as FeeDescriptionPair[]);
} }
{ {
const pairs = createDenominationPairTimeline(right, left) const pairs = createDenominationPairTimeline(right, left);
expect(t,pairs).deep.equals([{ expect(t, pairs).deep.equals([
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
value: VALUES[1], value: VALUES[1],
left: undefined, left: undefined,
right: VALUES[1], right: VALUES[1],
}, { },
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
value: VALUES[2], value: VALUES[2],
left: VALUES[2], left: VALUES[2],
right: undefined, right: undefined,
}] as FeeDescriptionPair[]) },
] as FeeDescriptionPair[]);
} }
}); });
test("should separate denominations of different value2", (t) => { test("should separate denominations of different value2", (t) => {
const left = [
const left = [{ {
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[2], until: ABS_TIME[2],
fee: VALUES[1], fee: VALUES[1],
}, { },
{
value: VALUES[1], value: VALUES[1],
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[4], until: ABS_TIME[4],
fee: VALUES[2], fee: VALUES[2],
}] as FeeDescription[]; },
] as FeeDescription[];
const right = [{ const right = [
{
value: VALUES[2], value: VALUES[2],
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
fee: VALUES[2], fee: VALUES[2],
}] as FeeDescription[]; },
] as FeeDescription[];
{ {
const pairs = createDenominationPairTimeline(left, right) const pairs = createDenominationPairTimeline(left, right);
expect(t,pairs).deep.equals([{ expect(t, pairs).deep.equals([
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[2], until: ABS_TIME[2],
value: VALUES[1], value: VALUES[1],
left: VALUES[1], left: VALUES[1],
right: undefined, right: undefined,
}, { },
{
from: ABS_TIME[2], from: ABS_TIME[2],
until: ABS_TIME[4], until: ABS_TIME[4],
value: VALUES[1], value: VALUES[1],
left: VALUES[2], left: VALUES[2],
right: undefined, right: undefined,
}, { },
{
from: ABS_TIME[1], from: ABS_TIME[1],
until: ABS_TIME[3], until: ABS_TIME[3],
value: VALUES[2], value: VALUES[2],
left: undefined, left: undefined,
right: VALUES[2], right: VALUES[2],
}] as FeeDescriptionPair[]) },
] as FeeDescriptionPair[]);
} }
// { // {
// const pairs = createDenominationPairTimeline(right, left) // const pairs = createDenominationPairTimeline(right, left)
@ -703,10 +811,8 @@ function expect(t:ExecutionContext, thing: any):any {
// bitcoinExchanges[1].denominations.filter(d => Amounts.cmp(d.value, Amounts.parseOrThrow('BITCOINBTC:0.01048576'))), // bitcoinExchanges[1].denominations.filter(d => Amounts.cmp(d.value, Amounts.parseOrThrow('BITCOINBTC:0.01048576'))),
// "stampExpireDeposit", "feeDeposit"); // "stampExpireDeposit", "feeDeposit");
// const pairs = createDenominationPairTimeline(left, right) // const pairs = createDenominationPairTimeline(left, right)
// }) // })
// }) // })
// }) // })

View File

@ -14,7 +14,16 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { AbsoluteTime, AmountJson, Amounts, DenominationInfo, FeeDescription, FeeDescriptionPair, TalerProtocolTimestamp, TimePoint } from "@gnu-taler/taler-util"; import {
AbsoluteTime,
AmountJson,
Amounts,
DenominationInfo,
FeeDescription,
FeeDescriptionPair,
TalerProtocolTimestamp,
TimePoint,
} from "@gnu-taler/taler-util";
/** /**
* Given a list of denominations with the same value and same period of time: * Given a list of denominations with the same value and same period of time:
@ -59,7 +68,10 @@ type PropsWithReturnType<T extends object, F> = Exclude<
* @param right list denominations @type {FeeDescription} * @param right list denominations @type {FeeDescription}
* @returns list of pairs for the same time * @returns list of pairs for the same time
*/ */
export function createDenominationPairTimeline(left: FeeDescription[], right: FeeDescription[]): FeeDescriptionPair[] { export function createDenominationPairTimeline(
left: FeeDescription[],
right: FeeDescription[],
): FeeDescriptionPair[] {
//both list empty, discarded //both list empty, discarded
if (left.length === 0 && right.length === 0) return []; if (left.length === 0 && right.length === 0) return [];
@ -69,31 +81,41 @@ export function createDenominationPairTimeline(left: FeeDescription[], right: Fe
let ri = 0; let ri = 0;
while (li < left.length && ri < right.length) { while (li < left.length && ri < right.length) {
const currentValue = Amounts.cmp(left[li].value, right[ri].value) < 0 ? left[li].value : right[ri].value; const currentValue =
Amounts.cmp(left[li].value, right[ri].value) < 0
? left[li].value
: right[ri].value;
let ll = 0 //left length (until next value) let ll = 0; //left length (until next value)
while (li + ll < left.length && Amounts.cmp(left[li + ll].value, currentValue) === 0) { while (
ll++ li + ll < left.length &&
Amounts.cmp(left[li + ll].value, currentValue) === 0
) {
ll++;
} }
let rl = 0 //right length (until next value) let rl = 0; //right length (until next value)
while (ri + rl < right.length && Amounts.cmp(right[ri + rl].value, currentValue) === 0) { while (
rl++ ri + rl < right.length &&
Amounts.cmp(right[ri + rl].value, currentValue) === 0
) {
rl++;
} }
const leftIsEmpty = ll === 0 const leftIsEmpty = ll === 0;
const rightIsEmpty = rl === 0 const rightIsEmpty = rl === 0;
//check which start after, add gap so both list starts at the same time //check which start after, add gap so both list starts at the same time
// one list may be empty // one list may be empty
const leftStarts: AbsoluteTime = const leftStarts: AbsoluteTime = leftIsEmpty
leftIsEmpty ? { t_ms: "never" } : left[li].from; ? { t_ms: "never" }
const rightStarts: AbsoluteTime = : left[li].from;
rightIsEmpty ? { t_ms: "never" } : right[ri].from; const rightStarts: AbsoluteTime = rightIsEmpty
? { t_ms: "never" }
: right[ri].from;
//first time cut is the smallest time //first time cut is the smallest time
let timeCut: AbsoluteTime = leftStarts; let timeCut: AbsoluteTime = leftStarts;
if (AbsoluteTime.cmp(leftStarts, rightStarts) < 0) { if (AbsoluteTime.cmp(leftStarts, rightStarts) < 0) {
const ends = const ends = rightIsEmpty ? left[li + ll - 1].until : right[0].from;
rightIsEmpty ? left[li + ll - 1].until : right[0].from;
right.splice(ri, 0, { right.splice(ri, 0, {
from: leftStarts, from: leftStarts,
@ -102,11 +124,10 @@ export function createDenominationPairTimeline(left: FeeDescription[], right: Fe
}); });
rl++; rl++;
timeCut = leftStarts timeCut = leftStarts;
} }
if (AbsoluteTime.cmp(leftStarts, rightStarts) > 0) { if (AbsoluteTime.cmp(leftStarts, rightStarts) > 0) {
const ends = const ends = leftIsEmpty ? right[ri + rl - 1].until : left[0].from;
leftIsEmpty ? right[ri + rl - 1].until : left[0].from;
left.splice(li, 0, { left.splice(li, 0, {
from: rightStarts, from: rightStarts,
@ -115,7 +136,7 @@ export function createDenominationPairTimeline(left: FeeDescription[], right: Fe
}); });
ll++; ll++;
timeCut = rightStarts timeCut = rightStarts;
} }
//check which ends sooner, add gap so both list ends at the same time //check which ends sooner, add gap so both list ends at the same time
@ -130,7 +151,6 @@ export function createDenominationPairTimeline(left: FeeDescription[], right: Fe
value: left[0].value, value: left[0].value,
}); });
rl++; rl++;
} }
if (AbsoluteTime.cmp(leftEnds, rightEnds) < 0) { if (AbsoluteTime.cmp(leftEnds, rightEnds) < 0) {
left.splice(li + ll, 0, { left.splice(li + ll, 0, {
@ -142,15 +162,23 @@ export function createDenominationPairTimeline(left: FeeDescription[], right: Fe
} }
//now both lists are non empty and (starts,ends) at the same time //now both lists are non empty and (starts,ends) at the same time
while (li < left.length && ri < right.length && Amounts.cmp(left[li].value, right[ri].value) === 0) { while (
li < left.length &&
if (AbsoluteTime.cmp(left[li].from, timeCut) !== 0 && AbsoluteTime.cmp(right[ri].from, timeCut) !== 0) { ri < right.length &&
Amounts.cmp(left[li].value, right[ri].value) === 0
) {
if (
AbsoluteTime.cmp(left[li].from, timeCut) !== 0 &&
AbsoluteTime.cmp(right[ri].from, timeCut) !== 0
) {
// timeCut comes from the latest "until" (expiration from the previous) // timeCut comes from the latest "until" (expiration from the previous)
// and this value comes from the latest left or right // and this value comes from the latest left or right
// it should be the same as the "from" from one of the latest left or right // it should be the same as the "from" from one of the latest left or right
// otherwise it means that there is missing a gap object in the middle // otherwise it means that there is missing a gap object in the middle
// the list is not complete and the behavior is undefined // the list is not complete and the behavior is undefined
throw Error('one of the list is not completed: list[i].until !== list[i+1].from') throw Error(
"one of the list is not completed: list[i].until !== list[i+1].from",
);
} }
pairList.push({ pairList.push({
@ -172,21 +200,26 @@ export function createDenominationPairTimeline(left: FeeDescription[], right: Fe
timeCut = right[ri].until; timeCut = right[ri].until;
ri++; ri++;
} }
pairList[pairList.length - 1].until = timeCut pairList[pairList.length - 1].until = timeCut;
if (li < left.length && Amounts.cmp(left[li].value, pairList[pairList.length - 1].value) !== 0) { if (
li < left.length &&
Amounts.cmp(left[li].value, pairList[pairList.length - 1].value) !== 0
) {
//value changed, should break //value changed, should break
//this if will catch when both (left and right) change at the same time //this if will catch when both (left and right) change at the same time
//if just one side changed it will catch in the while condition //if just one side changed it will catch in the while condition
break; break;
} }
} }
} }
//one of the list left or right can still have elements //one of the list left or right can still have elements
if (li < left.length) { if (li < left.length) {
let timeCut = pairList.length > 0 && Amounts.cmp(pairList[pairList.length - 1].value, left[li].value) === 0 ? pairList[pairList.length - 1].until : left[li].from; let timeCut =
pairList.length > 0 &&
Amounts.cmp(pairList[pairList.length - 1].value, left[li].value) === 0
? pairList[pairList.length - 1].until
: left[li].from;
while (li < left.length) { while (li < left.length) {
pairList.push({ pairList.push({
left: left[li].fee, left: left[li].fee,
@ -194,13 +227,17 @@ export function createDenominationPairTimeline(left: FeeDescription[], right: Fe
from: timeCut, from: timeCut,
until: left[li].until, until: left[li].until,
value: left[li].value, value: left[li].value,
}) });
timeCut = left[li].until timeCut = left[li].until;
li++; li++;
} }
} }
if (ri < right.length) { if (ri < right.length) {
let timeCut = pairList.length > 0 && Amounts.cmp(pairList[pairList.length - 1].value, right[ri].value) === 0 ? pairList[pairList.length - 1].until : right[ri].from; let timeCut =
pairList.length > 0 &&
Amounts.cmp(pairList[pairList.length - 1].value, right[ri].value) === 0
? pairList[pairList.length - 1].until
: right[ri].from;
while (ri < right.length) { while (ri < right.length) {
pairList.push({ pairList.push({
right: right[ri].fee, right: right[ri].fee,
@ -208,12 +245,12 @@ export function createDenominationPairTimeline(left: FeeDescription[], right: Fe
from: timeCut, from: timeCut,
until: right[ri].until, until: right[ri].until,
value: right[ri].value, value: right[ri].value,
}) });
timeCut = right[ri].until timeCut = right[ri].until;
ri++; ri++;
} }
} }
return pairList return pairList;
} }
/** /**

View File

@ -243,7 +243,7 @@ export async function scheduleRetry(
return await ws.db return await ws.db
.mktx((x) => [x.operationRetries]) .mktx((x) => [x.operationRetries])
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
tx.operationRetries tx.operationRetries;
scheduleRetryInTx(ws, tx, opId, errorDetail); scheduleRetryInTx(ws, tx, opId, errorDetail);
}); });
} }

View File

@ -130,8 +130,6 @@ export class SetTimeoutTimerAPI implements TimerAPI {
after(delayMs: number, callback: () => void): TimerHandle { after(delayMs: number, callback: () => void): TimerHandle {
return new TimeoutHandle(setTimeout(callback, delayMs)); return new TimeoutHandle(setTimeout(callback, delayMs));
} }
} }
export const timer = new SetTimeoutTimerAPI(); export const timer = new SetTimeoutTimerAPI();