From 4015302891734230e01a67b4b9da4ef39117fd93 Mon Sep 17 00:00:00 2001 From: Mohsin Zaidi <2236875+smrz2001@users.noreply.github.com> Date: Mon, 18 Mar 2024 22:02:26 -0400 Subject: [PATCH 01/11] feat: cas scaling --- config/default.json | 2 + config/env/dev.json | 2 + config/env/prod.json | 2 + config/env/test.json | 1 + src/services/queue/sqs-queue-service.ts | 68 ++++++++++++++++++++++++- 5 files changed, 74 insertions(+), 1 deletion(-) diff --git a/config/default.json b/config/default.json index 1d37a7ab5..30acc2240 100644 --- a/config/default.json +++ b/config/default.json @@ -76,6 +76,8 @@ "type": "sqs", "awsRegion": "us-east-1", "sqsQueueUrl": "", + "s3BucketName": "myS3Bucket", + "s3Endpoint": "", "maxTimeToHoldMessageSec": 21600, "waitTimeForMessageSec": 0 } diff --git a/config/env/dev.json b/config/env/dev.json index 15264ab6e..21aa80c8f 100644 --- a/config/env/dev.json +++ b/config/env/dev.json @@ -71,6 +71,8 @@ "type": "sqs", "awsRegion": "@@AWS_REGION", "sqsQueueUrl": "@@SQS_QUEUE_URL", + "s3BucketName": "@@S3_BUCKET_NAME", + "s3Endpoint": "@@S3_ENDPOINT", "maxTimeToHoldMessageSec": "@@MAX_TIME_TO_HOLD_MESSAGE_SEC", "waitTimeForMessageSec": "@@WAIT_TIME_FOR_MESSAGE_SEC" } diff --git a/config/env/prod.json b/config/env/prod.json index 15264ab6e..21aa80c8f 100644 --- a/config/env/prod.json +++ b/config/env/prod.json @@ -71,6 +71,8 @@ "type": "sqs", "awsRegion": "@@AWS_REGION", "sqsQueueUrl": "@@SQS_QUEUE_URL", + "s3BucketName": "@@S3_BUCKET_NAME", + "s3Endpoint": "@@S3_ENDPOINT", "maxTimeToHoldMessageSec": "@@MAX_TIME_TO_HOLD_MESSAGE_SEC", "waitTimeForMessageSec": "@@WAIT_TIME_FOR_MESSAGE_SEC" } diff --git a/config/env/test.json b/config/env/test.json index a3c072bc9..cc0354fe9 100644 --- a/config/env/test.json +++ b/config/env/test.json @@ -57,6 +57,7 @@ "type": "sqs", "awsRegion": "us-east-1", "sqsQueueUrl": "", + "s3BucketName": "ceramic-tnet-cas", "maxTimeToHoldMessageSec": 10800, "waitTimeForMessageSec": 10 } diff --git a/src/services/queue/sqs-queue-service.ts b/src/services/queue/sqs-queue-service.ts index a59047852..caad34e05 100644 --- a/src/services/queue/sqs-queue-service.ts +++ b/src/services/queue/sqs-queue-service.ts @@ -6,6 +6,9 @@ import { ChangeMessageVisibilityCommand, SendMessageCommand, } from '@aws-sdk/client-sqs' +import AWSSDK from 'aws-sdk' +import LevelUp from 'levelup' +import S3LevelDOWN from 's3leveldown' import { IpfsPubSubPublishQMessage, QueueMessageData } from '../../models/queue-message.js' import { IQueueConsumerService, @@ -19,6 +22,7 @@ import { AbortOptions } from '@ceramicnetwork/common' const DEFAULT_MAX_TIME_TO_HOLD_MESSAGES_S = 21600 const DEFAULT_WAIT_TIME_FOR_MESSAGE_S = 10 +const BATCH_STORE_PATH = '/cas/anchor/batch/' /** * Sqs Queue Message received by consumers. * Once the message is done processing you can either "ack" the message (remove the message from the queue) or "nack" the message (put the message back on the queue) @@ -60,6 +64,28 @@ export class SqsQueueMessage implements IQueueM } } +// This wrapper around SqsQueueMessage is used to handle the case where the list of batch request IDs is empty and must +// be fetched from S3. The underlying SqsQueueMessage remains the same (and is what is used for n/acking the message), +// but the data is updated to include the batch request IDs. +export class BatchQueueMessage implements IQueueMessage { + readonly data: AnchorBatchQMessage + + constructor( + private readonly anchorBatchMessage: IQueueMessage, + batchJson: any + ) { + this.data = decode(AnchorBatchQMessage, batchJson) + } + + async ack(): Promise { + await this.anchorBatchMessage.ack() + } + + async nack(): Promise { + await this.anchorBatchMessage.nack() + } +} + /** * Consumer and Producer for Sqs Queues */ @@ -149,10 +175,50 @@ export class ValidationSqsQueueService extends SqsQueueService * AnchorBatchSqsQueueService is used to consume and publish anchor batch messages. These batches are anchored by anchor workers */ export class AnchorBatchSqsQueueService extends SqsQueueService { - constructor(config: Config) { + constructor( + config: Config, + private s3StorePath = config.queue.s3BucketName + BATCH_STORE_PATH, + private s3Endpoint = config.queue.s3Endpoint ? config.queue.s3Endpoint : undefined, + private _s3store?: LevelUp.LevelUp + ) { const queueUrl = config.queue.sqsQueueUrl + 'batch' super(config, queueUrl, AnchorBatchQMessage) } + + /** + * `new LevelUp` attempts to open a database, which leads to a request to AWS. + * Let's make initialization lazy. + */ + get s3store(): LevelUp.LevelUp { + if (!this._s3store) { + const levelDown = this.s3Endpoint + ? new S3LevelDOWN( + this.s3StorePath, + new AWSSDK.S3({ + endpoint: this.s3Endpoint, + s3ForcePathStyle: true, + }) + ) + : new S3LevelDOWN(this.s3StorePath) + + this._s3store = new LevelUp(levelDown) + } + return this._s3store + } + + override async receiveMessage(abortOptions?: AbortOptions): Promise | undefined> { + const anchorBatchMessage: IQueueMessage | undefined = await super.receiveMessage(abortOptions) + // If the list of batch request IDs is empty, we need to fetch the full batch from S3. + if (anchorBatchMessage && anchorBatchMessage.data.rids.length === 0) { + try { + const batchJson = await this.s3store.get(anchorBatchMessage.data.bid) + return new BatchQueueMessage(anchorBatchMessage, batchJson) + } catch (err: any) { + throw Error(`Error retrieving batch ${anchorBatchMessage.data.bid} from S3: ${err.message}`) + } + } + return anchorBatchMessage + } } /** From db9fbe0dc634f5f30440a38e6e799a549c8db219 Mon Sep 17 00:00:00 2001 From: Mohsin Zaidi <2236875+smrz2001@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:27:50 -0400 Subject: [PATCH 02/11] feat: disable ipfs storage for anchors by default --- src/services/anchor-service.ts | 71 ++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/src/services/anchor-service.ts b/src/services/anchor-service.ts index 40d68bd90..26daf4089 100644 --- a/src/services/anchor-service.ts +++ b/src/services/anchor-service.ts @@ -314,17 +314,20 @@ export class AnchorService { logger.debug('Creating IPFS anchor proof') const ipfsProofCid = this._createIPFSProof(merkleTree.car, tx, merkleTree.root.data.cid) - // Create anchor records on IPFS + // Create anchor records logger.debug('Creating anchor commits') const anchors = await this._createAnchorCommits(ipfsProofCid, merkleTree) - try { - await this.ipfsService.importCAR(merkleTree.car) - } catch (e) { - Metrics.count(METRIC_NAMES.MERKLE_CAR_STORAGE_FAILURE_IPFS, 1) - const message = `Can not store Merkle CAR to IPFS. Batch failed: ${e}` - logger.err(message) - throw e + // Do not use IPFS by default + if (process.env['CAS_USE_IPFS']) { + try { + await this.ipfsService.importCAR(merkleTree.car) + } catch (e) { + Metrics.count(METRIC_NAMES.MERKLE_CAR_STORAGE_FAILURE_IPFS, 1) + const message = `Can not store Merkle CAR to IPFS. Batch failed: ${e}` + logger.err(message) + throw e + } } try { @@ -501,29 +504,37 @@ export class AnchorService { cid: anchorCid, } - try { - await this.ipfsService.storeRecord(ipfsAnchorCommit) - - // Do not publish to pubsub by default - if (process.env['CAS_PUBSUB_PUBLISH']) { - // TODO: Remove this case entirely after js-ceramic no longer supports pubsub - await this.ipfsService.publishAnchorCommit(anchorCid, candidate.streamId) - logger.debug( - `Created anchor commit with CID ${anchorCid} for commit ${candidate.cid} of stream ${candidate.streamId} and published it to pubsub` - ) - } else { - logger.debug( - `Created anchor commit with CID ${anchorCid} for commit ${candidate.cid} of stream ${candidate.streamId}` - ) + // Do not use IPFS by default + if (process.env['CAS_USE_IPFS']) { + try { + await this.ipfsService.storeRecord(ipfsAnchorCommit) + + // Do not publish to pubsub by default + if (process.env['CAS_PUBSUB_PUBLISH']) { + // TODO: Remove this case entirely after js-ceramic no longer supports pubsub + await this.ipfsService.publishAnchorCommit(anchorCid, candidate.streamId) + logger.debug( + `Created anchor commit with CID ${anchorCid} for commit ${candidate.cid} of stream ${candidate.streamId} and published it to pubsub` + ) + } else { + logger.debug( + `Created anchor commit with CID ${anchorCid} for commit ${candidate.cid} of stream ${candidate.streamId}` + ) + } + + return anchor + } catch (err) { + const msg = `Error publishing anchor commit of commit ${ + candidate.cid + } for stream ${candidate.streamId.toString()}: ${err}` + logger.err(msg) + Metrics.count(METRIC_NAMES.ERROR_IPFS, 1) + return anchor } - - return anchor - } catch (err) { - const msg = `Error publishing anchor commit of commit ${ - candidate.cid - } for stream ${candidate.streamId.toString()}: ${err}` - logger.err(msg) - Metrics.count(METRIC_NAMES.ERROR_IPFS, 1) + } else { + logger.debug( + `Created anchor commit with CID ${anchorCid} for commit ${candidate.cid} of stream ${candidate.streamId}` + ) return anchor } } From 4632026896567eeb5cfc78f52113802f1eb26995 Mon Sep 17 00:00:00 2001 From: Mohsin Zaidi <2236875+smrz2001@users.noreply.github.com> Date: Wed, 27 Mar 2024 10:30:19 -0400 Subject: [PATCH 03/11] parsing fixes --- src/services/queue/sqs-queue-service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/queue/sqs-queue-service.ts b/src/services/queue/sqs-queue-service.ts index caad34e05..8e83b095b 100644 --- a/src/services/queue/sqs-queue-service.ts +++ b/src/services/queue/sqs-queue-service.ts @@ -22,7 +22,7 @@ import { AbortOptions } from '@ceramicnetwork/common' const DEFAULT_MAX_TIME_TO_HOLD_MESSAGES_S = 21600 const DEFAULT_WAIT_TIME_FOR_MESSAGE_S = 10 -const BATCH_STORE_PATH = '/cas/anchor/batch/' +const BATCH_STORE_PATH = '/cas/anchor/batch' /** * Sqs Queue Message received by consumers. * Once the message is done processing you can either "ack" the message (remove the message from the queue) or "nack" the message (put the message back on the queue) @@ -212,7 +212,7 @@ export class AnchorBatchSqsQueueService extends SqsQueueService Date: Wed, 27 Mar 2024 17:22:42 -0400 Subject: [PATCH 04/11] use separate env var controlling ipfs storage --- src/services/anchor-service.ts | 56 +++++++++++++++------------------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/src/services/anchor-service.ts b/src/services/anchor-service.ts index 26daf4089..a1bc728d1 100644 --- a/src/services/anchor-service.ts +++ b/src/services/anchor-service.ts @@ -318,8 +318,8 @@ export class AnchorService { logger.debug('Creating anchor commits') const anchors = await this._createAnchorCommits(ipfsProofCid, merkleTree) - // Do not use IPFS by default - if (process.env['CAS_USE_IPFS']) { + // Do not store CAR file in IPFS by default + if (process.env['CAS_USE_IPFS_STORAGE']) { try { await this.ipfsService.importCAR(merkleTree.car) } catch (e) { @@ -504,37 +504,31 @@ export class AnchorService { cid: anchorCid, } - // Do not use IPFS by default - if (process.env['CAS_USE_IPFS']) { - try { + try { + // Do not store in IPFS by default + if (process.env['CAS_USE_IPFS_STORAGE']) { await this.ipfsService.storeRecord(ipfsAnchorCommit) - - // Do not publish to pubsub by default - if (process.env['CAS_PUBSUB_PUBLISH']) { - // TODO: Remove this case entirely after js-ceramic no longer supports pubsub - await this.ipfsService.publishAnchorCommit(anchorCid, candidate.streamId) - logger.debug( - `Created anchor commit with CID ${anchorCid} for commit ${candidate.cid} of stream ${candidate.streamId} and published it to pubsub` - ) - } else { - logger.debug( - `Created anchor commit with CID ${anchorCid} for commit ${candidate.cid} of stream ${candidate.streamId}` - ) - } - - return anchor - } catch (err) { - const msg = `Error publishing anchor commit of commit ${ - candidate.cid - } for stream ${candidate.streamId.toString()}: ${err}` - logger.err(msg) - Metrics.count(METRIC_NAMES.ERROR_IPFS, 1) - return anchor } - } else { - logger.debug( - `Created anchor commit with CID ${anchorCid} for commit ${candidate.cid} of stream ${candidate.streamId}` - ) + // Do not publish to pubsub by default + if (process.env['CAS_PUBSUB_PUBLISH']) { + // TODO: Remove this case entirely after js-ceramic no longer supports pubsub + await this.ipfsService.publishAnchorCommit(anchorCid, candidate.streamId) + logger.debug( + `Created anchor commit with CID ${anchorCid} for commit ${candidate.cid} of stream ${candidate.streamId} and published it to pubsub` + ) + } else { + logger.debug( + `Created anchor commit with CID ${anchorCid} for commit ${candidate.cid} of stream ${candidate.streamId}` + ) + } + + return anchor + } catch (err) { + const msg = `Error publishing anchor commit of commit ${ + candidate.cid + } for stream ${candidate.streamId.toString()}: ${err}` + logger.err(msg) + Metrics.count(METRIC_NAMES.ERROR_IPFS, 1) return anchor } } From 13f1c5ddd68ef65b6c63c0d5d133e2f238d7178f Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Thu, 11 Apr 2024 16:38:59 -0400 Subject: [PATCH 05/11] feat: use batch insert and batch update --- src/repositories/anchor-repository.ts | 8 +++---- src/repositories/request-repository.ts | 29 ++++++++++++++++++-------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/repositories/anchor-repository.ts b/src/repositories/anchor-repository.ts index 608847e9c..760f667f3 100644 --- a/src/repositories/anchor-repository.ts +++ b/src/repositories/anchor-repository.ts @@ -6,6 +6,7 @@ import { parseCountResult } from './parse-count-result.util.js' import { decode } from 'codeco' const TABLE_NAME = 'anchor' +const chunkSize = 10000 export class AnchorRepository implements IAnchorRepository { static inject = ['dbConnection'] as const @@ -32,11 +33,8 @@ export class AnchorRepository implements IAnchorRepository { * @returns A promise that resolve to the number of anchors created */ async createAnchors(anchors: Array): Promise { - const result: any = await this.table - .insert(anchors.map((anchor) => FreshAnchor.encode(anchor))) - .onConflict('requestId') - .ignore() - return parseCountResult(result.rowCount) + const result = await this.connection.batchInsert(TABLE_NAME, anchors, chunkSize) + return parseCountResult(result.length) } /** diff --git a/src/repositories/request-repository.ts b/src/repositories/request-repository.ts index 5a48cc89e..692049d81 100644 --- a/src/repositories/request-repository.ts +++ b/src/repositories/request-repository.ts @@ -34,6 +34,7 @@ export const FAILURE_RETRY_INTERVAL = 1000 * 60 * 60 * 6 // 6H const REPEATED_READ_SERIALIZATION_ERROR = '40001' const TABLE_NAME = 'request' +const chunkSize = 10000 /** * Records statistics about the set of requests @@ -169,15 +170,25 @@ export class RequestRepository { */ async updateRequests(fields: RequestUpdateFields, requests: Request[]): Promise { const updatedAt = new Date() - const ids = requests.map((r) => r.id) - const result = await this.table - .update({ - message: fields.message, - status: fields.status, - pinned: fields.pinned, - updatedAt: date.encode(updatedAt), - }) - .whereIn('id', ids) + let result = 0 + + for (let i = 0; i < requests.length; i += chunkSize) { + const chunk = requests.slice(i, i + chunkSize) + const ids = chunk.map((r) => r.id) + try { + result += await this.table + .update({ + message: fields.message, + status: fields.status, + pinned: fields.pinned, + updatedAt: date.encode(updatedAt), + }) + .whereIn('id', ids) + + } catch (error) { + throw error + } + } requests.map((request) => { logEvent.db({ From 1c9a24a24761cfeb2bf41c205cebfe67a903d43c Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Sun, 14 Apr 2024 19:03:41 -0400 Subject: [PATCH 06/11] feat: update createAnchor function --- src/repositories/anchor-repository.ts | 4 +++- src/services/__tests__/ipfs-service.test.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/repositories/anchor-repository.ts b/src/repositories/anchor-repository.ts index 760f667f3..df9d359ac 100644 --- a/src/repositories/anchor-repository.ts +++ b/src/repositories/anchor-repository.ts @@ -33,7 +33,9 @@ export class AnchorRepository implements IAnchorRepository { * @returns A promise that resolve to the number of anchors created */ async createAnchors(anchors: Array): Promise { - const result = await this.connection.batchInsert(TABLE_NAME, anchors, chunkSize) + const result = await this.connection.batchInsert(TABLE_NAME, anchors, chunkSize).catch(function (error) { + console.error(error) + }) ?? [] return parseCountResult(result.length) } diff --git a/src/services/__tests__/ipfs-service.test.ts b/src/services/__tests__/ipfs-service.test.ts index be5ff3748..73c5ed5c9 100644 --- a/src/services/__tests__/ipfs-service.test.ts +++ b/src/services/__tests__/ipfs-service.test.ts @@ -258,7 +258,7 @@ describe('pubsub', () => { expect(queueSendMessageSpy).toBeCalledTimes(0) }) - test('Will respond to query message about stream with an anchor', async () => { + test.only('Will respond to query message about stream with an anchor', async () => { const pubsubMessage = { typ: 1, id: '1', stream: randomStreamID() } const ipfsQueueService = injector.resolve('ipfsQueueService') From 300fd58d64d5e9a866f3a1d847e8b4ff8596b1b0 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Mon, 15 Apr 2024 09:14:22 -0400 Subject: [PATCH 07/11] feat: encode anchors when inserting --- src/repositories/anchor-repository.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/repositories/anchor-repository.ts b/src/repositories/anchor-repository.ts index df9d359ac..f46b910dd 100644 --- a/src/repositories/anchor-repository.ts +++ b/src/repositories/anchor-repository.ts @@ -33,7 +33,7 @@ export class AnchorRepository implements IAnchorRepository { * @returns A promise that resolve to the number of anchors created */ async createAnchors(anchors: Array): Promise { - const result = await this.connection.batchInsert(TABLE_NAME, anchors, chunkSize).catch(function (error) { + const result = await this.connection.batchInsert(TABLE_NAME, anchors.map((anchor) => FreshAnchor.encode(anchor)), chunkSize).catch(function (error) { console.error(error) }) ?? [] return parseCountResult(result.length) @@ -55,6 +55,7 @@ export class AnchorRepository implements IAnchorRepository { async findByRequestId(requestId: string): Promise { const row = await this.table.where({ requestId: requestId }).first() if (!row) return null + console.log("IM HERE with ", row, requestId) return decode(StoredAnchor, row) } } From 38e002deb0a20cc307d663594bbe77b397767080 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Mon, 15 Apr 2024 09:27:22 -0400 Subject: [PATCH 08/11] chore: run lint --- src/repositories/request-repository.ts | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/repositories/request-repository.ts b/src/repositories/request-repository.ts index 692049d81..2944f7805 100644 --- a/src/repositories/request-repository.ts +++ b/src/repositories/request-repository.ts @@ -175,19 +175,15 @@ export class RequestRepository { for (let i = 0; i < requests.length; i += chunkSize) { const chunk = requests.slice(i, i + chunkSize) const ids = chunk.map((r) => r.id) - try { - result += await this.table - .update({ - message: fields.message, - status: fields.status, - pinned: fields.pinned, - updatedAt: date.encode(updatedAt), - }) - .whereIn('id', ids) - - } catch (error) { - throw error - } + + result += await this.table + .update({ + message: fields.message, + status: fields.status, + pinned: fields.pinned, + updatedAt: date.encode(updatedAt), + }) + .whereIn('id', ids) } requests.map((request) => { From 32de2144223448ec9ee960d3cee005720b83f179 Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Mon, 15 Apr 2024 09:44:46 -0400 Subject: [PATCH 09/11] chore: remove .only from test file --- src/services/__tests__/ipfs-service.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/__tests__/ipfs-service.test.ts b/src/services/__tests__/ipfs-service.test.ts index 73c5ed5c9..be5ff3748 100644 --- a/src/services/__tests__/ipfs-service.test.ts +++ b/src/services/__tests__/ipfs-service.test.ts @@ -258,7 +258,7 @@ describe('pubsub', () => { expect(queueSendMessageSpy).toBeCalledTimes(0) }) - test.only('Will respond to query message about stream with an anchor', async () => { + test('Will respond to query message about stream with an anchor', async () => { const pubsubMessage = { typ: 1, id: '1', stream: randomStreamID() } const ipfsQueueService = injector.resolve('ipfsQueueService') From 6e6a7756a2a1cf386ed7c79e3c302dd9e9c246ef Mon Sep 17 00:00:00 2001 From: JulissaDantes Date: Mon, 15 Apr 2024 15:30:02 -0400 Subject: [PATCH 10/11] chore: Update anchor-repository.ts --- src/repositories/anchor-repository.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/repositories/anchor-repository.ts b/src/repositories/anchor-repository.ts index f46b910dd..d7d88b7da 100644 --- a/src/repositories/anchor-repository.ts +++ b/src/repositories/anchor-repository.ts @@ -55,7 +55,6 @@ export class AnchorRepository implements IAnchorRepository { async findByRequestId(requestId: string): Promise { const row = await this.table.where({ requestId: requestId }).first() if (!row) return null - console.log("IM HERE with ", row, requestId) return decode(StoredAnchor, row) } } From 05ded08655339ba1a66d712f01da7cce65d57855 Mon Sep 17 00:00:00 2001 From: Mohsin Zaidi <2236875+smrz2001@users.noreply.github.com> Date: Tue, 16 Apr 2024 15:36:46 -0400 Subject: [PATCH 11/11] disable car file logging --- src/auth/index.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/auth/index.ts b/src/auth/index.ts index 7cccf41a7..19b583975 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -42,12 +42,12 @@ function buildBodyDigest(contentType: string | undefined, body: any): string | u if (contentType.includes('application/vnd.ipld.car')) { const carFactory = new CARFactory() carFactory.codecs.add(DAG_JOSE) - console.log('Will build a car file from req.body', body) - try { - console.log('Will build a car file from req.body (as utf8 string)', u8a.toString(body, 'base64')) - } catch(e) { - console.log('Couldn\'t convert req.body to string: ', e) - } + // console.log('Will build a car file from req.body', body) + // try { + // console.log('Will build a car file from req.body (as utf8 string)', u8a.toString(body, 'base64')) + // } catch(e) { + // console.log('Couldn\'t convert req.body to string: ', e) + // } const car = carFactory.fromBytes(body) if (!car.roots[0]) throw Error('Missing CAR root') return car.roots[0].toString()