Skip to content

Commit b9ca373

Browse files
authored
Merge pull request #1242 from permaweb/jfrain99/is-wallet-fix
fix(mu): isWallet four steps
2 parents df67567 + 4a99e03 commit b9ca373

File tree

5 files changed

+148
-88
lines changed

5 files changed

+148
-88
lines changed

servers/cu/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ There are a few environment variables that you can set. Besides
149149
to be retrieved from the dryrun cache.
150150
- `DRY_RUN_PROCESS_CACHE_TTL`: the TTL of the dryrun process memory cache.
151151
If a eval stream is more nonces behind than this limit, a 503 is returned.
152+
- `SU_ROUTER_URL`: the SU router url to default to when checking if target is a wallet
153+
- `HB_ROUTER_URL`: the HB router url to default to when checking if target is a wallet
154+
- `ENABLE_HB_WALLET_CHECK`: whether to enable HB wallet check when checking if target is a wallet
152155

153156
## Tests
154157

servers/cu/src/domain/model.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,19 @@ export const domainConfigSchema = z.object({
262262
/**
263263
* The TTL of the dry run process cache in milliseconds.
264264
*/
265-
DRY_RUN_PROCESS_CACHE_TTL: positiveIntSchema
265+
DRY_RUN_PROCESS_CACHE_TTL: positiveIntSchema,
266+
/**
267+
* The SU router url to default to when checking if target is a wallet
268+
*/
269+
SU_ROUTER_URL: z.string(),
270+
/**
271+
* The HB router url to default to when checking if target is a wallet
272+
*/
273+
HB_ROUTER_URL: z.string(),
274+
/**
275+
* Whether to enable HB wallet check when checking if target is a wallet
276+
*/
277+
ENABLE_HB_WALLET_CHECK: z.boolean()
266278
})
267279

