Skip to content

blockchain/ConsensusAuth.hpp

Header-only helpers for consensus signing and validation. More...

Namespaces

Name
sgns

Functions

Name
outcome::result< std::vector< uint8_t > > ProposalSigningBytes(const ConsensusProposal & proposal)
Builds canonical bytes used to sign a consensus proposal.
outcome::result< std::vector< uint8_t > > VoteSigningBytes(const ConsensusVote & vote)
Builds canonical bytes used to sign a consensus vote.
outcome::result< std::vector< uint8_t > > VoteBundleSigningBytes(const ConsensusVoteBundle & bundle)
Builds canonical bytes used to sign a consensus vote bundle.
outcome::result< std::string > ComputeProposalId(const ConsensusProposal & proposal)
Computes deterministic proposal id from proposal content.
bool ValidateProposal(const ConsensusProposal & proposal)
Validates proposal basic shape, signature, and computed id.

Detailed Description

Header-only helpers for consensus signing and validation.

Date: 2026-02-07 Henrique A. Klein ([email protected])

Functions Documentation

function ProposalSigningBytes

inline outcome::result< std::vector< uint8_t > > ProposalSigningBytes(
    const ConsensusProposal & proposal
)

Builds canonical bytes used to sign a consensus proposal.

Parameters:

  • proposal Proposal object to serialize for signing.

Return: Serialized signing bytes on success, or std::errc::invalid_argument when protobuf serialization fails.

The signature field is cleared before serialization so signatures are never part of their own signing payload.

function VoteSigningBytes

inline outcome::result< std::vector< uint8_t > > VoteSigningBytes(
    const ConsensusVote & vote
)

Builds canonical bytes used to sign a consensus vote.

Parameters:

  • vote Vote object to serialize for signing.

Return: Serialized signing bytes on success, or std::errc::invalid_argument when protobuf serialization fails.

The signature field is cleared before serialization so signatures are never part of their own signing payload.

function VoteBundleSigningBytes

inline outcome::result< std::vector< uint8_t > > VoteBundleSigningBytes(
    const ConsensusVoteBundle & bundle
)

Builds canonical bytes used to sign a consensus vote bundle.

Parameters:

  • bundle Vote bundle to serialize for signing.

Return: Serialized signing bytes on success, or std::errc::invalid_argument when protobuf serialization fails.

The signature field is cleared before serialization so signatures are never part of their own signing payload.

function ComputeProposalId

inline outcome::result< std::string > ComputeProposalId(
    const ConsensusProposal & proposal
)

Computes deterministic proposal id from proposal content.

Parameters:

  • proposal Proposal used to derive the identifier.

Return: Lowercase hex SHA-256 proposal id on success, or propagated error when signing bytes cannot be produced.

The proposal id field is cleared before hashing to guarantee stable id derivation from the signed payload content only.

function ValidateProposal

inline bool ValidateProposal(
    const ConsensusProposal & proposal
)

Validates proposal basic shape, signature, and computed id.

Parameters:

  • proposal Proposal to validate.

Return: true when proposer id/signature/id are present, signature is valid, and computed proposal id matches; otherwise false.

Source code

#pragma once

#include <system_error>
#include <vector>

#include "account/GeniusAccount.hpp"
#include "base/hexutil.hpp"
#include "blockchain/impl/proto/Consensus.pb.h"
#include "crypto/hasher/hasher_impl.hpp"
#include <gsl/span>
#include "outcome/outcome.hpp"

namespace sgns
{
    inline outcome::result<std::vector<uint8_t>> ProposalSigningBytes( const ConsensusProposal &proposal )
    {
        ConsensusProposal copy = proposal;
        copy.clear_signature();
        std::string serialized;
        if ( !copy.SerializeToString( &serialized ) )
        {
            return outcome::failure( std::errc::invalid_argument );
        }
        return std::vector<uint8_t>( serialized.begin(), serialized.end() );
    }

    inline outcome::result<std::vector<uint8_t>> VoteSigningBytes( const ConsensusVote &vote )
    {
        ConsensusVote copy = vote;
        copy.clear_signature();
        std::string serialized;
        if ( !copy.SerializeToString( &serialized ) )
        {
            return outcome::failure( std::errc::invalid_argument );
        }
        return std::vector<uint8_t>( serialized.begin(), serialized.end() );
    }

    inline outcome::result<std::vector<uint8_t>> VoteBundleSigningBytes( const ConsensusVoteBundle &bundle )
    {
        ConsensusVoteBundle copy = bundle;
        copy.clear_signature();
        std::string serialized;
        if ( !copy.SerializeToString( &serialized ) )
        {
            return outcome::failure( std::errc::invalid_argument );
        }
        return std::vector<uint8_t>( serialized.begin(), serialized.end() );
    }

    inline outcome::result<std::string> ComputeProposalId( const ConsensusProposal &proposal )
    {
        ConsensusProposal copy = proposal;
        copy.clear_proposal_id();
        auto signing_bytes = ProposalSigningBytes( copy );
        if ( signing_bytes.has_error() )
        {
            return outcome::failure( signing_bytes.error() );
        }

        sgns::crypto::HasherImpl hasher;
        auto                     hash = hasher.sha2_256( signing_bytes.value().data(), signing_bytes.value().size() );
        return base::hex_lower( gsl::span<const uint8_t>( hash.data(), hash.size() ) );
    }

    inline bool ValidateProposal( const ConsensusProposal &proposal )
    {
        if ( proposal.proposer_id().empty() || proposal.signature().empty() || proposal.proposal_id().empty() )
        {
            return false;
        }

        auto signing_bytes = ProposalSigningBytes( proposal );
        if ( signing_bytes.has_error() )
        {
            return false;
        }

        if ( !GeniusAccount::VerifySignature( proposal.proposer_id(),
                                              proposal.signature(),
                                              signing_bytes.value() ) )
        {
            return false;
        }

        auto computed_id = ComputeProposalId( proposal );
        if ( computed_id.has_error() )
        {
            return false;
        }

        return computed_id.value() == proposal.proposal_id();
    }
}

Updated on 2026-06-05 at 17:22:19 -0700