GitHub Logo HIP-869: Dynamic Address Book - Stage 1 - HAPI Endpoints

Author Iris Simon
Working Group Kelly Greco, Michael Heinrichs, Mark Blackman
Requested By Hedera
Discussions-To https://github.com/hashgraph/hedera-improvement-proposal/pull/869
Status Accepted
Needs Council Approval Yes
Review period ends Tue, 14 Feb 2023 07:00:00 +0000
Type Standards Track
Category Core
Created 2024-01-22
Updated 2024-08-19

Abstract

The Dynamic Address Book will change the currently manually administered address book for Hedera into an HAPI managed configuration, updatable via signed Hedera transactions on a daily basis without the need to restart the consensus nodes to apply changes across the network.

Dynamic Address Book - Feature Summary

  • HAPI APIs to facilitate changes to the address book used across the Hedera network
  • Automated daily updates to node consensus weighting and SDK service requests
  • NMT integration to automate required configuration changes resulting from address book changes

Given the size of the work, the feature will be broken into 2 stages:

Stage 1 - HAPI Endpoints: Implement the HAPI endpoints for Address Book management within current Address Book implementation (config.txt, file 0.0.101,& file 0.0.102). In this phase HAPI endpoints are used to facilitate changes to the Address Book, however those changes are applied through existing, monthly upgrade process.

Stage 2 - Full Implementation - Full implementation of feature enabling daily changes to a new dynamic Address Book implementation through HAPI endpoints at 24 hour cycles matching Hedera’s staking period.

This HIP is focused on the HAPI Endpoints phase of the project only. A second HIP will be released for the Full Implementation stage of the project.

Motivation

Problems to Solve

  1. Changes to the address book can only be applied with a node restart requiring maintenance windows to activate Address Book changes locally.
  2. Changes to the address book is a complex, manual process that needs to be replicated across all nodes on the Network within a maintenance window.
  3. Currently node weighting is calculated and applied monthly whereas staking periods occur daily.

Feature Benefits:

  • Enables Hedera council and node operators to manage address book automatically.
  • Core building block to support eventual support for community and permissionless nodes.
  • Enables the rotation of node operators off the Hedera network should it be required as contracts expire.
  • NMT integration of local node firewall admin reduces cost & complexity of node operations.
  • Staking delegations within a single staking period directly contribute to a node’s consensus weight

Rationale

The architectural design of Hedera’s transaction-driven system was strategically implemented to empower the Hedera council members in overseeing network node participation. Furthermore this framework paves the way for the incorporation of community-led and permissionless nodes in the future. Under this design, independent node operators have the capability to autonomously submit transactions, thus efficiently managing the metadata related to their self-governed nodes.

Adopting a two-phase strategy, this approach facilitated the earlier release of HAPI endpoints, setting the stage for the subsequent deployment of a more automated solution. This phased implementation was key in accelerating both the delivery and utilization of the system.

User stories

Personas

Council - Hedera Council

Node Operator - Administrator of Hedera Consensus Nodes

User Stories:

  1. As the Council, we want to submit signed HAPI transactions to add a new consensus node to the Hedera network upon the next maintenance window, so that management of Hedera’s Address book is automated.

Acceptance: When the council initiates a HAPI transaction to add a new node, then the network acknowledges the transaction and performs the update to the network’s Address Book at the next maintenance window.

  1. As the Council, we want to submit signed HAPI transactions to remove a consensus node from the Hedera network upon the next maintenance window, so that management of Hedera’s Address book is automated.

Acceptance: When the council submits a HAPI transaction to remove a node, then the network should acknowledge the transaction and performs the update to the network’s Address Book at the next maintenance window.

  1. As a Node Operator, I want to submit a signed HAPI transaction that modifies one or both of an existing node’s IP addresses and/or ports, so I can independently perform address book related node operations.

