Skip to content

Conversation

@alexcos20
Copy link
Member

@alexcos20 alexcos20 commented Jan 28, 2026

Refactor: Centralize Key and Blockchain Management in OceanNode

Summary

This PR refactors the OceanNode singleton to centralize key management and blockchain connection management. The refactoring introduces a new KeyManager component with a pluggable key provider architecture, and a BlockchainRegistry to manage blockchain instances per chain ID. This improves modularity, testability, and sets the foundation for future key management solutions (e.g., GCP KMS).

Architecture Changes

Before

  • Keys were scattered across the codebase, accessed directly from config.keys
  • Blockchain instances were created ad-hoc in 22+ locations
  • No centralized key or blockchain lifecycle management
  • Difficult to extend with new key sources (e.g., KMS)

After

OceanNode (singleton)
├── Libp2pNode (one)
├── KeyManager (one)
│   ├── IKeyProvider (pluggable)
│   │   └── RawPrivateKeyProvider (current implementation)
│   ├── peerId
│   ├── libp2pPrivateKey
│   ├── ethAddress
│   └── getEvmSigner(provider)
│
├── BlockchainRegistry (one)
│   ├── Blockchain(chainId=1)
│   │   └── signer = keyManager.getEvmSigner(provider)
│   ├── Blockchain(chainId=137)
│   ├── Blockchain(chainId=56)
│   └── getBlockchain(chainId) → delegates to BlockchainRegistry

Key Components

1. KeyManager (src/components/KeyManager/index.ts)

Centralizes all key management for OceanNode:

  • Provides access to peerId, libp2p keys, EVM address, and EVM signers
  • Uses a pluggable IKeyProvider interface to support multiple key sources
  • Currently implements RawPrivateKeyProvider for raw private keys
  • Future: Can easily add GcpKmsProvider or other key providers

Key Methods:

  • getPeerId(): Returns libp2p PeerId
  • getLibp2pPrivateKey(): Returns libp2p private key
  • getEthAddress(): Returns Ethereum address
  • getEvmSigner(provider): Returns cached EVM signer for a provider
  • encrypt(data, algorithm): Encrypts data using node keys
  • decrypt(data, algorithm): Decrypts data using node keys
  • encryptStream(stream, algorithm): Encrypts streams (AES/ECIES)
  • decryptStream(stream, algorithm): Decrypts streams (AES/ECIES)

2. IKeyProvider Interface (src/@types/KeyManager.ts)

Defines the contract for all key provider implementations:

  • getType(): Returns provider type ('raw' | 'gcp-kms')
  • getPeerId(), getLibp2pPrivateKey(), getPublicKey()
  • getEthAddress(), getEthWallet()
  • getRawPrivateKeyBytes(): For EVM signer creation
  • encrypt(), decrypt(): Data encryption/decryption
  • encryptStream(), decryptStream(): Stream encryption/decryption
  • cleanup?(): Optional cleanup method

3. RawPrivateKeyProvider (src/components/KeyManager/providers/RawPrivateKeyProvider.ts)

Implements IKeyProvider for raw private keys:

  • Initializes synchronously in constructor (no async initialize() needed)
  • Derives peerId, libp2p keys, and Ethereum address from raw private key
  • Supports AES and ECIES encryption/decryption for both data and streams
  • Stream encryption uses Node.js Transform streams for efficient processing

4. BlockchainRegistry (src/components/BlockchainRegistry/index.ts)

Manages Blockchain instances per chain ID:

  • Lazy initialization: Creates Blockchain instances on-demand
  • Centralized access: Single source of truth for blockchain connections
  • Caches instances: Reuses Blockchain instances for the same chain ID
  • Provides getBlockchain(chainId) method that returns Blockchain | null

5. Refactored Blockchain Class (src/utils/blockchain.ts)

Updated to work with the new architecture:

  • Constructor now accepts KeyManager and uses it to get signers
  • Removed dependency on OceanNodeConfig for signer creation
  • Maintains backward compatibility during migration phase
  • Signers are obtained via KeyManager.getEvmSigner(provider)

6. Updated OceanNode (src/OceanNode.ts)

Enhanced singleton with new components:

  • Constructor accepts KeyManager and BlockchainRegistry as parameters (dependency injection)
  • getInstance() method updated to require these components
  • New accessor methods:
    • getKeyManager(): Returns KeyManager instance
    • getBlockchainRegistry(): Returns BlockchainRegistry instance
    • getBlockchain(chainId): Convenience method delegating to BlockchainRegistry

Migration Changes

Handlers Updated

All handlers now use OceanNode.getBlockchain() instead of creating new Blockchain instances:

  • ddoHandler.ts: Uses oceanNode.getBlockchain(chainId) for blockchain access
  • downloadHandler.ts: Migrated to use centralized blockchain access
  • startCompute.ts: Updated multiple locations to use oceanNode.getBlockchain(chainId)
  • initialize.ts: Updated compute initialization to use centralized blockchain
  • collectFeesHandler.ts: Admin handler now uses oceanNode.getBlockchain(chainId)

