Skip to content
This repository was archived by the owner on Dec 10, 2025. It is now read-only.

Commit 0a976b1

Browse files
authored
fix(backend): cors origin and tx reject (#4)
* fix(backend): cors origin and tx reject * chore logs
1 parent a8f7ce2 commit 0a976b1

File tree

8 files changed

+81
-10
lines changed

8 files changed

+81
-10
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ TESTNET_HTTP_RPC_URL = "https://testnet.ckb.dev"
99

1010
API_HTTP_PORT = 3000
1111
API_WS_PORT = 3001
12+
ALLOW_ORIGIN = "*" # for multiple allow origins, separate with , eg: "domain1.com,domain2.com"

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ pnpm dev
5454
cd frontend && pnpm dev
5555
```
5656

57+
## .env config
58+
59+
The following environment variables need to be configured:
60+
61+
- `MAINNET_DATABASE_FILE`: Path to SQLite database file for mainnet
62+
- `TESTNET_DATABASE_FILE`: Path to SQLite database file for testnet
63+
- `MAINNET_WS_RPC_URL`: CKB Node WebSocket RPC URL for mainnet
64+
- `TESTNET_WS_RPC_URL`: CKB Node WebSocket RPC URL for testnet
65+
- `MAINNET_HTTP_RPC_URL`: CKB Node HTTP RPC URL for mainnet
66+
- `TESTNET_HTTP_RPC_URL`: CKB Node HTTP RPC URL for testnet
67+
- `API_HTTP_PORT`: Port for Backend HTTP API server
68+
- `API_WS_PORT`: Port for Backend WebSocket server
69+
- `ALLOW_ORIGIN`: CORS allowed origins (default: "*") for Backend server
70+
- for multiple allow origins, separate with , eg: "domain1.com,domain2.com"
71+
72+
See `.env.example` for default values.
73+
5774
## License
5875

5976
This project is licensed under the MIT License - see the LICENSE file for details.

src/api/sever.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
import type { Hex } from "@ckb-ccc/core";
22
import express, { type Request, type Response } from "express";
3+
import { Config } from "../core/config";
34
import type { DB } from "../db";
45
import { logger } from "../util/logger";
56

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

910
app.use((_req, res, next) => {
10-
res.header("Access-Control-Allow-Origin", "*");
11-
res.header("Access-Control-Allow-Methods", "GET");
12-
res.header("Access-Control-Allow-Headers", "Content-Type");
11+
for (const origin of Config.allowOrigin) {
12+
res.header("Access-Control-Allow-Origin", origin);
13+
}
14+
if (Config.allowOrigin.length > 0) {
15+
res.header(
16+
"Access-Control-Allow-Methods",
17+
"GET, POST, PUT, DELETE",
18+
);
19+
res.header("Access-Control-Allow-Headers", "Content-Type");
20+
}
21+
1322
next();
1423
});
1524

src/core/config.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "dotenv/config";
22
import process from "node:process";
3+
import { URL } from "node:url";
34

45
export const Config = {
56
mainnetDatabaseFile:
@@ -19,4 +20,24 @@ export const Config = {
1920

2021
apiHttpPort: Number(process.env.API_HTTP_PORT ?? 3000),
2122
apiWsPort: Number(process.env.API_WS_PORT ?? 3001),
23+
allowOrigin: extractAllowOriginList(process.env.ALLOW_ORIGIN ?? "*"),
2224
};
25+
26+
export function extractAllowOriginList(value: string) {
27+
const list = value
28+
.split(",")
29+
// test valid url
30+
.filter((v) => {
31+
if (v === "*") {
32+
return true;
33+
}
34+
35+
try {
36+
new URL(v.trim());
37+
return true;
38+
} catch {
39+
return false;
40+
}
41+
});
42+
return list;
43+
}

src/core/type.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,26 @@ export interface JsonRpcPoolTransactionEntry {
1313
timestamp: Hex;
1414
}
1515

16+
export interface PoolTransactionReject {
17+
type: PoolTransactionRejectType;
18+
description: string;
19+
}
20+
21+
export enum PoolTransactionRejectType {
22+
LowFeeRate = "LowFeeRate", // transaction fee lower than config
23+
ExceededMaximumAncestorsCount = "ExceededMaximumAncestorsCount", // Transaction exceeded maximum ancestors count limit
24+
ExceededTransactionSizeLimit = "ExceededTransactionSizeLimit", // Transaction exceeded maximum size limit
25+
Full = "Full", // Transaction are replaced because the pool is full
26+
Duplicated = "Duplicated", // Transaction already exists in transaction_pool
27+
Malformed = "Malformed", // Malformed transaction
28+
DeclaredWrongCycles = "DeclaredWrongCycles", // Declared wrong cycles
29+
Resolve = "Resolve", // Resolve failed
30+
Verification = "Verification", // Verification failed
31+
Expiry = "Expiry", // Transaction expired
32+
RBFRejected = "RBFRejected", // RBF rejected
33+
Invalidated = "Invalidated", // Invalidated rejected
34+
}
35+
1636
export type DBBlockHeader = {
1737
id: number;
1838
compact_target: Hex;

src/core/ws.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import {
22
type JsonRpcBlock,
3-
type JsonRpcTransaction,
43
JsonRpcTransformers,
54
} from "@ckb-ccc/core/advancedBarrel";
65
import { WebSocket } from "ws";
76
import type { DB } from "../db";
87
import { logger } from "../util/logger";
98
import type {
109
JsonRpcPoolTransactionEntry,
11-
JsonRpcTransactionView,
10+
PoolTransactionReject,
1211
} from "./type";
1312

1413
export interface WebsocketTopicSubscriber {
@@ -100,10 +99,10 @@ export class Subscriber {
10099
sub_id: undefined,
101100
handler: ([tx, reason]: [
102101
JsonRpcPoolTransactionEntry,
103-
string,
102+
PoolTransactionReject,
104103
]) => {
105104
logger.debug(
106-
`new rejected tx: ${tx.transaction.hash.slice(0, 22)}, reason: ${reason}`,
105+
`new rejected tx: ${tx.transaction.hash.slice(0, 22)}, reason: ${JSON.stringify(reason)}`,
107106
);
108107
this.db.updateMempoolRejectedTransaction(tx, reason);
109108
},
@@ -115,7 +114,7 @@ export class Subscriber {
115114
run() {
116115
const topics = this.createTopicSubscriber();
117116
this.ws.on("open", () => {
118-
logger.info("Connected to CKB node ", this.ckbRpcUrl);
117+
logger.info(`Connected to CKB node ${this.ckbRpcUrl}`);
119118
for (const topic of topics) {
120119
this.ws.send(
121120
JSON.stringify({
@@ -159,7 +158,7 @@ export class Subscriber {
159158
});
160159

161160
this.ws.on("close", () => {
162-
logger.info("Disconnected from CKB node");
161+
logger.info(`Disconnected from CKB node ${this.ckbRpcUrl}`);
163162
});
164163
}
165164
}

src/db/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
DBBlockHeader,
1313
JsonRpcPoolTransactionEntry,
1414
JsonRpcTransactionView,
15+
PoolTransactionReject,
1516
} from "../core/type";
1617
import { getNowTimestamp } from "../util/time";
1718
import { DepType, HashType, TransactionStatus } from "./type";
@@ -371,8 +372,9 @@ export class DB {
371372

372373
updateMempoolRejectedTransaction(
373374
tx: JsonRpcPoolTransactionEntry,
374-
reason: string,
375+
txReject: PoolTransactionReject,
375376
) {
377+
const reason = `${txReject.type}: ${txReject.description}`;
376378
const getStmt = this.db.prepare<[Hex], { id: DBId }>(
377379
"SELECT id From transactions WHERE tx_hash = ?",
378380
);

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { testnetSubscriber } from "./core/sub";
55
import { logger } from "./util/logger";
66

77
async function main() {
8+
logger.info(`Config: ${JSON.stringify(Config, null, 2)}`);
9+
810
testnetSubscriber.run();
911

1012
const testnetSever = createServer(testnetDB);

0 commit comments

Comments
 (0)