Acceptance: When a Node Operator submits a HAPI transaction to modify a node’s primary IP address:port or secondary IP address:port, the network acknowledges the transaction and performs the update to the network’s Address Book at the next maintenance window.

  1. As a Node Operator, I want to submit a signed HAPI transaction that modifies a list of GRPC proxy endpoints supporting both IP and FQDN address formats, so I can independently perform address book related node operations.

Acceptance: When a Node Operator submits a HAPI transaction to modify a node’s IP address:port or FQDN:port, the network acknowledges the transaction and performs the update to the network’s Address Book at the next maintenance window.

  1. As a Node Operator, I want to submit a signed HAPI transaction that modifies a node’s description within the Address Book, so I can independently perform address book related node operations.

Acceptance: When a Node Operator submits a HAPI transaction to modify a node’s associated Description Field, then the network acknowledges the transaction and performs the update to the network’s Address Book at the next maintenance window.

  1. As a Node Operator, I want to submit a signed HAPI transaction that modifies a node’s public key within the Address Book used for signing, so I can independently perform address book related node operations.

Acceptance: When a Node Operator submits a HAPI transaction to modify a node’s associated Public Key, then the network acknowledges the transaction and performs the update the network’s Address Book at the next maintenance window.

  1. As a Node Operator, I want to submit a signed HAPI transaction that modifies a node’s Account ID within the Address Book, so I can independently perform address book related node operations.

Acceptance: When a Node Operator submits a HAPI transaction to modify a node’s Account ID, then the network acknowledges the transaction and performs the update the network’s Address Book at the next maintenance window.

  1. As a Node Operator, I want to submit a signed HAPI transaction that modifies a node’s X509 certificate hash within the Address Book, so I can independently perform address book related node operations.

Acceptance: When a Node Operator submits a HAPI transaction to modify a node’s associated X509 certificate hash, then the network acknowledges the transaction and performs the update the network’s Address Book at the next maintenance window.

Specification

This HIP proposes the introduction of a new NodeService API that enables a node operator to create, delete, and update nodes. All of these transactions must be signed by the Hedera Council.

service AddressBookService {
    /**
     * A transaction to create a new consensus node in the network
     * address book.
     * <p>
     * This transaction, once complete, SHALL add a new consensus node to the
     * network state.<br/>
     * The new consensus node SHALL remain in state, but SHALL NOT participate
     * in network consensus until the network updates the network configuration.
     * <p>
     * Hedera governing council authorization is REQUIRED for this transaction.
     */
    rpc createNode (proto.Transaction) returns (proto.TransactionResponse);

    /**
     * A transaction to remove a consensus node from the network address
     * book.
     * <p>
     * This transaction, once complete, SHALL remove the identified consensus
     * node from the network state.
     * <p>
     * Hedera governing council authorization is REQUIRED for this transaction.
     */
    rpc deleteNode (proto.Transaction) returns (proto.TransactionResponse);

    /**
     * A transaction to update an existing consensus node from the network
     * address book.
     * <p>
     * This transaction, once complete, SHALL modify the identified consensus
     * node state as requested.
     * <p>
     * This transaction MUST be authorized by the node operator.
     */
    rpc updateNode (proto.Transaction) returns (proto.TransactionResponse);
}

A new Hedera API will be added called NodeCreate, which falls under the Node Service category. This function is used by the node operator to create a new node. To complete this transaction, both the node operator and a council member must sign it.

