securecrdt/SecureCrdtRegistry.hpp¶
Instance registry mapping a base_key pattern to {signer-set source, required signature count, ISignedCRDTData factory}, resolvable at startup/runtime by key. Each SecureCrdt owns an independent instance so in-process nodes cannot replace one another's policy. More...
Namespaces¶
| Name |
|---|
| sgns |
| sgns::peerregistry |
| sgns::securecrdt |
Classes¶
| Name | |
|---|---|
| struct | sgns::securecrdt::SignerSetSnapshot Snapshot of an authorized signer set and its required signature count, as produced by an injected SignerSetSource. |
| struct | sgns::securecrdt::CandidateAuthorizationSnapshot |
| struct | sgns::securecrdt::CandidateDomainEntry |
| struct | sgns::securecrdt::SecureCrdtRegistryEntry Policy entry describing how a registered key pattern is verified and instantiated. |
| class | sgns::securecrdt::SecureCrdtRegistry Thread-safe registry resolving a CRDT key to its policy entry. |
Types¶
| Name | |
|---|---|
| using std::function< outcome::result< SignerSetSnapshot >(const std::string &base_key)> | SignerSetSource Injectable callback resolving the current authorized signer set for a given base_key. NOT hard-wired to TrustedPeerRegistry (which does not exist until Phase 10) - tests inject a fixed-list lambda. |
| using std::function< outcome::result< CandidateAuthorizationSnapshot >()> | CandidateAuthorizationSource |
Detailed Description¶
Instance registry mapping a base_key pattern to {signer-set source, required signature count, ISignedCRDTData factory}, resolvable at startup/runtime by key. Each SecureCrdt owns an independent instance so in-process nodes cannot replace one another's policy.
Date: 2026-07-23 Henrique A. Klein ([email protected])
Types Documentation¶
using SignerSetSource¶
using sgns::securecrdt::SignerSetSource = typedef std::function<outcome::result<SignerSetSnapshot>( const std::string &base_key )>;
Injectable callback resolving the current authorized signer set for a given base_key. NOT hard-wired to TrustedPeerRegistry (which does not exist until Phase 10) - tests inject a fixed-list lambda.
using CandidateAuthorizationSource¶
using sgns::securecrdt::CandidateAuthorizationSource = typedef std::function<outcome::result<CandidateAuthorizationSnapshot>()>;
Return SecureCrdt::Error::CANDIDATE_AUTHORIZATION_PENDING when local trust prerequisites are missing, so incoming approvals can be retried safely.
Source code¶
#ifndef SGNS_SECURECRDT_SECURECRDTREGISTRY_HPP
#define SGNS_SECURECRDT_SECURECRDTREGISTRY_HPP
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <regex>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include "outcome/outcome.hpp"
#include "securecrdt/ISignedCRDTData.hpp"
#include "securecrdt/SecureCrdtCandidate.hpp"
namespace sgns::peerregistry
{
class PeerRegistry; // complete type not needed here - association only (D-04);
// the adaptation helper lives in peerregistry/PeerRegistry.hpp
// to avoid an include cycle.
} // namespace sgns::peerregistry
namespace sgns::securecrdt
{
struct SignerSetSnapshot
{
std::vector<std::string> signer_set;
uint64_t required_signatures = 0;
};
using SignerSetSource = std::function<outcome::result<SignerSetSnapshot>( const std::string &base_key )>;
struct CandidateAuthorizationSnapshot
{
uint16_t network_id = 0;
CandidateKind kind = CandidateKind::TrustPolicy;
uint64_t next_version = 0;
std::string expected_previous_hash;
std::string authorizing_policy_hash;
std::vector<std::string> authorized_signers;
};
using CandidateAuthorizationSource = std::function<outcome::result<CandidateAuthorizationSnapshot>()>;
struct CandidateDomainEntry
{
std::string domain;
CandidateKind kind = CandidateKind::TrustPolicy;
CandidateAuthorizationSource authorization_source;
const void *owner_token = nullptr;
};
struct SecureCrdtRegistryEntry
{
std::string key_pattern;
SignerSetSource signer_set_source;
std::function<std::shared_ptr<ISignedCRDTData>()> make_instance;
std::regex compiled_pattern;
const void *owner_token = nullptr;
std::shared_ptr<sgns::peerregistry::PeerRegistry> peer_registry;
};
class SecureCrdtRegistry
{
public:
bool Register( const std::string &key_pattern, SecureCrdtRegistryEntry entry )
{
entry.key_pattern = key_pattern;
entry.compiled_pattern = std::regex( "/?" + key_pattern + "(/sig/[^/]+)?" );
{
// Unlink any replaced entry WITHOUT destroying it while the
// registry mutex is held: a replaced entry's peer_registry may
// own the last reference to a PeerRegistry whose destructor
// re-enters Unregister() -> UnregisterIf() (destruction
// re-entrancy; std::shared_mutex is not recursive).
std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
auto replaced = registry_.extract( key_pattern );
lock.unlock();
} // replaced node (if any) destroyed here, mutex released
std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
return registry_.insert_or_assign( key_pattern, std::move( entry ) ).second;
}
bool RegisterIfAbsent( const std::string &key_pattern, SecureCrdtRegistryEntry entry )
{
entry.key_pattern = key_pattern;
entry.compiled_pattern = std::regex( "/?" + key_pattern + "(/sig/[^/]+)?" );
bool inserted = false;
{
std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
// find-then-emplace under ONE continuous lock hold: emplace
// cannot lose the race, so the moved entry is never destroyed
// under the mutex (mirror of Register/UnregisterIf's
// extract-then-destroy destruction-reentrancy safety -- a
// failed insert's caller-owned entry copy is destroyed after
// the lock released).
if ( registry_.find( key_pattern ) == registry_.end() )
{
inserted = registry_.emplace( key_pattern, std::move( entry ) ).second;
}
} // lock released; a rejected entry copy is destroyed after this point
return inserted;
}
bool UnregisterIf( const std::string &key_pattern, const void *expected_token )
{
std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
auto it = registry_.find( key_pattern );
if ( it != registry_.end() && it->second.owner_token == expected_token )
{
// Unlink without destroying under the lock: the entry's
// peer_registry may own the last PeerRegistry reference, whose
// destructor re-enters Unregister() -> UnregisterIf()
// (destruction re-entrancy; std::shared_mutex is not recursive).
auto node = registry_.extract( it );
lock.unlock();
return true;
} // node destroyed here, mutex released
return false;
}
std::optional<SecureCrdtRegistryEntry> Resolve( const std::string &key ) const
{
std::shared_lock<std::shared_mutex> lock( registry_mutex_ );
for ( const auto &[pattern, entry] : registry_ )
{
if ( std::regex_match( key, entry.compiled_pattern ) )
{
return entry;
}
}
return std::nullopt;
}
std::vector<SecureCrdtRegistryEntry> AllEntries() const
{
std::shared_lock<std::shared_mutex> lock( registry_mutex_ );
std::vector<SecureCrdtRegistryEntry> entries;
entries.reserve( registry_.size() );
for ( const auto &[pattern, entry] : registry_ )
{
entries.push_back( entry );
}
return entries;
}
bool RegisterCandidateDomain( const std::string &domain, CandidateDomainEntry entry )
{
entry.domain = domain;
std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
return candidate_domains_.emplace( domain, std::move( entry ) ).second;
}
void UnregisterCandidateDomainIf( const std::string &domain, const void *expected_token )
{
std::unique_lock<std::shared_mutex> lock( registry_mutex_ );
auto it = candidate_domains_.find( domain );
if ( it != candidate_domains_.end() && it->second.owner_token == expected_token )
{
candidate_domains_.erase( it );
}
}
std::optional<CandidateDomainEntry> ResolveCandidateDomain( const std::string &domain ) const
{
std::shared_lock<std::shared_mutex> lock( registry_mutex_ );
const auto it = candidate_domains_.find( domain );
return it == candidate_domains_.end() ? std::nullopt : std::optional<CandidateDomainEntry>( it->second );
}
std::vector<CandidateDomainEntry> AllCandidateDomains() const
{
std::shared_lock<std::shared_mutex> lock( registry_mutex_ );
std::vector<CandidateDomainEntry> entries;
entries.reserve( candidate_domains_.size() );
for ( const auto &[domain, entry] : candidate_domains_ )
{
entries.push_back( entry );
}
return entries;
}
private:
std::unordered_map<std::string, SecureCrdtRegistryEntry> registry_;
std::unordered_map<std::string, CandidateDomainEntry> candidate_domains_;
mutable std::shared_mutex registry_mutex_;
};
} // namespace sgns::securecrdt
#endif // SGNS_SECURECRDT_SECURECRDTREGISTRY_HPP
Updated on 2026-10-07 at 18:59:02 +0000