Escrow Refactoring (src/components/core/utils/escrow.ts)

  • Constructor now requires BlockchainRegistry as a parameter
  • Introduced private getBlockchain(chainId) helper method
  • All methods (getPaymentAmountInWei, getNumberFromWei, getUserAvailableFunds, getLocks, getAuthorizations, createLock, claimLock, cancelExpiredLocks) now use the centralized helper
  • Removed all existingChain parameter patterns - cleaner API

Indexer Updates (src/components/Indexer/index.ts)

  • Constructor now requires BlockchainRegistry as a parameter
  • startThread() method retrieves Blockchain instances via blockchainRegistry.getBlockchain(chainID)
  • Removed fallback logic - relies on registry for blockchain access

P2P Updates (src/components/P2P/index.ts)

  • Constructor now accepts KeyManager as a parameter
  • Replaced direct access to config.keys.peerId, config.keys.publicKey, config.keys.privateKey with KeyManager methods:
    • keyManager.getPeerIdString()
    • keyManager.getLibp2pPublicKey()
    • keyManager.getLibp2pPrivateKey()

Main Initialization (src/index.ts)

  • Creates KeyManager instance: new KeyManager(config)
  • Creates BlockchainRegistry instance: new BlockchainRegistry(keyManager, config)
  • Passes both to OceanNode.getInstance()
  • Removed explicit await keyManager.initialize() call (initialization happens in constructor)

Configuration Changes

OceanNodeKeys Interface (src/@types/OceanNode.ts)

Enhanced to support key provider selection:

  • Added type?: 'raw' | 'gcp-kms': Specifies the key provider type
  • Added rawPrivateKey?: string: For raw private key configuration (future)
  • Added gcpKmsConfig?: For future GCP KMS integration

Note: The type field is currently inferred from the presence of privateKey in config, but the interface supports explicit type specification for future extensibility.

Stream Encryption/Decryption

Added support for streaming encryption/decryption in RawPrivateKeyProvider:

  • encryptStream(inputStream, algorithm): Encrypts a Readable stream
    • AES: Uses Node.js crypto.createCipheriv() for streaming encryption
    • ECIES: Collects all data first (ECIES doesn't support streaming), then encrypts
  • decryptStream(inputStream, algorithm): Decrypts a Readable stream
    • AES: Uses Node.js crypto.createDecipheriv() for streaming decryption
    • ECIES: Collects all data first, then decrypts

Benefits

  1. Centralized Key Management: All key access goes through KeyManager, making it easier to audit and secure
  2. Pluggable Architecture: Easy to add new key providers (GCP KMS, AWS KMS, etc.) without changing core logic
  3. Centralized Blockchain Management: Single source of truth for blockchain connections, reducing duplication
  4. Better Testability: Components can be easily mocked and tested in isolation
  5. Improved Maintainability: Clear separation of concerns and single responsibility principle
  6. Future-Proof: Architecture supports enterprise key management solutions

Testing

Updated test files to work with the new architecture:

  • src/test/integration/credentials.test.ts: Uses blockchainRegistry.getBlockchain()
  • src/test/integration/accessLists.test.ts: Updated to use new blockchain access pattern
  • src/test/unit/blockchain.test.ts: Tests BlockchainRegistry functionality

Files Changed

New Files

  • src/@types/KeyManager.ts: Type definitions for key provider architecture
  • src/components/KeyManager/index.ts: Main KeyManager class
  • src/components/KeyManager/providers/RawPrivateKeyProvider.ts: Raw private key provider implementation
  • src/components/BlockchainRegistry/index.ts: Blockchain registry implementation
  • docs/KeyManager.md: Documentation for KeyManager

Modified Files

  • src/OceanNode.ts: Added KeyManager and BlockchainRegistry integration
  • src/index.ts: Updated initialization flow
  • src/utils/blockchain.ts: Refactored to use KeyManager
  • src/@types/OceanNode.ts: Enhanced OceanNodeKeys interface
  • src/components/P2P/index.ts: Uses KeyManager for P2P keys
  • src/components/Indexer/index.ts: Uses BlockchainRegistry
  • src/components/core/utils/escrow.ts: Refactored to use BlockchainRegistry
  • src/components/core/handler/ddoHandler.ts: Uses OceanNode.getBlockchain()
  • src/components/core/handler/downloadHandler.ts: Uses OceanNode.getBlockchain()
  • src/components/core/compute/startCompute.ts: Uses OceanNode.getBlockchain()
  • src/components/core/compute/initialize.ts: Uses OceanNode.getBlockchain()
  • src/components/core/admin/collectFeesHandler.ts: Uses OceanNode.getBlockchain()
  • Multiple test files updated to work with new architecture

Related Issues

This refactoring addresses the need for:

  • Centralized key management
  • Support for enterprise key management solutions (KMS)
  • Reduced code duplication in blockchain instance creation
  • Better testability and maintainability

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants