diff --git a/src/blocks/blocks.controller.spec.ts b/src/blocks/blocks.controller.spec.ts index b46cdc16e..52272dcdd 100644 --- a/src/blocks/blocks.controller.spec.ts +++ b/src/blocks/blocks.controller.spec.ts @@ -12,7 +12,6 @@ import { GraphileWorkerService } from '../graphile-worker/graphile-worker.servic import { PrismaService } from '../prisma/prisma.service'; import { bootstrapTestApp } from '../test/test-app'; import { BlocksService } from './blocks.service'; -import { BatchUpdateGraffitiDto } from './dto/batch-update-graffiti.dto'; import { UpsertBlocksDto } from './dto/upsert-blocks.dto'; import { BlockOperation } from './enums/block-operation'; import { SerializedBlockWithTransactions } from './interfaces/serialized-block-with-transactions'; @@ -209,242 +208,6 @@ describe('BlocksController', () => { }); }); - describe('POST /blocks/batch_update_graffiti', () => { - it('throws error when too many update block objects provided', async () => { - const blockUpdate = { - hash: 'hash', - graffiti: - '676d702f202020202020202020202036356264346432632d6433373237383130', - }; - // array with 201 elements - const updates = new Array(201).fill(blockUpdate); - - await request(app.getHttpServer()) - .post('/blocks/batch_update_graffiti') - .set('Authorization', `Bearer ${API_KEY}`) - .send({ - updates, - }) - .expect(HttpStatus.UNPROCESSABLE_ENTITY); - }); - - it('throws error when too many update objects', async () => { - await request(app.getHttpServer()) - .post('/blocks/batch_update_graffiti') - .expect(HttpStatus.UNAUTHORIZED); - }); - - it('throw error when graffiti is not formatted correctly', async () => { - // graffiti is not a list - await request(app.getHttpServer()) - .post('/blocks/batch_update_graffiti') - .set('Authorization', `Bearer ${API_KEY}`) - .send({ - hash: 'hash', - graffiti: - '676d702f202020202020202020202036356264346432632d6433373237383130', - }) - .expect(HttpStatus.UNPROCESSABLE_ENTITY); - - await request(app.getHttpServer()) - .post('/blocks/batch_update_graffiti') - .set('Authorization', `Bearer ${API_KEY}`) - .send([ - { - hash: 'hash', - graffiti: - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0SSSS', - }, - ]) - .expect(HttpStatus.UNPROCESSABLE_ENTITY); - - await request(app.getHttpServer()) - .post('/blocks/batch_update_graffiti') - .set('Authorization', `Bearer ${API_KEY}`) - .send({ - updates: [ - { - hash: 'hash', - graffiti: - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0SSSS', - }, - ], - }) - .expect(HttpStatus.UNPROCESSABLE_ENTITY); - }); - - it('returns the block graffiti', async () => { - const updateGraffiti = jest - .spyOn(blocksService, 'batchUpdateGrafitti') - .mockImplementationOnce( - jest.fn(async (input: BatchUpdateGraffitiDto) => { - const updates = input.updates; - - const block1: Block = { - id: faker.datatype.number(), - created_at: new Date(), - updated_at: new Date(), - main: true, - network_version: 0, - time_since_last_block_ms: faker.datatype.number(), - hash: updates[0].hash, - difficulty: null, - work: null, - sequence: faker.datatype.number(), - timestamp: new Date(), - transactions_count: 0, - graffiti: updates[0].graffiti, - previous_block_hash: uuid(), - size: faker.datatype.number({ min: 1 }), - }; - - const block2: Block = { - id: faker.datatype.number(), - created_at: new Date(), - updated_at: new Date(), - main: true, - network_version: 0, - time_since_last_block_ms: faker.datatype.number(), - hash: updates[1].hash, - difficulty: null, - work: null, - sequence: faker.datatype.number(), - timestamp: new Date(), - transactions_count: 0, - graffiti: updates[1].graffiti, - previous_block_hash: uuid(), - size: faker.datatype.number({ min: 1 }), - }; - - await Promise.resolve([block1, block2]); - - return [block1, block2]; - }), - ); - - await request(app.getHttpServer()) - .post('/blocks/batch_update_graffiti') - .set('Authorization', `Bearer ${API_KEY}`) - .send({ - updates: [ - { - hash: 'hash1', - graffiti: - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9', - }, - { - hash: 'hash2', - graffiti: - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e0', - }, - ], - }) - .expect(HttpStatus.CREATED); - - expect(updateGraffiti).toHaveBeenCalledWith({ - updates: [ - { - hash: 'hash1', - graffiti: - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9', - }, - { - hash: 'hash2', - graffiti: - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e0', - }, - ], - }); - }); - }); - - describe('POST /blocks/update_graffiti', () => { - it('throws unauthorized error when no api key is provided', async () => { - await request(app.getHttpServer()) - .post('/blocks/update_graffiti') - .expect(HttpStatus.UNAUTHORIZED); - }); - - it('throw error when graffiti is not formatted correctly', async () => { - // graffiti is not hex - await request(app.getHttpServer()) - .post('/blocks/update_graffiti') - .set('Authorization', `Bearer ${API_KEY}`) - .send({ - hash: 'hash', - graffiti: - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0SSSS', - }) - .expect(HttpStatus.UNPROCESSABLE_ENTITY); - - // graffiti is not 64 characters long - await request(app.getHttpServer()) - .post('/blocks/update_graffiti') - .set('Authorization', `Bearer ${API_KEY}`) - .send({ - hash: 'hash', - graffiti: 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c', - }) - .expect(HttpStatus.UNPROCESSABLE_ENTITY); - - // graffiti is not 64 characters long - await request(app.getHttpServer()) - .post('/blocks/update_graffiti') - .set('Authorization', `Bearer ${API_KEY}`) - .send({ - hash: 'hash', - graffiti: - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2ca1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2ca1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c', - }) - .expect(HttpStatus.UNPROCESSABLE_ENTITY); - }); - - it('returns the block graffiti', async () => { - const updateGraffiti = jest - .spyOn(blocksService, 'updateGraffiti') - .mockImplementationOnce( - jest.fn(async (hash: string, graffiti: string) => { - const block: Block = { - id: faker.datatype.number(), - created_at: new Date(), - updated_at: new Date(), - main: true, - network_version: 0, - time_since_last_block_ms: faker.datatype.number(), - hash: hash, - difficulty: null, - work: null, - sequence: faker.datatype.number(), - timestamp: new Date(), - transactions_count: 0, - graffiti: graffiti, - previous_block_hash: uuid(), - size: faker.datatype.number({ min: 1 }), - }; - - await Promise.resolve(block); - - return block; - }), - ); - - await request(app.getHttpServer()) - .post('/blocks/update_graffiti') - .set('Authorization', `Bearer ${API_KEY}`) - .send({ - hash: 'hash', - graffiti: - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9', - }) - .expect(HttpStatus.CREATED); - - expect(updateGraffiti).toHaveBeenCalledWith( - 'hash', - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9', - ); - }); - }); - describe('GET /blocks', () => { describe('with no query parameters', () => { it('returns a list of blocks in descending order', async () => { diff --git a/src/blocks/blocks.controller.ts b/src/blocks/blocks.controller.ts index 620436694..59744ca44 100644 --- a/src/blocks/blocks.controller.ts +++ b/src/blocks/blocks.controller.ts @@ -29,12 +29,10 @@ import { PaginatedList } from '../common/interfaces/paginated-list'; import { divide } from '../common/utils/bigint'; import { serializedTransactionFromRecord } from '../transactions/utils/transaction-translator'; import { BlocksService } from './blocks.service'; -import { BatchUpdateGraffitiDto } from './dto/batch-update-graffiti.dto'; import { BlockQueryDto } from './dto/block-query.dto'; import { BlocksMetricsQueryDto } from './dto/blocks-metrics-query.dto'; import { BlocksQueryDto } from './dto/blocks-query.dto'; import { DisconnectBlocksDto } from './dto/disconnect-blocks.dto'; -import { UpdateGraffitiDto } from './dto/update-graffiti.dto'; import { UpsertBlocksDto } from './dto/upsert-blocks.dto'; import { SerializedBlock } from './interfaces/serialized-block'; import { SerializedBlockHead } from './interfaces/serialized-block-head'; @@ -47,7 +45,7 @@ import { } from './utils/block-translator'; import { serializedBlockMetricsFromRecord } from './utils/blocks-metrics-translator'; import { serializedBlocksStatusFromRecord } from './utils/blocks-status-translator'; -import { Asset, AssetDescription, Block, Transaction } from '.prisma/client'; +import { Asset, AssetDescription, Transaction } from '.prisma/client'; const MAX_SUPPORTED_TIME_RANGE_IN_DAYS = 90; @@ -62,47 +60,6 @@ export class BlocksController { private readonly blocksTransactionsLoader: BlocksTransactionsLoader, ) {} - @ApiExcludeEndpoint() - @Post('update_graffiti') - @UseGuards(ApiKeyGuard) - async updateGraffiti( - @Body( - new ValidationPipe({ - errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, - transform: true, - }), - ) - updateGraffitiDto: UpdateGraffitiDto, - ): Promise { - const block: Block = await this.blocksService.updateGraffiti( - updateGraffitiDto.hash, - updateGraffitiDto.graffiti, - ); - return serializedBlockFromRecord(block); - } - - @ApiExcludeEndpoint() - @Post('batch_update_graffiti') - @UseGuards(ApiKeyGuard) - async batchUpdateGraffiti( - @Body( - new ValidationPipe({ - errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY, - transform: true, - }), - ) - batchUpdateGraffitiDto: BatchUpdateGraffitiDto, - ): Promise> { - const blocks = await this.blocksService.batchUpdateGrafitti( - batchUpdateGraffitiDto, - ); - - return { - object: 'list', - data: blocks.map(serializedBlockFromRecord), - }; - } - @ApiExcludeEndpoint() @Post() @UseGuards(ApiKeyGuard) diff --git a/src/blocks/blocks.service.spec.ts b/src/blocks/blocks.service.spec.ts index 1e5a78b50..592aecf05 100644 --- a/src/blocks/blocks.service.spec.ts +++ b/src/blocks/blocks.service.spec.ts @@ -116,124 +116,6 @@ describe('BlocksService', () => { }); }); - describe('batchUpdateGraffiti', () => { - it('block not found', async () => { - const graffiti = - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9'; - const hash = uuid(); - - await expect( - blocksService.batchUpdateGrafitti({ - updates: [{ hash, graffiti }], - }), - ).rejects.toThrow(NotFoundException); - }); - - it('one block in multiple not found', async () => { - const block = await blocksService.upsert(prisma, { - hash: uuid(), - sequence: faker.datatype.number(), - difficulty: BigInt(faker.datatype.number()), - work: BigInt(faker.datatype.number()), - timestamp: new Date(), - transactionsCount: 1, - type: BlockOperation.CONNECTED, - graffiti: uuid(), - previousBlockHash: uuid(), - size: faker.datatype.number(), - }); - - const record = await blocksService.find(block.id); - expect(record).toMatchObject(block); - - // does not throw - await expect( - blocksService.batchUpdateGrafitti({ - updates: [{ hash: block.hash, graffiti: 'testGraffiti' }], - }), - ).resolves.not.toThrow(); - - await expect( - blocksService.batchUpdateGrafitti({ - updates: [ - { hash: block.hash, graffiti: 'testGraffiti' }, - { hash: uuid(), graffiti: 'testGraffiti' }, - ], - }), - ).rejects.toThrow(NotFoundException); - }); - - it('updates graffiti', async () => { - const block = await blocksService.upsert(prisma, { - hash: uuid(), - sequence: faker.datatype.number(), - difficulty: BigInt(faker.datatype.number()), - work: BigInt(faker.datatype.number()), - timestamp: new Date(), - transactionsCount: 1, - type: BlockOperation.CONNECTED, - graffiti: uuid(), - previousBlockHash: uuid(), - size: faker.datatype.number(), - }); - - const record = await blocksService.find(block.id); - expect(record).toMatchObject(block); - - await blocksService.batchUpdateGrafitti({ - updates: [{ hash: block.hash, graffiti: 'testGraffiti' }], - }); - - const updatedRecord = await blocksService.find(block.id); - - expect(updatedRecord).toMatchObject({ - ...block, - updated_at: updatedRecord?.updated_at, - graffiti: 'testGraffiti', - }); - }); - }); - - describe('updateGraffiti', () => { - it('block not found', async () => { - const graffiti = - 'a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9a1b3e4f2c8d0b7e9'; - const hash = uuid(); - - await expect( - blocksService.updateGraffiti(hash, graffiti), - ).rejects.toThrow(NotFoundException); - }); - - it('update graffiti', async () => { - const block = await blocksService.upsert(prisma, { - hash: uuid(), - sequence: faker.datatype.number(), - difficulty: BigInt(faker.datatype.number()), - work: BigInt(faker.datatype.number()), - timestamp: new Date(), - transactionsCount: 1, - type: BlockOperation.CONNECTED, - graffiti: uuid(), - previousBlockHash: uuid(), - size: faker.datatype.number(), - }); - - const record = await blocksService.find(block.id); - expect(record).toMatchObject(block); - - await blocksService.updateGraffiti(block.hash, 'testGraffiti'); - - const updatedRecord = await blocksService.find(block.id); - - expect(updatedRecord).toMatchObject({ - ...block, - updated_at: updatedRecord?.updated_at, - graffiti: 'testGraffiti', - }); - }); - }); - describe('miningReward', () => { it('returns the correct mining reward', () => { const reward = blocksService.miningReward(733106); diff --git a/src/blocks/blocks.service.ts b/src/blocks/blocks.service.ts index ffe14a89a..0fcbf2583 100644 --- a/src/blocks/blocks.service.ts +++ b/src/blocks/blocks.service.ts @@ -22,7 +22,6 @@ import { standardizeHash } from '../common/utils/hash'; import { assertValueIsSafeForPrisma } from '../common/utils/prisma'; import { PrismaService } from '../prisma/prisma.service'; import { BasePrismaClient } from '../prisma/types/base-prisma-client'; -import { BatchUpdateGraffitiDto } from './dto/batch-update-graffiti.dto'; import { BlockOperation } from './enums/block-operation'; import { BlocksDateMetrics } from './interfaces/blocks-date-metrics'; import { BlocksStatus } from './interfaces/blocks-status'; @@ -118,73 +117,6 @@ export class BlocksService { return { ...block, transactions }; } - async batchUpdateGrafitti(input: BatchUpdateGraffitiDto): Promise { - const networkVersion = this.config.get('NETWORK_VERSION'); - - const blocks = await this.prisma.block.findMany({ - where: { - hash: { - in: input.updates.map((update) => standardizeHash(update.hash)), - }, - network_version: networkVersion, - }, - }); - - if (blocks.length !== input.updates.length) { - throw new NotFoundException(); - } - - const hashGraffitiMap = new Map(); - input.updates.forEach((update) => { - hashGraffitiMap.set(update.hash, update.graffiti); - }); - - blocks.map((block) => { - const graffiti = hashGraffitiMap.get(block.hash); - if (graffiti) { - block.graffiti = graffiti; - } - }); - - await this.prisma.$transaction([ - ...blocks.map((block) => - this.prisma.block.update({ - data: { - graffiti: block.graffiti, - }, - where: { - id: block.id, - }, - }), - ), - ]); - - return blocks; - } - - async updateGraffiti(hash: string, graffiti: string): Promise { - const networkVersion = this.config.get('NETWORK_VERSION'); - const block = await this.prisma.readClient.block.findFirst({ - where: { - hash: standardizeHash(hash), - network_version: networkVersion, - }, - }); - - if (!block) { - throw new NotFoundException(); - } - - return await this.prisma.block.update({ - data: { - graffiti, - }, - where: { - id: block.id, - }, - }); - } - miningReward(sequence: number): number { if (sequence <= 1) { return 0; diff --git a/src/blocks/dto/batch-update-graffiti.dto.ts b/src/blocks/dto/batch-update-graffiti.dto.ts deleted file mode 100644 index 30300e781..000000000 --- a/src/blocks/dto/batch-update-graffiti.dto.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { - ArrayMaxSize, - ArrayMinSize, - IsArray, - ValidateNested, -} from 'class-validator'; -import { UpdateGraffitiDto } from './update-graffiti.dto'; - -export class BatchUpdateGraffitiDto { - @IsArray() - @ApiProperty({ description: 'hash + graffiti array' }) - @ArrayMinSize(1) - @ArrayMaxSize(200) - @ValidateNested({ each: true }) - @Type(() => UpdateGraffitiDto) - readonly updates!: UpdateGraffitiDto[]; -} diff --git a/src/blocks/dto/update-graffiti.dto.ts b/src/blocks/dto/update-graffiti.dto.ts deleted file mode 100644 index 6ba3aec96..000000000 --- a/src/blocks/dto/update-graffiti.dto.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -import { ApiProperty } from '@nestjs/swagger'; -import { - IsDefined, - IsString, - Matches, - MaxLength, - MinLength, -} from 'class-validator'; - -export class UpdateGraffitiDto { - @ApiProperty({ description: 'Block hash' }) - @IsDefined({ - message: '"hash" of the block', - }) - @IsString() - readonly hash!: string; - - @ApiProperty({ description: 'Block graffiti' }) - @IsDefined({ - message: '"graffiti" of the block', - }) - @MinLength(64, { - message: 'must be exactly 64 characters in length', - }) - @MaxLength(64, { - message: 'must be exactly 64 characters in length', - }) - @Matches(/^[0-9A-Fa-f]+$/, { - message: 'must be 64 length hex string', - }) - readonly graffiti!: string; -}