securecrdt/SecureCrdt.cpp¶
Implementation of the SecureCrdt local-write gate (D-03) and reader-side quorum re-derivation (D-04). More...
Namespaces¶
| Name |
|---|
| sgns |
| sgns::securecrdt |
Functions¶
| Name | |
|---|---|
| OUTCOME_CPP_DEFINE_CATEGORY_3(sgns::securecrdt , SecureCrdt::Error , e ) |
Detailed Description¶
Implementation of the SecureCrdt local-write gate (D-03) and reader-side quorum re-derivation (D-04).
Date: 2026-07-23 Henrique A. Klein ([email protected])
Functions Documentation¶
function OUTCOME_CPP_DEFINE_CATEGORY_3¶
Source code¶
#include "securecrdt/SecureCrdt.hpp"
#include "multisig/MultiSig.hpp"
OUTCOME_CPP_DEFINE_CATEGORY_3( sgns::securecrdt, SecureCrdt::Error, e )
{
using Error = sgns::securecrdt::SecureCrdt::Error;
switch ( e )
{
case Error::UNREGISTERED_KEY:
return "base_key has no SecureCrdtRegistry entry";
case Error::NO_VALUE_PROPOSED:
return "no value has been proposed at base_key yet";
case Error::INVALID_SIGNATURE:
return "signature verification failed against the current value";
case Error::MALFORMED_VALUE:
return "payload failed DeserializeFromBytes/Verify (codec/semantic check)";
}
return "unknown SecureCrdt::Error";
}
namespace sgns::securecrdt
{
namespace
{
std::string LastKeySegment( const std::string &key )
{
sgns::crdt::HierarchicalKey hk( key );
auto list = hk.GetList();
if ( list.size() < 2 )
{
return {};
}
return list[list.size() - 2];
}
} // namespace
SecureCrdt::SecureCrdt( std::shared_ptr<sgns::crdt::GlobalDB> db, std::string topic ) :
db_( std::move( db ) ), topic_( std::move( topic ) )
{
}
outcome::result<void> SecureCrdt::ProposeValue( const sgns::crdt::HierarchicalKey &base_key,
const std::vector<uint8_t> &payload )
{
logger_->trace( "{}: entry key={}", __func__, base_key.GetKey() );
const auto *entry = SecureCrdtRegistry::Resolve( base_key.GetKey() );
if ( !entry )
{
logger_->error( "{}: unregistered key={}", __func__, base_key.GetKey() );
return outcome::failure( Error::UNREGISTERED_KEY );
}
auto instance = entry->make_instance();
if ( !instance || !instance->DeserializeFromBytes( payload ) )
{
logger_->error( "{}: malformed payload rejected locally key={}", __func__, base_key.GetKey() );
return outcome::failure( Error::MALFORMED_VALUE );
}
if ( !instance->Verify( payload ) )
{
logger_->error( "{}: semantically-invalid payload rejected locally key={}", __func__,
base_key.GetKey() );
return outcome::failure( Error::MALFORMED_VALUE );
}
auto put_result = db_->Put( base_key, sgns::base::Buffer( payload ), { topic_ } );
if ( put_result.has_error() )
{
logger_->error( "{}: Put failed key={} error={}", __func__, base_key.GetKey(),
put_result.error().message() );
return put_result.error();
}
logger_->debug( "{}: value proposed key={}", __func__, base_key.GetKey() );
return outcome::success();
}
outcome::result<void> SecureCrdt::AddSignature( const sgns::crdt::HierarchicalKey &base_key,
const std::string &signer_address,
const std::vector<uint8_t> &signature )
{
logger_->trace( "{}: entry key={} signer={}", __func__, base_key.GetKey(), signer_address );
const auto *entry = SecureCrdtRegistry::Resolve( base_key.GetKey() );
if ( !entry )
{
logger_->error( "{}: unregistered key={}", __func__, base_key.GetKey() );
return outcome::failure( Error::UNREGISTERED_KEY );
}
auto current_value = db_->Get( base_key );
if ( current_value.has_error() )
{
logger_->error( "{}: no value proposed yet key={}", __func__, base_key.GetKey() );
return outcome::failure( Error::NO_VALUE_PROPOSED );
}
const std::vector<uint8_t> payload = current_value.value().toVector();
if ( !multisig::VerifyPayloadSignature( signer_address, signature, payload ) )
{
logger_->error( "{}: invalid signature rejected locally key={} signer={}", __func__,
base_key.GetKey(), signer_address );
return outcome::failure( Error::INVALID_SIGNATURE );
}
auto put_result = db_->Put( base_key.ChildString( "sig" ).ChildString( signer_address ),
sgns::base::Buffer( signature ), { topic_ } );
if ( put_result.has_error() )
{
logger_->error( "{}: Put failed key={} error={}", __func__, base_key.GetKey(),
put_result.error().message() );
return put_result.error();
}
logger_->debug( "{}: signature added key={} signer={}", __func__, base_key.GetKey(), signer_address );
return outcome::success();
}
outcome::result<std::optional<sgns::base::Buffer>> SecureCrdt::ReadIfQuorum(
const sgns::crdt::HierarchicalKey &base_key )
{
logger_->trace( "{}: entry key={}", __func__, base_key.GetKey() );
const auto *entry = SecureCrdtRegistry::Resolve( base_key.GetKey() );
if ( !entry )
{
logger_->error( "{}: unregistered key={}", __func__, base_key.GetKey() );
return outcome::failure( Error::UNREGISTERED_KEY );
}
auto current_value = db_->Get( base_key );
if ( current_value.has_error() )
{
logger_->debug( "{}: no value yet key={}", __func__, base_key.GetKey() );
return outcome::success( std::optional<sgns::base::Buffer>{} );
}
const std::vector<uint8_t> payload = current_value.value().toVector();
auto sig_query = db_->QueryKeyValues( base_key.ChildString( "sig" ).GetKey() );
multisig::CollectedSignatures collected_signatures;
if ( !sig_query.has_error() )
{
for ( const auto &[key_buffer, value_buffer] : sig_query.value() )
{
const std::string key_string( key_buffer.toString() );
const std::string address = LastKeySegment( key_string );
if ( address.empty() )
{
continue;
}
collected_signatures.emplace_back( address, value_buffer.toVector() );
}
}
auto signer_set_result = entry->signer_set_source( base_key.GetKey() );
if ( signer_set_result.has_error() )
{
logger_->error( "{}: signer_set_source failed key={}", __func__, base_key.GetKey() );
return signer_set_result.error();
}
const auto &snapshot = signer_set_result.value();
const multisig::MultiSig quorum( snapshot.signer_set, snapshot.required_signatures );
if ( !quorum.IsValid() )
{
logger_->error( "{}: invalid quorum configuration key={} required={} authorized={}",
__func__,
base_key.GetKey(),
quorum.RequiredSignatures(),
quorum.AuthorizedSignerCount() );
return outcome::success( std::optional<sgns::base::Buffer>{} );
}
const auto quorum_result = quorum.EvaluateQuorum( collected_signatures, payload );
if ( !quorum_result.has_quorum )
{
logger_->debug( "{}: quorum not met key={} valid_unique_count={}", __func__, base_key.GetKey(),
quorum_result.valid_unique_count );
return outcome::success( std::optional<sgns::base::Buffer>{} );
}
logger_->debug( "{}: quorum met key={} valid_unique_count={}", __func__, base_key.GetKey(),
quorum_result.valid_unique_count );
return outcome::success( std::optional<sgns::base::Buffer>{ current_value.value() } );
}
bool SecureCrdt::RegisterFilters()
{
logger_->trace( "{}: entry", __func__ );
bool all_registered = true;
auto weak_self = weak_from_this();
const auto entries = SecureCrdtRegistry::AllEntries();
for ( const auto &entry : entries )
{
const std::string pattern = "/?" + entry.key_pattern + "(/sig/[^/]+)?";
sgns::crdt::HierarchicalKey base_key( entry.key_pattern );
const bool registered = db_->RegisterElementFilter(
pattern,
[weak_self, base_key, entry]( const sgns::crdt::pb::Element &element )
-> std::optional<std::vector<sgns::crdt::pb::Element>>
{
if ( auto strong = weak_self.lock() )
{
return strong->FilterSecureCrdtUpdate( base_key, entry, element );
}
return std::nullopt;
} );
all_registered = all_registered && registered;
}
db_->AddListenTopic( topic_ );
logger_->info( "{}: result={}", __func__, all_registered );
return all_registered;
}
std::optional<std::vector<sgns::crdt::pb::Element>> SecureCrdt::FilterSecureCrdtUpdate(
const sgns::crdt::HierarchicalKey &base_key,
const SecureCrdtRegistryEntry &entry,
const sgns::crdt::pb::Element &element )
{
logger_->trace( "{}: entry key={}", __func__, element.key() );
std::vector<uint8_t> element_bytes( element.value().begin(), element.value().end() );
const sgns::crdt::HierarchicalKey element_key( element.key() );
if ( element_key == base_key )
{
auto instance = entry.make_instance();
if ( !instance || !instance->DeserializeFromBytes( element_bytes ) || !instance->Verify( element_bytes ) )
{
logger_->error( "{}: malformed/invalid remote value rejected key={}", __func__, element.key() );
return std::vector<sgns::crdt::pb::Element>{};
}
logger_->debug( "{}: remote value accepted key={}", __func__, element.key() );
return std::nullopt;
}
const std::string address = element_key.GetList().back();
auto current_value = db_->Get( base_key );
if ( current_value.has_error() )
{
logger_->error( "{}: no base value yet, rejecting signature key={}", __func__, element.key() );
return std::vector<sgns::crdt::pb::Element>{};
}
const std::vector<uint8_t> payload = current_value.value().toVector();
if ( !multisig::VerifyPayloadSignature( address, element_bytes, payload ) )
{
logger_->error( "{}: invalid remote signature rejected key={}", __func__, element.key() );
return std::vector<sgns::crdt::pb::Element>{};
}
logger_->debug( "{}: remote signature accepted key={}", __func__, element.key() );
return std::nullopt;
}
} // namespace sgns::securecrdt
Updated on 2026-08-06 at 13:59:20 +0000