message NodeCreateTransactionBody {
    /**
     * A Node account identifier.
     * <p>
     * This account identifier MUST be in the "account number" form.<br/>
     * This account identifier MUST NOT use the alias field.<br/>
     * If the identified account does not exist, this transaction SHALL fail.<br/>
     * Multiple nodes MAY share the same node account.<br/>
     * This field is REQUIRED.
     */
    proto.AccountID account_id = 1;

    /**
     * A short description of the node.
     * <p>
     * This value, if set, MUST NOT exceed 100 bytes when encoded as UTF-8.<br/>
     * This field is OPTIONAL.
     */
    string description = 2;

    /**
     * A list of service endpoints for gossip.
     * <p>
     * These endpoints SHALL represent the published endpoints to which other
     * consensus nodes may _gossip_ transactions.<br/>
     * These endpoints MUST specify a port.<br/>
     * This list MUST NOT be empty.<br/>
     * This list MUST NOT contain more than `10` entries.<br/>
     * The first two entries in this list SHALL be the endpoints published to
     * all consensus nodes.<br/>
     * All other entries SHALL be reserved for future use.
     * <p>
     * Each network may have additional requirements for these endpoints.
     * A client MUST check network-specific documentation for those
     * details.<br/>
     * If the network configuration value `gossipFqdnRestricted` is set, then
     * all endpoints in this list MUST supply only IP address.<br/>
     * If the network configuration value `gossipFqdnRestricted` is _not_ set,
     * then endpoints in this list MAY supply either IP address or FQDN, but
     * MUST NOT supply both values for the same endpoint.
     */
    repeated proto.ServiceEndpoint gossip_endpoint = 3;

    /**
     * A list of service endpoints for gRPC calls.
     * <p>
     * These endpoints SHALL represent the published gRPC endpoints to which
     * clients may submit transactions.<br/>
     * These endpoints MUST specify a port.<br/>
     * Endpoints in this list MAY supply either IP address or FQDN, but MUST
     * NOT supply both values for the same endpoint.<br/>
     * This list MUST NOT be empty.<br/>
     * This list MUST NOT contain more than `8` entries.
     */
    repeated proto.ServiceEndpoint service_endpoint = 4;

    /**
     * A certificate used to sign gossip events.
     * <p>
     * This value MUST be a certificate of a type permitted for gossip
     * signatures.<br/>
     * This value MUST be the DER encoding of the certificate presented.<br/>
     * This field is REQUIRED and MUST NOT be empty.
     */
    bytes gossip_ca_certificate = 5;

    /**
     * A hash of the node gRPC TLS certificate.
     * <p>
     * This value MAY be used to verify the certificate presented by the node
     * during TLS negotiation for gRPC.<br/>
     * This value MUST be a SHA-384 hash.<br/>
     * The TLS certificate to be hashed MUST first be in PEM format and MUST be
     * encoded with UTF-8 NFKD encoding to a stream of bytes provided to
     * the hash algorithm.<br/>
     * This field is OPTIONAL.
     */
    bytes grpc_certificate_hash = 6;

    /**
    * An administrative key controlled by the node operator.
     * <p>
     * This key MUST sign this transaction.<br/>
     * This key MUST sign each transaction to update this node.<br/>
     * This field MUST contain a valid `Key` value.<br/>
     * This field is REQUIRED and MUST NOT be set to an empty `KeyList`.
     */
    proto.Key admin_key = 7;
}

A new Hedera API called NodeDelete will be added under the Node Service. This API function is used by the node operator to delete a node. To perform this transaction, both the node operator and a council member need to sign it.

message NodeDeleteTransactionBody {
    /**
     * A consensus node identifier in the network state.
     * <p>
     * The node identified MUST exist in the network address book.<br/>
     * The node identified MUST NOT be deleted.<br/>
     * This value is REQUIRED.
     */
    uint64 node_id = 1;
}

A new Hedera API called NodeUpdate will be added under the Node Service. This function is used by the node operator to update a node. For this transaction, both the node operator and council member need to sign it.

