15std::array<unsigned char, 8> encode_length(std::size_t size) {
16 std::array<unsigned char, 8> out{};
17 const auto value =
static_cast<std::uint64_t
>(size);
18 for (std::size_t i = 0; i < out.size(); ++i) {
19 out[out.size() - 1 - i] =
static_cast<unsigned char>(value >> (8 * i));
26 "accountable:validate_context");
27 if (context.slot.empty()) {
42 if (bytes.size() != 32) {
45 std::array<unsigned char, 32> encoded{};
46 std::copy(bytes.begin(), bytes.end(), encoded.begin());
48 "accountable:scalar_encoding");
49 return ScalarSignature(scalar);
55 static const TaggedHash hash(
"Purify/Accountable/Nonce/v1");
56 const auto slot_length = encode_length(context.
slot.size());
58 slot_length, context.
slot});
62 const ConflictContext& context, std::span<const unsigned char> canonical_body,
68 static const TaggedHash hash(
"Purify/Accountable/Challenge/v1");
69 const auto slot_length = encode_length(context.
slot.size());
70 const auto body_length = encode_length(canonical_body.size());
72 slot_length, context.
slot, body_length, canonical_body});
76 if (challenge.is_zero()) {
83 const ConflictContext& context, std::span<const unsigned char> canonical_body,
86 "accountable:reconstruct_nonce");
87 std::array<unsigned char, 33> key{};
90 const auto negative_challenge = challenge.negate().to_bytes_be();
91 std::array<unsigned char, 33> negative_key_term{};
93 negative_key_term.data()) == 0) {
96 const auto scalar = signature.
serialize();
97 std::array<unsigned char, 33> nonce{};
107 std::span<const unsigned char> first_body,
const ScalarSignature& first_signature,
108 std::span<const unsigned char> second_body,
const ScalarSignature& second_signature,
111 "accountable:recover_first_challenge");
113 "accountable:recover_second_challenge");
114 const auto difference = first_challenge - second_challenge;
115 if (difference.is_zero()) {
121 const auto recovered = (first_scalar - second_scalar) * difference.inverse();
122 if (recovered.is_zero()) {
125 const auto scalar = recovered.to_bytes_be();
126 static constexpr std::array<unsigned char, 33> generator{
127 0x02, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0,
128 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d,
129 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98,
131 std::array<unsigned char, 33> point{};
132 if (
purify_point_scale(secp_context, generator.data(), scalar.data(), point.data()) == 0) {
137 if (point[0] != 0x02 || !std::equal(context.
signing_key.begin(), context.
signing_key.end(), point.begin() + 1)) {
142 if ((first_scalar - first_challenge * recovered).
is_zero()) {
Experimental public transcripts for scalar-only accountable signatures.
int purify_point_scale(purify_secp_context *context, const unsigned char point33[33], const unsigned char scalar32[32], unsigned char out33[33])
int purify_bppp_offset_commitment(purify_secp_context *context, const unsigned char commitment33[33], const unsigned char scalar32[32], unsigned char commitment_out33[33])
C ABI bridging Purify C++ code to secp256k1-zkp BPPP functionality.
static FieldElement from_bytes32(const std::array< unsigned char, 32 > &bytes)
Decodes a 32-byte big-endian field element.
static Result< FieldElement > try_from_bytes32(const std::array< unsigned char, 32 > &bytes)
Decodes a canonical 32-byte big-endian field element.
Reusable BIP340-style tagged SHA-256 helper.
std::array< unsigned char, 32 > digest_many(const std::array< std::span< const unsigned char >, Segments > &segments) const
Canonical 32-byte scalar response; carries no nonce or proof.
static Result< ScalarSignature > deserialize(std::span< const unsigned char > bytes)
Parse exactly 32 big-endian bytes below the group order, including zero.
std::array< unsigned char, 32 > serialize() const
Return the complete scalar-only wire representation.
Elliptic-curve helpers, fixed parameters, and hash-to-curve utilities for Purify.
#define PURIFY_RETURN_IF_ERROR(expr, context)
Evaluates an expected-like expression and returns the wrapped error on failure.
#define PURIFY_ASSIGN_OR_RETURN(lhs, expr, context)
Evaluates an expected-like expression, binds the value to lhs, and propagates errors.
Result< std::array< unsigned char, 33 > > reconstruct_nonce(const ConflictContext &context, std::span< const unsigned char > canonical_body, const ScalarSignature &signature, purify_secp_context *secp_context)
Reconstruct the full nonce point as sigma*G - e*Q, rejecting infinity.
Result< std::array< unsigned char, 32 > > nonce_evaluation_input(const ConflictContext &context, purify_secp_context *secp_context)
Derive the candidate deterministic Purify input from the registered slot.
Result< FieldElement > signature_challenge(const ConflictContext &context, std::span< const unsigned char > canonical_body, purify_secp_context *secp_context)
Compute the versioned Q/U/T/body challenge, without a nonce point.
Result< std::array< unsigned char, 32 > > recover_same_slot_key(const ConflictContext &context, std::span< const unsigned char > first_body, const ScalarSignature &first_signature, std::span< const unsigned char > second_body, const ScalarSignature &second_signature, purify_secp_context *secp_context)
Recover the exact signing scalar exposed by two same-slot responses.
Status require_secp_context(const purify_secp_context *context, const char *error_context)
constexpr Unexpected< Error > unexpected_error(ErrorCode code, const char *context=nullptr)
Constructs an unexpected Error value from a machine-readable code.
bool size_fits_u64(std::size_t value) noexcept
Expected< T, Error > Result
Expected-returning convenience alias for Purify value-producing APIs.
Expected< void, Error > Status
Expected-returning convenience alias for Purify status-only APIs.
int purify_bip340_validate_xonly_pubkey(purify_secp_context *context, const unsigned char xonly_pubkey32[32])
Returns nonzero when the x-only public key encoding parses successfully.
void purify_scalar_set_b32(purify_scalar *out, const unsigned char input32[32], int *overflow)
Parses a big-endian 32-byte scalar.
struct purify_secp_context purify_secp_context
Caller-owned reusable secp256k1 context handle shared across Purify C and C++ APIs.
Public context shared by conflicting statements in one slashable slot.
std::array< unsigned char, 32 > host_commitment
std::span< const unsigned char > slot
std::array< unsigned char, 32 > signing_key
static FieldElement from_raw(const purify_scalar &raw) noexcept
Opaque scalar storage compatible with secp256k1-zkp internal scalar storage.
int PURIFY_UINT_FN is_zero(const uint64_t value[PURIFY_UINT_WORDS])