diff --git a/yarn-project/archiver/package.json b/yarn-project/archiver/package.json index 478aedf90a1..75ae0a84e27 100644 --- a/yarn-project/archiver/package.json +++ b/yarn-project/archiver/package.json @@ -64,6 +64,7 @@ ] }, "dependencies": { + "@aztec/blob-sink": "workspace:^", "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/ethereum": "workspace:^", diff --git a/yarn-project/archiver/src/archiver/archiver.test.ts b/yarn-project/archiver/src/archiver/archiver.test.ts index 305a89874f0..e79d48becd4 100644 --- a/yarn-project/archiver/src/archiver/archiver.test.ts +++ b/yarn-project/archiver/src/archiver/archiver.test.ts @@ -1,3 +1,4 @@ +import { type BlobSinkClientInterface } from '@aztec/blob-sink/client'; import { InboxLeaf, type L1RollupConstants, L2Block } from '@aztec/circuit-types'; import { GENESIS_ARCHIVE_ROOT, PrivateLog } from '@aztec/circuits.js'; import { DefaultL1ContractsConfig } from '@aztec/ethereum'; @@ -53,6 +54,7 @@ describe('Archiver', () => { let publicClient: MockProxy>; let instrumentation: MockProxy; + let blobSinkClient: MockProxy; let archiverStore: ArchiverDataStore; let now: number; let l1Constants: L1RollupConstants; @@ -92,6 +94,7 @@ describe('Archiver', () => { ); }) as any, }); + blobSinkClient = mock(); const tracer = new NoopTelemetryClient().getTracer(); instrumentation = mock({ isEnabled: () => true, tracer }); @@ -109,6 +112,7 @@ describe('Archiver', () => { { rollupAddress, inboxAddress, registryAddress }, archiverStore, { pollingIntervalMs: 1000, batchSize: 1000 }, + blobSinkClient, instrumentation, l1Constants, ); diff --git a/yarn-project/archiver/src/archiver/archiver.ts b/yarn-project/archiver/src/archiver/archiver.ts index 7bbab882dbf..f1aeb5010ac 100644 --- a/yarn-project/archiver/src/archiver/archiver.ts +++ b/yarn-project/archiver/src/archiver/archiver.ts @@ -1,3 +1,4 @@ +import { type BlobSinkClientInterface } from '@aztec/blob-sink/client'; import { type GetUnencryptedLogsResponse, type InBlock, @@ -115,6 +116,7 @@ export class Archiver implements ArchiveSource, Traceable { private readonly l1Addresses: { rollupAddress: EthAddress; inboxAddress: EthAddress; registryAddress: EthAddress }, readonly dataStore: ArchiverDataStore, private readonly config: { pollingIntervalMs: number; batchSize: number }, + private readonly _blobSinkClient: BlobSinkClientInterface, private readonly instrumentation: ArchiverInstrumentation, private readonly l1constants: L1RollupConstants, private readonly log: Logger = createLogger('archiver'), @@ -145,7 +147,7 @@ export class Archiver implements ArchiveSource, Traceable { public static async createAndSync( config: ArchiverConfig, archiverStore: ArchiverDataStore, - telemetry: TelemetryClient, + deps: { telemetry: TelemetryClient; blobSinkClient: BlobSinkClientInterface }, blockUntilSynced = true, ): Promise { const chain = createEthereumChain(config.l1RpcUrl, config.l1ChainId); @@ -176,7 +178,8 @@ export class Archiver implements ArchiveSource, Traceable { pollingIntervalMs: config.archiverPollingIntervalMS ?? 10_000, batchSize: config.archiverBatchSize ?? 100, }, - await ArchiverInstrumentation.new(telemetry, () => archiverStore.estimateSize()), + deps.blobSinkClient, + await ArchiverInstrumentation.new(deps.telemetry, () => archiverStore.estimateSize()), { l1StartBlock, l1GenesisTime, epochDuration, slotDuration, ethereumSlotDuration }, ); await archiver.start(blockUntilSynced); diff --git a/yarn-project/archiver/src/factory.ts b/yarn-project/archiver/src/factory.ts index fee2c5325ad..15dc16a9bc6 100644 --- a/yarn-project/archiver/src/factory.ts +++ b/yarn-project/archiver/src/factory.ts @@ -1,3 +1,4 @@ +import { type BlobSinkClientInterface } from '@aztec/blob-sink/client'; import { type ArchiverApi, type Service } from '@aztec/circuit-types'; import { type ContractClassPublic, @@ -23,6 +24,7 @@ import { createArchiverClient } from './rpc/index.js'; export async function createArchiver( config: ArchiverConfig & DataStoreConfig, + blobSinkClient: BlobSinkClientInterface, telemetry: TelemetryClient = new NoopTelemetryClient(), opts: { blockUntilSync: boolean } = { blockUntilSync: true }, ): Promise> { @@ -31,7 +33,7 @@ export async function createArchiver( const archiverStore = new KVArchiverDataStore(store, config.maxLogs); await registerProtocolContracts(archiverStore); await registerCommonContracts(archiverStore); - return Archiver.createAndSync(config, archiverStore, telemetry, opts.blockUntilSync); + return Archiver.createAndSync(config, archiverStore, { telemetry, blobSinkClient }, opts.blockUntilSync); } else { return createArchiverClient(config.archiverUrl); } diff --git a/yarn-project/archiver/tsconfig.json b/yarn-project/archiver/tsconfig.json index 901d60f657d..050085a41a4 100644 --- a/yarn-project/archiver/tsconfig.json +++ b/yarn-project/archiver/tsconfig.json @@ -6,6 +6,9 @@ "tsBuildInfoFile": ".tsbuildinfo" }, "references": [ + { + "path": "../blob-sink" + }, { "path": "../circuit-types" }, diff --git a/yarn-project/aztec-node/package.json b/yarn-project/aztec-node/package.json index 9edee48067e..3c384dc33b2 100644 --- a/yarn-project/aztec-node/package.json +++ b/yarn-project/aztec-node/package.json @@ -64,6 +64,7 @@ "dependencies": { "@aztec/archiver": "workspace:^", "@aztec/bb-prover": "workspace:^", + "@aztec/blob-sink": "workspace:^", "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/epoch-cache": "workspace:^", diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 9d73ab601ef..56b044638d6 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -1,5 +1,6 @@ import { createArchiver } from '@aztec/archiver'; import { BBCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover'; +import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client'; import { type AztecNode, type ClientProtocolCircuitVerifier, @@ -139,11 +140,13 @@ export class AztecNodeService implements AztecNode, Traceable { logger?: Logger; publisher?: L1Publisher; dateProvider?: DateProvider; + blobSinkClient?: BlobSinkClientInterface; } = {}, ): Promise { const telemetry = deps.telemetry ?? new NoopTelemetryClient(); const log = deps.logger ?? createLogger('node'); const dateProvider = deps.dateProvider ?? new DateProvider(); + const blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config.blobSinkUrl); const ethereumChain = createEthereumChain(config.l1RpcUrl, config.l1ChainId); //validate that the actual chain id matches that specified in configuration if (config.l1ChainId !== ethereumChain.chainInfo.id) { @@ -152,7 +155,7 @@ export class AztecNodeService implements AztecNode, Traceable { ); } - const archiver = await createArchiver(config, telemetry, { blockUntilSync: true }); + const archiver = await createArchiver(config, blobSinkClient, telemetry, { blockUntilSync: true }); // we identify the P2P transaction protocol by using the rollup contract address. // this may well change in future @@ -190,6 +193,7 @@ export class AztecNodeService implements AztecNode, Traceable { const sequencer = config.disableValidator ? undefined : await SequencerClient.new(config, { + ...deps, validatorClient, p2pClient, worldStateSynchronizer, @@ -199,7 +203,7 @@ export class AztecNodeService implements AztecNode, Traceable { l1ToL2MessageSource: archiver, telemetry, dateProvider, - ...deps, + blobSinkClient, }); return new AztecNodeService( diff --git a/yarn-project/aztec-node/tsconfig.json b/yarn-project/aztec-node/tsconfig.json index 2f5e8c847ce..be7bdcc3550 100644 --- a/yarn-project/aztec-node/tsconfig.json +++ b/yarn-project/aztec-node/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../bb-prover" }, + { + "path": "../blob-sink" + }, { "path": "../circuit-types" }, diff --git a/yarn-project/aztec/package.json b/yarn-project/aztec/package.json index c348e3dcbb1..7c476c67213 100644 --- a/yarn-project/aztec/package.json +++ b/yarn-project/aztec/package.json @@ -35,6 +35,7 @@ "@aztec/aztec-node": "workspace:^", "@aztec/aztec.js": "workspace:^", "@aztec/bb-prover": "workspace:^", + "@aztec/blob-sink": "workspace:^", "@aztec/bot": "workspace:^", "@aztec/builder": "workspace:^", "@aztec/circuit-types": "workspace:^", diff --git a/yarn-project/aztec/src/cli/cmds/start_archiver.ts b/yarn-project/aztec/src/cli/cmds/start_archiver.ts index 05851de847b..3713a0e1aa3 100644 --- a/yarn-project/aztec/src/cli/cmds/start_archiver.ts +++ b/yarn-project/aztec/src/cli/cmds/start_archiver.ts @@ -1,5 +1,6 @@ import { Archiver, type ArchiverConfig, KVArchiverDataStore, archiverConfigMappings } from '@aztec/archiver'; import { createLogger } from '@aztec/aztec.js'; +import { createBlobSinkClient } from '@aztec/blob-sink/client'; import { ArchiverApiSchema } from '@aztec/circuit-types'; import { type NamespacedApiHandlers } from '@aztec/foundation/json-rpc/server'; import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config'; @@ -31,7 +32,9 @@ export async function startArchiver( const archiverStore = new KVArchiverDataStore(store, archiverConfig.maxLogs); const telemetry = await createAndStartTelemetryClient(getTelemetryClientConfig()); - const archiver = await Archiver.createAndSync(archiverConfig, archiverStore, telemetry, true); + // TODO(https://github.com/AztecProtocol/aztec-packages/issues/10056): place CL url in config here + const blobSinkClient = createBlobSinkClient(); + const archiver = await Archiver.createAndSync(archiverConfig, archiverStore, { telemetry, blobSinkClient }, true); services.archiver = [archiver, ArchiverApiSchema]; signalHandlers.push(archiver.stop); return services; diff --git a/yarn-project/aztec/src/cli/cmds/start_node.ts b/yarn-project/aztec/src/cli/cmds/start_node.ts index 1758ea35a38..1d93fd1e4ad 100644 --- a/yarn-project/aztec/src/cli/cmds/start_node.ts +++ b/yarn-project/aztec/src/cli/cmds/start_node.ts @@ -91,7 +91,7 @@ export async function startNode( const telemetry = await createAndStartTelemetryClient(telemetryConfig); // Create and start Aztec Node - const node = await createAztecNode(nodeConfig, telemetry); + const node = await createAztecNode(nodeConfig, { telemetry }); // Add node and p2p to services list services.node = [node, AztecNodeApiSchema]; diff --git a/yarn-project/aztec/src/sandbox.ts b/yarn-project/aztec/src/sandbox.ts index a271f11d05f..19f0f9b1653 100644 --- a/yarn-project/aztec/src/sandbox.ts +++ b/yarn-project/aztec/src/sandbox.ts @@ -2,6 +2,7 @@ import { type AztecNodeConfig, AztecNodeService, getConfigEnvVars } from '@aztec/aztec-node'; import { AnvilTestWatcher, EthCheatCodes, SignerlessWallet, retryUntil } from '@aztec/aztec.js'; import { DefaultMultiCallEntrypoint } from '@aztec/aztec.js/entrypoint'; +import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client'; import { type AztecNode } from '@aztec/circuit-types'; import { setupCanonicalL2FeeJuice } from '@aztec/cli/setup-contracts'; import { @@ -143,8 +144,10 @@ export async function createSandbox(config: Partial = {}) { await watcher.start(); } - const client = await createAndStartTelemetryClient(getTelemetryClientConfig()); - const node = await createAztecNode(aztecNodeConfig, client); + const telemetry = await createAndStartTelemetryClient(getTelemetryClientConfig()); + // Create a local blob sink client inside the sandbox, no http connectivity + const blobSinkClient = createBlobSinkClient(); + const node = await createAztecNode(aztecNodeConfig, { telemetry, blobSinkClient }); const pxe = await createAztecPXE(node); if (config.enableGas) { @@ -168,9 +171,12 @@ export async function createSandbox(config: Partial = {}) { * Create and start a new Aztec RPC HTTP Server * @param config - Optional Aztec node settings. */ -export async function createAztecNode(config: Partial = {}, telemetryClient?: TelemetryClient) { +export async function createAztecNode( + config: Partial = {}, + deps: { telemetry?: TelemetryClient; blobSinkClient?: BlobSinkClientInterface } = {}, +) { const aztecNodeConfig: AztecNodeConfig = { ...getConfigEnvVars(), ...config }; - const node = await AztecNodeService.createAndSync(aztecNodeConfig, { telemetry: telemetryClient }); + const node = await AztecNodeService.createAndSync(aztecNodeConfig, deps); return node; } diff --git a/yarn-project/aztec/tsconfig.json b/yarn-project/aztec/tsconfig.json index 6f0e2ced57d..437089e01a7 100644 --- a/yarn-project/aztec/tsconfig.json +++ b/yarn-project/aztec/tsconfig.json @@ -24,6 +24,9 @@ { "path": "../bb-prover" }, + { + "path": "../blob-sink" + }, { "path": "../bot" }, diff --git a/yarn-project/blob-sink/package.json b/yarn-project/blob-sink/package.json index 7090bf99527..daa2c07ca27 100644 --- a/yarn-project/blob-sink/package.json +++ b/yarn-project/blob-sink/package.json @@ -3,7 +3,8 @@ "version": "0.1.0", "type": "module", "exports": { - ".": "./dest/index.js" + "./server": "./dest/server/index.js", + "./client": "./dest/client/index.js" }, "inherits": [ "../package.common.json" diff --git a/yarn-project/blob-sink/src/client/blob-sink-client-tests.ts b/yarn-project/blob-sink/src/client/blob-sink-client-tests.ts new file mode 100644 index 00000000000..3e77a5ffe6b --- /dev/null +++ b/yarn-project/blob-sink/src/client/blob-sink-client-tests.ts @@ -0,0 +1,72 @@ +import { Blob } from '@aztec/foundation/blob'; +import { Fr } from '@aztec/foundation/fields'; + +import { type BlobSinkClientInterface } from './interface.js'; + +/** + * Shared test suite for blob sink clients + * @param createClient - Function that creates a client instance for testing + * @param cleanup - Optional cleanup function to run after each test + */ +export function runBlobSinkClientTests( + createClient: () => Promise<{ client: BlobSinkClientInterface; cleanup: () => Promise }>, +) { + let client: BlobSinkClientInterface; + let cleanup: () => Promise; + + beforeEach(async () => { + const setup = await createClient(); + client = setup.client; + cleanup = setup.cleanup; + }); + + afterEach(async () => { + await cleanup(); + }); + + it('should send and retrieve blobs', async () => { + const testFields = [Fr.random(), Fr.random(), Fr.random()]; + const blob = Blob.fromFields(testFields); + const blockId = '0x1234'; + + const success = await client.sendBlobsToBlobSink(blockId, [blob]); + expect(success).toBe(true); + + const retrievedBlobs = await client.getBlobSidecar(blockId); + expect(retrievedBlobs).toHaveLength(1); + expect(retrievedBlobs[0].fieldsHash.toString()).toBe(blob.fieldsHash.toString()); + expect(retrievedBlobs[0].commitment.toString('hex')).toBe(blob.commitment.toString('hex')); + }); + + it('should handle multiple blobs', async () => { + const blobs = [ + Blob.fromFields([Fr.random(), Fr.random()]), + Blob.fromFields([Fr.random(), Fr.random()]), + Blob.fromFields([Fr.random(), Fr.random()]), + ]; + const blockId = '0x5678'; + + const success = await client.sendBlobsToBlobSink(blockId, blobs); + expect(success).toBe(true); + + const retrievedBlobs = await client.getBlobSidecar(blockId); + expect(retrievedBlobs).toHaveLength(3); + + for (let i = 0; i < blobs.length; i++) { + expect(retrievedBlobs[i].fieldsHash.toString()).toBe(blobs[i].fieldsHash.toString()); + expect(retrievedBlobs[i].commitment.toString('hex')).toBe(blobs[i].commitment.toString('hex')); + } + + // Can request blobs by index + const retrievedBlobsByIndex = await client.getBlobSidecar(blockId, [0, 2]); + expect(retrievedBlobsByIndex).toHaveLength(2); + expect(retrievedBlobsByIndex[0].fieldsHash.toString()).toBe(blobs[0].fieldsHash.toString()); + expect(retrievedBlobsByIndex[1].fieldsHash.toString()).toBe(blobs[2].fieldsHash.toString()); + }); + + it('should return empty array for non-existent block', async () => { + const blockId = '0xnonexistent'; + const retrievedBlobs = await client.getBlobSidecar(blockId); + expect(retrievedBlobs).toEqual([]); + }); +} diff --git a/yarn-project/blob-sink/src/client/factory.ts b/yarn-project/blob-sink/src/client/factory.ts new file mode 100644 index 00000000000..796746a0ed8 --- /dev/null +++ b/yarn-project/blob-sink/src/client/factory.ts @@ -0,0 +1,13 @@ +import { MemoryBlobStore } from '../blobstore/memory_blob_store.js'; +import { HttpBlobSinkClient } from './http.js'; +import { type BlobSinkClientInterface } from './interface.js'; +import { LocalBlobSinkClient } from './local.js'; + +export function createBlobSinkClient(blobSinkUrl?: string): BlobSinkClientInterface { + if (!blobSinkUrl) { + const blobStore = new MemoryBlobStore(); + return new LocalBlobSinkClient(blobStore); + } + + return new HttpBlobSinkClient(blobSinkUrl); +} diff --git a/yarn-project/blob-sink/src/client/http.test.ts b/yarn-project/blob-sink/src/client/http.test.ts new file mode 100644 index 00000000000..046fb0811d0 --- /dev/null +++ b/yarn-project/blob-sink/src/client/http.test.ts @@ -0,0 +1,35 @@ +import { Blob } from '@aztec/foundation/blob'; +import { Fr } from '@aztec/foundation/fields'; + +import { BlobSinkServer } from '../server/server.js'; +import { runBlobSinkClientTests } from './blob-sink-client-tests.js'; +import { HttpBlobSinkClient } from './http.js'; + +describe('HttpBlobSinkClient', () => { + runBlobSinkClientTests(async () => { + const server = new BlobSinkServer({ + port: 0, + }); + await server.start(); + + const client = new HttpBlobSinkClient(`http://localhost:${server.port}`); + + return { + client, + cleanup: async () => { + await server.stop(); + }, + }; + }); + + it('should handle server connection errors gracefully', async () => { + const client = new HttpBlobSinkClient('http://localhost:12345'); // Invalid port + const blob = Blob.fromFields([Fr.random()]); + + const success = await client.sendBlobsToBlobSink('0x1234', [blob]); + expect(success).toBe(false); + + const retrievedBlobs = await client.getBlobSidecar('0x1234'); + expect(retrievedBlobs).toEqual([]); + }); +}); diff --git a/yarn-project/blob-sink/src/client/http.ts b/yarn-project/blob-sink/src/client/http.ts new file mode 100644 index 00000000000..444772a2e9c --- /dev/null +++ b/yarn-project/blob-sink/src/client/http.ts @@ -0,0 +1,76 @@ +import { Blob } from '@aztec/foundation/blob'; +import { type Logger, createLogger } from '@aztec/foundation/log'; + +import { type BlobSinkClientInterface } from './interface.js'; + +export class HttpBlobSinkClient implements BlobSinkClientInterface { + private readonly log: Logger; + + constructor(private readonly blobSinkUrl: string) { + this.log = createLogger('aztec:blob-sink-client'); + } + + public async sendBlobsToBlobSink(blockHash: string, blobs: Blob[]): Promise { + // TODO(md): for now we are assuming the indexes of the blobs will be 0, 1, 2 + // When in reality they will not, but for testing purposes this is fine + if (!this.blobSinkUrl) { + this.log.verbose('No blob sink url configured'); + return false; + } + + this.log.verbose(`Sending ${blobs.length} blobs to blob sink`); + try { + const res = await fetch(`${this.blobSinkUrl}/blob_sidecar`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + // eslint-disable-next-line camelcase + block_id: blockHash, + blobs: blobs.map((b, i) => ({ blob: b.toBuffer(), index: i })), + }), + }); + + if (res.ok) { + return true; + } + + this.log.error('Failed to send blobs to blob sink', res.status); + return false; + } catch (err) { + this.log.error(`Error sending blobs to blob sink`, err); + return false; + } + } + + public async getBlobSidecar(blockHash: string, indices?: number[]): Promise { + if (!this.blobSinkUrl) { + this.log.verbose('No blob sink url configured'); + return []; + } + + try { + let url = `${this.blobSinkUrl}/eth/v1/beacon/blob_sidecars/${blockHash}`; + if (indices && indices.length > 0) { + url += `?indices=${indices.join(',')}`; + } + + const res = await fetch(url); + + if (res.ok) { + const body = await res.json(); + const blobs = body.data.map((b: { blob: string; index: number }) => + Blob.fromBuffer(Buffer.from(b.blob, 'hex')), + ); + return blobs; + } + + this.log.error('Failed to get blob sidecar', res.status); + return []; + } catch (err) { + this.log.error(`Error getting blob sidecar`, err); + return []; + } + } +} diff --git a/yarn-project/blob-sink/src/client/index.ts b/yarn-project/blob-sink/src/client/index.ts new file mode 100644 index 00000000000..30172c11e39 --- /dev/null +++ b/yarn-project/blob-sink/src/client/index.ts @@ -0,0 +1,4 @@ +export * from './http.js'; +export * from './local.js'; +export * from './interface.js'; +export * from './factory.js'; diff --git a/yarn-project/blob-sink/src/client/interface.ts b/yarn-project/blob-sink/src/client/interface.ts new file mode 100644 index 00000000000..c98e450a6bc --- /dev/null +++ b/yarn-project/blob-sink/src/client/interface.ts @@ -0,0 +1,6 @@ +import { type Blob } from '@aztec/foundation/blob'; + +export interface BlobSinkClientInterface { + sendBlobsToBlobSink(blockId: string, blobs: Blob[]): Promise; + getBlobSidecar(blockId: string, indices?: number[]): Promise; +} diff --git a/yarn-project/blob-sink/src/client/local.test.ts b/yarn-project/blob-sink/src/client/local.test.ts new file mode 100644 index 00000000000..d9348335c5b --- /dev/null +++ b/yarn-project/blob-sink/src/client/local.test.ts @@ -0,0 +1,16 @@ +import { MemoryBlobStore } from '../blobstore/memory_blob_store.js'; +import { runBlobSinkClientTests } from './blob-sink-client-tests.js'; +import { LocalBlobSinkClient } from './local.js'; + +describe('LocalBlobSinkClient', () => { + runBlobSinkClientTests(() => { + const store = new MemoryBlobStore(); + const client = new LocalBlobSinkClient(store); + return Promise.resolve({ + client, + cleanup: async () => { + // No cleanup needed for memory store + }, + }); + }); +}); diff --git a/yarn-project/blob-sink/src/client/local.ts b/yarn-project/blob-sink/src/client/local.ts new file mode 100644 index 00000000000..df18903a611 --- /dev/null +++ b/yarn-project/blob-sink/src/client/local.ts @@ -0,0 +1,29 @@ +import { type Blob } from '@aztec/foundation/blob'; + +import { type BlobStore } from '../blobstore/index.js'; +import { BlobWithIndex } from '../types/blob_with_index.js'; +import { type BlobSinkClientInterface } from './interface.js'; + +export class LocalBlobSinkClient implements BlobSinkClientInterface { + private readonly blobStore: BlobStore; + + constructor(blobStore: BlobStore) { + this.blobStore = blobStore; + } + + public async sendBlobsToBlobSink(blockId: string, blobs: Blob[]): Promise { + await this.blobStore.addBlobSidecars( + blockId, + blobs.map((blob, index) => new BlobWithIndex(blob, index)), + ); + return true; + } + + public async getBlobSidecar(blockId: string, indices?: number[]): Promise { + const blobSidecars = await this.blobStore.getBlobSidecars(blockId, indices); + if (!blobSidecars) { + return []; + } + return blobSidecars.map(blob => blob.blob); + } +} diff --git a/yarn-project/blob-sink/src/config.ts b/yarn-project/blob-sink/src/server/config.ts similarity index 100% rename from yarn-project/blob-sink/src/config.ts rename to yarn-project/blob-sink/src/server/config.ts diff --git a/yarn-project/blob-sink/src/factory.ts b/yarn-project/blob-sink/src/server/factory.ts similarity index 100% rename from yarn-project/blob-sink/src/factory.ts rename to yarn-project/blob-sink/src/server/factory.ts diff --git a/yarn-project/blob-sink/src/index.ts b/yarn-project/blob-sink/src/server/index.ts similarity index 100% rename from yarn-project/blob-sink/src/index.ts rename to yarn-project/blob-sink/src/server/index.ts index 25844130c2f..6bff610d93d 100644 --- a/yarn-project/blob-sink/src/index.ts +++ b/yarn-project/blob-sink/src/server/index.ts @@ -1,3 +1,3 @@ export * from './server.js'; -export * from './config.js'; export * from './factory.js'; +export * from './config.js'; diff --git a/yarn-project/blob-sink/src/metrics.ts b/yarn-project/blob-sink/src/server/metrics.ts similarity index 93% rename from yarn-project/blob-sink/src/metrics.ts rename to yarn-project/blob-sink/src/server/metrics.ts index 28e2b6308c0..17410629f90 100644 --- a/yarn-project/blob-sink/src/metrics.ts +++ b/yarn-project/blob-sink/src/server/metrics.ts @@ -1,6 +1,6 @@ import { type Histogram, Metrics, type TelemetryClient, type UpDownCounter } from '@aztec/telemetry-client'; -import { type BlobWithIndex } from './types/blob_with_index.js'; +import { type BlobWithIndex } from '../types/blob_with_index.js'; export class BlobSinkMetrics { /** The number of blobs in the blob store */ diff --git a/yarn-project/blob-sink/src/blob-sink.test.ts b/yarn-project/blob-sink/src/server/server.test.ts similarity index 100% rename from yarn-project/blob-sink/src/blob-sink.test.ts rename to yarn-project/blob-sink/src/server/server.test.ts diff --git a/yarn-project/blob-sink/src/server.ts b/yarn-project/blob-sink/src/server/server.ts similarity index 87% rename from yarn-project/blob-sink/src/server.ts rename to yarn-project/blob-sink/src/server/server.ts index 45c79f6991d..0727ba4a70f 100644 --- a/yarn-project/blob-sink/src/server.ts +++ b/yarn-project/blob-sink/src/server/server.ts @@ -6,14 +6,15 @@ import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import express, { type Express, type Request, type Response, json } from 'express'; import { type Server } from 'http'; +import { type AddressInfo } from 'net'; import { z } from 'zod'; -import { type BlobStore, DiskBlobStore } from './blobstore/index.js'; -import { MemoryBlobStore } from './blobstore/memory_blob_store.js'; +import { type BlobStore, DiskBlobStore } from '../blobstore/index.js'; +import { MemoryBlobStore } from '../blobstore/memory_blob_store.js'; +import { type PostBlobSidecarRequest, blockIdSchema, indicesSchema } from '../types/api.js'; +import { BlobWithIndex } from '../types/index.js'; import { type BlobSinkConfig } from './config.js'; import { BlobSinkMetrics } from './metrics.js'; -import { type PostBlobSidecarRequest, blockIdSchema, indicesSchema } from './types/api.js'; -import { BlobWithIndex } from './types/index.js'; /** * Example usage: @@ -23,7 +24,7 @@ import { BlobWithIndex } from './types/index.js'; * await service.stop(); */ export class BlobSinkServer { - public readonly port: number; + public port: number; private app: Express; private server: Server | null = null; @@ -135,8 +136,17 @@ export class BlobSinkServer { } public start(): Promise { - return new Promise(resolve => { + return new Promise((resolve, reject) => { this.server = this.app.listen(this.port, () => { + // Extract port from server address, allows setting address when + // server is started with port 0 + const address = this.server?.address() as AddressInfo | null; + if (!address) { + this.log.error('Server address not found'); + void this.stop().then(() => reject(new Error('Server address not found'))); + } + + this.port = address!.port; this.log.info(`Server is running on http://localhost:${this.port}`); resolve(); }); diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index de355e1fae6..17ca49f88bc 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -187,7 +187,7 @@ describe('L1Publisher integration', () => { ethereumSlotDuration: config.ethereumSlotDuration, blobSinkUrl: BLOB_SINK_URL, }, - new NoopTelemetryClient(), + { telemetry: new NoopTelemetryClient() }, ); coinbase = config.coinbase || EthAddress.random(); diff --git a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts index dd212a72379..d27c217dea9 100644 --- a/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts +++ b/yarn-project/end-to-end/src/e2e_prover/e2e_prover_test.ts @@ -20,6 +20,8 @@ import { TestCircuitVerifier, type UltraKeccakHonkProtocolArtifact, } from '@aztec/bb-prover'; +import { createBlobSinkClient } from '@aztec/blob-sink/client'; +import { type BlobSinkServer } from '@aztec/blob-sink/server'; import { compileContract } from '@aztec/ethereum'; import { Buffer32 } from '@aztec/foundation/buffer'; import { RollupAbi, TestERC20Abi } from '@aztec/l1-artifacts'; @@ -76,6 +78,7 @@ export class FullProverTest { aztecNode!: AztecNode; pxe!: PXEService; cheatCodes!: CheatCodes; + blobSink!: BlobSinkServer; private provenComponents: ProvenSetup[] = []; private bbConfigCleanup?: () => Promise; private acvmConfigCleanup?: () => Promise; @@ -163,8 +166,11 @@ export class FullProverTest { aztecNode: this.aztecNode, deployL1ContractsValues: this.l1Contracts, cheatCodes: this.cheatCodes, + blobSink: this.blobSink, } = this.context); + const blobSinkClient = createBlobSinkClient(`http://localhost:${this.blobSink.port}`); + // Configure a full prover PXE let acvmConfig: Awaited> | undefined; let bbConfig: Awaited> | undefined; @@ -247,6 +253,7 @@ export class FullProverTest { this.logger.verbose('Starting archiver for new prover node'); const archiver = await createArchiver( { ...this.context.aztecNodeConfig, dataDirectory: undefined }, + blobSinkClient, new NoopTelemetryClient(), { blockUntilSync: true }, ); @@ -282,6 +289,7 @@ export class FullProverTest { this.proverNode = await createProverNode(proverConfig, { aztecNodeTxProvider: this.aztecNode, archiver: archiver as Archiver, + blobSinkClient, }); await this.proverNode.start(); diff --git a/yarn-project/end-to-end/src/e2e_synching.test.ts b/yarn-project/end-to-end/src/e2e_synching.test.ts index a618bc6ec2e..24aeb10de9a 100644 --- a/yarn-project/end-to-end/src/e2e_synching.test.ts +++ b/yarn-project/end-to-end/src/e2e_synching.test.ts @@ -45,6 +45,7 @@ import { createLogger, sleep, } from '@aztec/aztec.js'; +import { createBlobSinkClient } from '@aztec/blob-sink/client'; // eslint-disable-next-line no-restricted-imports import { L2Block, tryStop } from '@aztec/circuit-types'; import { type AztecAddress } from '@aztec/circuits.js'; @@ -382,6 +383,8 @@ describe('e2e_synching', () => { await (sequencer as any).stop(); await watcher?.stop(); + const blobSinkClient = createBlobSinkClient(`http://localhost:${blobSink?.port ?? 5052}`); + const sequencerPK: `0x${string}` = `0x${getPrivateKeyFromIndex(0)!.toString('hex')}`; const publisher = new L1Publisher( { @@ -395,7 +398,7 @@ describe('e2e_synching', () => { ethereumSlotDuration: ETHEREUM_SLOT_DURATION, blobSinkUrl: `http://localhost:${blobSink?.port ?? 5052}`, }, - new NoopTelemetryClient(), + { telemetry: new NoopTelemetryClient(), blobSinkClient }, ); const blocks = variant.loadBlocks(); @@ -498,7 +501,10 @@ describe('e2e_synching', () => { await aztecNode.stop(); } - const archiver = await createArchiver(opts.config!); + const blobSinkClient = createBlobSinkClient(`http://localhost:${opts.blobSink?.port ?? 5052}`); + const archiver = await createArchiver(opts.config!, blobSinkClient, new NoopTelemetryClient(), { + blockUntilSync: true, + }); const pendingBlockNumber = await rollup.read.getPendingBlockNumber(); const worldState = await createWorldStateSynchronizer(opts.config!, archiver, new NoopTelemetryClient()); diff --git a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts index 42663cf4cc0..44e677aa46a 100644 --- a/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts +++ b/yarn-project/end-to-end/src/fixtures/snapshot_manager.ts @@ -14,7 +14,7 @@ import { type Wallet, } from '@aztec/aztec.js'; import { deployInstance, registerContractClass } from '@aztec/aztec.js/deployment'; -import { type BlobSinkServer, createBlobSinkServer } from '@aztec/blob-sink'; +import { type BlobSinkServer, createBlobSinkServer } from '@aztec/blob-sink/server'; import { type DeployL1ContractsArgs, createL1Clients, getL1ContractsConfigEnvVars, l1Artifacts } from '@aztec/ethereum'; import { EthCheatCodesWithState, startAnvil } from '@aztec/ethereum/test'; import { asyncMap } from '@aztec/foundation/async-map'; diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index 85ae078728f..c65003d08e2 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -27,7 +27,8 @@ import { import { deployInstance, registerContractClass } from '@aztec/aztec.js/deployment'; import { DefaultMultiCallEntrypoint } from '@aztec/aztec.js/entrypoint'; import { type BBNativePrivateKernelProver } from '@aztec/bb-prover'; -import { type BlobSinkServer, createBlobSinkServer } from '@aztec/blob-sink'; +import { createBlobSinkClient } from '@aztec/blob-sink/client'; +import { type BlobSinkServer, createBlobSinkServer } from '@aztec/blob-sink/server'; import { type EthAddress, FEE_JUICE_INITIAL_MINT, Fr, Gas, getContractClassFromArtifact } from '@aztec/circuits.js'; import { type DeployL1ContractsArgs, @@ -391,7 +392,8 @@ export async function setup( // Blob sink service - blobs get posted here and served from here const blobSinkPort = await getPort(); const blobSink = await createBlobSinkServer({ port: blobSinkPort }); - config.blobSinkUrl = `http://127.0.0.1:${blobSinkPort}`; + await blobSink.start(); + config.blobSinkUrl = `http://localhost:${blobSinkPort}`; const deployL1ContractsValues = opts.deployL1ContractsValues ?? (await setupL1Contracts(config.l1RpcUrl, publisherHdAccount!, logger, opts, chain)); @@ -454,8 +456,15 @@ export async function setup( config.l1PublishRetryIntervalMS = 100; const telemetry = await telemetryPromise; - const publisher = new TestL1Publisher(config, telemetry); - const aztecNode = await AztecNodeService.createAndSync(config, { telemetry, publisher, dateProvider }); + + const blobSinkClient = createBlobSinkClient(config.blobSinkUrl); + const publisher = new TestL1Publisher(config, { telemetry, blobSinkClient }); + const aztecNode = await AztecNodeService.createAndSync(config, { + telemetry, + publisher, + dateProvider, + blobSinkClient, + }); const sequencer = aztecNode.getSequencer(); let proverNode: ProverNode | undefined = undefined; @@ -725,9 +734,13 @@ export async function createAndSyncProverNode( stop: () => Promise.resolve(), }; + const blobSinkClient = createBlobSinkClient(); // Creating temp store and archiver for simulated prover node const archiverConfig = { ...aztecNodeConfig, dataDirectory }; - const archiver = await createArchiver(archiverConfig, new NoopTelemetryClient(), { blockUntilSync: true }); + const telemetry = new NoopTelemetryClient(); + const archiver = await createArchiver(archiverConfig, blobSinkClient, telemetry, { + blockUntilSync: true, + }); // Prover node config is for simulated proofs const proverConfig: ProverNodeConfig = { @@ -751,7 +764,7 @@ export async function createAndSyncProverNode( }; // Use testing l1 publisher - const publisher = new TestL1Publisher(proverConfig, new NoopTelemetryClient()); + const publisher = new TestL1Publisher(proverConfig, { telemetry, blobSinkClient }); const proverNode = await createProverNode(proverConfig, { aztecNodeTxProvider: aztecNodeWithoutStop, diff --git a/yarn-project/prover-node/package.json b/yarn-project/prover-node/package.json index 13a872b8864..34ba7498a2f 100644 --- a/yarn-project/prover-node/package.json +++ b/yarn-project/prover-node/package.json @@ -54,6 +54,7 @@ "dependencies": { "@aztec/archiver": "workspace:^", "@aztec/bb-prover": "workspace:^", + "@aztec/blob-sink": "workspace:^", "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/epoch-cache": "workspace:^", diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index 84bf1b39ad4..300fd664138 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -1,4 +1,5 @@ import { type Archiver, createArchiver } from '@aztec/archiver'; +import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client'; import { type ProverCoordination, type ProvingJobBroker } from '@aztec/circuit-types'; import { EpochCache } from '@aztec/epoch-cache'; import { createEthereumChain } from '@aztec/ethereum'; @@ -34,12 +35,14 @@ export async function createProverNode( aztecNodeTxProvider?: ProverCoordination; archiver?: Archiver; publisher?: L1Publisher; + blobSinkClient?: BlobSinkClientInterface; broker?: ProvingJobBroker; } = {}, ) { const telemetry = deps.telemetry ?? new NoopTelemetryClient(); + const blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config.blobSinkUrl); const log = deps.log ?? createLogger('prover-node'); - const archiver = deps.archiver ?? (await createArchiver(config, telemetry, { blockUntilSync: true })); + const archiver = deps.archiver ?? (await createArchiver(config, blobSinkClient, telemetry, { blockUntilSync: true })); log.verbose(`Created archiver and synced to block ${await archiver.getBlockNumber()}`); const worldStateConfig = { ...config, worldStateProvenBlocksOnly: false }; @@ -50,7 +53,7 @@ export async function createProverNode( const prover = await createProverClient(config, worldStateSynchronizer, broker, telemetry); // REFACTOR: Move publisher out of sequencer package and into an L1-related package - const publisher = deps.publisher ?? new L1Publisher(config, telemetry); + const publisher = deps.publisher ?? new L1Publisher(config, { telemetry, blobSinkClient }); const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config); diff --git a/yarn-project/prover-node/tsconfig.json b/yarn-project/prover-node/tsconfig.json index b4f4776b00d..80cf2c8db2d 100644 --- a/yarn-project/prover-node/tsconfig.json +++ b/yarn-project/prover-node/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../bb-prover" }, + { + "path": "../blob-sink" + }, { "path": "../circuit-types" }, diff --git a/yarn-project/sequencer-client/package.json b/yarn-project/sequencer-client/package.json index fb874975daa..925c28bdabe 100644 --- a/yarn-project/sequencer-client/package.json +++ b/yarn-project/sequencer-client/package.json @@ -29,6 +29,7 @@ "dependencies": { "@aztec/aztec.js": "workspace:^", "@aztec/bb-prover": "workspace:^", + "@aztec/blob-sink": "workspace:^", "@aztec/circuit-types": "workspace:^", "@aztec/circuits.js": "workspace:^", "@aztec/ethereum": "workspace:^", diff --git a/yarn-project/sequencer-client/src/client/sequencer-client.ts b/yarn-project/sequencer-client/src/client/sequencer-client.ts index 14a41668893..10119d98057 100644 --- a/yarn-project/sequencer-client/src/client/sequencer-client.ts +++ b/yarn-project/sequencer-client/src/client/sequencer-client.ts @@ -1,3 +1,4 @@ +import { type BlobSinkClientInterface } from '@aztec/blob-sink/client'; import { type L1ToL2MessageSource, type L2BlockSource, type WorldStateSynchronizer } from '@aztec/circuit-types'; import { type ContractDataSource } from '@aztec/circuits.js'; import { isAnvilTestChain } from '@aztec/ethereum'; @@ -46,6 +47,7 @@ export class SequencerClient { l1ToL2MessageSource: L1ToL2MessageSource; telemetry: TelemetryClient; publisher?: L1Publisher; + blobSinkClient?: BlobSinkClientInterface; dateProvider: DateProvider; }, ) { @@ -59,7 +61,8 @@ export class SequencerClient { l1ToL2MessageSource, telemetry: telemetryClient, } = deps; - const publisher = deps.publisher ?? new L1Publisher(config, telemetryClient); + const publisher = + deps.publisher ?? new L1Publisher(config, { telemetry: telemetryClient, blobSinkClient: deps.blobSinkClient }); const globalsBuilder = new GlobalVariableBuilder(config); const publicProcessorFactory = new PublicProcessorFactory(contractDataSource, deps.dateProvider, telemetryClient); diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts index 689c03c71d1..a000d6f80f5 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.test.ts @@ -1,3 +1,4 @@ +import { HttpBlobSinkClient } from '@aztec/blob-sink/client'; import { L2Block } from '@aztec/circuit-types'; import { EthAddress } from '@aztec/circuits.js'; import { @@ -91,6 +92,7 @@ describe('L1Publisher', () => { let blockHash: Buffer; let body: Buffer; + let blobSinkClient: HttpBlobSinkClient; let mockBlobSinkServer: Server | undefined = undefined; // An l1 publisher with some private methods exposed @@ -100,6 +102,7 @@ describe('L1Publisher', () => { beforeEach(() => { mockBlobSinkServer = undefined; + blobSinkClient = new HttpBlobSinkClient(BLOB_SINK_URL); l2Block = L2Block.random(42); @@ -136,7 +139,7 @@ describe('L1Publisher', () => { Pick & L1TxUtilsConfig; - publisher = new L1Publisher(config, new NoopTelemetryClient()); + publisher = new L1Publisher(config, { telemetry: new NoopTelemetryClient(), blobSinkClient }); (publisher as any)['rollupContract'] = rollupContract; (publisher as any)['publicClient'] = publicClient; diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index e1c8d631f4e..26b33f910d1 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -1,3 +1,4 @@ +import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client'; import { ConsensusPayload, type EpochProofClaim, @@ -29,6 +30,7 @@ import { InterruptibleSleep } from '@aztec/foundation/sleep'; import { Timer } from '@aztec/foundation/timer'; import { EmpireBaseAbi, RollupAbi, SlasherAbi } from '@aztec/l1-artifacts'; import { type TelemetryClient } from '@aztec/telemetry-client'; +import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import pick from 'lodash.pick'; import { @@ -177,8 +179,7 @@ export class L1Publisher { protected account: PrivateKeyAccount; protected ethereumSlotDuration: bigint; - private blobSinkUrl: string | undefined; - + private blobSinkClient: BlobSinkClientInterface; // @note - with blobs, the below estimate seems too large. // Total used for full block from int_l1_pub e2e test: 1m (of which 86k is 1x blob) // Total used for emptier block from above test: 429k (of which 84k is 1x blob) @@ -189,12 +190,15 @@ export class L1Publisher { constructor( config: TxSenderConfig & PublisherConfig & Pick, - client: TelemetryClient, + deps: { telemetry?: TelemetryClient; blobSinkClient?: BlobSinkClientInterface } = {}, ) { this.sleepTimeMs = config?.l1PublishRetryIntervalMS ?? 60_000; this.ethereumSlotDuration = BigInt(config.ethereumSlotDuration); - this.blobSinkUrl = config.blobSinkUrl; - this.metrics = new L1PublisherMetrics(client, 'L1Publisher'); + + const telemetry = deps.telemetry ?? new NoopTelemetryClient(); + this.blobSinkClient = deps.blobSinkClient ?? createBlobSinkClient(config.blobSinkUrl); + + this.metrics = new L1PublisherMetrics(telemetry, 'L1Publisher'); const { l1RpcUrl: rpcUrl, l1ChainId: chainId, publisherPrivateKey, l1Contracts } = config; const chain = createEthereumChain(rpcUrl, chainId); @@ -1143,38 +1147,8 @@ export class L1Publisher { * In the future this will move to be the beacon block id - which takes a bit more work * to calculate and will need to be mocked in e2e tests */ - protected async sendBlobsToBlobSink(blockHash: string, blobs: Blob[]): Promise { - // TODO(md): for now we are assuming the indexes of the blobs will be 0, 1, 2 - // When in reality they will not, but for testing purposes this is fine - if (!this.blobSinkUrl) { - this.log.verbose('No blob sink url configured'); - return false; - } - - this.log.verbose(`Sending ${blobs.length} blobs to blob sink`); - try { - const res = await fetch(`${this.blobSinkUrl}/blob_sidecar`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - // eslint-disable-next-line camelcase - block_id: blockHash, - blobs: blobs.map((b, i) => ({ blob: b.toBuffer(), index: i })), - }), - }); - - if (res.ok) { - return true; - } - - this.log.error('Failed to send blobs to blob sink', res.status); - return false; - } catch (err) { - this.log.error(`Error sending blobs to blob sink`, err); - return false; - } + protected sendBlobsToBlobSink(blockHash: string, blobs: Blob[]): Promise { + return this.blobSinkClient.sendBlobsToBlobSink(blockHash, blobs); } } diff --git a/yarn-project/sequencer-client/tsconfig.json b/yarn-project/sequencer-client/tsconfig.json index 4161156625e..a5653fa17fd 100644 --- a/yarn-project/sequencer-client/tsconfig.json +++ b/yarn-project/sequencer-client/tsconfig.json @@ -12,6 +12,9 @@ { "path": "../bb-prover" }, + { + "path": "../blob-sink" + }, { "path": "../circuit-types" }, diff --git a/yarn-project/yarn.lock b/yarn-project/yarn.lock index e1fa34cd008..f73b616c338 100644 --- a/yarn-project/yarn.lock +++ b/yarn-project/yarn.lock @@ -87,6 +87,7 @@ __metadata: version: 0.0.0-use.local resolution: "@aztec/archiver@workspace:archiver" dependencies: + "@aztec/blob-sink": "workspace:^" "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/ethereum": "workspace:^" @@ -150,6 +151,7 @@ __metadata: dependencies: "@aztec/archiver": "workspace:^" "@aztec/bb-prover": "workspace:^" + "@aztec/blob-sink": "workspace:^" "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/epoch-cache": "workspace:^" @@ -242,6 +244,7 @@ __metadata: "@aztec/aztec-node": "workspace:^" "@aztec/aztec.js": "workspace:^" "@aztec/bb-prover": "workspace:^" + "@aztec/blob-sink": "workspace:^" "@aztec/bot": "workspace:^" "@aztec/builder": "workspace:^" "@aztec/circuit-types": "workspace:^" @@ -1085,6 +1088,7 @@ __metadata: dependencies: "@aztec/archiver": "workspace:^" "@aztec/bb-prover": "workspace:^" + "@aztec/blob-sink": "workspace:^" "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/epoch-cache": "workspace:^" @@ -1187,6 +1191,7 @@ __metadata: "@aztec/archiver": "workspace:^" "@aztec/aztec.js": "workspace:^" "@aztec/bb-prover": "workspace:^" + "@aztec/blob-sink": "workspace:^" "@aztec/circuit-types": "workspace:^" "@aztec/circuits.js": "workspace:^" "@aztec/ethereum": "workspace:^"