chore: sync all changes to Gitea
Some checks failed
Deploy to Phoenix / deploy (push) Has been cancelled

- Config, docs, scripts, and backup manifests
- Submodule refs unchanged (m = modified content in submodules)

Made-with: Cursor
This commit is contained in:
defiQUG
2026-03-02 11:37:34 -08:00
parent ed85135249
commit b3a8fe4496
883 changed files with 73580 additions and 4796 deletions

View File

@@ -1,262 +0,0 @@
/**
* RPC Handler - Handles JSON-RPC 2.0 requests and routes them appropriately
*/
import { BesuClient } from '../clients/besu-client';
import { TxInterceptor } from '../interceptors/tx-interceptor';
import { Web3SignerClient } from '../clients/web3signer-client';
export interface JsonRpcRequest {
jsonrpc: string;
method: string;
params?: any[];
id: string | number | null;
}
export interface JsonRpcResponse {
jsonrpc: string;
result?: any;
error?: {
code: number;
message: string;
data?: any;
};
id: string | number | null;
}
// Dangerous methods that should be denied by default
// These are admin/debug methods that expose sensitive information
const DENIED_METHODS = [
'admin_',
'debug_',
'txpool_',
'miner_',
];
// Private network consensus methods (CLIQUE, IBFT, QBFT)
// These are allowed to pass through - they are used for private network management
const PRIVATE_NETWORK_METHODS = [
'clique_',
'ibft_',
'qbft_',
'perm_',
];
// Methods that should be intercepted
const INTERCEPTED_METHODS = ['eth_sendTransaction'];
export class RpcHandler {
private besuClient: BesuClient;
private txInterceptor: TxInterceptor;
private web3SignerClient?: Web3SignerClient;
private chainId: number;
private allowPrivateNetworkMethods: boolean;
constructor(
besuClient: BesuClient,
txInterceptor: TxInterceptor,
chainId: number,
allowPrivateNetworkMethods: boolean = true,
web3SignerClient?: Web3SignerClient
) {
this.besuClient = besuClient;
this.txInterceptor = txInterceptor;
this.web3SignerClient = web3SignerClient;
this.chainId = chainId;
this.allowPrivateNetworkMethods = allowPrivateNetworkMethods;
}
private isMethodDenied(method: string): boolean {
// Always deny admin/debug methods
if (DENIED_METHODS.some(prefix => method.startsWith(prefix))) {
return true;
}
// Conditionally deny private network methods based on configuration
if (!this.allowPrivateNetworkMethods) {
return PRIVATE_NETWORK_METHODS.some(prefix => method.startsWith(prefix));
}
return false;
}
private isPrivateNetworkMethod(method: string): boolean {
return PRIVATE_NETWORK_METHODS.some(prefix => method.startsWith(prefix));
}
private isMethodIntercepted(method: string): boolean {
return INTERCEPTED_METHODS.includes(method);
}
async handleRequest(request: JsonRpcRequest): Promise<JsonRpcResponse> {
// Validate JSON-RPC version
if (request.jsonrpc !== '2.0') {
return {
jsonrpc: '2.0',
error: {
code: -32600,
message: 'Invalid Request',
data: 'jsonrpc must be "2.0"',
},
id: request.id,
};
}
// Validate method
if (!request.method || typeof request.method !== 'string') {
return {
jsonrpc: '2.0',
error: {
code: -32600,
message: 'Invalid Request',
data: 'method is required and must be a string',
},
id: request.id,
};
}
// Check if method is denied
if (this.isMethodDenied(request.method)) {
const isPrivateMethod = this.isPrivateNetworkMethod(request.method);
const reason = isPrivateMethod
? 'Private network methods are disabled (set ALLOW_PRIVATE_NETWORK_METHODS=true)'
: 'Method is denied for security reasons';
return {
jsonrpc: '2.0',
error: {
code: -32601,
message: 'Method not found',
data: `Method ${request.method} is not allowed: ${reason}`,
},
id: request.id,
};
}
// Handle intercepted methods
if (this.isMethodIntercepted(request.method)) {
return this.handleInterceptedMethod(request);
}
// Pass through to Besu
return this.handlePassThrough(request);
}
private async handleInterceptedMethod(request: JsonRpcRequest): Promise<JsonRpcResponse> {
if (request.method === 'eth_sendTransaction') {
if (!request.params || !Array.isArray(request.params) || request.params.length === 0) {
return {
jsonrpc: '2.0',
error: {
code: -32602,
message: 'Invalid params',
data: 'eth_sendTransaction requires a transaction object parameter',
},
id: request.id,
};
}
const txObject = request.params[0];
const fromAddress = txObject.from;
// Smart interception: Check if address has key in Web3Signer
// If no key, pass through to Besu (user wallet like MetaMask)
// If key exists, intercept and sign via Web3Signer (service wallet)
if (this.web3SignerClient && fromAddress) {
try {
const hasKey = await this.web3SignerClient.hasKey(fromAddress);
if (!hasKey) {
// User wallet (MetaMask, etc.) - no key in Web3Signer
// Pass through to Besu - user will sign locally
// Note: Besu doesn't support unsigned eth_sendTransaction,
// but this allows the proper error to come from Besu
console.log(`Address ${fromAddress} has no key in Web3Signer, passing through to Besu`);
return this.handlePassThrough(request);
}
// Service wallet - has key in Web3Signer, intercept and sign
console.log(`Address ${fromAddress} has key in Web3Signer, intercepting and signing`);
} catch (error: any) {
// If key check fails, log warning but still try to intercept
// (fallback to original behavior)
console.warn(`Failed to check if address has key: ${error.message}, attempting interception`);
}
} else if (!this.web3SignerClient) {
// No Web3Signer client configured, pass through to Besu
console.log('Web3Signer not configured, passing through to Besu');
return this.handlePassThrough(request);
}
// Intercept and sign via Web3Signer (service wallet)
try {
const txHash = await this.txInterceptor.interceptAndSign(txObject);
return {
jsonrpc: '2.0',
result: txHash,
id: request.id,
};
} catch (error: any) {
return {
jsonrpc: '2.0',
error: {
code: -32000,
message: 'Server error',
data: error.message || 'Transaction processing failed',
},
id: request.id,
};
}
}
// Should not reach here
return {
jsonrpc: '2.0',
error: {
code: -32601,
message: 'Method not found',
},
id: request.id,
};
}
private async handlePassThrough(request: JsonRpcRequest): Promise<JsonRpcResponse> {
try {
const params = request.params || [];
const result = await this.besuClient.callRpc(request.method, params);
return {
jsonrpc: '2.0',
result,
id: request.id,
};
} catch (error: any) {
// Map common errors to JSON-RPC error codes
let errorCode = -32000; // Server error
let errorMessage = error.message || 'Internal error';
if (error.message?.includes('Method not found')) {
errorCode = -32601;
errorMessage = 'Method not found';
} else if (error.message?.includes('Invalid params')) {
errorCode = -32602;
errorMessage = 'Invalid params';
}
return {
jsonrpc: '2.0',
error: {
code: errorCode,
message: errorMessage,
data: error.message,
},
id: request.id,
};
}
}
async handleBatch(requests: JsonRpcRequest[]): Promise<JsonRpcResponse[]> {
const promises = requests.map(req => this.handleRequest(req));
return Promise.all(promises);
}
}