Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Development #126

Merged
merged 7 commits into from
Oct 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,29 @@ CREATE TABLE "Block" (
CONSTRAINT "Block_pkey" PRIMARY KEY ("height")
);

-- CreateTable
CREATE TABLE "BlockchainApp" (
"chainID" TEXT NOT NULL,
"chainName" TEXT NOT NULL,
"address" TEXT NOT NULL,
"status" TEXT,
"lastCertificateHeight" INTEGER,
"lastUpdated" INTEGER,
"escrowedKLY" TEXT,

CONSTRAINT "BlockchainApp_pkey" PRIMARY KEY ("chainID")
);

-- CreateTable
CREATE TABLE "Escrow" (
"id" SERIAL NOT NULL,
"tokenID" TEXT NOT NULL,
"amount" TEXT NOT NULL,
"blockchainAppId" TEXT NOT NULL,

CONSTRAINT "Escrow_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "cachedSchemas" (
"id" INTEGER NOT NULL,
Expand Down Expand Up @@ -252,6 +275,12 @@ CREATE UNIQUE INDEX "Block_height_key" ON "Block"("height");
-- CreateIndex
CREATE UNIQUE INDEX "Block_id_key" ON "Block"("id");

-- CreateIndex
CREATE UNIQUE INDEX "BlockchainApp_chainID_key" ON "BlockchainApp"("chainID");

-- CreateIndex
CREATE UNIQUE INDEX "BlockchainApp_chainName_key" ON "BlockchainApp"("chainName");

-- CreateIndex
CREATE UNIQUE INDEX "NextBlockToSync_id_key" ON "NextBlockToSync"("id");

Expand Down Expand Up @@ -279,6 +308,9 @@ ALTER TABLE "Asset" ADD CONSTRAINT "Asset_height_fkey" FOREIGN KEY ("height") RE
-- AddForeignKey
ALTER TABLE "Block" ADD CONSTRAINT "Block_generatorAddress_fkey" FOREIGN KEY ("generatorAddress") REFERENCES "Account"("address") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "Escrow" ADD CONSTRAINT "Escrow_blockchainAppId_fkey" FOREIGN KEY ("blockchainAppId") REFERENCES "BlockchainApp"("chainID") ON DELETE RESTRICT ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "ChainEvents" ADD CONSTRAINT "ChainEvents_height_fkey" FOREIGN KEY ("height") REFERENCES "Block"("height") ON DELETE RESTRICT ON UPDATE CASCADE;

Expand Down
18 changes: 18 additions & 0 deletions prisma/schema/blockchainApp.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
model BlockchainApp {
chainID String @id @unique
chainName String @unique
address String
status String?
lastCertificateHeight Int?
lastUpdated Int?
escrowedKLY String?
escrow Escrow[]
}

model Escrow {
id Int @id @default(autoincrement())
tokenID String
amount String
blockchainApp BlockchainApp @relation(fields: [blockchainAppId], references: [chainID])
blockchainAppId String
}
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { APP_FILTER } from '@nestjs/core';
import { LokiExceptionFilter } from './filters/all-exceptions.filter';
import { BlockchainModule } from './modules/blockchain/blockchain.module';
import { FeesModule } from './modules/fees/fees.module';
import { InteroperabilityModule } from './modules/interoperability/interoperability.module';

@Module({
imports: [
Expand Down Expand Up @@ -63,6 +64,7 @@ import { FeesModule } from './modules/fees/fees.module';
RewardModule,
BlockchainModule,
FeesModule,
InteroperabilityModule,
],
controllers: [],
providers: [
Expand Down
8 changes: 6 additions & 2 deletions src/modules/block/block.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ export class BlockController {

const where: Prisma.BlockWhereInput = {
...(blockID && { id: blockID }),
...(height && { height: ControllerHelpers.buildRangeCondition(height) }),
...(height && {
AND: [
{ height: ControllerHelpers.buildRangeCondition(height) },
{ height: { not: 0 } }, // Exclude genesis block by default
],
}),
...(timestamp && { timestamp: ControllerHelpers.buildRangeCondition(timestamp) }),
...(generatorAddress && { generatorAddress }),
height: { not: 0 }, // Exclude genesis block be default
};

const [blocks, total] = await Promise.all([
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { PrismaService } from 'src/modules/prisma/prisma.service';
import { NodeApiService } from 'src/modules/node-api/node-api.service';
import { LokiLogger } from 'nestjs-loki-logger';
import { Block, Transaction } from '../../interfaces/block.interface';
import { TxEvents, UpdateBlockFee } from '../../interfaces/transaction.interface';
import { TxTypes, UpdateBlockFee } from '../../interfaces/transaction.interface';
import { getKlayr32AddressFromPublicKey } from 'src/utils/helpers';
import { Prisma } from '@prisma/client';
import { Payload } from '@prisma/client/runtime/library';
Expand Down Expand Up @@ -93,15 +93,18 @@ export class IndexTransactionHandler implements ICommandHandler<IndexTransaction
let receivingChainID = null;

switch (`${tx.module}:${tx.command}`) {
case TxEvents.POS_STAKE:
case TxTypes.POS_STAKE:
// TODO: this is not complete because validators are added after all txs, but decent quick solution which covers 80% +
await this.handlePosStake(txParams);
break;

case TxEvents.TOKEN_TRANSFER_CROSS_CHAIN:
case TxTypes.TOKEN_TRANSFER_CROSS_CHAIN:
receivingChainID = this.handleTransferCrossChain(txParams);
break;

case TxTypes.INTEROPERABILITY_REGISTER_SIDECHAIN:
await this.handleRegisterSidechain(txParams, senderAddress);

default:
break;
}
Expand Down Expand Up @@ -174,12 +177,17 @@ export class IndexTransactionHandler implements ICommandHandler<IndexTransaction
);
}

// ! Dont know if it works, will check on deployment
private handleTransferCrossChain(txParams: any): string {
this.logger.debug('Handling cross chain transfer');
this.logger.debug(`Params: ${txParams}`);
const receivingChainID = txParams.receivingChainID;
this.logger.debug(`Receiving chain ID: ${receivingChainID}`);
return receivingChainID;
return txParams.receivingChainID;
}

private async handleRegisterSidechain(txParams: any, sender: string): Promise<void> {
await this.prisma.blockchainApp.create({
data: {
chainID: txParams.chainID,
chainName: txParams.name,
address: sender,
},
});
}
}
72 changes: 72 additions & 0 deletions src/modules/indexer/event/commands/update-sidechain.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// handlers/token-execution-result.handler.ts
import { CommandHandler, ICommand, ICommandHandler } from '@nestjs/cqrs';
import { PrismaService } from 'src/modules/prisma/prisma.service';
import { NodeApiService } from 'src/modules/node-api/node-api.service';
import { LokiLogger } from 'nestjs-loki-logger';
import { ChainAccountUpdateEvent } from '../types';

enum BlockchainAppStatus {
Registered = 'registered',
Activated = 'activated',
Terminated = 'terminated',
}

export class UpdateSidechainCommand implements ICommand {
constructor(public readonly data: ChainAccountUpdateEvent) {}
}

@CommandHandler(UpdateSidechainCommand)
export class UpdateSidechainHandler implements ICommandHandler<UpdateSidechainCommand> {
private readonly logger = new LokiLogger(UpdateSidechainHandler.name);

constructor(
private readonly prisma: PrismaService,
private readonly nodeApi: NodeApiService,
) {}

async execute(command: UpdateSidechainCommand): Promise<void> {
this.logger.debug('Updating sidechain...', command.data.toString());
const { name, lastCertificate, status } = command.data;
const app = await this.prisma.blockchainApp.findUnique({
where: { chainName: name },
});

if (!app) {
this.logger.error(`BlockchainApp with chainName ${name} not found.`);
return;
}

const { escrowedAmounts } = this.nodeApi.tokenSummaryInfo.escrowedAmounts;
const escrowForApp = escrowedAmounts.find((escrow) => escrow.escrowChainID === app.chainID);

await this.prisma.blockchainApp.update({
where: { chainName: name },
data: {
status: this.getStatusString(status),
lastCertificateHeight: lastCertificate.height,
lastUpdated: lastCertificate.timestamp,
escrowedKLY: escrowForApp?.amount || '0',
escrow: {
deleteMany: {},
create: {
tokenID: escrowForApp.tokenID,
amount: escrowForApp.amount,
},
},
},
});
}

private getStatusString(status: number): string | undefined {
switch (status) {
case 0:
return BlockchainAppStatus.Registered;
case 1:
return BlockchainAppStatus.Activated;
case 2:
return BlockchainAppStatus.Terminated;
default:
return undefined;
}
}
}
4 changes: 4 additions & 0 deletions src/modules/indexer/event/event.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
filterValidatorStaked,
} from './event.filters';
import {
InteroperabilityChainAccountUpdated,
InteroperabilityCommandExecutionResult,
InteroperabilityEventTypes,
} from './gateways/interoperability.gateway';
Expand Down Expand Up @@ -44,6 +45,8 @@ export const eventGatewayMap = {

[InteroperabilityEventTypes.INTEROPERABILITY_COMMAND_EXECUTION_RESULT]:
InteroperabilityCommandExecutionResult,
[InteroperabilityEventTypes.INTEROPERABILITY_CHAIN_ACCOUNT_UPDATED]:
InteroperabilityChainAccountUpdated,
};

export const eventFilterMap = {
Expand All @@ -62,4 +65,5 @@ export const eventFilterMap = {
[TokenEventTypes.TOKEN_MINT]: filterTokenLockUnLockBurnMint,

[InteroperabilityEventTypes.INTEROPERABILITY_COMMAND_EXECUTION_RESULT]: alwaysTrue,
[InteroperabilityEventTypes.INTEROPERABILITY_CHAIN_ACCOUNT_UPDATED]: alwaysTrue,
};
10 changes: 10 additions & 0 deletions src/modules/indexer/event/gateways/interoperability.gateway.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import { ChainEvent } from 'src/modules/indexer/interfaces/chain-event.interface';
import { EventGateway } from './event.gateway';
import { ExecutionResultCommand } from '../commands/execution-result.command';
import { UpdateSidechainCommand } from '../commands/update-sidechain.command';
import { ChainAccountUpdateEvent } from '../types';

export enum InteroperabilityEventTypes {
INTEROPERABILITY_COMMAND_EXECUTION_RESULT = 'interoperability:commandExecutionResult',
INTEROPERABILITY_CHAIN_ACCOUNT_UPDATED = 'interoperability:chainAccountUpdated',
}

export class InteroperabilityCommandExecutionResult extends EventGateway {
async processChainEvent(event: ChainEvent): Promise<void> {
await this.commandBus.execute(new ExecutionResultCommand(event));
}
}

export class InteroperabilityChainAccountUpdated extends EventGateway {
async processChainEvent(event: ChainEvent): Promise<void> {
const data = JSON.parse(event.data as string) as ChainAccountUpdateEvent;
await this.commandBus.execute(new UpdateSidechainCommand(data));
}
}
13 changes: 13 additions & 0 deletions src/modules/indexer/event/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,16 @@ export type PosValidatorStaked = {
amount: string;
result: number;
};

export type ChainAccountUpdateEvent = {
name: string;
lastCertificate: LastCertificate;
status: number;
};

export type LastCertificate = {
height: number;
timestamp: number;
stateRoot: string;
validatorsHash: string;
};
2 changes: 2 additions & 0 deletions src/modules/indexer/indexer.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ProcessValidatorHandler } from './event/commands/process-validator.comm
import { AddStakesHandler } from './event/commands/add-stakes.command';
import { UpdateValidatorRanksHandler } from './event/commands/update-validator-ranks.command';
import { UpdateAccountHandler } from './event/commands/update-account.command';
import { UpdateSidechainHandler } from './event/commands/update-sidechain.command';

@Module({
imports: [CqrsModule, StateModule, NodeApiModule],
Expand Down Expand Up @@ -45,6 +46,7 @@ import { UpdateAccountHandler } from './event/commands/update-account.command';
AddStakesHandler,
UpdateValidatorRanksHandler,
UpdateAccountHandler,
UpdateSidechainHandler,
],
exports: [IndexerService],
})
Expand Down
4 changes: 3 additions & 1 deletion src/modules/indexer/interfaces/transaction.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ export enum ExecutionEventData {
FAILED = '0800',
}

export enum TxEvents {
export enum TxTypes {
POS_STAKE = 'pos:stake',
POS_REGISTER_VALIDATOR = 'pos:registerValidator',
POS_CHANGE_COMMISSION = 'pos:changeCommission',

TOKEN_TRANSFER = 'token:transfer',
TOKEN_TRANSFER_CROSS_CHAIN = 'token:transferCrossChain',

INTEROPERABILITY_REGISTER_SIDECHAIN = 'interoperability:registerSidechain',
}
16 changes: 16 additions & 0 deletions src/modules/interoperability/dto/get-app-stats-res.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ApiResponseOptions } from '@nestjs/swagger';

export class GetAppsStatsResDto {
registered: number;
activated: number;
terminated: number;
totalSupplyKLY: string;
totalStakedKLY: string;
currentAnnualInflationRate: string;
}

export const getAppsStatsResponse: ApiResponseOptions = {
status: 200,
description: 'Blockchain applications statistics have been successfully retrieved.',
type: GetAppsStatsResDto,
};
23 changes: 23 additions & 0 deletions src/modules/interoperability/dto/get-apps-res.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ApiResponseOptions } from '@nestjs/swagger';

class Escrow {
tokenID: string;
amount: string;
}
export class GetAppsResDto {
chainID: string;
chainName: string;
status: string;
address: string;
lastCerticateHeight: number;
lastUpdated: number;
escrowedKLY: string;
escrow: Escrow[];
}

export const getAppsResponse: ApiResponseOptions = {
status: 200,
description: 'Tokens meta data have been successfully retrieved.',
type: GetAppsResDto,
isArray: true,
};
Loading
Loading