Skip to content

Commit

Permalink
sync strategy for landline logs
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewliu08 committed Sep 3, 2024
1 parent c15f994 commit affcdba
Show file tree
Hide file tree
Showing 3 changed files with 316 additions and 8 deletions.
19 changes: 11 additions & 8 deletions apps/daimo-mobile/src/sync/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { daimoChainFromId } from "@daimo/contract";
import * as SplashScreen from "expo-splash-screen";

import { getNetworkState, updateNetworkState } from "./networkState";
import { addLandlineTransfers } from "./syncLandline";
import { i18NLocale } from "../i18n";
import { getAccountManager } from "../logic/accountManager";
import { SEND_DEADLINE_SECS } from "../logic/opSender";
Expand Down Expand Up @@ -170,7 +171,7 @@ async function fetchSync(
landlineSessionURL: result.landlineSessionURL,
numLandlineAccounts: (result.landlineAccounts || []).length,
};
// console.log(`[SYNC] got history ${JSON.stringify(syncSummary)}`);
console.log(`[SYNC] got history ${JSON.stringify(syncSummary)}`);

// Validation
assert(result.address === account.address, "wrong address");
Expand Down Expand Up @@ -335,15 +336,17 @@ function addNamedAccounts(old: EAccount[], found: EAccount[]): EAccount[] {

/** Add transfers based on new Transfer event logs */
function addTransfers(
old: TransferClog[],
logs: TransferClog[]
oldLogs: TransferClog[],
newLogs: TransferClog[]
): TransferClog[] {
// Sort new logs
const { logs, remaining } = addLandlineTransfers(oldLogs, newLogs);

logs.push(...remaining);

// Sort logs
logs.sort((a, b) => {
if (a.blockNumber !== b.blockNumber) return a.blockNumber! - b.blockNumber!;
return a.logIndex! - b.logIndex!;
return a.timestamp - b.timestamp;
});

// old finalized logs + new logs
return [...old, ...logs];
return logs;
}
86 changes: 86 additions & 0 deletions apps/daimo-mobile/src/sync/syncLandline.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
TransferClog,
TransferSwapClog,
getTransferClogType,
} from "@daimo/common";

/**
* All old landline clogs should be bundled into a single clog with the most
* up-to-date offchainTransfer.
*
* Landline TransferClog sync strategy:
* 1. If an old log matches by tx hash, then it is the on-chain counterpart
* of the incoming landline clog. Keep the on-chain part of the clog and
* update the offchainTransfer to the incoming landline clog's.
* 2. If an old log matches by just the transferID, then it is a potentially
* outdated landline clog. Replace the old clog with the incoming landline clog.
*/
export function addLandlineTransfers(
oldLogs: TransferClog[],
newLogs: TransferClog[]
): {
logs: TransferClog[];
remaining: TransferClog[];
} {
// Separate new landline clogs from other clogs
const landlineLogs: TransferSwapClog[] = [];
const remainingLogs: TransferClog[] = [];
for (const log of newLogs) {
if (getTransferClogType(log) === "landline") {
landlineLogs.push(log as TransferSwapClog);
} else {
remainingLogs.push(log);
}
}

// Flag to mark which old logs have been replaced by a landline log
const replacedOldLog: boolean[] = Array(oldLogs.length).fill(false);

const updatedLandlineLogs: TransferSwapClog[] = [];
for (const landlineLog of landlineLogs) {
const matchingTransfers: TransferSwapClog[] = [];
for (let i = 0; i < oldLogs.length; i++) {
const oldLog = oldLogs[i];
if (
getTransferClogType(oldLog) !== "landline" &&
getTransferClogType(oldLog) !== "transfer"
) {
continue;
}

const oldSwapLog = oldLog as TransferSwapClog;

// All old logs which represent the same landline transfer should be replaced
if (landlineLog.txHash && oldSwapLog.txHash === landlineLog.txHash) {
matchingTransfers.push(oldSwapLog);
replacedOldLog[i] = true;
} else if (
oldSwapLog.offchainTransfer?.transferID ===
landlineLog.offchainTransfer!.transferID
) {
replacedOldLog[i] = true;
}
}

// Replace all old logs with a single updated landline log
if (matchingTransfers.length > 0) {
const updatedLog: TransferSwapClog = {
...matchingTransfers[matchingTransfers.length - 1],
offchainTransfer: landlineLog.offchainTransfer,
};
updatedLandlineLogs.push(updatedLog);
} else {
updatedLandlineLogs.push(landlineLog);
}
}

const allLogs: TransferClog[] = [];
for (let i = 0; i < oldLogs.length; i++) {
if (!replacedOldLog[i]) {
allLogs.push(oldLogs[i]);
}
}
allLogs.push(...updatedLandlineLogs);

return { logs: allLogs, remaining: remainingLogs };
}
219 changes: 219 additions & 0 deletions apps/daimo-mobile/test/sync.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import { OpStatus, TransferClog, TransferSwapClog } from "@daimo/common";