268280
export const bufferSchema = z.any().refine(buffer => {

servers/mu/src/config.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ export const domainConfigSchema = z.object({
8282
CUSTOM_CU_MAP_FILE_PATH: z.string(),
8383
IP_WALLET_RATE_LIMIT: positiveIntSchema,
8484
IP_WALLET_RATE_LIMIT_INTERVAL: positiveIntSchema,
85-
STALE_CURSOR_RANGE: positiveIntSchema
85+
STALE_CURSOR_RANGE: positiveIntSchema,
86+
SU_ROUTER_URL: z.string(),
87+
HB_ROUTER_URL: z.string(),
88+
ENABLE_HB_WALLET_CHECK: z.boolean()
8689
})
8790

8891
/**
@@ -141,7 +144,10 @@ const CONFIG_ENVS = {
141144
CUSTOM_CU_MAP_FILE_PATH: process.env.CUSTOM_CU_MAP_FILE_PATH || 'custom-cu-map.json',
142145
IP_WALLET_RATE_LIMIT: process.env.IP_WALLET_RATE_LIMIT || 2000,
143146
IP_WALLET_RATE_LIMIT_INTERVAL: process.env.IP_WALLET_RATE_LIMIT_INTERVAL || 1000 * 60 * 60,
144-
STALE_CURSOR_RANGE: process.env.STALE_CURSOR_RANGE || 1 * 24 * 60 * 60 * 1000
147+
STALE_CURSOR_RANGE: process.env.STALE_CURSOR_RANGE || 1 * 24 * 60 * 60 * 1000,
148+
SU_ROUTER_URL: process.env.SU_ROUTER_URL || 'https://su-router.ao-testnet.xyz',
149+
HB_ROUTER_URL: process.env.HB_ROUTER_URL || 'https://forward.computer',
150+
ENABLE_HB_WALLET_CHECK: process.env.ENABLE_HB_WALLET_CHECK !== 'false'
145151
},
146152
production: {
147153
MODE,
@@ -177,7 +183,10 @@ const CONFIG_ENVS = {
177183
CUSTOM_CU_MAP_FILE_PATH: process.env.CUSTOM_CU_MAP_FILE_PATH || 'custom-cu-map.json',
178184
IP_WALLET_RATE_LIMIT: process.env.IP_WALLET_RATE_LIMIT || 2000,
179185
IP_WALLET_RATE_LIMIT_INTERVAL: process.env.IP_WALLET_RATE_LIMIT_INTERVAL || 1000 * 60 * 60,
180-
STALE_CURSOR_RANGE: process.env.STALE_CURSOR_RANGE || 1 * 24 * 60 * 60 * 1000
186+
STALE_CURSOR_RANGE: process.env.STALE_CURSOR_RANGE || 1 * 24 * 60 * 60 * 1000,
187+
SU_ROUTER_URL: process.env.SU_ROUTER_URL || 'https://su-router.ao-testnet.xyz',
188+
HB_ROUTER_URL: process.env.HB_ROUTER_URL || 'https://forward.computer',
189+
ENABLE_HB_WALLET_CHECK: process.env.ENABLE_HB_WALLET_CHECK !== 'false'
181190
}
182191
}
183192

servers/mu/src/domain/clients/gateway.js

Lines changed: 112 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ function isWalletWith ({
66
histogram,
77
ARWEAVE_URL,
88
GRAPHQL_URL,
9+
SU_ROUTER_URL,
10+
HB_ROUTER_URL,
11+
ENABLE_HB_WALLET_CHECK,
912
logger,
1013
setById,
1114
getById
@@ -22,7 +25,7 @@ function isWalletWith ({
2225
/**
2326
* @name isWallet
2427
* Given an id, check if it is a process or a wallet.
25-
* First, check the cache. Then, check Arweave.
28+
* 4-step process: 1. Check SU router, 2. Check HyperBeam, 3. Check Arweave, 4. Check GraphQL
2629
*
2730
* @param id - The id to check if it is a process or a wallet
2831
* @param logId - The logId to aggregate the logs by
@@ -38,98 +41,125 @@ function isWalletWith ({
3841
return cachedIsWallet.isWallet
3942
}
4043

41-
logger({ log: `id: ${id} not cached checking arweave for tx`, logId })
44+
logger({ log: `id: ${id} not cached, starting 4-step check`, logId })
4245

43-
/*
44-
Only if this is actually a tx will this
45-
return true. That means if it doesn't its
46-
either a wallet or something else.
47-
*/
48-
return backoff(
49-
() =>
50-
walletFetch(joinUrl({ url: ARWEAVE_URL, path: `/${id}` }), { method: 'HEAD' })
51-
.then(okRes),
52-
{
53-
maxRetries: 4,
54-
delay: 500,
55-
log: logger,
56-
logId,
57-
name: `isWallet(${id})`
46+
// Step 1: Check SU router
47+
try {
48+
logger({ log: `Step 1: Checking SU router for process ${id}`, logId })
49+
const suResponse = await walletFetch(`${SU_ROUTER_URL}/processes/${id}`)
50+
if (suResponse.ok) {
51+
logger({ log: `Found process in SU router for ${id}`, logId })
52+
return setById(id, { isWallet: false }).then(() => false)
5853
}
59-
)
60-
.then((res) => {
61-
return setById(id, { isWallet: !res.ok }).then(() => {
62-
return !res.ok
63-
})
64-
})
65-
.catch((_err) => {
66-
logger({ log: `Arweave HEAD request failed for ${id}, trying GraphQL fallback`, logId })
54+
} catch (err) {
55+
logger({ log: `Step 1: SU router check failed for ${id}: ${err.message}`, logId })
56+
}
57+
58+
// Step 2: Check HyperBeam
59+
if (ENABLE_HB_WALLET_CHECK) {
60+
try {
61+
logger({ log: `Step 2: Checking HyperBeam for process ${id}`, logId })
62+
const hyperbeamResponse = await walletFetch(`${HB_ROUTER_URL}/${id}[email protected]/info/[email protected]`)
63+
if (hyperbeamResponse.status === 200) {
64+
logger({ log: `Found process in HyperBeam for ${id}`, logId })
65+
return setById(id, { isWallet: false }).then(() => false)
66+
}
67+
} catch (err) {
68+
logger({ log: `Step 2: HyperBeam check failed for ${id}: ${err.message}`, logId })
69+
}
70+
} else {
71+
logger({ log: `Step 2: Skipping HyperBeam check for ${id}`, logId })
72+
}
73+
74+
// Step 3: Check Arweave
75+
try {
76+
logger({ log: `Step 3: Checking Arweave for ${id}`, logId })
77+
const arweaveResponse = await backoff(
78+
() =>
79+
walletFetch(joinUrl({ url: ARWEAVE_URL, path: `/${id}` }), { method: 'HEAD' })
80+
.then(okRes),
81+
{
82+
maxRetries: 4,
83+
delay: 500,
84+
log: logger,
85+
logId,
86+
name: `isWallet(${id})`
87+
}
88+
)
89+
90+
if (arweaveResponse.ok) {
91+
logger({ log: `Found transaction in Arweave for ${id}`, logId })
92+
return setById(id, { isWallet: false }).then(() => false)
93+
}
94+
} catch (err) {
95+
logger({ log: `Step 3: Arweave check failed for ${id}: ${err.message}`, logId })
96+
}
6797

68-
const query = `
69-
query GetTransactionByID($id: ID!) {
70-
transactions(
71-
tags: [
72-
{name:"Data-Protocol", values: ["ao"]},
73-
{name:"Type", values: ["Process"]}
74-
]
75-
first: 100
76-
sort:HEIGHT_DESC
77-
ids:[$id]
78-
) {
79-
edges {
80-
cursor
81-
node {
82-
id
83-
data {
84-
size
85-
}
86-
owner{
87-
address
88-
}
89-
tags {
90-
name
91-
value
92-
}
93-
bundledIn {
94-
id
95-
}
96-
signature
97-
recipient
98+
// Step 4: Check GraphQL
99+
try {
100+
logger({ log: `Step 4: Checking GraphQL for ${id}`, logId })
101+
102+
const query = `
103+
query GetTransactionByID($id: ID!) {
104+
transactions(
105+
tags: [
106+
{name:"Data-Protocol", values: ["ao"]},
107+
{name:"Type", values: ["Process"]}
108+
]
109+
first: 100
110+
sort:HEIGHT_DESC
111+
ids:[$id]
112+
) {
113+
edges {
114+
cursor
115+
node {
116+
id
117+
data {
118+
size
119+
}
120+
owner{
121+
address
122+
}
123+
tags {
124+
name
125+
value
98126
}
127+
bundledIn {
128+
id
129+
}
130+
signature
131+
recipient
99132
}
100133
}
101134
}
102-
`
135+
}
136+
`
103137

104-
return fetch(GRAPHQL_URL, {
105-
method: 'POST',
106-
headers: { 'Content-Type': 'application/json' },
107-
body: JSON.stringify({
108-
query,
109-
variables: { id }
110-
})
138+
const gqlResponse = await fetch(GRAPHQL_URL, {
139+
method: 'POST',
140+
headers: { 'Content-Type': 'application/json' },
141+
body: JSON.stringify({
142+
query,
143+
variables: { id }
111144
})
112-
.then(response => {
113-
if (!response.ok) {
114-
throw new Error(`GraphQL request failed: ${response.statusText}`)
115-
}
116-
return response.json()
117-
})
118-
.then(result => {
119-
const hasProcessTransaction = result.data?.transactions?.edges?.length > 0
120-
const isWallet = !hasProcessTransaction
145+
})
121146

122-
logger({ log: `GraphQL result for ${id}: ${hasProcessTransaction ? 'found process' : 'no process found'}, isWallet: ${isWallet}`, logId })
147+
if (gqlResponse.ok) {
148+
const result = await gqlResponse.json()
149+
const hasProcessTransaction = result.data?.transactions?.edges?.length > 0
123150

124-
return setById(id, { isWallet }).then(() => isWallet)
125-
})
126-
.catch(gqlError => {
127-
logger({ log: `GraphQL fallback also failed for ${id}, defaulting to wallet: ${gqlError.message}`, logId })
128-
return setById(id, { isWallet: true }).then(() => {
129-
return true
130-
})
131-
})
132-
})
151+
if (hasProcessTransaction) {
152+
logger({ log: `Found process in GraphQL for ${id}`, logId })
153+
return setById(id, { isWallet: false }).then(() => false)
154+
}
155+
}
156+
} catch (err) {
157+
logger({ log: `Step 4: GraphQL check failed for ${id}: ${err.message}`, logId })
158+
}
159+
160+
// If no process found in any step, it's a wallet
161+
logger({ log: `No process found in any step for ${id}, treating as wallet`, logId })
162+
return setById(id, { isWallet: true }).then(() => true)
133163
}
134164
}
135165

servers/mu/src/domain/index.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ export const createApis = async (ctx) => {
9494
const SPAWN_PUSH_ENABLED = ctx.SPAWN_PUSH_ENABLED
9595
const ALLOW_PUSHES_AFTER = ctx.ALLOW_PUSHES_AFTER
9696
const STALE_CURSOR_RANGE = ctx.STALE_CURSOR_RANGE
97+
const SU_ROUTER_URL = ctx.SU_ROUTER_URL
98+
const HB_ROUTER_URL = ctx.HB_ROUTER_URL
99+
const ENABLE_HB_WALLET_CHECK = ctx.ENABLE_HB_WALLET_CHECK
97100

98101
const logger = ctx.logger
99102
const fetch = ctx.fetch
@@ -231,7 +234,7 @@ export const createApis = async (ctx) => {
231234
fetchHyperBeamResult: cuClient.fetchHyperBeamResultWith({ fetch, HB_URL, histogram, logger: sendDataItemLogger }),
232235
fetchSchedulerProcess: schedulerClient.fetchSchedulerProcessWith({ getByProcess, setByProcess, fetch, histogram, logger: sendDataItemLogger }),
233236
crank,
234-
isWallet: gatewayClient.isWalletWith({ fetch, histogram, ARWEAVE_URL, GRAPHQL_URL, logger: sendDataItemLogger }),
237+
isWallet: gatewayClient.isWalletWith({ fetch, histogram, ARWEAVE_URL, GRAPHQL_URL, SU_ROUTER_URL, HB_ROUTER_URL, ENABLE_HB_WALLET_CHECK, logger: sendDataItemLogger }),
235238
isHyperBeamProcess: gatewayClient.isHyperBeamProcessWith({ fetch, GRAPHQL_URL, logger: sendDataItemLogger, getIsHyperBeamProcess, setIsHyperBeamProcess }),
236239
logger: sendDataItemLogger,
237240
writeDataItemArweave: uploaderClient.uploadDataItemWith({ UPLOADER_URL, logger: sendDataItemLogger, fetch, histogram }),
@@ -384,6 +387,9 @@ export const createResultApis = async (ctx) => {
384387
const ARWEAVE_URL = ctx.ARWEAVE_URL
385388
const SPAWN_PUSH_ENABLED = ctx.SPAWN_PUSH_ENABLED
386389
const RELAY_MAP = ctx.RELAY_MAP
390+
const SU_ROUTER_URL = ctx.SU_ROUTER_URL
391+
const HB_ROUTER_URL = ctx.HB_ROUTER_URL
392+
const ENABLE_HB_WALLET_CHECK = ctx.ENABLE_HB_WALLET_CHECK
387393

388394
const logger = ctx.logger
389395
const fetch = ctx.fetch
@@ -423,7 +429,7 @@ export const createResultApis = async (ctx) => {
423429
buildAndSign: signerClient.buildAndSignWith({ MU_WALLET, logger: processMsgLogger }),
424430
fetchResult: cuClient.resultWith({ fetch: fetchWithCache, histogram, CU_URL, logger: processMsgLogger }),
425431
fetchHyperBeamResult: cuClient.fetchHyperBeamResultWith({ fetch, HB_URL, histogram, logger: processMsgLogger }),
426-
isWallet: gatewayClient.isWalletWith({ fetch, histogram, ARWEAVE_URL, GRAPHQL_URL, logger: processMsgLogger, setById, getById }),
432+
isWallet: gatewayClient.isWalletWith({ fetch, histogram, ARWEAVE_URL, GRAPHQL_URL, SU_ROUTER_URL, HB_ROUTER_URL, ENABLE_HB_WALLET_CHECK, logger: processMsgLogger, setById, getById }),
427433
writeDataItemArweave: uploaderClient.uploadDataItemWith({ UPLOADER_URL, logger: processMsgLogger, fetch, histogram }),
428434
isHyperBeamProcess: gatewayClient.isHyperBeamProcessWith({ fetch, GRAPHQL_URL, logger: processMsgLogger, getIsHyperBeamProcess, setIsHyperBeamProcess }),
429435
RELAY_MAP,

0 commit comments

Comments
 (0)