NodeUpdateTransaction - Test specification

Description:

This test specification for NodeUpdateTransaction is to be one of many for testing the functionality of the Hedera SDKs. The SDK under test will use the language specific JSON-RPC server return responses back to the test driver.

Design:

Each test within the test specification is linked to one of the functions within NodeUpdateTransaction. Each function is tested with a mix of boundaries. The inputs for each test are a range of valid, minimum, maximum, negative and invalid values for the method. The expected response of a passed test can be a correct error response code or seen as the result of node queries. A successful transaction (the transaction reached consensus and was applied to state) can be determined by getting a TransactionReceipt or TransactionRecord, or can be determined by using queries such as AddressBookQuery and investigating for the required changes (creations, updates, etc.). The mirror node can also be used to determine if a transaction was successful via its rest API. Error codes are obtained from the response code proto files.

Transaction properties:

https://docs.hedera.com/hedera/sdks-and-apis/sdks/node-service

Node protobufs:

https://github.com/hashgraph/hedera-protobufs/blob/main/services/node_update.proto

Response codes:

https://github.com/hashgraph/hedera-protobufs/blob/main/services/response_code.proto

Mirror Node APIs:

https://docs.hedera.com/hedera/sdks-and-apis/rest-api

JSON-RPC API Endpoint Documentation

Method Name

updateNode

Input Parameters

Parameter Name Type Required/Optional Description/Notes
nodeId string optional The ID of the node to update.
accountId string optional The account ID that will be associated with the node.
description string optional A short description of the node (max 100 bytes).
gossipEndpoints array of ServiceEndpointParams optional List of service endpoints for gossip (max 10 entries).
serviceEndpoints array of ServiceEndpointParams optional List of service endpoints for gRPC calls (max 8 entries).
gossipCaCertificate string optional Certificate used to sign gossip events (DER encoding).
grpcCertificateHash string optional Hash of the node gRPC TLS certificate (SHA-384).
grpcWebProxyEndpoint ServiceEndpointParams optional Proxy endpoint for gRPC web calls.
adminKey string optional Administrative key controlled by the node operator.
declineReward boolean optional Whether the node declines rewards.
commonTransactionParams json object optional Common transaction parameters.

Output Parameters

Parameter Name Type Description/Notes
status string The status of the submitted transaction (from a TransactionReceipt).

Additional Notes

The tests contained in this specification will assume that valid accounts were already successfully created. <CREATED_ACCOUNT_ID> will denote the ID of the account associated with the node, and <CREATED_ACCOUNT_PRIVATE_KEY> will denote the private key of the account as a DER-encoded hex string. Tests will assume valid admin keys have already been generated. <CREATED_ADMIN_KEY> will denote the admin key as a DER-encoded hex string. <VALID_GOSSIP_CERTIFICATE> will denote a valid DER-encoded certificate for gossip signing.

Property/Function Tests

NodeId:

  • Tests the node identifier parameter for updating a consensus node
Test no Name Input Expected response Implemented (Y/N)
1 Updates a node with a valid node ID nodeId=<EXISTING_NODE_ID> The node update is successful Y
2 Cannot update a node with an invalid node ID nodeId=<NON_EXISTING_NODE_ID> The node update fails with INVALID_NODE_ID Y
3 Cannot update a node without providing a node ID The node update fails with internal SDK error Y
4 Cannot update a node with a negative node ID nodeId="-1" The node update fails with internal SDK error Y
5 Cannot update a node with uint64 max node ID nodeId="18,446,744,073,709,551,615" The node update fails with INVALID_NODE_ID Y

AccountId:

  • Tests the account ID parameter for updating a consensus node
Test no Name Input Expected response Implemented (Y/N)
1 Updates a node with valid account ID nodeId=<EXISTING_NODE_ID>, accountId=<CREATED_ACCOUNT_ID>, adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds. Y
2 Fails with empty account ID nodeId=<EXISTING_NODE_ID>, accountId="", adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with an SDK internal error. Y
3 Fails with non-existent account ID nodeId=<EXISTING_NODE_ID>, accountId="123.456.789", adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with an INVALID_ACCOUNT_ID response code. Y
4 Fails with invalid account ID nodeId=<EXISTING_NODE_ID>, accountId="invalid.account.id", adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with an SDK internal error. Y

