Skip to content

Commit

Permalink
fix(backend): cors origin and tx reject (#4)
Browse files Browse the repository at this point in the history
* fix(backend): cors origin and tx reject

* chore logs
  • Loading branch information
RetricSu authored Jan 13, 2025
1 parent a8f7ce2 commit 0a976b1
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 10 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ TESTNET_HTTP_RPC_URL = "https://testnet.ckb.dev"

API_HTTP_PORT = 3000
API_WS_PORT = 3001
ALLOW_ORIGIN = "*" # for multiple allow origins, separate with , eg: "domain1.com,domain2.com"
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,23 @@ pnpm dev
cd frontend && pnpm dev
```

## .env config

The following environment variables need to be configured:

- `MAINNET_DATABASE_FILE`: Path to SQLite database file for mainnet
- `TESTNET_DATABASE_FILE`: Path to SQLite database file for testnet
- `MAINNET_WS_RPC_URL`: CKB Node WebSocket RPC URL for mainnet
- `TESTNET_WS_RPC_URL`: CKB Node WebSocket RPC URL for testnet
- `MAINNET_HTTP_RPC_URL`: CKB Node HTTP RPC URL for mainnet
- `TESTNET_HTTP_RPC_URL`: CKB Node HTTP RPC URL for testnet
- `API_HTTP_PORT`: Port for Backend HTTP API server
- `API_WS_PORT`: Port for Backend WebSocket server
- `ALLOW_ORIGIN`: CORS allowed origins (default: "*") for Backend server
- for multiple allow origins, separate with , eg: "domain1.com,domain2.com"

See `.env.example` for default values.

## License

This project is licensed under the MIT License - see the LICENSE file for details.
15 changes: 12 additions & 3 deletions src/api/sever.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
import type { Hex } from "@ckb-ccc/core";
import express, { type Request, type Response } from "express";
import { Config } from "../core/config";
import type { DB } from "../db";
import { logger } from "../util/logger";

export function createServer(db: DB) {
const app = express();

app.use((_req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET");
res.header("Access-Control-Allow-Headers", "Content-Type");
for (const origin of Config.allowOrigin) {
res.header("Access-Control-Allow-Origin", origin);
}
if (Config.allowOrigin.length > 0) {
res.header(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE",
);
res.header("Access-Control-Allow-Headers", "Content-Type");
}

next();
});

Expand Down
21 changes: 21 additions & 0 deletions src/core/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "dotenv/config";
import process from "node:process";
import { URL } from "node:url";

export const Config = {
mainnetDatabaseFile:
Expand All @@ -19,4 +20,24 @@ export const Config = {

apiHttpPort: Number(process.env.API_HTTP_PORT ?? 3000),
apiWsPort: Number(process.env.API_WS_PORT ?? 3001),
allowOrigin: extractAllowOriginList(process.env.ALLOW_ORIGIN ?? "*"),
};

export function extractAllowOriginList(value: string) {
const list = value
.split(",")
// test valid url
.filter((v) => {
if (v === "*") {
return true;
}

try {
new URL(v.trim());
return true;
} catch {
return false;
}
});
return list;
}
20 changes: 20 additions & 0 deletions src/core/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,26 @@ export interface JsonRpcPoolTransactionEntry {
timestamp: Hex;
}

export interface PoolTransactionReject {
type: PoolTransactionRejectType;
description: string;
}

export enum PoolTransactionRejectType {
LowFeeRate = "LowFeeRate", // transaction fee lower than config
ExceededMaximumAncestorsCount = "ExceededMaximumAncestorsCount", // Transaction exceeded maximum ancestors count limit
ExceededTransactionSizeLimit = "ExceededTransactionSizeLimit", // Transaction exceeded maximum size limit
Full = "Full", // Transaction are replaced because the pool is full
Duplicated = "Duplicated", // Transaction already exists in transaction_pool
Malformed = "Malformed", // Malformed transaction
DeclaredWrongCycles = "DeclaredWrongCycles", // Declared wrong cycles
Resolve = "Resolve", // Resolve failed
Verification = "Verification", // Verification failed
Expiry = "Expiry", // Transaction expired
RBFRejected = "RBFRejected", // RBF rejected
Invalidated = "Invalidated", // Invalidated rejected
}

export type DBBlockHeader = {
id: number;
compact_target: Hex;
Expand Down
11 changes: 5 additions & 6 deletions src/core/ws.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import {
type JsonRpcBlock,
type JsonRpcTransaction,
JsonRpcTransformers,
} from "@ckb-ccc/core/advancedBarrel";
import { WebSocket } from "ws";
import type { DB } from "../db";
import { logger } from "../util/logger";
import type {
JsonRpcPoolTransactionEntry,
JsonRpcTransactionView,
PoolTransactionReject,
} from "./type";

export interface WebsocketTopicSubscriber {
Expand Down Expand Up @@ -100,10 +99,10 @@ export class Subscriber {
sub_id: undefined,
handler: ([tx, reason]: [
JsonRpcPoolTransactionEntry,
string,
PoolTransactionReject,
]) => {
logger.debug(
`new rejected tx: ${tx.transaction.hash.slice(0, 22)}, reason: ${reason}`,
`new rejected tx: ${tx.transaction.hash.slice(0, 22)}, reason: ${JSON.stringify(reason)}`,
);
this.db.updateMempoolRejectedTransaction(tx, reason);
},
Expand All @@ -115,7 +114,7 @@ export class Subscriber {
run() {
const topics = this.createTopicSubscriber();
this.ws.on("open", () => {
logger.info("Connected to CKB node ", this.ckbRpcUrl);
logger.info(`Connected to CKB node ${this.ckbRpcUrl}`);
for (const topic of topics) {
this.ws.send(
JSON.stringify({
Expand Down Expand Up @@ -159,7 +158,7 @@ export class Subscriber {
});

this.ws.on("close", () => {
logger.info("Disconnected from CKB node");
logger.info(`Disconnected from CKB node ${this.ckbRpcUrl}`);
});
}
}
4 changes: 3 additions & 1 deletion src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
DBBlockHeader,
JsonRpcPoolTransactionEntry,
JsonRpcTransactionView,
PoolTransactionReject,
} from "../core/type";
import { getNowTimestamp } from "../util/time";
import { DepType, HashType, TransactionStatus } from "./type";
Expand Down Expand Up @@ -371,8 +372,9 @@ export class DB {

updateMempoolRejectedTransaction(
tx: JsonRpcPoolTransactionEntry,
reason: string,
txReject: PoolTransactionReject,
) {
const reason = `${txReject.type}: ${txReject.description}`;
const getStmt = this.db.prepare<[Hex], { id: DBId }>(
"SELECT id From transactions WHERE tx_hash = ?",
);
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { testnetSubscriber } from "./core/sub";
import { logger } from "./util/logger";

async function main() {
logger.info(`Config: ${JSON.stringify(Config, null, 2)}`);

testnetSubscriber.run();

const testnetSever = createServer(testnetDB);
Expand Down

0 comments on commit 0a976b1

Please sign in to comment.