Skip to content

Commit 778792a

Browse files
committed
sessions: add arweave owner
1 parent eebd766 commit 778792a

File tree

2 files changed

+81
-78
lines changed

2 files changed

+81
-78
lines changed

packages/sessions/src/trackers/arweave.ts

+79-75
Original file line numberDiff line numberDiff line change
@@ -4,25 +4,32 @@ import { CachedEIP5719 } from '@0xsequence/replacer'
44
import { ethers } from 'ethers'
55
import { ConfigTracker, PresignedConfig, PresignedConfigLink } from '../tracker'
66

7-
const RATE_LIMIT_RETRY_DELAY = 5 * 60 * 1000
8-
97
// depending on @0xsequence/abi breaks 0xsequence's proxy-transport-channel integration test
108
const MAIN_MODULE_ABI = [
119
'function execute((bool delegateCall, bool revertOnError, uint256 gasLimit, address target, uint256 value, bytes data)[] calldata transactions, uint256 nonce, bytes calldata signature)'
1210
]
1311

12+
export interface Options {
13+
readonly namespace?: string
14+
readonly owners?: string[]
15+
readonly eip5719Provider?: ethers.Provider
16+
readonly rateLimitRetryDelayMs?: number
17+
}
18+
19+
export const defaults = {
20+
namespace: 'Sequence-Sessions',
21+
owners: ['AZ6R2mG8zxW9q7--iZXGrBknjegHoPzmG5IG-nxvMaM'],
22+
eip5719Provider: undefined,
23+
rateLimitRetryDelayMs: 5 * 60 * 1000
24+
}
25+
1426
export class ArweaveReader implements ConfigTracker, migrator.PresignedMigrationTracker {
1527
private readonly configs: Map<string, Promise<commons.config.Config | undefined>> = new Map()
16-
1728
private readonly eip5719?: CachedEIP5719
1829

19-
constructor(
20-
readonly namespace = 'Sequence-Sessions',
21-
readonly owners?: string[],
22-
eip5719Provider?: ethers.Provider
23-
) {
24-
if (eip5719Provider) {
25-
this.eip5719 = new CachedEIP5719(eip5719Provider)
30+
constructor(readonly options: Options = defaults) {
31+
if (options.eip5719Provider) {
32+
this.eip5719 = new CachedEIP5719(options.eip5719Provider)
2633
}
2734
}
2835

@@ -42,62 +49,62 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
4249
}
4350
const fromCheckpoint = BigInt(fromConfig.checkpoint)
4451

45-
const items = Object.entries(
46-
await findItems({ Type: 'config update', Wallet: wallet }, { namespace: this.namespace, owners: this.owners })
47-
).flatMap(([id, tags]) => {
48-
try {
49-
const { Signer: signer, Subdigest: subdigest, Digest: digest, 'To-Config': toImageHash } = tags
52+
const items = Object.entries(await findItems({ Type: 'config update', Wallet: wallet }, this.options)).flatMap(
53+
([id, tags]) => {
54+
try {
55+
const { Signer: signer, Subdigest: subdigest, Digest: digest, 'To-Config': toImageHash } = tags
56+
57+
let signatureType: 'eip-712' | 'eth_sign' | 'erc-1271'
58+
switch (tags['Signature-Type']) {
59+
case 'eip-712':
60+
case 'eth_sign':
61+
case 'erc-1271':
62+
signatureType = tags['Signature-Type']
63+
break
64+
default:
65+
throw new Error(`unknown signature type ${tags['Signature-Type']}`)
66+
}
5067

51-
let signatureType: 'eip-712' | 'eth_sign' | 'erc-1271'
52-
switch (tags['Signature-Type']) {
53-
case 'eip-712':
54-
case 'eth_sign':
55-
case 'erc-1271':
56-
signatureType = tags['Signature-Type']
57-
break
58-
default:
59-
throw new Error(`unknown signature type ${tags['Signature-Type']}`)
60-
}
68+
let toCheckpoint: bigint
69+
try {
70+
toCheckpoint = BigInt(tags['To-Checkpoint'])
71+
} catch {
72+
throw new Error(`to checkpoint is not a number: ${tags['To-Checkpoint']}`)
73+
}
74+
if (toCheckpoint <= fromCheckpoint) {
75+
return []
76+
}
6177

62-
let toCheckpoint: bigint
63-
try {
64-
toCheckpoint = BigInt(tags['To-Checkpoint'])
65-
} catch {
66-
throw new Error(`to checkpoint is not a number: ${tags['To-Checkpoint']}`)
67-
}
68-
if (toCheckpoint <= fromCheckpoint) {
69-
return []
70-
}
78+
if (!ethers.isAddress(signer)) {
79+
throw new Error(`signer is not an address: ${signer}`)
80+
}
7181

72-
if (!ethers.isAddress(signer)) {
73-
throw new Error(`signer is not an address: ${signer}`)
74-
}
82+
if (!ethers.isHexString(subdigest, 32)) {
83+
throw new Error(`subdigest is not a hash: ${subdigest}`)
84+
}
7585

76-
if (!ethers.isHexString(subdigest, 32)) {
77-
throw new Error(`subdigest is not a hash: ${subdigest}`)
78-
}
86+
if (!ethers.isHexString(digest, 32)) {
87+
throw new Error(`digest is not a hash: ${digest}`)
88+
}
7989

80-
if (!ethers.isHexString(digest, 32)) {
81-
throw new Error(`digest is not a hash: ${digest}`)
82-
}
90+
let chainId: bigint
91+
try {
92+
chainId = BigInt(tags['Chain-ID'])
93+
} catch {
94+
throw new Error(`chain id is not a number: ${tags['Chain-ID']}`)
95+
}
8396

84-
let chainId: bigint
85-
try {
86-
chainId = BigInt(tags['Chain-ID'])
87-
} catch {
88-
throw new Error(`chain id is not a number: ${tags['Chain-ID']}`)
89-
}
97+
if (!ethers.isHexString(toImageHash, 32)) {
98+
throw new Error(`to config is not a hash: ${toImageHash}`)
99+
}
90100

91-
if (!ethers.isHexString(toImageHash, 32)) {
92-
throw new Error(`to config is not a hash: ${toImageHash}`)
101+
return [{ id, signatureType, signer, subdigest, digest, chainId, toImageHash, toCheckpoint }]
102+
} catch (error) {
103+
console.warn(`invalid wallet ${wallet} config update ${id}:`, error)
104+
return []
93105
}
94-
95-
return [{ id, signatureType, signer, subdigest, digest, chainId, toImageHash, toCheckpoint }]
96-
} catch (error) {
97-
console.warn(`invalid wallet ${wallet} config update ${id}:`, error)
98-
return []
99106
}
100-
})
107+
)
101108

102109
const signatures: Map<string, Map<string, (typeof items)[number]>> = new Map()
103110
let candidates: typeof items = []
@@ -166,7 +173,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
166173
nextCandidateSigners.map(async signer => {
167174
const { id, subdigest, signatureType } = nextCandidateItems.get(signer)!
168175
try {
169-
let signature = await (await fetchItem(id)).text()
176+
let signature = await (await fetchItem(id, this.options.rateLimitRetryDelayMs)).text()
170177
switch (signatureType) {
171178
case 'eip-712':
172179
signature += '01'
@@ -240,9 +247,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
240247
}
241248

242249
const config = (async (imageHash: string): Promise<commons.config.Config | undefined> => {
243-
const items = Object.entries(
244-
await findItems({ Type: 'config', Config: imageHash }, { namespace: this.namespace, owners: this.owners })
245-
).flatMap(([id, tags]) => {
250+
const items = Object.entries(await findItems({ Type: 'config', Config: imageHash }, this.options)).flatMap(([id, tags]) => {
246251
try {
247252
const version = Number(tags.Version)
248253
if (!version) {
@@ -269,7 +274,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
269274

270275
for (const { id, version } of items) {
271276
try {
272-
const config = { ...(await (await fetchItem(id)).json()), version }
277+
const config = { ...(await (await fetchItem(id, this.options.rateLimitRetryDelayMs)).json()), version }
273278
if (config.tree) {
274279
config.tree = toTopology(config.tree)
275280
}
@@ -307,9 +312,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
307312
}): Promise<{ imageHash: string; context: commons.context.WalletContext } | undefined> {
308313
const wallet = ethers.getAddress(args.wallet)
309314

310-
const items = Object.entries(
311-
await findItems({ Type: 'wallet', Wallet: wallet }, { namespace: this.namespace, owners: this.owners })
312-
).flatMap(([id, tags]) => {
315+
const items = Object.entries(await findItems({ Type: 'wallet', Wallet: wallet }, this.options)).flatMap(([id, tags]) => {
313316
try {
314317
const { 'Deploy-Config': imageHash } = tags
315318

@@ -367,7 +370,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
367370
for (const [id, tags] of Object.entries(
368371
await findItems(
369372
{ Type: ['signature', 'config update'], Signer: signer, Witness: args.allSignatures ? undefined : 'true' },
370-
{ namespace: this.namespace, owners: this.owners }
373+
this.options
371374
)
372375
)) {
373376
const { Wallet: wallet, Subdigest: subdigest, Digest: digest, 'Chain-ID': chainId } = tags
@@ -396,7 +399,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
396399
throw new Error('incorrect subdigest')
397400
}
398401

399-
const signature = fetchItem(id).then(async response => {
402+
const signature = fetchItem(id, this.options.rateLimitRetryDelayMs).then(async response => {
400403
const signature = (await response.text()) + signatureType
401404
if (this.eip5719) {
402405
try {
@@ -439,7 +442,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
439442
'From-Version': `${fromVersion}`,
440443
'From-Config': fromImageHash
441444
},
442-
{ namespace: this.namespace, owners: this.owners }
445+
this.options
443446
)
444447
).flatMap(([id, tags]) => {
445448
try {
@@ -483,7 +486,7 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
483486
const { id, toVersion, toImageHash, executor } = items[0]
484487

485488
const [data, toConfig] = await Promise.all([
486-
fetchItem(id).then(response => response.text()),
489+
fetchItem(id, this.options.rateLimitRetryDelayMs).then(response => response.text()),
487490
this.configOfImageHash({ imageHash: toImageHash })
488491
])
489492

@@ -511,10 +514,11 @@ export class ArweaveReader implements ConfigTracker, migrator.PresignedMigration
511514

512515
async function findItems(
513516
filter: { [name: string]: undefined | string | string[] },
514-
options?: { namespace?: string; owners?: string[]; pageSize?: number; maxResults?: number }
517+
options?: Options & { pageSize?: number; maxResults?: number }
515518
): Promise<{ [id: string]: { [tag: string]: string } }> {
516519
const namespace = options?.namespace
517520
const owners = options?.owners
521+
const rateLimitRetryDelayMs = options?.rateLimitRetryDelayMs ?? defaults.rateLimitRetryDelayMs
518522
const pageSize = options?.pageSize ?? 100
519523
const maxResults = options?.maxResults
520524

@@ -561,9 +565,9 @@ async function findItems(
561565
break
562566
}
563567
console.warn(
564-
`rate limited by arweave.net, trying again in ${RATE_LIMIT_RETRY_DELAY / 1000} seconds at ${new Date(Date.now() + RATE_LIMIT_RETRY_DELAY).toLocaleTimeString()}`
568+
`rate limited by arweave.net, trying again in ${rateLimitRetryDelayMs / 1000} seconds at ${new Date(Date.now() + rateLimitRetryDelayMs).toLocaleTimeString()}`
565569
)
566-
await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_RETRY_DELAY))
570+
await new Promise(resolve => setTimeout(resolve, rateLimitRetryDelayMs))
567571
}
568572

569573
const {
@@ -588,16 +592,16 @@ async function findItems(
588592
)
589593
}
590594

591-
async function fetchItem(id: string): Promise<Response> {
595+
async function fetchItem(id: string, rateLimitRetryDelayMs = defaults.rateLimitRetryDelayMs): Promise<Response> {
592596
while (true) {
593597
const response = await fetch(`https://arweave.net/${id}`, { redirect: 'follow' })
594598
if (response.status !== 429) {
595599
return response
596600
}
597601
console.warn(
598-
`rate limited by arweave.net, trying again in ${RATE_LIMIT_RETRY_DELAY / 1000} seconds at ${new Date(Date.now() + RATE_LIMIT_RETRY_DELAY).toLocaleTimeString()}`
602+
`rate limited by arweave.net, trying again in ${rateLimitRetryDelayMs / 1000} seconds at ${new Date(Date.now() + rateLimitRetryDelayMs).toLocaleTimeString()}`
599603
)
600-
await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_RETRY_DELAY))
604+
await new Promise(resolve => setTimeout(resolve, rateLimitRetryDelayMs))
601605
}
602606
}
603607

packages/sessions/tests/arweave.spec.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ class MockProvider extends ethers.AbstractProvider {
1515
}
1616

1717
describe('Arweave config reader', () => {
18-
const namespace = 'xOovxYFkIwBpEwSi'
19-
const owners = ['lJYCA4xBPJeZSgr9AF_4pHp4HVGvTOa4NYKJRoMBP5c']
20-
const arweave = new trackers.arweave.ArweaveReader(namespace, owners)
18+
const options = { namespace: 'xOovxYFkIwBpEwSi', owners: ['lJYCA4xBPJeZSgr9AF_4pHp4HVGvTOa4NYKJRoMBP5c'] }
19+
const arweave = new trackers.arweave.ArweaveReader(options)
2120
const sessions = new trackers.remote.RemoteConfigTracker('http://localhost:5555')
2221
const provider = new MockProvider()
2322

0 commit comments

Comments
 (0)