message NodeUpdateTransactionBody {
    /**
     * A consensus node identifier in the network state.
     * <p>
     * The node identified MUST exist in the network address book.<br/>
     * The node identified MUST NOT be deleted.<br/>
     * This value is REQUIRED.
     */
    uint64 node_id = 1;

    /**
     * An account identifier.
     * <p>
     * If set, this SHALL replace the node account identifier.<br/>
     * If set, this transaction MUST be signed by the active `key` for _both_
     * the current node account _and_ the identified new node account.
     */
    proto.AccountID account_id = 2;

    /**
     * A short description of the node.
     * <p>
     * This value, if set, MUST NOT exceed 100 bytes when encoded as UTF-8.<br/>
     * If set, this value SHALL replace the previous value.
     */
    google.protobuf.StringValue description = 3;

    /**
     * A list of service endpoints for gossip.
     * <p>
     * If set, this list MUST meet the following requirements.
     * <hr/>
     * These endpoints SHALL represent the published endpoints to which other
     * consensus nodes may _gossip_ transactions.<br/>
     * These endpoints SHOULD NOT specify both address and DNS name.<br/>
     * This list MUST NOT be empty.<br/>
     * This list MUST NOT contain more than `10` entries.<br/>
     * The first two entries in this list SHALL be the endpoints published to
     * all consensus nodes.<br/>
     * All other entries SHALL be reserved for future use.
     * <p>
     * Each network may have additional requirements for these endpoints.
     * A client MUST check network-specific documentation for those
     * details.<br/>
     * <blockquote>Example<blockquote>
     * Hedera Mainnet _requires_ that address be specified, and does not
     * permit DNS name (FQDN) to be specified.<br/>
     * Mainnet also requires that the first entry be an "internal" IP
     * address and the second entry be an "external" IP address.
     * </blockquote>
     * <blockquote>
     * Solo, however, _requires_ DNS name (FQDN) but also permits
     * address.
     * </blockquote></blockquote>
     * <p>
     * If set, the new list SHALL replace the existing list.
     */
    repeated proto.ServiceEndpoint gossip_endpoint = 4;

    /**
     * A list of service endpoints for gRPC calls.
     * <p>
     * If set, this list MUST meet the following requirements.
     * <hr/>
     * These endpoints SHALL represent the published endpoints to which clients
     * may submit transactions.<br/>
     * These endpoints SHOULD specify address and port.<br/>
     * These endpoints MAY specify a DNS name.<br/>
     * These endpoints SHOULD NOT specify both address and DNS name.<br/>
     * This list MUST NOT be empty.<br/>
     * This list MUST NOT contain more than `8` entries.
     * <p>
     * Each network may have additional requirements for these endpoints.
     * A client MUST check network-specific documentation for those
     * details.
     * <p>
     * If set, the new list SHALL replace the existing list.
     */
    repeated proto.ServiceEndpoint service_endpoint = 5;

    /**
     * A certificate used to sign gossip events.
     * <p>
     * This value MUST be a certificate of a type permitted for gossip
     * signatures.<br/>
     * This value MUST be the DER encoding of the certificate presented.
     * <p>
     * If set, the new value SHALL replace the existing bytes value.
     */
    google.protobuf.BytesValue gossip_ca_certificate = 6;

    /**
     * A hash of the node gRPC TLS certificate.
     * <p>
     * This value MAY be used to verify the certificate presented by the node
     * during TLS negotiation for gRPC.<br/>
     * This value MUST be a SHA-384 hash.<br/>
     * The TLS certificate to be hashed MUST first be in PEM format and MUST be
     * encoded with UTF-8 NFKD encoding to a stream of bytes provided to
     * the hash algorithm.<br/>
     * <p>
     * If set, the new value SHALL replace the existing hash value.
     */
    google.protobuf.BytesValue grpc_certificate_hash = 7;

    /**
    * An administrative key controlled by the node operator.
     * <p>
     * This field is OPTIONAL.<br/>
     * If set, this key MUST sign this transaction.<br/>
     * If set, this key MUST sign each subsequent transaction to
     * update this node.<br/>
     * If set, this field MUST contain a valid `Key` value.<br/>
     * If set, this field MUST NOT be set to an empty `KeyList`.
     */
    proto.Key admin_key = 8;
}

The following are some changes to the existing HAPI.

Added three HederaFunctionalities:

enum HederaFunctionality {

	[...]

    /**
    * Create a node
    */
    NodeCreate = 89;

    /**
     * Update a node
     */
    NodeUpdate = 90;

    /**
     * Delete a node
     */
    NodeDelete = 91;
}

Added domain_name in ServiceEndpoint.