Description:

  • Tests the description parameter with various lengths and formats
Test no Name Input Expected response Implemented (Y/N)
1 Updates a node with valid description nodeId=<EXISTING_NODE_ID>, description="test description" adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds and the node has the specified description. Y
2 Updates a node with description at maximum length (100 bytes) nodeId=<EXISTING_NODE_ID>, description=<100_BYTE_STRING> adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds and the node has the specified description. Y
3 Updates a node with description exceeding maximum length nodeId=<EXISTING_NODE_ID>, description=<101_BYTE_STRING> adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR Y
4 Updates a node with description containing only whitespace nodeId=<EXISTING_NODE_ID>, description=" " adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds and the node has the whitespace description. Y
5 Updates a node with description containing special characters nodeId=<EXISTING_NODE_ID>, description="!@#$%^&*()_+-=[]{};':",./<>?" adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds and the node has the special character description. Y
6 Updates a node with description containing unicode characters nodeId=<EXISTING_NODE_ID>, description="测试节点描述 🚀" adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds and the node has the unicode description. Y
7 Updates a node with description containing exactly 100 ASCII characters nodeId=<EXISTING_NODE_ID>, description="a".repeat(100) adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds and the node has the 100-character description. Y
8 Updates a node with description containing exactly 100 UTF-8 bytes (fewer characters) nodeId=<EXISTING_NODE_ID>, description="🚀".repeat(25) adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds and the node has the 100-byte description. Y

GossipEndpoints:

  • Tests the gossip endpoints parameter with various configurations
