@0xsequence/sessions
Advanced tools
Comparing version 0.0.0-20230307215941 to 0.0.0-20230309013947
@@ -346,5 +346,6 @@ 'use strict'; | ||
if (isPlainNode(node)) { | ||
const [left, right] = await Promise.all([_this.loadTopology(node.left), _this.loadTopology(node.right)]); | ||
return { | ||
left: await _this.loadTopology(node.left), | ||
right: await _this.loadTopology(node.right) | ||
left, | ||
right | ||
}; | ||
@@ -1253,12 +1254,3 @@ } | ||
async function allSafe(promises, fallback) { | ||
const results = []; | ||
for (const p of promises) { | ||
try { | ||
results.push(await p); | ||
} catch (_unused) { | ||
// Ignore | ||
results.push(fallback); | ||
} | ||
} | ||
return results; | ||
return Promise.all(promises.map(promise => promise.catch(() => fallback))); | ||
} | ||
@@ -1448,7 +1440,18 @@ class MultipleTracker { | ||
var _this = this; | ||
const configs = new Map(); | ||
const configOf = imageHash => { | ||
if (!configs.has(imageHash)) { | ||
configs.set(imageHash, this.configOfImageHash({ | ||
imageHash | ||
})); | ||
} | ||
return configs.get(imageHash); | ||
}; | ||
// We need to check both, and return the one with the highest checkpoint | ||
// eventually we could try to combine them, but for now we'll just return | ||
// the one with the highest checkpoint | ||
const results = await Promise.all([this.tracker.loadPresignedConfiguration(args), this.cache.loadPresignedConfiguration(args)]); | ||
const checkpoints = await Promise.all(results.map(async function (r) { | ||
const results = [this.tracker.loadPresignedConfiguration(args), this.cache.loadPresignedConfiguration(args)]; | ||
const checkpoints = await Promise.all(results.map(async function (result) { | ||
const r = await result; | ||
const last = r[r.length - 1]; | ||
@@ -1458,5 +1461,3 @@ if (!last) return undefined; | ||
// TODO: This will fire a lot of requests, optimize it | ||
const config = await _this.configOfImageHash({ | ||
imageHash: last.nextImageHash | ||
}); | ||
const config = await configOf(last.nextImageHash); | ||
if (!config) return undefined; | ||
@@ -1475,13 +1476,4 @@ return { | ||
if (!best) return []; | ||
const configs = new Map(); | ||
const config = imageHash => { | ||
if (!configs.has(imageHash)) { | ||
configs.set(imageHash, this.configOfImageHash({ | ||
imageHash | ||
})); | ||
} | ||
return configs.get(imageHash); | ||
}; | ||
best.result.forEach(async function (res) { | ||
const nextConfig = await config(res.nextImageHash); | ||
const nextConfig = await configOf(res.nextImageHash); | ||
if (nextConfig) { | ||
@@ -1571,2 +1563,38 @@ _this.savePresignedConfiguration({ | ||
class PromiseCache { | ||
constructor() { | ||
this.cache = void 0; | ||
this.cache = new Map(); | ||
} | ||
do(key, validMilliseconds, task, ...args) { | ||
key = `${key}:${ethers.ethers.utils.keccak256(ethers.ethers.utils.toUtf8Bytes(JSON.stringify(args)))}`; | ||
let entry = this.cache.get(key); | ||
if (entry) { | ||
if (entry.expiration) { | ||
if (new Date() >= entry.expiration) { | ||
entry = undefined; | ||
this.cache.delete(key); | ||
} | ||
} | ||
} | ||
if (!entry) { | ||
entry = { | ||
promise: Promise.reject('unreachable') | ||
}; | ||
if (validMilliseconds === undefined) { | ||
entry.promise = task(...args); | ||
} else { | ||
entry.promise = task(...args).then(result => { | ||
if (entry) { | ||
entry.expiration = new Date(Date.now() + validMilliseconds); | ||
} | ||
return result; | ||
}); | ||
} | ||
this.cache.set(key, entry); | ||
} | ||
return entry.promise; | ||
} | ||
} | ||
// This tracks wraps another tracker and dedupes calls to it, so in any calls | ||
@@ -1580,62 +1608,33 @@ // are sent in short succession, only the first call is forwarded to the | ||
this.verbose = verbose; | ||
this.pending = new Map(); | ||
this.cache = new PromiseCache(); | ||
} | ||
async dedupe(key, fn, ...args) { | ||
this.clear(); | ||
// TODO: Replace with a faster hash function | ||
const subkey = `${key}:${ethers.ethers.utils.keccak256(ethers.ethers.utils.toUtf8Bytes(JSON.stringify(args)))}`; | ||
const now = Date.now(); | ||
const pending = this.pending.get(subkey); | ||
if (pending && now - pending.time < this.window) { | ||
if (this.verbose) { | ||
console.log(`dedupe hit: ${subkey} -> found (${pending.time})`); | ||
} | ||
return pending.promise; | ||
} | ||
const promise = fn(...args); | ||
this.pending.set(subkey, { | ||
promise, | ||
time: now | ||
}); | ||
return promise; | ||
} | ||
clear() { | ||
// remove all pending calls past the window | ||
const now = Date.now(); | ||
for (const [key, pending] of this.pending) { | ||
if (now - pending.time > this.window) { | ||
this.pending.delete(key); | ||
} | ||
} | ||
} | ||
configOfImageHash(args) { | ||
return this.dedupe('configOfImageHash', args => this.tracker.configOfImageHash(args), args); | ||
return this.cache.do('configOfImageHash', this.window, args => this.tracker.configOfImageHash(args), args); | ||
} | ||
getMigration(address, fromImageHash, fromVersion, chainId) { | ||
return this.dedupe('getMigration', (...args) => this.tracker.getMigration(...args), address, fromImageHash, fromVersion, chainId); | ||
return this.cache.do('getMigration', this.window, (...args) => this.tracker.getMigration(...args), address, fromImageHash, fromVersion, chainId); | ||
} | ||
saveMigration(address, signed, contexts) { | ||
return this.dedupe('saveMigration', (...args) => this.tracker.saveMigration(...args), address, signed, contexts); | ||
return this.cache.do('saveMigration', undefined, (...args) => this.tracker.saveMigration(...args), address, signed, contexts); | ||
} | ||
loadPresignedConfiguration(args) { | ||
return this.dedupe('loadPresignedConfiguration', args => this.tracker.loadPresignedConfiguration(args), args); | ||
return this.cache.do('loadPresignedConfiguration', this.window, args => this.tracker.loadPresignedConfiguration(args), args); | ||
} | ||
savePresignedConfiguration(args) { | ||
return this.dedupe('savePresignedConfiguration', args => this.tracker.savePresignedConfiguration(args), args); | ||
return this.cache.do('savePresignedConfiguration', undefined, args => this.tracker.savePresignedConfiguration(args), args); | ||
} | ||
saveWitnesses(args) { | ||
return this.dedupe('saveWitnesses', args => this.tracker.saveWitnesses(args), args); | ||
return this.cache.do('saveWitnesses', undefined, args => this.tracker.saveWitnesses(args), args); | ||
} | ||
saveWalletConfig(args) { | ||
return this.dedupe('saveWalletConfig', args => this.tracker.saveWalletConfig(args), args); | ||
return this.cache.do('saveWalletConfig', undefined, args => this.tracker.saveWalletConfig(args), args); | ||
} | ||
imageHashOfCounterfactualWallet(args) { | ||
return this.dedupe('imageHashOfCounterfactualWallet', args => this.tracker.imageHashOfCounterfactualWallet(args), args); | ||
return this.cache.do('imageHashOfCounterfactualWallet', undefined, args => this.tracker.imageHashOfCounterfactualWallet(args), args); | ||
} | ||
saveCounterfactualWallet(args) { | ||
return this.dedupe('saveCounterfactualWallet', args => this.tracker.saveCounterfactualWallet(args), args); | ||
return this.cache.do('saveCounterfactualWallet', undefined, args => this.tracker.saveCounterfactualWallet(args), args); | ||
} | ||
walletsOfSigner(args) { | ||
return this.dedupe('walletsOfSigner', args => this.tracker.walletsOfSigner(args), args); | ||
return this.cache.do('walletsOfSigner', this.window, args => this.tracker.walletsOfSigner(args), args); | ||
} | ||
@@ -1642,0 +1641,0 @@ } |
@@ -346,5 +346,6 @@ 'use strict'; | ||
if (isPlainNode(node)) { | ||
const [left, right] = await Promise.all([_this.loadTopology(node.left), _this.loadTopology(node.right)]); | ||
return { | ||
left: await _this.loadTopology(node.left), | ||
right: await _this.loadTopology(node.right) | ||
left, | ||
right | ||
}; | ||
@@ -1253,12 +1254,3 @@ } | ||
async function allSafe(promises, fallback) { | ||
const results = []; | ||
for (const p of promises) { | ||
try { | ||
results.push(await p); | ||
} catch (_unused) { | ||
// Ignore | ||
results.push(fallback); | ||
} | ||
} | ||
return results; | ||
return Promise.all(promises.map(promise => promise.catch(() => fallback))); | ||
} | ||
@@ -1448,7 +1440,18 @@ class MultipleTracker { | ||
var _this = this; | ||
const configs = new Map(); | ||
const configOf = imageHash => { | ||
if (!configs.has(imageHash)) { | ||
configs.set(imageHash, this.configOfImageHash({ | ||
imageHash | ||
})); | ||
} | ||
return configs.get(imageHash); | ||
}; | ||
// We need to check both, and return the one with the highest checkpoint | ||
// eventually we could try to combine them, but for now we'll just return | ||
// the one with the highest checkpoint | ||
const results = await Promise.all([this.tracker.loadPresignedConfiguration(args), this.cache.loadPresignedConfiguration(args)]); | ||
const checkpoints = await Promise.all(results.map(async function (r) { | ||
const results = [this.tracker.loadPresignedConfiguration(args), this.cache.loadPresignedConfiguration(args)]; | ||
const checkpoints = await Promise.all(results.map(async function (result) { | ||
const r = await result; | ||
const last = r[r.length - 1]; | ||
@@ -1458,5 +1461,3 @@ if (!last) return undefined; | ||
// TODO: This will fire a lot of requests, optimize it | ||
const config = await _this.configOfImageHash({ | ||
imageHash: last.nextImageHash | ||
}); | ||
const config = await configOf(last.nextImageHash); | ||
if (!config) return undefined; | ||
@@ -1475,13 +1476,4 @@ return { | ||
if (!best) return []; | ||
const configs = new Map(); | ||
const config = imageHash => { | ||
if (!configs.has(imageHash)) { | ||
configs.set(imageHash, this.configOfImageHash({ | ||
imageHash | ||
})); | ||
} | ||
return configs.get(imageHash); | ||
}; | ||
best.result.forEach(async function (res) { | ||
const nextConfig = await config(res.nextImageHash); | ||
const nextConfig = await configOf(res.nextImageHash); | ||
if (nextConfig) { | ||
@@ -1571,2 +1563,38 @@ _this.savePresignedConfiguration({ | ||
class PromiseCache { | ||
constructor() { | ||
this.cache = void 0; | ||
this.cache = new Map(); | ||
} | ||
do(key, validMilliseconds, task, ...args) { | ||
key = `${key}:${ethers.ethers.utils.keccak256(ethers.ethers.utils.toUtf8Bytes(JSON.stringify(args)))}`; | ||
let entry = this.cache.get(key); | ||
if (entry) { | ||
if (entry.expiration) { | ||
if (new Date() >= entry.expiration) { | ||
entry = undefined; | ||
this.cache.delete(key); | ||
} | ||
} | ||
} | ||
if (!entry) { | ||
entry = { | ||
promise: Promise.reject('unreachable') | ||
}; | ||
if (validMilliseconds === undefined) { | ||
entry.promise = task(...args); | ||
} else { | ||
entry.promise = task(...args).then(result => { | ||
if (entry) { | ||
entry.expiration = new Date(Date.now() + validMilliseconds); | ||
} | ||
return result; | ||
}); | ||
} | ||
this.cache.set(key, entry); | ||
} | ||
return entry.promise; | ||
} | ||
} | ||
// This tracks wraps another tracker and dedupes calls to it, so in any calls | ||
@@ -1580,62 +1608,33 @@ // are sent in short succession, only the first call is forwarded to the | ||
this.verbose = verbose; | ||
this.pending = new Map(); | ||
this.cache = new PromiseCache(); | ||
} | ||
async dedupe(key, fn, ...args) { | ||
this.clear(); | ||
// TODO: Replace with a faster hash function | ||
const subkey = `${key}:${ethers.ethers.utils.keccak256(ethers.ethers.utils.toUtf8Bytes(JSON.stringify(args)))}`; | ||
const now = Date.now(); | ||
const pending = this.pending.get(subkey); | ||
if (pending && now - pending.time < this.window) { | ||
if (this.verbose) { | ||
console.log(`dedupe hit: ${subkey} -> found (${pending.time})`); | ||
} | ||
return pending.promise; | ||
} | ||
const promise = fn(...args); | ||
this.pending.set(subkey, { | ||
promise, | ||
time: now | ||
}); | ||
return promise; | ||
} | ||
clear() { | ||
// remove all pending calls past the window | ||
const now = Date.now(); | ||
for (const [key, pending] of this.pending) { | ||
if (now - pending.time > this.window) { | ||
this.pending.delete(key); | ||
} | ||
} | ||
} | ||
configOfImageHash(args) { | ||
return this.dedupe('configOfImageHash', args => this.tracker.configOfImageHash(args), args); | ||
return this.cache.do('configOfImageHash', this.window, args => this.tracker.configOfImageHash(args), args); | ||
} | ||
getMigration(address, fromImageHash, fromVersion, chainId) { | ||
return this.dedupe('getMigration', (...args) => this.tracker.getMigration(...args), address, fromImageHash, fromVersion, chainId); | ||
return this.cache.do('getMigration', this.window, (...args) => this.tracker.getMigration(...args), address, fromImageHash, fromVersion, chainId); | ||
} | ||
saveMigration(address, signed, contexts) { | ||
return this.dedupe('saveMigration', (...args) => this.tracker.saveMigration(...args), address, signed, contexts); | ||
return this.cache.do('saveMigration', undefined, (...args) => this.tracker.saveMigration(...args), address, signed, contexts); | ||
} | ||
loadPresignedConfiguration(args) { | ||
return this.dedupe('loadPresignedConfiguration', args => this.tracker.loadPresignedConfiguration(args), args); | ||
return this.cache.do('loadPresignedConfiguration', this.window, args => this.tracker.loadPresignedConfiguration(args), args); | ||
} | ||
savePresignedConfiguration(args) { | ||
return this.dedupe('savePresignedConfiguration', args => this.tracker.savePresignedConfiguration(args), args); | ||
return this.cache.do('savePresignedConfiguration', undefined, args => this.tracker.savePresignedConfiguration(args), args); | ||
} | ||
saveWitnesses(args) { | ||
return this.dedupe('saveWitnesses', args => this.tracker.saveWitnesses(args), args); | ||
return this.cache.do('saveWitnesses', undefined, args => this.tracker.saveWitnesses(args), args); | ||
} | ||
saveWalletConfig(args) { | ||
return this.dedupe('saveWalletConfig', args => this.tracker.saveWalletConfig(args), args); | ||
return this.cache.do('saveWalletConfig', undefined, args => this.tracker.saveWalletConfig(args), args); | ||
} | ||
imageHashOfCounterfactualWallet(args) { | ||
return this.dedupe('imageHashOfCounterfactualWallet', args => this.tracker.imageHashOfCounterfactualWallet(args), args); | ||
return this.cache.do('imageHashOfCounterfactualWallet', undefined, args => this.tracker.imageHashOfCounterfactualWallet(args), args); | ||
} | ||
saveCounterfactualWallet(args) { | ||
return this.dedupe('saveCounterfactualWallet', args => this.tracker.saveCounterfactualWallet(args), args); | ||
return this.cache.do('saveCounterfactualWallet', undefined, args => this.tracker.saveCounterfactualWallet(args), args); | ||
} | ||
walletsOfSigner(args) { | ||
return this.dedupe('walletsOfSigner', args => this.tracker.walletsOfSigner(args), args); | ||
return this.cache.do('walletsOfSigner', this.window, args => this.tracker.walletsOfSigner(args), args); | ||
} | ||
@@ -1642,0 +1641,0 @@ } |
@@ -342,5 +342,6 @@ import { v2, v1, universal, commons } from '@0xsequence/core'; | ||
if (isPlainNode(node)) { | ||
const [left, right] = await Promise.all([_this.loadTopology(node.left), _this.loadTopology(node.right)]); | ||
return { | ||
left: await _this.loadTopology(node.left), | ||
right: await _this.loadTopology(node.right) | ||
left, | ||
right | ||
}; | ||
@@ -1249,12 +1250,3 @@ } | ||
async function allSafe(promises, fallback) { | ||
const results = []; | ||
for (const p of promises) { | ||
try { | ||
results.push(await p); | ||
} catch (_unused) { | ||
// Ignore | ||
results.push(fallback); | ||
} | ||
} | ||
return results; | ||
return Promise.all(promises.map(promise => promise.catch(() => fallback))); | ||
} | ||
@@ -1444,7 +1436,18 @@ class MultipleTracker { | ||
var _this = this; | ||
const configs = new Map(); | ||
const configOf = imageHash => { | ||
if (!configs.has(imageHash)) { | ||
configs.set(imageHash, this.configOfImageHash({ | ||
imageHash | ||
})); | ||
} | ||
return configs.get(imageHash); | ||
}; | ||
// We need to check both, and return the one with the highest checkpoint | ||
// eventually we could try to combine them, but for now we'll just return | ||
// the one with the highest checkpoint | ||
const results = await Promise.all([this.tracker.loadPresignedConfiguration(args), this.cache.loadPresignedConfiguration(args)]); | ||
const checkpoints = await Promise.all(results.map(async function (r) { | ||
const results = [this.tracker.loadPresignedConfiguration(args), this.cache.loadPresignedConfiguration(args)]; | ||
const checkpoints = await Promise.all(results.map(async function (result) { | ||
const r = await result; | ||
const last = r[r.length - 1]; | ||
@@ -1454,5 +1457,3 @@ if (!last) return undefined; | ||
// TODO: This will fire a lot of requests, optimize it | ||
const config = await _this.configOfImageHash({ | ||
imageHash: last.nextImageHash | ||
}); | ||
const config = await configOf(last.nextImageHash); | ||
if (!config) return undefined; | ||
@@ -1471,13 +1472,4 @@ return { | ||
if (!best) return []; | ||
const configs = new Map(); | ||
const config = imageHash => { | ||
if (!configs.has(imageHash)) { | ||
configs.set(imageHash, this.configOfImageHash({ | ||
imageHash | ||
})); | ||
} | ||
return configs.get(imageHash); | ||
}; | ||
best.result.forEach(async function (res) { | ||
const nextConfig = await config(res.nextImageHash); | ||
const nextConfig = await configOf(res.nextImageHash); | ||
if (nextConfig) { | ||
@@ -1567,2 +1559,38 @@ _this.savePresignedConfiguration({ | ||
class PromiseCache { | ||
constructor() { | ||
this.cache = void 0; | ||
this.cache = new Map(); | ||
} | ||
do(key, validMilliseconds, task, ...args) { | ||
key = `${key}:${ethers.utils.keccak256(ethers.utils.toUtf8Bytes(JSON.stringify(args)))}`; | ||
let entry = this.cache.get(key); | ||
if (entry) { | ||
if (entry.expiration) { | ||
if (new Date() >= entry.expiration) { | ||
entry = undefined; | ||
this.cache.delete(key); | ||
} | ||
} | ||
} | ||
if (!entry) { | ||
entry = { | ||
promise: Promise.reject('unreachable') | ||
}; | ||
if (validMilliseconds === undefined) { | ||
entry.promise = task(...args); | ||
} else { | ||
entry.promise = task(...args).then(result => { | ||
if (entry) { | ||
entry.expiration = new Date(Date.now() + validMilliseconds); | ||
} | ||
return result; | ||
}); | ||
} | ||
this.cache.set(key, entry); | ||
} | ||
return entry.promise; | ||
} | ||
} | ||
// This tracks wraps another tracker and dedupes calls to it, so in any calls | ||
@@ -1576,62 +1604,33 @@ // are sent in short succession, only the first call is forwarded to the | ||
this.verbose = verbose; | ||
this.pending = new Map(); | ||
this.cache = new PromiseCache(); | ||
} | ||
async dedupe(key, fn, ...args) { | ||
this.clear(); | ||
// TODO: Replace with a faster hash function | ||
const subkey = `${key}:${ethers.utils.keccak256(ethers.utils.toUtf8Bytes(JSON.stringify(args)))}`; | ||
const now = Date.now(); | ||
const pending = this.pending.get(subkey); | ||
if (pending && now - pending.time < this.window) { | ||
if (this.verbose) { | ||
console.log(`dedupe hit: ${subkey} -> found (${pending.time})`); | ||
} | ||
return pending.promise; | ||
} | ||
const promise = fn(...args); | ||
this.pending.set(subkey, { | ||
promise, | ||
time: now | ||
}); | ||
return promise; | ||
} | ||
clear() { | ||
// remove all pending calls past the window | ||
const now = Date.now(); | ||
for (const [key, pending] of this.pending) { | ||
if (now - pending.time > this.window) { | ||
this.pending.delete(key); | ||
} | ||
} | ||
} | ||
configOfImageHash(args) { | ||
return this.dedupe('configOfImageHash', args => this.tracker.configOfImageHash(args), args); | ||
return this.cache.do('configOfImageHash', this.window, args => this.tracker.configOfImageHash(args), args); | ||
} | ||
getMigration(address, fromImageHash, fromVersion, chainId) { | ||
return this.dedupe('getMigration', (...args) => this.tracker.getMigration(...args), address, fromImageHash, fromVersion, chainId); | ||
return this.cache.do('getMigration', this.window, (...args) => this.tracker.getMigration(...args), address, fromImageHash, fromVersion, chainId); | ||
} | ||
saveMigration(address, signed, contexts) { | ||
return this.dedupe('saveMigration', (...args) => this.tracker.saveMigration(...args), address, signed, contexts); | ||
return this.cache.do('saveMigration', undefined, (...args) => this.tracker.saveMigration(...args), address, signed, contexts); | ||
} | ||
loadPresignedConfiguration(args) { | ||
return this.dedupe('loadPresignedConfiguration', args => this.tracker.loadPresignedConfiguration(args), args); | ||
return this.cache.do('loadPresignedConfiguration', this.window, args => this.tracker.loadPresignedConfiguration(args), args); | ||
} | ||
savePresignedConfiguration(args) { | ||
return this.dedupe('savePresignedConfiguration', args => this.tracker.savePresignedConfiguration(args), args); | ||
return this.cache.do('savePresignedConfiguration', undefined, args => this.tracker.savePresignedConfiguration(args), args); | ||
} | ||
saveWitnesses(args) { | ||
return this.dedupe('saveWitnesses', args => this.tracker.saveWitnesses(args), args); | ||
return this.cache.do('saveWitnesses', undefined, args => this.tracker.saveWitnesses(args), args); | ||
} | ||
saveWalletConfig(args) { | ||
return this.dedupe('saveWalletConfig', args => this.tracker.saveWalletConfig(args), args); | ||
return this.cache.do('saveWalletConfig', undefined, args => this.tracker.saveWalletConfig(args), args); | ||
} | ||
imageHashOfCounterfactualWallet(args) { | ||
return this.dedupe('imageHashOfCounterfactualWallet', args => this.tracker.imageHashOfCounterfactualWallet(args), args); | ||
return this.cache.do('imageHashOfCounterfactualWallet', undefined, args => this.tracker.imageHashOfCounterfactualWallet(args), args); | ||
} | ||
saveCounterfactualWallet(args) { | ||
return this.dedupe('saveCounterfactualWallet', args => this.tracker.saveCounterfactualWallet(args), args); | ||
return this.cache.do('saveCounterfactualWallet', undefined, args => this.tracker.saveCounterfactualWallet(args), args); | ||
} | ||
walletsOfSigner(args) { | ||
return this.dedupe('walletsOfSigner', args => this.tracker.walletsOfSigner(args), args); | ||
return this.cache.do('walletsOfSigner', this.window, args => this.tracker.walletsOfSigner(args), args); | ||
} | ||
@@ -1638,0 +1637,0 @@ } |
@@ -9,6 +9,4 @@ import { commons } from "@0xsequence/core"; | ||
verbose: boolean; | ||
private readonly pending; | ||
private readonly cache; | ||
constructor(tracker: migrator.PresignedMigrationTracker & ConfigTracker, window?: number, verbose?: boolean); | ||
dedupe<T, Y extends Array<any>>(key: string, fn: (...args: Y) => Promise<T>, ...args: Y): Promise<T>; | ||
clear(): void; | ||
configOfImageHash(args: { | ||
@@ -15,0 +13,0 @@ imageHash: string; |
{ | ||
"name": "@0xsequence/sessions", | ||
"version": "0.0.0-20230307215941", | ||
"version": "0.0.0-20230309013947", | ||
"description": "tools for migrating sequence wallets to new versions", | ||
@@ -12,5 +12,5 @@ "repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/sessions", | ||
"dependencies": { | ||
"@0xsequence/core": "0.0.0-20230307215941", | ||
"@0xsequence/migration": "0.0.0-20230307215941", | ||
"@0xsequence/replacer": "0.0.0-20230307215941", | ||
"@0xsequence/core": "0.0.0-20230309013947", | ||
"@0xsequence/migration": "0.0.0-20230309013947", | ||
"@0xsequence/replacer": "0.0.0-20230309013947", | ||
"ethers": "^5.5.2", | ||
@@ -20,4 +20,4 @@ "idb": "^7.1.1" | ||
"devDependencies": { | ||
"@0xsequence/signhub": "0.0.0-20230307215941", | ||
"@0xsequence/tests": "0.0.0-20230307215941", | ||
"@0xsequence/signhub": "0.0.0-20230309013947", | ||
"@0xsequence/tests": "0.0.0-20230309013947", | ||
"@istanbuljs/nyc-config-typescript": "^1.0.2", | ||
@@ -24,0 +24,0 @@ "fake-indexeddb": "^4.0.1", |
@@ -15,7 +15,16 @@ | ||
async loadPresignedConfiguration(args: { wallet: string; fromImageHash: string; longestPath?: boolean | undefined }): Promise<PresignedConfigLink[]> { | ||
const configs = new Map<string, Promise<commons.config.Config | undefined>>() | ||
const configOf = (imageHash: string): Promise<commons.config.Config | undefined> => { | ||
if (!configs.has(imageHash)) { | ||
configs.set(imageHash, this.configOfImageHash({ imageHash })) | ||
} | ||
return configs.get(imageHash)! | ||
} | ||
// We need to check both, and return the one with the highest checkpoint | ||
// eventually we could try to combine them, but for now we'll just return | ||
// the one with the highest checkpoint | ||
const results = await Promise.all([this.tracker.loadPresignedConfiguration(args), this.cache.loadPresignedConfiguration(args)]) | ||
const checkpoints = await Promise.all(results.map(async (r) => { | ||
const results = [this.tracker.loadPresignedConfiguration(args), this.cache.loadPresignedConfiguration(args)] | ||
const checkpoints = await Promise.all(results.map(async result => { | ||
const r = await result | ||
const last = r[r.length - 1] | ||
@@ -25,3 +34,3 @@ if (!last) return undefined | ||
// TODO: This will fire a lot of requests, optimize it | ||
const config = await this.configOfImageHash({ imageHash: last.nextImageHash }) | ||
const config = await configOf(last.nextImageHash) | ||
if (!config) return undefined | ||
@@ -41,11 +50,4 @@ | ||
const configs = new Map<string, Promise<commons.config.Config | undefined>>() | ||
const config = (imageHash: string): Promise<commons.config.Config | undefined> => { | ||
if (!configs.has(imageHash)) { | ||
configs.set(imageHash, this.configOfImageHash({ imageHash })) | ||
} | ||
return configs.get(imageHash)! | ||
} | ||
best.result.forEach(async res => { | ||
const nextConfig = await config(res.nextImageHash) | ||
const nextConfig = await configOf(res.nextImageHash) | ||
if (nextConfig) { | ||
@@ -52,0 +54,0 @@ this.savePresignedConfiguration({ |
import { commons } from "@0xsequence/core" | ||
import { migrator } from "@0xsequence/migration"; | ||
import { BigNumber, BigNumberish, ethers } from "ethers"; | ||
import { BigNumber, BigNumberish } from "ethers"; | ||
import { ConfigTracker, PresignedConfig, PresignedConfigLink } from "../tracker"; | ||
import { PromiseCache } from "./promise-cache"; | ||
@@ -10,3 +11,3 @@ // This tracks wraps another tracker and dedupes calls to it, so in any calls | ||
export class DedupedTracker implements migrator.PresignedMigrationTracker, ConfigTracker { | ||
private readonly pending: Map<string, { promise: Promise<any>, time: number }> = new Map(); | ||
private readonly cache: PromiseCache = new PromiseCache(); | ||
@@ -19,71 +20,41 @@ constructor( | ||
async dedupe<T, Y extends Array<any>>(key: string, fn: (...args: Y) => Promise<T>, ...args: Y): Promise<T> { | ||
this.clear() | ||
// TODO: Replace with a faster hash function | ||
const subkey = `${key}:${ethers.utils.keccak256(ethers.utils.toUtf8Bytes(JSON.stringify(args)))}` | ||
const now = Date.now() | ||
const pending = this.pending.get(subkey) | ||
if (pending && now - pending.time < this.window) { | ||
if (this.verbose) { | ||
console.log(`dedupe hit: ${subkey} -> found (${pending.time})`) | ||
} | ||
return pending.promise as Promise<T> | ||
} | ||
const promise = fn(...args) | ||
this.pending.set(subkey, { promise, time: now }) | ||
return promise | ||
} | ||
clear() { | ||
// remove all pending calls past the window | ||
const now = Date.now() | ||
for (const [key, pending] of this.pending) { | ||
if (now - pending.time > this.window) { | ||
this.pending.delete(key) | ||
} | ||
} | ||
} | ||
configOfImageHash(args: { imageHash: string; }): Promise<commons.config.Config | undefined> { | ||
return this.dedupe('configOfImageHash', (args) => this.tracker.configOfImageHash(args), args) | ||
return this.cache.do('configOfImageHash', this.window, args => this.tracker.configOfImageHash(args), args) | ||
} | ||
getMigration(address: string, fromImageHash: string, fromVersion: number, chainId: BigNumberish): Promise<migrator.SignedMigration | undefined> { | ||
return this.dedupe('getMigration', (...args) => this.tracker.getMigration(...args), address, fromImageHash, fromVersion, chainId) | ||
return this.cache.do('getMigration', this.window, (...args) => this.tracker.getMigration(...args), address, fromImageHash, fromVersion, chainId) | ||
} | ||
saveMigration(address: string, signed: migrator.SignedMigration, contexts: commons.context.VersionedContext): Promise<void> { | ||
return this.dedupe('saveMigration', (...args) => this.tracker.saveMigration(...args), address, signed, contexts) | ||
return this.cache.do('saveMigration', undefined, (...args) => this.tracker.saveMigration(...args), address, signed, contexts) | ||
} | ||
loadPresignedConfiguration(args: { wallet: string; fromImageHash: string; longestPath?: boolean | undefined; }): Promise<PresignedConfigLink[]> { | ||
return this.dedupe('loadPresignedConfiguration', (args) => this.tracker.loadPresignedConfiguration(args), args) | ||
return this.cache.do('loadPresignedConfiguration', this.window, args => this.tracker.loadPresignedConfiguration(args), args) | ||
} | ||
savePresignedConfiguration(args: PresignedConfig): Promise<void> { | ||
return this.dedupe('savePresignedConfiguration', (args) => this.tracker.savePresignedConfiguration(args), args) | ||
return this.cache.do('savePresignedConfiguration', undefined, args => this.tracker.savePresignedConfiguration(args), args) | ||
} | ||
saveWitnesses(args: { wallet: string; digest: string; chainId: BigNumberish; signatures: string[]; }): Promise<void> { | ||
return this.dedupe('saveWitnesses', (args) => this.tracker.saveWitnesses(args), args) | ||
return this.cache.do('saveWitnesses', undefined, args => this.tracker.saveWitnesses(args), args) | ||
} | ||
saveWalletConfig(args: { config: commons.config.Config; }): Promise<void> { | ||
return this.dedupe('saveWalletConfig', (args) => this.tracker.saveWalletConfig(args), args) | ||
return this.cache.do('saveWalletConfig', undefined, args => this.tracker.saveWalletConfig(args), args) | ||
} | ||
imageHashOfCounterfactualWallet(args: { wallet: string; }): Promise<{ imageHash: string; context: commons.context.WalletContext; } | undefined> { | ||
return this.dedupe('imageHashOfCounterfactualWallet', (args) => this.tracker.imageHashOfCounterfactualWallet(args), args) | ||
return this.cache.do('imageHashOfCounterfactualWallet', undefined, args => this.tracker.imageHashOfCounterfactualWallet(args), args) | ||
} | ||
saveCounterfactualWallet(args: { config: commons.config.Config; context: commons.context.WalletContext[]; }): Promise<void> { | ||
return this.dedupe('saveCounterfactualWallet', (args) => this.tracker.saveCounterfactualWallet(args), args) | ||
return this.cache.do('saveCounterfactualWallet', undefined, args => this.tracker.saveCounterfactualWallet(args), args) | ||
} | ||
walletsOfSigner(args: { signer: string; }): Promise<{ wallet: string; proof: { digest: string; chainId: BigNumber; signature: string; }; }[]> { | ||
return this.dedupe('walletsOfSigner', (args) => this.tracker.walletsOfSigner(args), args) | ||
return this.cache.do('walletsOfSigner', this.window, args => this.tracker.walletsOfSigner(args), args) | ||
} | ||
} |
@@ -27,6 +27,4 @@ import { commons, universal, v1, v2 } from '@0xsequence/core' | ||
if (isPlainNode(node)) { | ||
return { | ||
left: await this.loadTopology(node.left), | ||
right: await this.loadTopology(node.right) | ||
} | ||
const [left, right] = await Promise.all([this.loadTopology(node.left), this.loadTopology(node.right)]) | ||
return { left, right } | ||
} | ||
@@ -33,0 +31,0 @@ |
@@ -31,14 +31,3 @@ import { ConfigTracker, PresignedConfig, PresignedConfigLink } from '../tracker' | ||
export async function allSafe<T>(promises: Promise<T>[], fallback: T): Promise<T[]> { | ||
const results: T[] = [] | ||
for (const p of promises) { | ||
try { | ||
results.push(await p) | ||
} catch { | ||
// Ignore | ||
results.push(fallback) | ||
} | ||
} | ||
return results | ||
return Promise.all(promises.map(promise => promise.catch(() => fallback))) | ||
} | ||
@@ -45,0 +34,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
284750
35
7308
+ Added@0xsequence/abi@0.0.0-20230309013947(transitive)
+ Added@0xsequence/account@0.0.0-20230309013947(transitive)
+ Added@0xsequence/api@0.0.0-20230309013947(transitive)
+ Added@0xsequence/auth@0.0.0-20230309013947(transitive)
+ Added@0xsequence/core@0.0.0-20230309013947(transitive)
+ Added@0xsequence/guard@0.0.0-20230309013947(transitive)
+ Added@0xsequence/indexer@0.0.0-20230309013947(transitive)
+ Added@0xsequence/metadata@0.0.0-20230309013947(transitive)
+ Added@0xsequence/migration@0.0.0-20230309013947(transitive)
+ Added@0xsequence/network@0.0.0-20230309013947(transitive)
+ Added@0xsequence/provider@0.0.0-20230309013947(transitive)
+ Added@0xsequence/relayer@0.0.0-20230309013947(transitive)
+ Added@0xsequence/replacer@0.0.0-20230309013947(transitive)
+ Added@0xsequence/signhub@0.0.0-20230309013947(transitive)
+ Added@0xsequence/utils@0.0.0-20230309013947(transitive)
+ Added@0xsequence/wallet@0.0.0-20230309013947(transitive)
- Removed@0xsequence/abi@0.0.0-20230307215941(transitive)
- Removed@0xsequence/account@0.0.0-20230307215941(transitive)
- Removed@0xsequence/api@0.0.0-20230307215941(transitive)
- Removed@0xsequence/auth@0.0.0-20230307215941(transitive)
- Removed@0xsequence/core@0.0.0-20230307215941(transitive)
- Removed@0xsequence/guard@0.0.0-20230307215941(transitive)
- Removed@0xsequence/indexer@0.0.0-20230307215941(transitive)
- Removed@0xsequence/metadata@0.0.0-20230307215941(transitive)
- Removed@0xsequence/migration@0.0.0-20230307215941(transitive)
- Removed@0xsequence/network@0.0.0-20230307215941(transitive)
- Removed@0xsequence/provider@0.0.0-20230307215941(transitive)
- Removed@0xsequence/relayer@0.0.0-20230307215941(transitive)
- Removed@0xsequence/replacer@0.0.0-20230307215941(transitive)
- Removed@0xsequence/signhub@0.0.0-20230307215941(transitive)
- Removed@0xsequence/utils@0.0.0-20230307215941(transitive)
- Removed@0xsequence/wallet@0.0.0-20230307215941(transitive)