message ServiceEndpoint {

	[...]

    /**
     * A node domain name.<br/>
     * This MUST be the fully qualified domain(DNS) name of the node.<br/>
     * This value MUST NOT be more than 253 characters.
     * domain_name and ipAddressV4 are mutually exclusive.
     * When the `domain_name` field is set, the `ipAddressV4` field MUST NOT be set.<br/>
     * When the `ipAddressV4` field is set, the `domain_name` field MUST NOT be set.
     */
    string domain_name = 3;
}

Added node_id in TransactionReceipt:

message TransactionReceipt {

	[...]

    /**
    * In the receipt of a NodeCreate, NodeUpdate, NodeDelete, the id of the newly created node.
    * An affected node identifier.<br/>
    * This value SHALL be set following a `createNode` transaction.<br/>
    * This value SHALL be set following a `updateNode` transaction.<br/>
    * This value SHALL be set following a `deleteNode` transaction.<br/>
    * This value SHALL NOT be set following any other transaction.
    */
    uint64 node_id = 15;

}

Added a few response codes.

enum ResponseCodeEnum {

	[...]

    /**
     * A transaction failed because the consensus node identified is
     * deleted from the address book.
     */
    NODE_DELETED = 338;

    /**
     * A transaction failed because the consensus node identified is not valid or
     * does not exist in state.
     */
    INVALID_NODE_ID = 339;

    /**
     * A transaction failed because one or more entries in the list of
     * service endpoints for the `gossip_endpoint` field is invalid.<br/>
     * The most common cause for this response is a service endpoint that has
     * the domain name (DNS) set rather than address and port.
     */
    INVALID_GOSSIP_ENDPOINT = 340;

    /**
     * A transaction failed because the node account identifier provided
     * does not exist or is not valid.<br/>
     * One common source of this error is providing a node account identifier
     * using the "alias" form rather than "numeric" form.
     */
    INVALID_NODE_ACCOUNT_ID = 341;

    /**
     * A transaction failed because the description field cannot be encoded
     * as UTF-8 or is more than 100 bytes when encoded.
     */
    INVALID_NODE_DESCRIPTION = 342;

    /**
     * A transaction failed because one or more entries in the list of
     * service endpoints for the `service_endpoint` field is invalid.<br/>
     * The most common cause for this response is a service endpoint that has
     * the domain name (DNS) set rather than address and port.
     */
    INVALID_SERVICE_ENDPOINT = 343;

    /**
     * A transaction failed because the TLS certificate provided for the
     * node is missing or invalid.<br/>
     * The certificate MUST be a TLS certificate of a type permitted for gossip
     * signatures.<br/>
     * The value presented MUST be a UTF-8 NFKD encoding of the TLS
     * certificate.<br/>
     * The certificate encoded MUST be in PEM format.<br/>
     * The `gossip_ca_certificate` field is REQUIRED and MUST NOT be empty.
     */
    INVALID_GOSSIP_CA_CERTIFICATE = 344;

    /**
     * A transaction failed because the hash provided for the gRPC certificate
     * is present but invalid.<br/>
     * The `grpc_certificate_hash` MUST be a SHA-384 hash.<br/>
     * The input hashed MUST be a UTF-8 NFKD encoding of the actual TLS
     * certificate.<br/>
     * The certificate to be encoded MUST be in PEM format.
     */
    INVALID_GRPC_CERTIFICATE = 345;

    /**
     * The maximum number of nodes allowed in the address book have been created.
     */
    MAX_NODES_CREATED = 347;

    /**
     * In ServiceEndpoint, domain_name and ipAddressV4 are mutually exclusive
     */
    IP_FQDN_CANNOT_BE_SET_FOR_SAME_ENDPOINT = 348;

    /**
     *  Fully qualified domain name is not allowed in gossip_endpoint
     */
    GOSSIP_ENDPOINT_CANNOT_HAVE_FQDN = 349;

    /**
     * In ServiceEndpoint, domain_name size too large
     */
    FQDN_SIZE_TOO_LARGE = 350;

