- Add GraphQL dashboard operations, ApolloProvider, CardDescription, label/checkbox/alert - Fix case-sensitive UI imports, Crossplane VM metadata uid, VMList spec parsing - Extend next-auth session user (id, role); fairness filters as unknown; ESLint relax to warnings - Remove unused session destructure across pages; next.config without skip TS/ESLint api: GraphQL/WebSocket hardening, logger import in websocket service Made-with: Cursor
287 lines
8.0 KiB
TypeScript
287 lines
8.0 KiB
TypeScript
/**
|
|
* Blockchain Service
|
|
* Enterprise Ethereum Alliance (EEA) blockchain integration
|
|
* For identity verification, resource tracking, and compliance
|
|
*/
|
|
|
|
import { logger } from '../lib/logger.js'
|
|
import { ethers } from 'ethers'
|
|
|
|
export interface BlockchainConfig {
|
|
rpcUrl?: string
|
|
networkId?: string
|
|
contractAddress?: string
|
|
privateKey?: string
|
|
enabled: boolean
|
|
}
|
|
|
|
export interface IdentityVerificationResult {
|
|
verified: boolean
|
|
address?: string
|
|
timestamp?: Date
|
|
error?: string
|
|
}
|
|
|
|
class BlockchainService {
|
|
private config: BlockchainConfig
|
|
private initialized: boolean = false
|
|
private provider: ethers.JsonRpcProvider | null = null
|
|
private wallet: ethers.Wallet | null = null
|
|
private identityContract: ethers.Contract | null = null
|
|
|
|
constructor() {
|
|
this.config = {
|
|
enabled: process.env.BLOCKCHAIN_ENABLED === 'true',
|
|
rpcUrl: process.env.BLOCKCHAIN_RPC_URL,
|
|
networkId: process.env.BLOCKCHAIN_NETWORK_ID,
|
|
contractAddress: process.env.BLOCKCHAIN_IDENTITY_CONTRACT_ADDRESS,
|
|
privateKey: process.env.BLOCKCHAIN_PRIVATE_KEY,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize blockchain service
|
|
*/
|
|
async initialize(): Promise<void> {
|
|
if (!this.config.enabled) {
|
|
logger.info('Blockchain service is disabled')
|
|
this.initialized = true
|
|
return
|
|
}
|
|
|
|
if (!this.config.rpcUrl) {
|
|
logger.warn('Blockchain RPC URL not configured, service will operate in mock mode')
|
|
this.initialized = true
|
|
return
|
|
}
|
|
|
|
try {
|
|
if (!this.config.rpcUrl) {
|
|
throw new Error('Blockchain RPC URL is required when blockchain is enabled')
|
|
}
|
|
|
|
// Initialize Ethers.js provider
|
|
this.provider = new ethers.JsonRpcProvider(this.config.rpcUrl)
|
|
|
|
// Initialize wallet if private key is provided
|
|
if (this.config.privateKey) {
|
|
this.wallet = new ethers.Wallet(this.config.privateKey, this.provider)
|
|
logger.info('Blockchain wallet initialized', {
|
|
address: this.wallet.address,
|
|
})
|
|
}
|
|
|
|
// Load identity contract if address is provided
|
|
if (this.config.contractAddress) {
|
|
// Basic ABI for identity verification contract
|
|
// In production, this would be loaded from contract artifacts
|
|
const identityABI = [
|
|
'function isVerified(address user) external view returns (bool)',
|
|
'function registerIdentity(address user, bytes32 userIdHash) external returns (bool)',
|
|
'function revokeIdentity(address user) external returns (bool)',
|
|
]
|
|
|
|
const contractAddress = this.config.contractAddress
|
|
const signer = this.wallet || this.provider
|
|
|
|
this.identityContract = new ethers.Contract(
|
|
contractAddress,
|
|
identityABI,
|
|
signer
|
|
)
|
|
|
|
// Verify contract is deployed
|
|
const code = await this.provider.getCode(contractAddress)
|
|
if (code === '0x') {
|
|
throw new Error(`Contract not deployed at address ${contractAddress}`)
|
|
}
|
|
|
|
logger.info('Identity contract loaded', { contractAddress })
|
|
}
|
|
|
|
logger.info('Blockchain service initialized', {
|
|
networkId: this.config.networkId,
|
|
contractAddress: this.config.contractAddress,
|
|
providerUrl: this.config.rpcUrl,
|
|
})
|
|
|
|
this.initialized = true
|
|
} catch (error) {
|
|
logger.error('Failed to initialize blockchain service', { error })
|
|
throw error
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify blockchain identity
|
|
* Checks if a user's blockchain address is registered and verified
|
|
*/
|
|
async verifyIdentity(
|
|
userId: string,
|
|
blockchainAddress: string
|
|
): Promise<IdentityVerificationResult> {
|
|
if (!this.initialized) {
|
|
await this.initialize()
|
|
}
|
|
|
|
if (!this.config.enabled) {
|
|
logger.info('Blockchain verification skipped (disabled)', { userId, blockchainAddress })
|
|
return {
|
|
verified: true, // Allow when blockchain is disabled
|
|
address: blockchainAddress,
|
|
timestamp: new Date(),
|
|
}
|
|
}
|
|
|
|
if (!blockchainAddress || !blockchainAddress.match(/^0x[a-fA-F0-9]{40}$/)) {
|
|
logger.warn('Invalid blockchain address format', { userId, blockchainAddress })
|
|
return {
|
|
verified: false,
|
|
address: blockchainAddress,
|
|
error: 'Invalid blockchain address format',
|
|
}
|
|
}
|
|
|
|
try {
|
|
if (!this.provider) {
|
|
throw new Error('Blockchain provider not initialized')
|
|
}
|
|
|
|
if (!this.identityContract) {
|
|
// If contract is not configured, verify address format only
|
|
logger.info('Blockchain identity verification (no contract)', {
|
|
userId,
|
|
blockchainAddress,
|
|
})
|
|
return {
|
|
verified: true,
|
|
address: blockchainAddress,
|
|
timestamp: new Date(),
|
|
}
|
|
}
|
|
|
|
// Query identity contract for verification status
|
|
const isVerified = await this.identityContract.isVerified(blockchainAddress) as boolean
|
|
|
|
logger.info('Blockchain identity verification', {
|
|
userId,
|
|
blockchainAddress,
|
|
verified: isVerified,
|
|
networkId: this.config.networkId,
|
|
})
|
|
|
|
return {
|
|
verified: isVerified,
|
|
address: blockchainAddress,
|
|
timestamp: new Date(),
|
|
...(isVerified ? {} : { error: 'Address not verified on blockchain' }),
|
|
}
|
|
} catch (error) {
|
|
logger.error('Blockchain verification failed', { userId, blockchainAddress, error })
|
|
return {
|
|
verified: false,
|
|
address: blockchainAddress,
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register identity on blockchain
|
|
* Registers a user's blockchain address in the identity contract
|
|
*/
|
|
async registerIdentity(
|
|
userId: string,
|
|
blockchainAddress: string,
|
|
metadata?: Record<string, unknown>
|
|
): Promise<{ success: boolean; transactionHash?: string; error?: string }> {
|
|
if (!this.initialized) {
|
|
await this.initialize()
|
|
}
|
|
|
|
if (!this.config.enabled) {
|
|
logger.info('Blockchain registration skipped (disabled)', { userId, blockchainAddress })
|
|
return { success: true }
|
|
}
|
|
|
|
try {
|
|
if (!this.provider) {
|
|
throw new Error('Blockchain provider not initialized')
|
|
}
|
|
|
|
if (!this.identityContract) {
|
|
throw new Error('Identity contract not configured')
|
|
}
|
|
|
|
if (!this.wallet) {
|
|
throw new Error('Blockchain wallet not configured (private key required for registration)')
|
|
}
|
|
|
|
// Create hash of userId for on-chain storage
|
|
const userIdHash = ethers.id(userId)
|
|
|
|
// Prepare and send transaction
|
|
logger.info('Registering identity on blockchain', {
|
|
userId,
|
|
blockchainAddress,
|
|
userIdHash,
|
|
})
|
|
|
|
const tx = await this.identityContract.registerIdentity(blockchainAddress, userIdHash)
|
|
|
|
logger.info('Blockchain registration transaction sent', {
|
|
transactionHash: tx.hash,
|
|
userId,
|
|
blockchainAddress,
|
|
})
|
|
|
|
// Wait for transaction confirmation
|
|
const receipt = await tx.wait()
|
|
|
|
if (!receipt) {
|
|
throw new Error('Transaction receipt not received')
|
|
}
|
|
|
|
logger.info('Blockchain identity registered', {
|
|
transactionHash: receipt.hash,
|
|
blockNumber: receipt.blockNumber,
|
|
userId,
|
|
blockchainAddress,
|
|
})
|
|
|
|
return {
|
|
success: true,
|
|
transactionHash: receipt.hash,
|
|
}
|
|
} catch (error) {
|
|
logger.error('Blockchain registration failed', { userId, blockchainAddress, error })
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if blockchain service is enabled
|
|
*/
|
|
isEnabled(): boolean {
|
|
return this.config.enabled
|
|
}
|
|
|
|
/**
|
|
* Check if blockchain service is initialized
|
|
*/
|
|
isInitialized(): boolean {
|
|
return this.initialized
|
|
}
|
|
}
|
|
|
|
// Singleton instance
|
|
export const blockchainService = new BlockchainService()
|
|
|
|
/** Called from server startup; wraps singleton initialize. */
|
|
export async function initBlockchainService(): Promise<void> {
|
|
await blockchainService.initialize()
|
|
}
|