From 8c82680dc563cbe5fdcbb8dcd9b456b23f9fa065 Mon Sep 17 00:00:00 2001 From: dtaghavi Date: Thu, 3 Jul 2025 20:35:44 +0000 Subject: [PATCH 1/3] Start of Node Op registration --- .../include/sysio.system/node_op.hpp | 26 +++++++++++++++++++ .../include/sysio.system/sysio.system.hpp | 1 + 2 files changed, 27 insertions(+) create mode 100644 contracts/sysio.system/include/sysio.system/node_op.hpp diff --git a/contracts/sysio.system/include/sysio.system/node_op.hpp b/contracts/sysio.system/include/sysio.system/node_op.hpp new file mode 100644 index 0000000..1345409 --- /dev/null +++ b/contracts/sysio.system/include/sysio.system/node_op.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +/** + * + */ +class [[sysio::contract("sysio.system")]] node_operator : sysio::contract { + public: + using sysio::contract::contract; + + struct [[sysio::table, sysio::contract("sysio.system")]] oproot { + checksum256_t op_root; // Current root for node operator registration + uint64_t order; // Most recent order number for node operator registration + + }; + + struct [[sysio::table("opreg"), sysio::contract("sysio.system")]] node_op_reg { + uint64_t order; // Unique identifier for the node operator + name username; // The name of the node operator + sysio::public_key signing_key; // The public signing key of the node operator + checksum256_t op_root; // Checksum for the node operator registration + uint64_t reg_status; + }; +}; \ No newline at end of file diff --git a/contracts/sysio.system/include/sysio.system/sysio.system.hpp b/contracts/sysio.system/include/sysio.system/sysio.system.hpp index 9280b4b..1dc5cad 100644 --- a/contracts/sysio.system/include/sysio.system/sysio.system.hpp +++ b/contracts/sysio.system/include/sysio.system/sysio.system.hpp @@ -201,6 +201,7 @@ namespace sysiosystem { return sysio::block_signing_authority_v0{ .threshold = 1, .keys = {{producer_key, 1}} }; } + // TODO: We ultimately don't need unpaid_blocks / last_claim_time / location / total_votes since these will relate to our new 'scores' table. We can add assigned operator column to know which operator is assigned to a producer. // Defines `producer_info` structure to be stored in `producer_info` table, added after version 1.0 struct [[sysio::table, sysio::contract("sysio.system")]] producer_info { name owner; From 629c26723c9703b0965707ed3c31b947c1df4884 Mon Sep 17 00:00:00 2001 From: dtaghavi Date: Mon, 7 Jul 2025 20:10:41 +0000 Subject: [PATCH 2/3] Initial table definitions - Initial table structures and indexes with a couple of TODO comments. --- .../include/sysio.system/node_op.hpp | 94 +++++++++++++++++-- 1 file changed, 84 insertions(+), 10 deletions(-) diff --git a/contracts/sysio.system/include/sysio.system/node_op.hpp b/contracts/sysio.system/include/sysio.system/node_op.hpp index 1345409..3c5f723 100644 --- a/contracts/sysio.system/include/sysio.system/node_op.hpp +++ b/contracts/sysio.system/include/sysio.system/node_op.hpp @@ -10,17 +10,91 @@ class [[sysio::contract("sysio.system")]] node_operator : sysio::contract { public: using sysio::contract::contract; - struct [[sysio::table, sysio::contract("sysio.system")]] oproot { - checksum256_t op_root; // Current root for node operator registration - uint64_t order; // Most recent order number for node operator registration - + /** + * @brief Tracks the current root for node operator registration, the most recent order number, the block number when the root was last updated, and the account name of the last registered node operator. + * + * TODO: Should block_num be uint128_t? Or should we use block_timestamp? + */ + struct [[sysio::table, sysio::contract("sysio.system")]] oproothead { + oproothead(){} // TODO: Do we need this? What values should we initialize to? + + bytes op_root = {}; // Current root for node operator registration + uint64_t order = 0; // Most recent order number for node operator registration + uint128_t block_num = 0; // Block number when the root was last updated + sysio::name last_registered = {}; // The account name of the node operator last registered + + SYSIO_SERIALIZE(oproothead, (op_root)(order)(block_num)(last_registered)) + }; + + /** + * @brief A complete registration record for node operators. Includes all necessary information to replay oproots. + * + * Reg Status values: 0 = initialized, 1 = propagated, 2 = finalized, 3 = unregistered + * + * TODO: Determine type for reg_status. Probably smallest available integer type? + */ + struct [[sysio::table, sysio::contract("sysio.system")]] opreg { + uint64_t order; // Order number for the node operator registration + sysio::name username; // The account name of the node operator + sysio::public_key signing_key; // The public Signing Key of the node operator + bytes op_root; // Checksum for the node operator registration + uint64_t reg_status; // Registration status of the node operator + + uint64_t primary_key() const { return order; } + uint64_t by_username() const { return username.value; } + checksum256 by_signing_key() const { return sysio::sha256(reinterpret_cast(&signing_key), sizeof(signing_key)); } + checksum256 by_op_root() const { return sysio::sha256(op_root.data(), op_root.size()); } + uint64_t by_status() const { return reg_status; } }; + typedef sysio::multi_index<"opreg"_n, opreg, + sysio::indexed_by<"byusername"_n, sysio::const_mem_fun>, + sysio::indexed_by<"bysigningkey"_n, sysio::const_mem_fun>, + sysio::indexed_by<"byoproot"_n, sysio::const_mem_fun>, + sysio::indexed_by<"bystatus"_n, sysio::const_mem_fun> + > opreg_table; - struct [[sysio::table("opreg"), sysio::contract("sysio.system")]] node_op_reg { - uint64_t order; // Unique identifier for the node operator - name username; // The name of the node operator - sysio::public_key signing_key; // The public signing key of the node operator - checksum256_t op_root; // Checksum for the node operator registration - uint64_t reg_status; + /** + * @brief Scoped by operator's account name, this table is used to track the registration of node operators on external chains. Upon full registration to all chains, the node operator will be added to the `operators` table and the `opregext` table will be cleared. + * + * TODO: Ideal type for block_num? This represents external chain block number. + */ + struct [[sysio::table, sysio::contract("sysio.system")]] opregext { + sysio::name external_chain; // The external chain where operator registration needs to be propagated + sysio::name facilitator; // The facilitator account that will handle the propagation on the external chain + uint128_t block_num; // The block number of the external chain when the registration was made + bytes trx_id; // The transaction ID of the registration on the external chain + bytes trx_signature; // The transaction signature of the registration on the external chain + bytes signed_trx; // The signed transaction of the registration on the external chain, to be broadcasted on their behalf if needed + + uint64_t primary_key() const { return external_chain.value; } }; + + typedef sysio::multi_index<"opregext"_n, opregext> opregext_table; + + /** + * @brief The final node operator roster, tracking all node operators that have been registered, their scores, and their active status. + * + * TODO: Determine score's type. Do we want RPC_URL? + */ + struct [[sysio::table, sysio::contract("sysio.system")]] operators { + sysio::name username; // The account name of the node operator + sysio::public_key signing_key; // The public Signing Key of the node operator + sysio::name producing_as; // The static producer account name the node operator is producing as (alpha - uniform) + uint128_t score; // The score of the node operator, used for ranking + string rpc_url; // The RPC URL of the node operator + bool is_active; // Whether the node operator is currently active or not + + uint64_t primary_key() const { return username.value; } + checksum256 by_signing_key() const { return sysio::sha256(reinterpret_cast(&signing_key), sizeof(signing_key)); } + checksum256 by_producing_as() const { return sysio::sha256(producing_as.to_string().c_str(), producing_as.to_string().size()); } + uint128_t by_score() const { return score; } + bool by_is_active() const { return is_active; } + }; + + typedef sysio::multi_index<"operators"_n, operators, + sysio::indexed_by<"bysigningkey"_n, sysio::const_mem_fun>, + sysio::indexed_by<"byproducingas"_n, sysio::const_mem_fun>, + sysio::indexed_by<"byscore"_n, sysio::const_mem_fun>, + sysio::indexed_by<"byisactive"_n, sysio::const_mem_fun> + > operators_table; }; \ No newline at end of file From 7c5cc4f6bf3323ae3dd947acf158d03eac991609 Mon Sep 17 00:00:00 2001 From: dtaghavi Date: Mon, 7 Jul 2025 21:00:28 +0000 Subject: [PATCH 3/3] Stubbed out registration init action --- .../include/sysio.system/node_op.hpp | 17 ++++++++++++++--- contracts/sysio.system/src/node_op.cpp | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 contracts/sysio.system/src/node_op.cpp diff --git a/contracts/sysio.system/include/sysio.system/node_op.hpp b/contracts/sysio.system/include/sysio.system/node_op.hpp index 3c5f723..340ca39 100644 --- a/contracts/sysio.system/include/sysio.system/node_op.hpp +++ b/contracts/sysio.system/include/sysio.system/node_op.hpp @@ -10,6 +10,18 @@ class [[sysio::contract("sysio.system")]] node_operator : sysio::contract { public: using sysio::contract::contract; + // ----- Node Operator Registration Actions ----- + /** + * @brief Action to register a node operator. This action is used to initiate the registration process for a node operator. + * + * @param username The account name of the node operator. + * @param signing_key The public Signing Key of the node operator. + */ + [[sysio::action("regnodeop")]] + void regnodeop(const sysio::name& username, const sysio::public_key& signing_key); + + // ----- Node Operator Registration Tables ----- + /** * @brief Tracks the current root for node operator registration, the most recent order number, the block number when the root was last updated, and the account name of the last registered node operator. * @@ -74,14 +86,13 @@ class [[sysio::contract("sysio.system")]] node_operator : sysio::contract { /** * @brief The final node operator roster, tracking all node operators that have been registered, their scores, and their active status. * - * TODO: Determine score's type. Do we want RPC_URL? + * TODO: Determine score's type. */ struct [[sysio::table, sysio::contract("sysio.system")]] operators { sysio::name username; // The account name of the node operator sysio::public_key signing_key; // The public Signing Key of the node operator sysio::name producing_as; // The static producer account name the node operator is producing as (alpha - uniform) - uint128_t score; // The score of the node operator, used for ranking - string rpc_url; // The RPC URL of the node operator + uint128_t score; // The score of the node operator, used for ranking bool is_active; // Whether the node operator is currently active or not uint64_t primary_key() const { return username.value; } diff --git a/contracts/sysio.system/src/node_op.cpp b/contracts/sysio.system/src/node_op.cpp new file mode 100644 index 0000000..3d3d3bf --- /dev/null +++ b/contracts/sysio.system/src/node_op.cpp @@ -0,0 +1,18 @@ +#include + +//TODO: Determine how registration fee will be handled. Some sort of check must be done. +void node_operator::regnodeop(const sysio::name& username, const sysio::public_key& signing_key) { + // Ensure user is registering themselves. + require_auth(username); + + // Ensure the signing key is a valid K1 public key. + sysio::check(signing_key.which() == sysio::key_type::k1, "Signing key must be a K1 public key"); + + // TODO: Make sure username is not already registered as an 'active' node operator. Should only allow re-registration if the node operator is 'inactive' in operators table (active == false ) and is trying to subtitute their signing key. Otherwise they should simply toggle back to active status. + + // TODO: Create opreg row. + + // TODO: Create opregext row for each external chain. Based on 'wnsmanager' table where chain == active. + + // TODO: Update oproothead row update the order and block_num. +} \ No newline at end of file