Skip to content

Commit

Permalink
fixup! max iteration
Browse files Browse the repository at this point in the history
  • Loading branch information
jprudent committed Feb 7, 2025
1 parent 9b4c9ce commit b2f142e
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 48 deletions.
13 changes: 13 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@
"type": "node",
"request": "attach",
"skipFiles": ["<node_internals>/**"]
},
{
"type": "node",
"request": "launch",
"name": "Debug Jest Tests",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["--runInBand", "coin-tezoz", "api/index.test.ts"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"skipFiles": ["<node_internals>/**"],
"runtimeArgs": ["--inspect-brk"],
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/dist/**/*.js"]
}
]
}
4 changes: 2 additions & 2 deletions libs/coin-modules/coin-tezos/src/api/index.integ.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ describe("Tezos Api", () => {
expect(isSenderOrReceipt).toBeTruthy();
expect(operation.block).toBeDefined();
});
}, 15000);
});

it("returns all operations", async () => {
// When
Expand All @@ -67,7 +67,7 @@ describe("Tezos Api", () => {
// Find a way to create a unique id. In Tezos, the same hash may represent different operations in case of delegation.
const checkSet = new Set(tx.map(elt => `${elt.hash}${elt.type}${elt.senders[0]}`));
expect(checkSet.size).toEqual(tx.length);
}, 15000);
});
});

