13#include <secp256k1_schnorrsig.h>
17 static const auto secp_context
19 decltype(&secp256k1_context_destroy)>(
20 secp256k1_context_create(SECP256K1_CONTEXT_VERIFY),
21 &secp256k1_context_destroy);
24 return std::tie(m_code, m_data_err, m_idx)
25 == std::tie(rhs.m_code, rhs.m_data_err, rhs.m_idx);
29 return std::tie(m_code, m_idx) == std::tie(rhs.m_code, rhs.m_idx);
33 return std::tie(m_code, m_idx) == std::tie(rhs.m_code, rhs.m_idx);
37 -> std::optional<tx_error> {
43 for(
size_t idx = 0; idx < tx.m_inputs.size(); idx++) {
44 const auto& inp = tx.m_inputs[idx];
47 auto&& [code, data] = input_err.value();
52 for(
size_t idx = 0; idx < tx.m_outputs.size(); idx++) {
53 const auto& out = tx.m_outputs[idx];
61 if(in_out_set_error) {
62 return in_out_set_error;
65 for(
size_t idx = 0; idx < tx.m_witness.size(); idx++) {
76 -> std::optional<tx_error> {
79 return input_count_err;
83 if(output_count_err) {
84 return output_count_err;
88 if(witness_count_err) {
89 return witness_count_err;
102 std::pair<input_error_code, std::optional<output_error_code>>> {
112 -> std::optional<tx_error> {
113 uint64_t input_total{0};
114 for(
const auto& inp : tx.m_inputs) {
115 if(input_total + inp.m_prevout_data.m_value <= input_total) {
118 input_total += inp.m_prevout_data.m_value;
121 uint64_t output_total{0};
122 for(
const auto& out : tx.m_outputs) {
123 if(output_total + out.m_value <= output_total) {
126 output_total += out.m_value;
129 if(input_total != output_total) {
139 -> std::optional<witness_error_code> {
140 const auto& witness_program = tx.m_witness[idx];
141 if(witness_program.empty()) {
159 -> std::optional<witness_error_code> {
161 if(witness_len_err) {
162 return witness_len_err;
165 const auto witness_commitment_err
167 if(witness_commitment_err) {
168 return witness_commitment_err;
172 if(witness_sig_err) {
173 return witness_sig_err;
181 -> std::optional<witness_error_code> {
182 const auto& wit = tx.m_witness[idx];
183 if(wit.size() != p2pk_witness_len) {
192 -> std::optional<witness_error_code> {
193 const auto& wit = tx.m_witness[idx];
194 const auto witness_program_hash
195 =
hash_data(wit.data(), p2pk_witness_prog_len);
197 const auto& witness_program_commitment
198 = tx.m_inputs[idx].m_prevout_data.m_witness_program_commitment;
200 if(witness_program_hash != witness_program_commitment) {
209 -> std::optional<witness_error_code> {
210 const auto& wit = tx.m_witness[idx];
211 secp256k1_xonly_pubkey pubkey{};
216 std::memcpy(pubkey_arr.data(),
219 if(secp256k1_xonly_pubkey_parse(secp_context.get(),
228 std::array<unsigned char, sig_len> sig_arr{};
229 std::memcpy(sig_arr.data(),
230 &wit[p2pk_witness_prog_len],
232 if(secp256k1_schnorrsig_verify(secp_context.get(),
244 -> std::optional<tx_error> {
245 if(tx.m_inputs.empty()) {
253 -> std::optional<tx_error> {
254 if(tx.m_outputs.empty()) {
262 -> std::optional<tx_error> {
263 if(tx.m_inputs.size() != tx.m_witness.size()) {
271 -> std::optional<tx_error> {
272 std::set<cbdc::transaction::out_point> inps;
274 for(
size_t idx = 0; idx < tx.m_inputs.size(); idx++) {
275 const auto& inp = tx.m_inputs[idx];
276 const auto it = inps.find(inp.m_prevout);
277 if(it != inps.end()) {
282 inps.insert(inp.m_prevout);
289 -> std::optional<output_error_code> {
290 if(out.m_value < 1) {
299 witness_program.insert(witness_program.begin(),
302 const auto wit_commit
303 =
hash_data(witness_program.data(), witness_program.size());
316 return "More inputs than witnesses";
319 return "Input values do not equal output values";
321 return "Total value of inputs or outputs overflows a 64-bit "
324 return "Unknown error";
332 return "Prevout data error";
334 return "Duplicate outpoint";
336 return "Unknown error";
344 return "Output has zero value";
346 return "Unknown error";
352 auto ret =
"Input error (idx: " + std::to_string(err.m_idx)
354 if(err.m_data_err.has_value()) {
355 ret +=
", Data error: " +
to_string(err.m_data_err.value());
364 return "Incorrect witness data length";
367 return "Witness missing script type";
370 return "Witness commitment does not match witness program";
373 return "Witness signature is invalid";
376 return "Witness public key is invalid";
379 return "Witness contains an unknown script type";
381 return "Unknown error";
386 return "Witness error (idx: " + std::to_string(err.m_idx)
391 return "Output error (idx: " + std::to_string(err.m_idx)
409 const std::unordered_set<pubkey_t, hashing::null>& pubkeys,
410 size_t threshold) ->
bool {
411 if(tx.m_attestations.size() < threshold) {
415 return std::all_of(tx.m_attestations.begin(),
416 tx.m_attestations.end(),
417 [&](
const auto& att) {
418 return pubkeys.find(att.first) != pubkeys.end()
419 && tx.verify(secp_context.get(), att);
struct secp256k1_context_struct secp256k1_context
auto check_input_structure(const cbdc::transaction::input &inp) -> std::optional< std::pair< input_error_code, std::optional< output_error_code > > >
auto check_p2pk_witness_commitment(const cbdc::transaction::full_tx &tx, size_t idx) -> std::optional< witness_error_code >
auto check_input_set(const cbdc::transaction::full_tx &tx) -> std::optional< tx_error >
auto get_p2pk_witness_commitment(const pubkey_t &payee) -> hash_t
std:: variant< input_error, output_error, witness_error, tx_error_code > tx_error
An error that may occur when sentinels or clients statically validate a transaction.
auto to_string(cbdc::transaction::validation::tx_error_code err) -> std::string
auto check_tx(const cbdc::transaction::full_tx &tx) -> std::optional< tx_error >
Runs static validation checks on the given transaction.
auto check_attestations(const transaction::compact_tx &tx, const std::unordered_set< pubkey_t, hashing::null > &pubkeys, size_t threshold) -> bool
Validates the sentinel attestations attached to a compact transaction.
auto check_output_count(const cbdc::transaction::full_tx &tx) -> std::optional< tx_error >
auto check_witness_count(const cbdc::transaction::full_tx &tx) -> std::optional< tx_error >
tx_error_code
Types of errors that may occur when a sentinel statically validates a transaction.
@ no_inputs
There are no inputs.
@ no_outputs
There are no outputs.
@ missing_witness
The number of witnesses and inputs do not match.
@ value_overflow
The total value of inputs/outputs overflows a 64-bit integer.
@ asymmetric_values
The total values of inputs and outputs do not match.
auto check_p2pk_witness(const cbdc::transaction::full_tx &tx, size_t idx) -> std::optional< witness_error_code >
auto check_output_value(const cbdc::transaction::output &out) -> std::optional< output_error_code >
auto check_input_count(const cbdc::transaction::full_tx &tx) -> std::optional< tx_error >
witness_error_code
Types of errors that may occur when sentinels validate witness commitments.
@ invalid_signature
The witness's signature is invalid.
@ malformed
The witness's format appears invalid.
@ unknown_witness_program_type
The validation system does not recognize the provided witness_program_type.
@ invalid_public_key
The witness's public key is invalid.
@ program_mismatch
The witness's specified program doesn't match its commitment.
@ missing_witness_program_type
The witness did not provide a witness_program_type.
witness_program_type
Specifies how validators should interpret the witness program.
output_error_code
A transaction input validation error.
@ zero_value
The output's value is 0.
auto check_tx_structure(const cbdc::transaction::full_tx &tx) -> std::optional< tx_error >
auto check_p2pk_witness_signature(const cbdc::transaction::full_tx &tx, size_t idx) -> std::optional< witness_error_code >
auto check_p2pk_witness_len(const cbdc::transaction::full_tx &tx, size_t idx) -> std::optional< witness_error_code >
auto check_witness(const cbdc::transaction::full_tx &tx, size_t idx) -> std::optional< witness_error_code >
input_error_code
Types of input validation errors.
@ duplicate
More than one transaction input contains the same output.
@ data_error
A transaction input includes invalid output data.
auto check_in_out_set(const cbdc::transaction::full_tx &tx) -> std::optional< tx_error >
auto tx_id(const full_tx &tx) noexcept -> hash_t
Calculates the unique hash of a full transaction.
std::array< unsigned char, cbdc::hash_size > hash_t
SHA256 hash container.
auto to_vector(const std::array< unsigned char, S > &arr) -> std::vector< std::byte >
Converts an std::array into an std::vector of the same size via copy.
auto hash_data(const std::byte *data, size_t len) -> hash_t
Calculates the SHA256 hash of the specified data.
std::array< unsigned char, pubkey_len > pubkey_t
A public key of a public/private keypair.
Variant handler template.
A condensed, hash-only transaction representation.
An output of a transaction.
An error that may occur when sentinels validate transaction outputs.
auto operator==(const output_error &rhs) const -> bool
An error that may occur when sentinels validate witness commitments.
auto operator==(const witness_error &rhs) const -> bool