Skip to content

title: networkregistry/NetworkRegistry.cpp summary: Implementation of the SecureCRDT-backed per-privateNetworkId membership registry (D-03, D-06): TPR-majority bootstrap, cached self-governance, secret-free records.


networkregistry/NetworkRegistry.cpp

Implementation of the SecureCRDT-backed per-privateNetworkId membership registry (D-03, D-06): TPR-majority bootstrap, cached self-governance, secret-free records. More...

Namespaces

Name
sgns
sgns::networkregistry

Detailed Description

Implementation of the SecureCRDT-backed per-privateNetworkId membership registry (D-03, D-06): TPR-majority bootstrap, cached self-governance, secret-free records.

Date: 2026-09-01 Henrique A. Klein ([email protected])

Source code

#include "networkregistry/NetworkRegistry.hpp"

#include <algorithm>
#include <system_error>
#include <unordered_set>

#include "base/hexutil.hpp"
#include "crdt/globaldb/globaldb.hpp"
#include "securecrdt/QuorumThresholdValidation.hpp"

namespace sgns::networkregistry
{
    namespace
    {
        bool HasPeerIdMultihashPrefix( const std::string &entry )
        {
            return entry.rfind( "Qm", 0 ) == 0 || entry.rfind( "12D3KooW", 0 ) == 0;
        }

        bool IsLowerHex( const std::string &value )
        {
            if ( value.empty() || value.size() % 2 != 0 )
            {
                return false;
            }
            return std::all_of( value.begin(), value.end(), []( unsigned char c ) {
                return ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'f' );
            } );
        }

        std::vector<std::string> SplitLines( const std::string &raw )
        {
            std::vector<std::string> lines;
            size_t                   start = 0;
            while ( start <= raw.size() )
            {
                const auto pos = raw.find( '\n', start );
                if ( pos == std::string::npos )
                {
                    if ( start < raw.size() )
                    {
                        lines.push_back( raw.substr( start ) );
                    }
                    break;
                }
                lines.push_back( raw.substr( start, pos - start ) );
                start = pos + 1;
            }
            return lines;
        }

        std::string EscapeRegex( const std::string &value )
        {
            static const std::string metacharacters = R"(\.^$|()[]{}*+?)";
            std::string              result;
            result.reserve( value.size() * 2 );
            for ( const char byte : value )
            {
                if ( metacharacters.find( byte ) != std::string::npos )
                {
                    result.push_back( '\\' );
                }
                result.push_back( byte );
            }
            return result;
        }

        bool ParseCountLine( const std::string &line, const std::string &tag, size_t &count )
        {
            if ( line.rfind( tag + " ", 0 ) != 0 )
            {
                return false;
            }
            const std::string number = line.substr( tag.size() + 1 );
            if ( number.empty() || number.size() > 9 )
            {
                return false;
            }
            if ( !std::all_of( number.begin(), number.end(), []( unsigned char c ) { return c >= '0' && c <= '9'; } ) )
            {
                return false;
            }
            count = static_cast<size_t>( std::stoull( number ) );
            return true;
        }