    /**
     * ServiceEndpoint is invalid
     */
    INVALID_ENDPOINT = 351;

    /**
     * The number of gossip endpoints exceeds the limit
     */
    GOSSIP_ENDPOINTS_EXCEEDED_LIMIT = 352;

    /**
     * The number of service endpoints exceeds the limit
     */
    SERVICE_ENDPOINTS_EXCEEDED_LIMIT = 356;

    /*
     * The IPv4 address is invalid
     */
    INVALID_IPV4_ADDRESS = 357;
    
    /*
     * The node account is not allowed to be updated
     */
    UPDATE_NODE_ACCOUNT_NOT_ALLOWED = 359;

}

Each transaction made through NodeService will have a corresponding transaction record. This record will be included in the record stream file for Mirror Nodes to consume.

During stage1, node changes will not be active until the network is upgraded. When the node starts, the Platform still uses config.txt to create an AddressBook, which is then passed to Services. This AddressBook contains the activated nodes in the network. Services use the activated nodes to calculate weight.

When executing the next freeze transaction with freeze_type set to PREPARE_UPGRADE, services will update network configuration according to the pending modifications for all node create, update, or delete transactions since the last upgrade and merge any pending state to active state.

All HIPs that introduce backward incompatibilities must include a section describing these incompatibilities and their severity. The HIP must explain how the author proposes to deal with these incompatibilities. HIP submissions without a sufficient backward compatibility treatise may be rejected outright.

Mirror node update

The mirror node will process the new Node transactions and service_endpoint information, then return that information through its existing APIs.

The following changes will be made to the GRPC API:

  • Update the network service to add domain_name and admin_key.

The following changes will be made to the REST API:

  • Update the /api/v1/network/nodes endpoint to return domain_name and admin_key

Example response:

{
  "nodes": [
    {
      "admin_key": {
        "_type": "ED25519",
        "key": "308201a2300d06092a864886f70d01010105000382018f003082018a028100e0"
      },
      "description": "address book 1",
      "file_id": "0.0.102",
      "max_stake": 50000,
      "memo": "0.0.4",
      "min_stake": 1000,
      "node_account_id": "0.0.4",
      "node_cert_hash": "0x01d173753810c0aae794ba72d5443c292e9ff962b01046220dd99f5816422696e0569c977e2f169e1e5688afc8f4aa16",
      "node_id": 1,
      "public_key": "0x4a5ad514f0957fa170a676210c9bdbddf3bc9519702cf915fa6767a40463b96f",
      "reward_rate_start": 1000000,
      "service_endpoints": [
        {
          "domain_name": "examplenode.com",
          "ip_address_v4": "128.0.0.6",
          "port": 50216
        }
      ],
      "stake": 20000,
      "stake_not_rewarded": 19900,
      "stake_rewarded": 100,
      "staking_period": {
        "from": "1655164800.000000000",
        "to": "1655251200.000000000"
      },
      "timestamp": {
        "from": "187654.000123457",
        "to": null
      }
    }
  ],
  "links": {
    "next": null
  }
}

Security Implications

No security issues identified as of yet

How to Teach This

To educate and facilitate a great customer experience, the following will be required:

  • Documentation - Updates to blogs, playbooks, and technical documentation will be required. Additionally previous descriptions of Address Book functionality should be deprecated across existing content.
  • Tooling - Updates to tooling to facilitate node operation or transaction construction and submittal will be required to facilitate a great customer experience.
  • Transition plan - Since this is transferring responsibility to council members, a formal transition plan should be implemented to ensure a successful hand-off.

Reference Implementation

The reference implementation must be complete before any HIP is given the status of “Final”. The final implementation must include test code and documentation.

Rejected Ideas

TBD

Open Issues

Stage 2 - Full Dynamic Book Implementation

References

TBD

Copyright/license

This document is licensed under the Apache License, Version 2.0 – see LICENSE or (https://www.apache.org/licenses/LICENSE-2.0)

Citation

Please cite this document as: