apps/playground-api/src/controllers/RpcController.ts
import {
Controller,
Param,
Post,
Body,
ForbiddenException,
HttpCode,
PipeTransform,
Injectable,
ArgumentMetadata
} from '@nestjs/common'
import { ApiClient } from '@defichain/jellyfish-api-core'
import { IsArray, IsOptional } from 'class-validator'
import { Transform } from 'class-transformer'
/**
* MethodWhitelist is a whitelist validation pipe to check whether a plain old rpc can be
* routed through playground. Non whitelisted method call will result in a ForbiddenException.
*
* Direct access to DeFiD should not be allowed, that could used used as an attack against
* DeFi playground services. (e.g. by changing our peers)
*/
@Injectable()
export class MethodBlacklist implements PipeTransform {
static methods: string[] = [
'clearmempool',
'pruneblockchain',
'invalidateblock',
'reconsiderblock',
'submitblock',
'submitheader',
'addnode',
'disconnectnode',
'setban',
'setnetworkactive',
'clearbanned',
'help',
'stop',
'uptime',
'backupwallet',
'createwallet',
'dumpwallet',
'encryptwallet',
'importaddress',
'importmulti',
'importprivkey',
'importprunedfunds',
'importpubkey',
'importwallet',
'loadwallet',
'lockunspent',
'rescanblockchain',
'sethdseed',
'settxfee',
'unloadwallet',
'walletlock'
]
transform (value: string, metadata: ArgumentMetadata): string {
if (MethodBlacklist.methods.includes(value)) {
throw new ForbiddenException('RPC method is blacklisted')
}
return value
}
}
export class CallRequest {
@IsOptional()
@IsArray()
@Transform(({ value }) => value !== undefined ? value : [])
params!: any[]
}
@Controller('/v0/playground/rpc')
export class RpcController {
constructor (private readonly client: ApiClient) {
}
@Post('/:method')
@HttpCode(200)
async call (@Param('method', MethodBlacklist) method: string, @Body() call?: CallRequest): Promise<any> {
return await this.client.call(method, call?.params ?? [], 'lossless')
}
}