        sgns::base::Logger &FactoryLogger()
        {
            static sgns::base::Logger logger = sgns::base::createLogger( "networkregistry" );
            return logger;
        }
    } // namespace

    //
    // NetworkMembershipPayload
    //
    // Wire layout (line-based, '\n'-delimited; PeerId base58 and hex address
    // characters never contain '\n'):
    //   SGNS-NETREG-1
    //   PEERS <n>
    //   SIGNERS <m>
    //   VERSION <v>
    //   FINGERPRINT <hex-or-empty>
    //   <peer 1..n>
    //   <signer 1..m>
    //
    NetworkMembershipPayload::NetworkMembershipPayload( std::vector<std::string> peers,
                                                        std::vector<std::string> signers,
                                                        uint32_t                pnet_key_version,
                                                        std::string             pnet_key_fingerprint ) :
        network_peers( std::move( peers ) ),
        network_signers( std::move( signers ) ),
        pnet_key_version( pnet_key_version ),
        pnet_key_fingerprint( std::move( pnet_key_fingerprint ) )
    {
    }

    std::vector<uint8_t> NetworkMembershipPayload::SerializeToBytes() const
    {
        std::string out;
        out.append( MAGIC.data(), MAGIC.size() );
        out += '\n';
        out += "PEERS " + std::to_string( network_peers.size() ) + '\n';
        out += "SIGNERS " + std::to_string( network_signers.size() ) + '\n';
        out += "VERSION " + std::to_string( pnet_key_version ) + '\n';
        out += "FINGERPRINT " + pnet_key_fingerprint + '\n';
        for ( const auto &peer : network_peers )
        {
            out += peer;
            out += '\n';
        }
        for ( const auto &signer : network_signers )
        {
            out += signer;
            out += '\n';
        }
        return std::vector<uint8_t>( out.begin(), out.end() );
    }

    std::optional<NetworkMembershipPayload> NetworkMembershipPayload::FromBytes( const std::vector<uint8_t> &bytes )
    {
        if ( bytes.empty() )
        {
            return std::nullopt;
        }

        const std::string raw( bytes.begin(), bytes.end() );
        if ( raw.rfind( std::string( MAGIC ) + "\n", 0 ) != 0 )
        {
            return std::nullopt;
        }

        const auto lines = SplitLines( raw.substr( MAGIC.size() + 1 ) );
        // 4 header lines + n peers + m signers
        if ( lines.size() < 4 )
        {
            return std::nullopt;
        }

        size_t peer_count   = 0;
        size_t signer_count = 0;
        if ( !ParseCountLine( lines[0], "PEERS", peer_count ) || !ParseCountLine( lines[1], "SIGNERS", signer_count ) )
        {
            return std::nullopt;
        }

        const std::string version_line = lines[2];
        if ( version_line.rfind( "VERSION ", 0 ) != 0 )
        {
            return std::nullopt;
        }
        const std::string version_number = version_line.substr( 8 );
        if ( version_number.empty() || version_number.size() > 9 ||
             !std::all_of( version_number.begin(),
                           version_number.end(),
                           []( unsigned char c ) { return c >= '0' && c <= '9'; } ) )
        {
            return std::nullopt;
        }

        const std::string fingerprint_line = lines[3];
        if ( fingerprint_line.rfind( "FINGERPRINT ", 0 ) != 0 )
        {
            return std::nullopt;
        }
        const std::string fingerprint = fingerprint_line.substr( 12 );

        if ( lines.size() != 4 + peer_count + signer_count )
        {
            return std::nullopt;
        }

        NetworkMembershipPayload payload;
        payload.network_peers.assign( lines.begin() + 4,
                                      lines.begin() + 4 + static_cast<std::ptrdiff_t>( peer_count ) );
        payload.network_signers.assign( lines.begin() + 4 + static_cast<std::ptrdiff_t>( peer_count ), lines.end() );
        payload.pnet_key_version      = static_cast<uint32_t>( std::stoull( version_number ) );
        payload.pnet_key_fingerprint = fingerprint;
        return payload;
    }

    bool NetworkMembershipPayload::DeserializeFromBytes( const std::vector<uint8_t> &bytes )
    {
        auto payload = FromBytes( bytes );
        if ( !payload )
        {
            return false;
        }
        *this = std::move( *payload );
        return true;
    }

    bool NetworkMembershipPayload::Verify( const std::vector<uint8_t> &payload ) const
    {
        if ( payload.empty() )
        {
            return false;
        }

        // Structural-only check: re-parse independently and validate shape --
        // never diff against cached/mutable state (TrustedPeerListPayload
        // convention).
        auto decoded = FromBytes( payload );
        if ( !decoded )
        {
            return false;
        }

        if ( decoded->network_peers.empty() )
        {
            return false;
        }

        std::unordered_set<std::string> unique_entries;
        for ( const auto &peer : decoded->network_peers )
        {
            if ( peer.empty() || !HasPeerIdMultihashPrefix( peer ) || !unique_entries.insert( peer ).second )
            {
                return false; // empty, non-PeerId, or duplicate entry
            }
        }
        for ( const auto &signer : decoded->network_signers )
        {
            if ( !sgns::base::IsHexAddress( signer ) || !unique_entries.insert( signer ).second )
            {
                return false; // malformed or duplicate signer address
            }
        }
        if ( decoded->pnet_key_version < 1 )
        {
            return false;
        }
        if ( !decoded->pnet_key_fingerprint.empty() && !IsLowerHex( decoded->pnet_key_fingerprint ) )
        {
            return false;
        }
        return true;
    }

    void NetworkMembershipPayload::Apply()
    {
        // Cache overwrite happens in NetworkRegistry::TryConfirm (the owning
        // registry), mirroring TrustedPeerListPayload's no-op Apply.
    }

    //
    // NetworkRegistry
    //
    NetworkRegistry::NetworkRegistry( std::shared_ptr<sgns::securecrdt::SecureCrdt>           secure_crdt,
                                      std::shared_ptr<sgns::trustedpeer::TrustedPeerRegistry> global_trusted_peers,
                                      std::string                                            private_network_id,
                                      std::vector<std::string>                               initial_network_peers,
                                      uint64_t                                               network_quorum_threshold,
                                      std::vector<std::string>                               initial_network_signers,
                                      std::string                                            pnet_key_fingerprint,
                                      sgns::crdt::HierarchicalKey                            base_key,
                                      std::shared_ptr<sgns::crdt::GlobalDB>                   global_db ) :
        secure_crdt_( std::move( secure_crdt ) ),
        global_trusted_peers_( std::move( global_trusted_peers ) ),
        base_key_( std::move( base_key ) ),
        network_quorum_threshold_( network_quorum_threshold ),
        cached_network_peers_( std::move( initial_network_peers ) ),
        cached_network_signers_( std::move( initial_network_signers ) ),
        pnet_key_fingerprint_( std::move( pnet_key_fingerprint ) ),
        private_network_id_( std::move( private_network_id ) ),
        global_db_( std::move( global_db ) )
    {
        // Snapshot the TPR bootstrap authority NOW (cached-only resolution
        // never consults the live TPR again -- Pitfall 9).
        tpr_bootstrap_peers_     = global_trusted_peers_->GetCurrentPeers();
        tpr_majority_threshold_  = sgns::securecrdt::StrictMajorityQuorumFloor( tpr_bootstrap_peers_.size() );
    }

    NetworkRegistry::~NetworkRegistry()
    {
        Unregister();
    }

    sgns::crdt::HierarchicalKey NetworkRegistry::DefaultBaseKey( const std::string &private_network_id )
    {
        return sgns::crdt::HierarchicalKey( "network-registry" ).ChildString( private_network_id );
    }

    outcome::result<std::shared_ptr<NetworkRegistry>> NetworkRegistry::New(
        std::shared_ptr<sgns::securecrdt::SecureCrdt>           secure_crdt,
        std::shared_ptr<sgns::trustedpeer::TrustedPeerRegistry> global_trusted_peers,
        std::string                                            private_network_id,
        std::vector<std::string>                               initial_network_peers,
        uint64_t                                               network_quorum_threshold,
        std::vector<std::string>                               initial_network_signers,
        std::string                                            pnet_key_fingerprint,
        std::shared_ptr<sgns::crdt::GlobalDB>                   global_db )
    {
        if ( !secure_crdt || !global_trusted_peers || private_network_id.empty() )
        {
            return outcome::failure( std::errc::invalid_argument );
        }

        const auto tpr_peers = global_trusted_peers->GetCurrentPeers();
        // Floor check #1: the bootstrap threshold is the strict majority of
        // the TPR's CURRENT peer set (D-06) -- the bootstrap record confirms
        // only with a majority of the global trusted peers.
        const auto bootstrap_threshold = sgns::securecrdt::StrictMajorityQuorumFloor( tpr_peers.size() );
        auto       floor_result        = sgns::securecrdt::ValidateQuorumThreshold( bootstrap_threshold, tpr_peers.size() );
        if ( floor_result.has_error() )
        {
            return floor_result.error();
        }
        // Floor check #2: the self-governance quorum must clear the strict
        // majority floor over the network membership.
        floor_result = sgns::securecrdt::ValidateQuorumThreshold( network_quorum_threshold, initial_network_peers.size() );
        if ( floor_result.has_error() )
        {
            return floor_result.error();
        }
        // Floor check #2b: when member signers are provisioned, the
        // self-governance quorum must also be satisfiable by them.
        if ( !initial_network_signers.empty() )
        {
            floor_result = sgns::securecrdt::ValidateQuorumThreshold( network_quorum_threshold,
                                                                     initial_network_signers.size() );
            if ( floor_result.has_error() )
            {
                return floor_result.error();
            }
        }

        // WR-03: non-destructive duplicate check. Resolving an existing
        // policy entry for this network id means a live registry already
        // owns the branch. Fail here -- BEFORE any make_shared/Register call
        // -- so nothing is registered, no instance is created, and the live
        // entry (and the registry still using it) remains fully functional.
        // Without this, SecureCrdtRegistry::Register would REPLACE the live
        // entry, the destroyed duplicate's ~NetworkRegistry -> Unregister()
        // would then remove the newly inserted entry, and the first registry
        // would be left with no policy entry (every subsequent write failing
        // UNREGISTERED_KEY).
        const auto base_key = DefaultBaseKey( private_network_id );
        if ( secure_crdt->Registry().Resolve( base_key.GetKey() ).has_value() )
        {
            // Names the public network id / derived base key only -- never
            // any key material (D-03 logging posture).
            FactoryLogger()->error( "{}: a registry for private network {} is already registered (base key {}); "
                                    "duplicate construction refused, live registry untouched",
                                    __func__,
                                    private_network_id,
                                    base_key.GetKey() );
            return outcome::failure( std::errc::address_in_use );
        }

        auto instance = std::make_shared<NetworkRegistry>( std::move( secure_crdt ),
                                                           std::move( global_trusted_peers ),
                                                           private_network_id,
                                                           std::move( initial_network_peers ),
                                                           network_quorum_threshold,
                                                           std::move( initial_network_signers ),
                                                           std::move( pnet_key_fingerprint ),
                                                           base_key,
                                                           std::move( global_db ) );
        if ( !instance->RegisterSignerSetSource() )
        {
            return outcome::failure( std::errc::address_in_use );
        }
        if ( instance->global_db_ )
        {
            // G-WR-02: fail-closed construction. A failed change-callback
            // registration would otherwise leave New succeeding with NO live
            // refresh on this node -- the 15-12 live-widening core feature
            // silently dead. Fail construction instead.
            if ( !instance->RegisterCrdtChangeCallback() )
            {
                instance->logger_->error( "{}: registering the CRDT change callback for pattern "
                                          "\"{}\" failed (pattern already live on the GlobalDB?); "
                                          "failing construction -- live membership refresh must not "
                                          "silently degrade",
                                          __func__,
                                          "/?" + EscapeRegex( instance->base_key_.GetKey() ) + "(/sig/.*)?" );
                // Explicit teardown (NOT destructor-driven): the just-registered
                // policy entry's peer_registry strong capture pins the instance
                // in the SecureCrdtRegistry, so ~NetworkRegistry would never
                // run while the caller still holds the failure result.
                // Unregister() removes the entry (owner_token matches THIS
                // instance), breaking the pin; no refresh thread and no
                // callback pattern exist at this point (cleared on the
                // failure path), so the rest of the teardown is a no-op.
                instance->Unregister();
                return outcome::failure( std::errc::address_in_use );
            }
        }
        // WR-04: both production wiring paths run SecureCrdt::RegisterFilters()
        // BEFORE this factory (GeniusNode.cpp:1037 vs :1059; the
        // TrustStartupController path has the same ordering), so the
        // network-registry/<id> pattern would otherwise receive NO ingest
        // element filter and remote-originated unsigned/under-signed
        // membership payloads and sig children would be accepted into the
        // datastore without canonical-signer verification. RegisterFilters()
        // is safely re-runnable: it re-snapshots AllEntries() (now including
        // this registry's pattern) and re-installs one element filter per
        // pattern (re-registration replaces-and-succeeds).
        if ( !instance->secure_crdt_->RegisterFilters() )
        {
            instance->logger_->error( "{}: re-registering SecureCrdt ingest filters failed for base key {}",
                                      __func__,
                                      instance->base_key_.GetKey() );
            // Same pinning hazard as the callback-failure branch above: the
            // registered policy entry strongly captures the instance, so the
            // explicit Unregister() here is what actually removes it (and any
            // partially installed per-pattern filter) -- without it a "failed"
            // New would leave a live zombie policy entry that blocks every
            // retry with address_in_use.
            instance->Unregister();
            return outcome::failure( std::errc::io_error );
        }
        instance->logger_->info( "{}: SecureCrdt ingest filters re-registered -- network-registry branch {} "
                                 "is now ingest-filtered",
                                 __func__,
                                 instance->base_key_.GetKey() );
        return instance;
    }

    bool NetworkRegistry::RegisterSignerSetSource()
    {
        sgns::securecrdt::SecureCrdtRegistryEntry entry;
        entry.signer_set_source = [weak_self = weak_from_this()](
                                      const std::string & ) -> outcome::result<sgns::securecrdt::SignerSetSnapshot>
        {
            auto self = weak_self.lock();
            if ( !self )
            {
                return sgns::securecrdt::SignerSetSnapshot{};
            }
            return self->ResolveSignerSet();
        };
        entry.make_instance = []() -> std::shared_ptr<sgns::securecrdt::ISignedCRDTData>
        { return std::make_shared<NetworkMembershipPayload>(); };
        entry.owner_token = &registry_token_;
        // D-04: record the explicit per-key authority so Resolve() callers
        // can see which PeerRegistry owns this network's branch.
        entry.peer_registry = shared_from_this();

        // G-WR-04: atomic-detecting insert. The Resolve() pre-check in New()
        // is only the descriptive fast path; THIS call is the safety
        // mechanism -- RegisterIfAbsent can never replace a live entry, so
        // two racing New() constructions can no longer clobber each other's
        // policy entry (the loser fails with address_in_use and its teardown
        // removes nothing it does not own).
        return secure_crdt_->Registry().RegisterIfAbsent( EscapeRegex( base_key_.GetKey() ), std::move( entry ) );
    }

    bool NetworkRegistry::RegisterCrdtChangeCallback()
    {
        // BurnConfig pattern (re-derived for a trust-transitioning registry):
        // refresh the cache when a base_key or sig/<addr> child element
        // arrives, so later quorum-signed membership changes land without an
        // explicit TryConfirm call. Unlike BurnConfig, this registry's
        // signer-set authority TRANSITIONS at confirmation, so the quorum
        // read prunes stale signature children (GlobalDB::Remove) -- running
        // that re-entrantly from inside a datastore Put callback corrupts
        // the datastore. The callback therefore only flags + notifies; the
        // refresh thread performs the actual TryConfirm.
        change_callback_pattern_ = "/?" + EscapeRegex( base_key_.GetKey() ) + "(/sig/.*)?";
        auto weak_self           = weak_from_this();
        const bool registered    = global_db_->RegisterNewElementCallback(
            change_callback_pattern_,
            [weak_self]( sgns::crdt::CRDTCallbackManager::NewDataPair, const std::string & )
            {
                if ( auto self = weak_self.lock() )
                {
                    self->refresh_pending_.store( true, std::memory_order_release );
                    std::lock_guard<std::mutex> lock( self->refresh_mutex_ );
                    self->refresh_cv_.notify_one();
                }
            } );
        if ( !registered )
        {
            // G-WR-02: clear the pattern so the teardown path (and the
            // caller's failure handling) knows no callback of ours is live --
            // the pre-existing registration under this pattern belongs to
            // someone else and must not be removed by our Unregister().
            change_callback_pattern_.clear();
            logger_->error( "{}: change-callback pattern \"{}\" could not be registered "
                            "(already live on the GlobalDB)",
                            __func__,
                            "/?" + EscapeRegex( base_key_.GetKey() ) + "(/sig/.*)?" );
            return false;
        }
        refresh_stopping_.store( false, std::memory_order_release );
        refresh_thread_ = std::thread( [weak_self] {
            if ( auto self = weak_self.lock() )
            {
                self->RefreshLoop();
            }
        } );
        return true;
    }

    void NetworkRegistry::RefreshLoop()
    {
        while ( !refresh_stopping_.load( std::memory_order_acquire ) )
        {
            std::unique_lock<std::mutex> lock( refresh_mutex_ );
            refresh_cv_.wait( lock,
                              [&]
                              {
                                  return refresh_pending_.load( std::memory_order_acquire )
                                      || refresh_stopping_.load( std::memory_order_acquire );
                              } );
            if ( refresh_stopping_.load( std::memory_order_acquire ) )
            {
                return;
            }
            // WR-02: drain-once semantics -- the notification that woke this
            // iteration is consumed here, WHILE still holding refresh_mutex_
            // (before the unlock below, which stays before TryConfirm). The
            // mutex-guarded clear is ordered against the datastore callback's
            // mutex-guarded notify, so a notification arriving DURING
            // TryConfirm re-sets the flag and the next loop iteration wakes
            // and processes it -- no lost refresh. After the last
            // notification the wait predicate is false again and the thread
            // returns to waiting (no permanent busy-spin of TryConfirm +
            // GlobalDB scans). Deliberately NO retry semantics: re-setting
            // the flag after a success(false) TryConfirm would resurrect the
            // spin.
            refresh_pending_.store( false, std::memory_order_release );
            lock.unlock();

            refresh_attempts_.fetch_add( 1, std::memory_order_relaxed );
            auto confirmed = TryConfirm();
            if ( confirmed.has_error() )
            {
                logger_->warn( "{}: TryConfirm failed: {}", __func__, confirmed.error().message() );
            }
        }
    }

    uint64_t NetworkRegistry::RefreshAttemptsForTesting() const
    {
        return refresh_attempts_.load( std::memory_order_relaxed );
    }

    outcome::result<sgns::securecrdt::SignerSetSnapshot> NetworkRegistry::CurrentSignerSet() const
    {
        return ResolveSignerSet();
    }

    outcome::result<sgns::securecrdt::SignerSetSnapshot> NetworkRegistry::ResolveSignerSet() const
    {
        // Cached state ONLY -- NEVER ReadIfQuorum from here (Pitfall 9: this
        // runs inside SecureCrdt's verification flow).
        std::shared_lock<std::shared_mutex> lock( cache_mutex_ );
        if ( !bootstrap_confirmed_ )
        {
            return sgns::securecrdt::SignerSetSnapshot{ tpr_bootstrap_peers_, tpr_majority_threshold_ };
        }
        return sgns::securecrdt::SignerSetSnapshot{ cached_network_signers_, network_quorum_threshold_ };
    }

    outcome::result<void> NetworkRegistry::SeedBootstrap( const std::vector<std::string> &initial_network_peers )
    {
        logger_->info( "{}: seeding bootstrap membership record ({} peers) for private network",
                       __func__,
                       initial_network_peers.size() );

        std::vector<std::string> signers;
        {
            std::shared_lock<std::shared_mutex> lock( cache_mutex_ );
            signers = cached_network_signers_;
        }
        const NetworkMembershipPayload payload( initial_network_peers,
                                                std::move( signers ),
                                                1,
                                                pnet_key_fingerprint_ );
        // No self-signature: TPR member nodes add signatures through the
        // standard propose/sign flow until the TPR-majority quorum is met.
        return secure_crdt_->ProposeValue( base_key_, payload.SerializeToBytes() );
    }

    outcome::result<void> NetworkRegistry::ProposeMembershipChange( const std::vector<std::string> &new_peers,
                                                                    const std::vector<std::string> &new_signers )
    {
        logger_->info( "{}: proposing membership change ({} peers)", __func__, new_peers.size() );
        std::vector<std::string> signers = new_signers;
        if ( signers.empty() )
        {
            // Empty replacement would permanently fail-close self-governance:
            // keep the currently-cached signer list instead.
            std::shared_lock<std::shared_mutex> lock( cache_mutex_ );
            signers = cached_network_signers_;
        }
        std::string fingerprint;
        {
            std::shared_lock<std::shared_mutex> lock( cache_mutex_ );
            fingerprint = pnet_key_fingerprint_;
        }
        const NetworkMembershipPayload payload( new_peers, std::move( signers ), 1, std::move( fingerprint ) );
        return secure_crdt_->ProposeValue( base_key_, payload.SerializeToBytes() );
    }

    outcome::result<void> NetworkRegistry::SignMembershipChange( const std::string          &signer_address,
                                                                 const std::vector<uint8_t> &signature )
    {
        logger_->info( "{}: signing membership change (signer={})", __func__, signer_address );
        return secure_crdt_->AddSignature( base_key_,
                                           signer_address,
                                           std::vector<uint8_t>( signature.begin(), signature.end() ) );
    }

    outcome::result<bool> NetworkRegistry::TryConfirm()
    {
        auto read_result = secure_crdt_->ReadIfQuorum( base_key_ );
        if ( read_result.has_error() )
        {
            return read_result.error();
        }

        if ( !read_result.value().has_value() )
        {
            return outcome::success( false );
        }

        const auto bytes   = read_result.value()->toVector();
        auto       payload = NetworkMembershipPayload::FromBytes( bytes );
        if ( !payload )
        {
            logger_->error( "{}: confirmed value failed to deserialize", __func__ );
            return outcome::failure( std::errc::bad_message );
        }
        if ( !payload->Verify( bytes ) )
        {
            logger_->error( "{}: confirmed value failed structural verification", __func__ );
            return outcome::failure( std::errc::bad_message );
        }
        payload->Apply();

        {
            std::unique_lock<std::shared_mutex> lock( cache_mutex_ );
            cached_network_peers_   = payload->GetNetworkPeers();
            cached_network_signers_ = payload->GetNetworkSigners();
            bootstrap_confirmed_    = true;
        }

        logger_->info( "{}: confirmed membership record ({} peers)", __func__, payload->GetNetworkPeers().size() );
        return outcome::success( true );
    }

    std::vector<std::string> NetworkRegistry::GetCurrentPeers() const
    {
        std::shared_lock<std::shared_mutex> lock( cache_mutex_ );
        return cached_network_peers_;
    }

    bool NetworkRegistry::IsBootstrapConfirmed() const
    {
        std::shared_lock<std::shared_mutex> lock( cache_mutex_ );
        return bootstrap_confirmed_;
    }

    void NetworkRegistry::Unregister()
    {
        // Step 1: remove the policy entry (compare-and-remove with the owner
        // token). `removed_own_entry` scopes the Step-4 filter teardown to the
        // case where THIS instance owned the pattern -- a duplicate-New loser
        // (or a teardown racing a newer owner) must never strip the LIVE
        // registry's ingest filter.
        const bool removed_own_entry = secure_crdt_
            && secure_crdt_->Registry().UnregisterIf( EscapeRegex( base_key_.GetKey() ), &registry_token_ );
        // Step 2: stop + join the refresh thread BEFORE tearing down the
        // callback or the db references it uses.
        refresh_stopping_.store( true, std::memory_order_release );
        {
            std::lock_guard<std::mutex> lock( refresh_mutex_ );
            refresh_cv_.notify_all();
        }
        if ( refresh_thread_.joinable() )
        {
            refresh_thread_.join();
        }
        // Step 3: remove the change callback (only when OUR registration is
        // the live one -- the pattern is empty on every path that did not
        // register, so a pre-existing foreign callback under the same pattern
        // is never touched).
        if ( global_db_ && !change_callback_pattern_.empty() )
        {
            global_db_->UnregisterNewElementCallback( change_callback_pattern_ );
            change_callback_pattern_.clear();
        }
        // Step 4 (G-WR-01): remove the ingest element filter LAST. The
        // filter's lambda captures the registry entry BY VALUE (strong
        // peer_registry pin, 15-11) -- removing it may drop the final
        // NetworkRegistry reference and re-enter ~NetworkRegistry ->
        // Unregister(); every earlier step is already torn down by then, and
        // the re-entered call's Step 1/Step 4 are no-ops (entry gone ->
        // removed_own_entry false; pattern already removed -> erase-remove on
        // a missing pattern).
        if ( secure_crdt_ && removed_own_entry )
        {
            secure_crdt_->UnregisterFiltersFor( EscapeRegex( base_key_.GetKey() ) );
        }
    }
} // namespace sgns::networkregistry

Updated on 2026-10-06 at 13:34:21 +0000