import { addLandlineTransfers } from "../src/sync/syncLandline";

describe("addLandlineTransfers", () => {
it("adds a new landline deposit clog when there are no existing clogs", () => {
const oldLogs: TransferClog[] = [];
const newLandlineClog: TransferSwapClog = {
type: "transfer",
from: "0x1985EA6E9c68E1C272d8209f3B478AC2Fdb25c87",
to: "0x6af35dF65594398726140cf1bf0339e94c7A817F",
amount: 1000000,
timestamp: 1234567890,
status: OpStatus.confirmed,
offchainTransfer: {
type: "landline",
transferType: "deposit",
status: "processing",
accountID: "asdf-asdf-asdf-asdf-asdf",
timeStart: 1234567890,
},
};

const result = addLandlineTransfers(oldLogs, [newLandlineClog]);

expect(result.logs).toEqual([newLandlineClog]);
expect(result.remaining).toEqual([]);
});

it("replaces an existing landline deposit clog with a new one", () => {
const oldLandlineClog: TransferSwapClog = {
type: "transfer",
from: "0x1985EA6E9c68E1C272d8209f3B478AC2Fdb25c87",
to: "0x6af35dF65594398726140cf1bf0339e94c7A817F",
amount: 1000000,
timestamp: 1234567890,
status: OpStatus.confirmed,
offchainTransfer: {
type: "landline",
transferType: "deposit",
status: "processing",
accountID: "asdf-asdf-asdf-asdf-asdf",
timeStart: 1234567890,
},
};

const newLandlineClog: TransferSwapClog = {
...oldLandlineClog,
status: OpStatus.failed,
offchainTransfer: {
type: "landline",
transferType: "deposit",
status: "failed",
statusMessage: "Failed to deposit",
accountID: "asdf-asdf-asdf-asdf-asdf",
timeStart: 1234567890,
timeExpected: 1234569999,
},
};

const result = addLandlineTransfers([oldLandlineClog], [newLandlineClog]);

expect(result.logs).toEqual([newLandlineClog]);
expect(result.remaining).toEqual([]);
});

it("merges a transfer clog and an old landline clog with a new landline clog", () => {
const oldTransferClog: TransferSwapClog = {
type: "transfer",
from: "0x1985EA6E9c68E1C272d8209f3B478AC2Fdb25c87",
to: "0x6af35dF65594398726140cf1bf0339e94c7A817F",
amount: 1000000,
timestamp: 1234570000,
status: OpStatus.confirmed,
txHash:
"0x1d6e083a6009de3dc3672f2dd799e52604d819c5b98e3beb77c50ec259630060",
};

const oldLandlineClog: TransferSwapClog = {
type: "transfer",
from: "0x1985EA6E9c68E1C272d8209f3B478AC2Fdb25c87",
to: "0x6af35dF65594398726140cf1bf0339e94c7A817F",
amount: 1000000,
timestamp: 1234567890,
status: OpStatus.confirmed,
offchainTransfer: {
type: "landline",
transferType: "deposit",
status: "processing",
accountID: "asdf-asdf-asdf-asdf-asdf",
timeStart: 1234567890,
},
};

const newLandlineClog: TransferSwapClog = {
...oldLandlineClog,
status: OpStatus.confirmed,
txHash:
"0x1d6e083a6009de3dc3672f2dd799e52604d819c5b98e3beb77c50ec259630060",
offchainTransfer: {
type: "landline",
transferType: "deposit",
status: "completed",
accountID: "asdf-asdf-asdf-asdf-asdf",
timeStart: 1234567890,
timeExpected: 1234569999,
timeFinish: 1234570000,
},
};

const result = addLandlineTransfers(
[oldTransferClog, oldLandlineClog],
[newLandlineClog]
);

// The on-chain part of the old transfer should be combined with the
// updated offchain part of the new landline clog
const expectedLog: TransferSwapClog = {
...oldTransferClog,
offchainTransfer: newLandlineClog.offchainTransfer,
};
expect(result.logs).toEqual([expectedLog]);
expect(result.remaining).toEqual([]);
});

it("merges a new landline withdrawal clog with an old transfer clog", () => {
const oldTransferClog: TransferSwapClog = {
type: "transfer",
from: "0x4D350d99364634e07B01a9986662787DD3755F0A",
to: "0xf8736A44a2420d856d28B9B2f8374973755CcdB5",
amount: 1230000,
timestamp: 1234567890,
status: OpStatus.confirmed,
txHash:
"0x95c66cac0607b2c076f85b935d8b4df801ecf15bcb6d9f58afc324d132e6b8b0",
};

const newLandlineClog: TransferSwapClog = {
type: "transfer",
from: "0x4D350d99364634e07B01a9986662787DD3755F0A",
to: "0xf8736A44a2420d856d28B9B2f8374973755CcdB5",
amount: 1230000,
timestamp: 1234567999,
status: OpStatus.confirmed,
txHash:
"0x95c66cac0607b2c076f85b935d8b4df801ecf15bcb6d9f58afc324d132e6b8b0",
offchainTransfer: {
type: "landline",
transferType: "withdrawal",
status: "processing",
accountID: "asdf-asdf-asdf-asdf-asdf",
timeStart: 1234567999,
},
};

const result = addLandlineTransfers([oldTransferClog], [newLandlineClog]);
console.log(result.logs);

// The on-chain part of the old transfer should be combined with the
// updated offchain part of the new landline clog
const expectedLog: TransferSwapClog = {
...oldTransferClog,
offchainTransfer: newLandlineClog.offchainTransfer,
};
expect(result.logs).toEqual([expectedLog]);
expect(result.remaining).toEqual([]);
});

it("merges a new landline withdrawal clog with an old landline clog", () => {
const oldLandlineClog: TransferSwapClog = {
type: "transfer",
from: "0x4D350d99364634e07B01a9986662787DD3755F0A",
to: "0xf8736A44a2420d856d28B9B2f8374973755CcdB5",
amount: 1230000,
timestamp: 1234567890,
status: OpStatus.confirmed,
txHash:
"0x95c66cac0607b2c076f85b935d8b4df801ecf15bcb6d9f58afc324d132e6b8b0",
offchainTransfer: {
type: "landline",
transferType: "withdrawal",
status: "processing",
accountID: "asdf-asdf-asdf-asdf-asdf",
timeStart: 1234567999,
},
};

const newLandlineClog: TransferSwapClog = {
type: "transfer",
from: "0x4D350d99364634e07B01a9986662787DD3755F0A",
to: "0xf8736A44a2420d856d28B9B2f8374973755CcdB5",
amount: 1230000,
timestamp: 1234570000,
status: OpStatus.confirmed,
txHash:
"0x95c66cac0607b2c076f85b935d8b4df801ecf15bcb6d9f58afc324d132e6b8b0",
offchainTransfer: {
type: "landline",
transferType: "withdrawal",
status: "completed",
accountID: "asdf-asdf-asdf-asdf-asdf",
timeStart: 1234567999,
timeExpected: 1234569999,
timeFinish: 1234570000,
},
};

const result = addLandlineTransfers([oldLandlineClog], [newLandlineClog]);

// The on-chain part of the old transfer should be combined with the
// updated offchain part of the new landline clog
const expectedLog: TransferSwapClog = {
...oldLandlineClog,
offchainTransfer: newLandlineClog.offchainTransfer,
};
expect(result.logs).toEqual([expectedLog]);
expect(result.remaining).toEqual([]);
});
});

0 comments on commit affcdba

Please sign in to comment.