Test no Name Input Expected response Implemented (Y/N)
1 Update a node with single IP address endpoint nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[{ipAddressV4=<VALID_HEX_IP_ADDRESS>, port=50211}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds. Y
2 Update a node with domain name endpoint nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[{domainName="node.example.com", port=50211}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds. Y
3 Update a node with multiple gossip endpoints nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[{ipAddressV4=<VALID_HEX_IP_ADDRESS>, port=50211}, {ipAddressV4=<VALID_HEX_IP_ADDRESS_2>, port=50212}, {domainName="node.example.com", port=50213}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds. Y
4 Update a node with maximum allowed gossip endpoints (10) nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[10 endpoints], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds. Y
5 Fails with too many gossip endpoints (11) nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[11 endpoints], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR Y
6 Fails with empty gossip endpoints nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] Internal error Y
7 Fails with missing port in endpoint nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[{ipAddressV4=<VALID_HEX_IP_ADDRESS>}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with INVALID_ENDPOINT. Y
8 Fails with both IP and domain in same endpoint nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[{ipAddressV4=<VALID_HEX_IP_ADDRESS>, domainName="node.example.com", port=50211}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with an SDK internal error. Y
9 Fails with invalid IP address format nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[{ipAddressV4=<INVALID_HEX_IP_ADDRESS>, port=50211}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with INVALID_ENDPOINT. Y
10 Fails with invalid port number (negative) nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[{ipAddressV4=<VALID_HEX_IP_ADDRESS>, port=-1}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with an SDK internal error. Y
11 Fails with invalid port number (too high) nodeId=<EXISTING_NODE_ID>, gossipEndpoints=[{ipAddressV4=<VALID_HEX_IP_ADDRESS>, port=65536}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with an SDK internal error. Y

ServiceEndpoints:

  • Tests the service endpoints parameter with various configurations
Test no Name Input Expected response Implemented (Y/N)
1 Updates a node with valid service endpoints nodeId=<EXISTING_NODE_ID>, serviceEndpoints=[{ipAddressV4:"127.0.0.1", port:50212}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] SUCCESS Y
2 Updates a node with multiple valid service endpoints nodeId=<EXISTING_NODE_ID>, serviceEndpoints=[{ipAddressV4:"127.0.0.1", port:50212}, {ipAddressV4:"127.0.0.2", port:50213}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] SUCCESS Y
3 Updates a node with maximum service endpoints (8) nodeId=<EXISTING_NODE_ID>, serviceEndpoints=8 endpoints, adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] SUCCESS Y
4 Cannot update a node with more than maximum service endpoints nodeId=<EXISTING_NODE_ID>, serviceEndpoints=9 endpoints, adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR Y
5 Cannot update a node with empty service endpoints list nodeId=<EXISTING_NODE_ID>, serviceEndpoints=[] , adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR Y
6 Updates a node with service endpoints containing domain names nodeId=<EXISTING_NODE_ID>, serviceEndpoints=[{domainName:"grpc.hedera.com", port:443}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] SUCCESS Y
7 Fails with invalid service endpoint (missing port) nodeId=<EXISTING_NODE_ID>, serviceEndpoints=[{ipAddressV4="127.0.0.1"}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with INVALID_ENDPOINT. Y
8 Fails with both IP and domain in same service endpoint nodeId=<EXISTING_NODE_ID>, serviceEndpoints=[{ipAddressV4=<VALID_HEX_IP_ADDRESS>, domainName="service.example.com", port=50212}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR Y
9 Fails with invalid IP address format in service endpoint nodeId=<EXISTING_NODE_ID>, serviceEndpoints=[{ipAddressV4=<INVALID_HEX_IP_ADDRESS>, port=50212}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with INVALID_ENDPOINT. Y
10 Fails with invalid port number (negative) in service endpoint nodeId=<EXISTING_NODE_ID>, serviceEndpoints=[{ipAddressV4=<VALID_HEX_IP_ADDRESS>, port=-1}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR Y
11 Fails with invalid port number (too high) in service endpoint nodeId=<EXISTING_NODE_ID>, serviceEndpoints=[{ipAddressV4=<VALID_HEX_IP_ADDRESS>, port=65536}], adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with INVALID_ENDPOINT. Y

GossipCaCertificate:

  • Tests the gossip CA certificate parameter
Test no Name Input Expected response Implemented (Y/N)
1 Updates a node with a valid gossip CA certificate nodeId=<EXISTING_NODE_ID>, gossipCaCertificate=<VALID_DER_CERTIFICATE>, adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signer=[<CREATED_ACCOUNT_PRIVATE_KEY>] SUCCESS Y
2 Cannot update a node with an empty gossip CA certificate nodeId=<EXISTING_NODE_ID>, gossipCaCertificate="", adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signer=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR Y
3 Cannot update a node with an invalid gossip CA certificate format nodeId=<EXISTING_NODE_ID>, gossipCaCertificate="invalid_certificate_format", adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signer=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR Y
4 Fails with malformed hex string nodeId=<EXISTING_NODE_ID>, gossipCaCertificate="not_hex_string", adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signer=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR Y

GrpcCertificateHash:

  • Tests the gRPC certificate hash parameter
Test no Name Input Expected response Implemented (Y/N)
1 Update a node with valid gRPC certificate hash nodeId=<EXISTING_NODE_ID>, grpcCertificateHash="a1b2c3d4e5f6", adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds. Y
2 Fails with empty certificate hash nodeId=<EXISTING_NODE_ID>, grpcCertificateHash="", adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with INVALID_GRPC_CERTIFICATE_HASH. Y
3 Cannot update a node with an invalid gRPC certificate hash format nodeId=<EXISTING_NODE_ID>, grpcCertificateHash="invalid_hash_format" INTERNAL_ERROR. Y
4 Fails with malformed hex string nodeId=<EXISTING_NODE_ID>, grpcCertificateHash="not_hex_string", adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR. Y

GrpcWebProxyEndpoint:

  • Tests the gRPC web proxy endpoint parameter
Test no Name Input Expected response Implemented (Y/N)
1 Update a node with gRPC web proxy endpoint nodeId=<EXISTING_NODE_ID>, grpcWebProxyEndpoint={ipAddressV4=<VALID_HEX_IP_ADDRESS>, port=50213}, adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update fails with INVALID_SERVICE_ENDPOINT. Y
2 Update a node with domain-based gRPC web proxy endpoint nodeId=<EXISTING_NODE_ID>, grpcWebProxyEndpoint={domainName="proxy.example.com", port=50213}, adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds. Y
3 Deletes a gRPC web proxy endpoint nodeId=<EXISTING_NODE_ID>, grpcWebProxyEndpoint={}, adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] SUCCESS Y

AdminKey:

Test no Name Input Expected response Implemented (Y/N)
1 Update a node with valid ED25519 public key as admin key nodeId=<EXISTING_NODE_ID>, adminKey=<VALID_ED25519_PUBLIC_KEY>, commonTransactionParams.signers=[<CORRESPONDING_ED25519_PRIVATE_KEY>] The node update succeeds. Y
2 Update a node with valid ECDSAsecp256k1 public key as admin key nodeId=<EXISTING_NODE_ID>, adminKey=<VALID_ECDSA_SECP256K1_PUBLIC_KEY>, commonTransactionParams.signers=[<CORRESPONDING_ECDSA_SECP256K1_PRIVATE_KEY>] The node update succeeds. Y
3 Update a node with valid ED25519 private key as admin key nodeId=<EXISTING_NODE_ID>, adminKey=<VALID_ED25519_PRIVATE_KEY>, commonTransactionParams.signers=[<CORRESPONDING_ED25519_PRIVATE_KEY>] The node update succeeds. Y
4 Update a node with valid ECDSAsecp256k1 private key as admin key nodeId=<EXISTING_NODE_ID>, adminKey=<VALID_ECDSA_SECP256K1_PRIVATE_KEY>, commonTransactionParams.signers=[<CORRESPONDING_ECDSA_SECP256K1_PRIVATE_KEY>] The node update succeeds. Y
5 Update a node with valid KeyList of ED25519 and ECDSAsecp256k1 keys as admin key nodeId=<EXISTING_NODE_ID>, adminKey=<VALID_KEYLIST>, commonTransactionParams.signers=[<CORRESPONDING_ED25519_PRIVATE_KEY>] The node update succeeds. Y
6 Update a node with valid nested KeyList (three levels) as admin key nodeId=<EXISTING_NODE_ID>, adminKey=<VALID_NESTED_KEYLIST>, commonTransactionParams.signers=[<CORRESPONDING_ED25519_PRIVATE_KEY>] The node update succeeds. Y
7 Update a node with valid ThresholdKey of ED25519 and ECDSAsecp256k1 keys as admin key nodeId=<EXISTING_NODE_ID>, adminKey=<VALID_THRESHOLD_KEY>, commonTransactionParams.signers=[<CORRESPONDING_ED25519_PRIVATE_KEY>] The node update succeeds. Y
8 Fails with valid admin key without signing with the new key nodeId=<EXISTING_NODE_ID>, adminKey=<VALID_KEY> The node update fails with an INVALID_SIGNATURE response code. Y
9 Fails with invalid admin key format nodeId=<EXISTING_NODE_ID>, adminKey="invalid_key", commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] INTERNAL_ERROR Y

DeclineReward:

Test no Name Input Expected response Implemented (Y/N)
1 Update a node that accepts rewards (default) nodeId=<EXISTING_NODE_ID>, adminKey=<CREATED_ADMIN_KEY>, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds. Y
2 Update a node that declines rewards nodeId=<EXISTING_NODE_ID>, adminKey=<CREATED_ADMIN_KEY>, declineReward=true, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds. Y
3 Update a node with explicit declineReward: false nodeId=<EXISTING_NODE_ID>, adminKey=<CREATED_ADMIN_KEY>, declineReward=false, commonTransactionParams.signers=[<CREATED_ACCOUNT_PRIVATE_KEY>] The node update succeeds. Y