describe("lastBlock", () => {
Expand Down
36 changes: 33 additions & 3 deletions libs/coin-modules/coin-tezos/src/api/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Operation } from "@ledgerhq/coin-framework/lib/api/types";
import { createApi } from "./index";

const mockGetTransactions = jest.fn();
const logicGetTransactions = jest.fn();
jest.mock("../logic", () => ({
listOperations: async () => {
return mockGetTransactions();
return logicGetTransactions();
},
}));

Expand All @@ -28,8 +29,12 @@ const api = createApi({
});

describe("get operations", () => {
afterEach(() => {
logicGetTransactions.mockClear();
});

it("operations", async () => {
mockGetTransactions.mockResolvedValue([[], ""]);
logicGetTransactions.mockResolvedValue([[], ""]);

// When
const [operations, token] = await api.listOperations("addr", { minHeight: 100 });
Expand All @@ -38,4 +43,29 @@ describe("get operations", () => {
expect(operations).toEqual([]);
expect(token).toEqual("");
});

const op: Operation = {
hash: "opHash",
address: "tz1...",
type: "transaction",
value: BigInt(1000),
fee: BigInt(100),
block: {
hash: "blockHash",
height: 123456,
time: new Date(),
},
senders: ["tz1Sender"],
recipients: ["tz1Recipient"],
date: new Date(),
transactionSequenceNumber: 1,
};

it("stops iterating after 10 iterations", async () => {
logicGetTransactions.mockResolvedValue([[op], "888"]);
const [operations, token] = await api.listOperations("addr", { minHeight: 100 });
expect(logicGetTransactions).toHaveBeenCalledTimes(10);
expect(operations.length).toBe(10);
expect(token).toEqual("888");
});
});
58 changes: 26 additions & 32 deletions libs/coin-modules/coin-tezos/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,55 +74,49 @@ type PaginationState = {
currentIteration: number;
readonly minHeight: number;
continueIterations: boolean;
nextCursor: string;
nextCursor?: string;
accumulator: Operation[];
};

async function fetchNextPage(address: string, state: PaginationState): Promise<PaginationState> {
const [operations, newNextCursor] = await listOperations(address, {
limit: state.pageSize,
token: state.nextCursor,
sort: "Ascending",
minHeight: state.minHeight,
});
const newCurrentIteration = state.currentIteration + 1;
let continueIteration = newNextCursor !== "";
if (newCurrentIteration >= state.maxIterations) {
log("coin:tezos", "(api/operations): max iterations reached", state.maxIterations);
continueIteration = false;
}
const accumulated = operations.concat(state.accumulator);
return {
...state,
continueIterations: continueIteration,
currentIteration: newCurrentIteration,
nextCursor: newNextCursor,
accumulator: accumulated,
};
}

async function operationsFromHeight(
address: string,
start: number,
): Promise<[Operation[], string]> {
async function fetchNextPage(state: PaginationState): Promise<PaginationState> {
console.log("🐊🐊🐊🐊🐊", { ...state, accumulator: state.accumulator.length });
const [operations, newNextCursor] = await listOperations(address, {
limit: state.pageSize,
token: state.nextCursor,
sort: "Ascending",
});
const newCurrentIteration = state.currentIteration + 1;
const inBoundsOperations = operations.filter(op => op.block.height >= state.minHeight);
assert(
(operations.length == 0 && newNextCursor === "") ||
(operations.length > 0 && newNextCursor !== ""),
);
const isTruncated = operations.length !== inBoundsOperations.length;
let continueIteration = !(newNextCursor === "" || isTruncated);
if (newCurrentIteration >= state.maxIterations) {
log("coin:tezos", "(api/operations): max iterations reached", state.maxIterations);
continueIteration = false;
}
const accumulated = inBoundsOperations.concat(state.accumulator);
return {
...state,
continueIterations: continueIteration,
nextCursor: newNextCursor,
accumulator: accumulated,
};
}

const firstState: PaginationState = {
pageSize: 200,
maxIterations: 10,
currentIteration: 0,
minHeight: start,
continueIterations: true,
nextCursor: "0", // this is supposed to be the lowest operation id
accumulator: [],
};

let state = await fetchNextPage(firstState);
let state = await fetchNextPage(address, firstState);
while (state.continueIterations) {
state = await fetchNextPage(state);
state = await fetchNextPage(address, state);
}
return [state.accumulator, state.nextCursor || ""];
}
Expand Down
10 changes: 6 additions & 4 deletions libs/coin-modules/coin-tezos/src/logic/listOperations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ jest.mock("../network", () => ({
},
}));

const options: { sort: "Ascending" | "Descending" } = { sort: "Ascending" };
const options: { sort: "Ascending" | "Descending"; minHeight: number } = {
sort: "Ascending",
minHeight: 0,
};

describe("listOperations", () => {
afterEach(() => {
// mockGetServerInfos.mockClear();
// mockNetworkGetTransactions.mockClear();
mockNetworkGetTransactions.mockClear();
});

it("should return no operations", async () => {
Expand Down Expand Up @@ -106,7 +108,7 @@ describe("listOperations", () => {
const op1 = { ...undelegate, level: "1" };
const op2 = { ...undelegate, level: "2" };
mockNetworkGetTransactions.mockResolvedValue([op1, op2]);
const [results, _] = await listOperations("any address", { sort: "Ascending" });
const [results, _] = await listOperations("any address", options);
expect(results.map(op => op.block.height)).toEqual(["2", "1"]);
});
});
10 changes: 8 additions & 2 deletions libs/coin-modules/coin-tezos/src/logic/listOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,20 @@ export type Operation = {
* "Descending" returns newest operation first, "Ascending" returns oldest operation first.
* It doesn't control the order of the operations in the result list:
* operations are always returned sorted in descending order (newest first).
* @param minHeight retrieve operations from a specific block height until top most (inclusive).
* @param token a token to be used for pagination
* @returns a list of operations is descending (newest first) order and a token to be used for pagination
*/
export async function listOperations(
address: string,
{ token, limit, sort }: { limit?: number; token?: string; sort: "Ascending" | "Descending" },
{
token,
limit,
sort,
minHeight,
}: { limit?: number; token?: string; sort: "Ascending" | "Descending"; minHeight: number },
): Promise<[Operation[], string]> {
let options: AccountsGetOperationsOptions = { limit, sort };
let options: AccountsGetOperationsOptions = { limit, sort, "level.ge": minHeight };
if (token) {
options = { ...options, lastId: JSON.parse(token) };
}
Expand Down
2 changes: 2 additions & 0 deletions libs/coin-modules/coin-tezos/src/network/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ export type AccountsGetOperationsOptions = {
lastId?: number; // used as a pagination cursor to fetch more transactions
limit?: number;
sort?: "Descending" | "Ascending";
// the minimum height of the block the operation is in
"level.ge": number;
};

export type APIOperation =
Expand Down
9 changes: 4 additions & 5 deletions libs/coin-modules/coin-tezos/src/network/tzkt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import URL from "url";
import { log } from "@ledgerhq/logs";
import network from "@ledgerhq/live-network";
import coinConfig from "../config";
import { APIAccount, APIBlock, APIOperation, AccountsGetOperationsOptions as GetAccountOperationsOptions } from "./types";
import { APIAccount, APIBlock, APIOperation, AccountsGetOperationsOptions } from "./types";

const getExplorerUrl = () => coinConfig.getCoinConfig().explorer.url;

Expand Down Expand Up @@ -36,7 +36,7 @@ const api = {
// https://api.tzkt.io/#operation/Accounts_GetOperations
async getAccountOperations(
address: string,
query: GetAccountOperationsOptions,
query: AccountsGetOperationsOptions,
): Promise<APIOperation[]> {
// Remove undefined from query
Object.entries(query).forEach(
Expand All @@ -52,7 +52,7 @@ const api = {
},
};


// TODO this has same purpose as api/listOperations
export const fetchAllTransactions = async (
address: string,
lastId?: number,
Expand All @@ -62,9 +62,8 @@ export const fetchAllTransactions = async (
do {
const newOps = await api.getAccountOperations(address, {
lastId,
// FIXME use object
// FIXME check if this is a number or string, documentation is unclear
sort: "Ascending",
"level.ge": 0,
});
if (newOps.length === 0) return ops;
ops = ops.concat(newOps);
Expand Down

0 comments on commit b2f142e

Please sign in to comment.