关于NODE_PROPERTIES等节点表的用途、操作及CordaRPCClient功能问询
Great questions! Let's break this down step by step, since you're looking to extend node metadata in Corda.
1. What's the purpose of the NODE_PROPERTIES table?
The NODE_PROPERTIES table is Corda's built-in storage for custom node-level key-value attributes. It’s designed specifically to let you store extra, user-defined metadata for your nodes that isn’t covered by Corda’s default node info tables (like NODE_INFOS). Common use cases include storing business-specific identifiers, environment tags, or custom configuration flags that need to persist with the node.
2. How to populate and query NODE_PROPERTIES
You have two reliable approaches here: using Corda's official RPC APIs (recommended) or direct database operations (use cautiously for edge cases).
Populating Key-Value Pairs
Via Corda RPC (Recommended)
Use the NodeManagementService from CordaRPCClient to safely set properties without touching the database directly. Here's a Kotlin example:
import net.corda.client.rpc.CordaRPCClient import net.corda.core.messaging.start import net.corda.node.services.api.NodeManagementService fun setNodeProperty(host: String, port: Int, username: String, password: String, key: String, value: String) { val hostAndPort = CordaRPCClientConfiguration.HostAndPort(host, port) CordaRPCClient(hostAndPort).start(username, password).use { connection -> val nodeManagementService = connection.proxy.serviceHub.cordaService(NodeManagementService::class.java) nodeManagementService.setNodeProperty(key, value) } }
This method handles duplicate keys automatically (updating the value if the key already exists).
Direct Database Operation
If you need to bulk-load properties or work outside the RPC context, you can use SQL. Syntax varies slightly by database:
- MySQL/MariaDB:
INSERT INTO NODE_PROPERTIES (KEY, VALUE) VALUES ('custom.business.unit', 'retail') ON DUPLICATE KEY UPDATE VALUE = 'retail'; - PostgreSQL:
INSERT INTO NODE_PROPERTIES (KEY, VALUE) VALUES ('custom.business.unit', 'retail') ON CONFLICT (KEY) DO UPDATE SET VALUE = EXCLUDED.VALUE;
Querying NODE_PROPERTIES
Via Corda RPC
Again, use RPC to stay aligned with Corda's design:
fun getNodeProperties(host: String, port: Int, username: String, password: String): Map<String, String> { val hostAndPort = CordaRPCClientConfiguration.HostAndPort(host, port) return CordaRPCClient(hostAndPort).start(username, password).use { connection -> val nodeManagementService = connection.proxy.serviceHub.cordaService(NodeManagementService::class.java) nodeManagementService.getNodeProperties() } } // To get a single property: fun getSingleNodeProperty(host: String, port: Int, username: String, password: String, key: String): String? { val hostAndPort = CordaRPCClientConfiguration.HostAndPort(host, port) return CordaRPCClient(hostAndPort).start(username, password).use { connection -> val nodeManagementService = connection.proxy.serviceHub.cordaService(NodeManagementService::class.java) nodeManagementService.getNodeProperty(key) } }
Direct Database Query
-- Get all properties SELECT * FROM NODE_PROPERTIES; -- Get a specific property value SELECT VALUE FROM NODE_PROPERTIES WHERE KEY = 'custom.business.unit';
3. Querying other node tables (NODE_INFOS, NODE_NAMED_IDENTITIES, NODE_INFO_HOSTS)
These tables store Corda's core node metadata. You can access this data via RPC (preferred) or direct SQL.
Via Corda RPC
Use the NodeInfoService to fetch structured, type-safe data:
import net.corda.core.node.NodeInfo fun getCurrentNodeInfo(host: String, port: Int, username: String, password: String): NodeInfo { val hostAndPort = CordaRPCClientConfiguration.HostAndPort(host, port) return CordaRPCClient(hostAndPort).start(username, password).use { connection -> connection.proxy.nodeInfo() } } // Extract data corresponding to the tables: val nodeInfo = getCurrentNodeInfo("localhost", 10006, "user1", "test") // NODE_INFOS: Core node details val nodeId = nodeInfo.nodeId val platformVersion = nodeInfo.platformVersion // NODE_NAMED_IDENTITIES: Legal identities val legalIdentities = nodeInfo.legalIdentitiesAndCerts.map { it.name.toString() } // NODE_INFO_HOSTS: Network addresses val nodeAddresses = nodeInfo.addresses.map { it.hostAndPort } // To get all nodes in the network: fun getAllNetworkNodes(host: String, port: Int, username: String, password: String): List<NodeInfo> { val hostAndPort = CordaRPCClientConfiguration.HostAndPort(host, port) return CordaRPCClient(hostAndPort).start(username, password).use { connection -> connection.proxy.networkMapSnapshot() } }
Direct Database Queries
NODE_INFOS:SELECT ID, NODE_HASH, PLATFORM_VERSION, SERIAL FROM NODE_INFOS;NODE_NAMED_IDENTITIES(join withNODE_INFOSfor context):SELECT ni.NAME, ni.IDENTITY, n.NODE_HASH FROM NODE_NAMED_IDENTITIES ni JOIN NODE_INFOS n ON ni.NODE_INFO_ID = n.ID;NODE_INFO_HOSTS(join withNODE_INFOS):SELECT nh.HOST, nh.PORT, n.NODE_HASH FROM NODE_INFO_HOSTS nh JOIN NODE_INFOS n ON nh.NODE_INFO_ID = n.ID;
4. Does CordaRPCClient provide service-level functions for this?
Absolutely! Corda exposes two key RPC services to handle node metadata operations:
NodeInfoService: Provides read-only access to node info and network map data (maps directly toNODE_INFOS,NODE_NAMED_IDENTITIES,NODE_INFO_HOSTS). UsenodeInfo()for the current node,networkMapSnapshot()for all network nodes.NodeManagementService: Handles write operations for node properties (viasetNodeProperty) and other node management tasks. It's the official way to modifyNODE_PROPERTIESsafely.
Recommendation for Storing Extra Node Attributes
Stick to using NODE_PROPERTIES with the NodeManagementService RPC calls. This ensures compatibility across Corda versions (direct database queries risk breaking if table structures change) and follows Corda's security and transactional guarantees.
内容的提问来源于stack exchange,提问作者N.S.

