From 2a74deacbd5a111cff550d1d3c0f6ca126e7d20c Mon Sep 17 00:00:00 2001 From: Dan Anderson Date: Wed, 30 Sep 2026 01:33:48 -0400 Subject: [PATCH] Bound vault sweep classification reads to selected vaults --- apps/api/src/indexer/vault-contents.ts | 4 +++- apps/api/tests/vault-contents.test.ts | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/api/src/indexer/vault-contents.ts b/apps/api/src/indexer/vault-contents.ts index 2ba0266..51129e6 100644 --- a/apps/api/src/indexer/vault-contents.ts +++ b/apps/api/src/indexer/vault-contents.ts @@ -150,13 +150,15 @@ export async function classifyVaults(env: Env): Promise> // Outbound SWEEPS are ALSO a crack — a sweep moves EVERYTHING out of the address at once (no per-asset // row), so any outbound sweep from a funded vault empties it. Merge with sends; earliest wins. + // Keep selected vaults outermost: scanning sweeps first makes SQLite probe every selected rowid + // for each sweep. A 1,000-vault production batch read 1.5M rows instead of 3k indexed probes. const sweepRows = ( await env.CORE_DB.prepare( `WITH ${selected} SELECT source.address,sweep.block_time,destination.address destination FROM selected vault - JOIN sweeps sweep ON sweep.source_id=vault.btc_address_id + CROSS JOIN sweeps sweep INDEXED BY idx_sweeps_source ON sweep.source_id=vault.btc_address_id JOIN address_dictionary source ON source.address_id=sweep.source_id JOIN address_dictionary destination ON destination.address_id=sweep.destination_id ORDER BY sweep.source_id,sweep.block_time`, diff --git a/apps/api/tests/vault-contents.test.ts b/apps/api/tests/vault-contents.test.ts index 7ea2a56..e544332 100644 --- a/apps/api/tests/vault-contents.test.ts +++ b/apps/api/tests/vault-contents.test.ts @@ -56,6 +56,7 @@ test("vault classification derives compact identities, contents, and crack recip WHERE destination_address_id IS NOT NULL; CREATE TABLE balances(address_id INTEGER,asset_id INTEGER,quantity TEXT,quantity_normalized TEXT); CREATE TABLE sweeps(source_id INTEGER,destination_id INTEGER,block_time INTEGER); + CREATE INDEX idx_sweeps_source ON sweeps(source_id); INSERT INTO address_dictionary VALUES (10,'vault-single'),(11,'vault-multi'),(12,'vault-foreign'),(20,'funder'),(30,'cracker'),(40,'contract'); INSERT INTO asset_dictionary VALUES(1,'XCP'),(2,'CARD'),(3,'OTHER'); @@ -69,6 +70,7 @@ test("vault classification derives compact identities, contents, and crack recip (4,20,11,NULL,NULL,3,'100000000','1',1,101), (5,20,99,NULL,10,2,'100000000','1',1,110), (6,99,99,10,30,2,'100000000','1',3,190); + INSERT INTO sweeps VALUES(11,30,150),(12,30,160),(20,30,170); `); const result = await classifyVaults({ CORE_DB: d1(sqlite) } as never); assert.equal(result.classified, 3); @@ -105,8 +107,8 @@ test("vault classification derives compact identities, contents, and crack recip funded: 1, contents_asset: null, contents_qty: null, - cracked_at: null, - cracker: null, + cracked_at: 150, + cracker: "cracker", }, { token_id: "3",