Skip to content

Instantly share code, notes, and snippets.

@jamesob
Last active April 15, 2025 18:03
Show Gist options
  • Save jamesob/02d3755b7bc34c10d57caef36535f637 to your computer and use it in GitHub Desktop.
Save jamesob/02d3755b7bc34c10d57caef36535f637 to your computer and use it in GitHub Desktop.
Sig validation vs. hash time
#include <iostream>
#include <vector>
#include <random>
#include <chrono>
#include <iomanip>
#include <cassert>
// Include libsecp256k1
#include <secp256k1.h>
// Include OpenSSL for SHA-256
#include <openssl/sha.h>
// Size definitions
constexpr size_t MEGABYTE = 1024 * 1024;
constexpr size_t DATA_SIZE = 4 * MEGABYTE;
constexpr size_t MSG_SIZE = 32; // Message size for signatures (32 bytes for SHA-256 hash)
constexpr size_t NUM_SIGNATURES = (DATA_SIZE / 4) / MSG_SIZE; // 1MB worth of sigs
// Generate random data
std::vector<uint8_t> generate_random_data(size_t size) {
std::vector<uint8_t> data(size);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distrib(0, 255);
for (size_t i = 0; i < size; ++i) {
data[i] = static_cast<uint8_t>(distrib(gen));
}
return data;
}
// Benchmark SHA-256 hashing for 4MB of data
double benchmark_sha256(const std::vector<uint8_t>& data) {
auto start = std::chrono::high_resolution_clock::now();
// Hash the entire 4MB in one go
uint8_t hash[SHA256_DIGEST_LENGTH];
SHA256_CTX sha256;
SHA256_Init(&sha256);
SHA256_Update(&sha256, data.data(), data.size());
SHA256_Final(hash, &sha256);
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
return elapsed.count();
}
// Create signatures for benchmarking validation
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> prepare_signatures(secp256k1_context* ctx, const uint8_t* seckey, const secp256k1_pubkey* pubkey) {
std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>> sig_data;
sig_data.reserve(NUM_SIGNATURES);
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distrib(0, 255);
// Create signatures for random 32-byte messages
for (size_t i = 0; i < NUM_SIGNATURES; i++) {
// Create a random 32-byte message (could be a hash of actual data)
std::vector<uint8_t> msg(MSG_SIZE);
for (size_t j = 0; j < MSG_SIZE; j++) {
msg[j] = static_cast<uint8_t>(distrib(gen));
}
// Sign the message
secp256k1_ecdsa_signature signature;
// Use deterministic nonce generation (RFC6979)
assert(secp256k1_ecdsa_sign(ctx, &signature, msg.data(), seckey, nullptr, nullptr) == 1);
// Serialize the signature
std::vector<uint8_t> sig_bytes(64);
assert(secp256k1_ecdsa_signature_serialize_compact(ctx, sig_bytes.data(), &signature) == 1);
// Store message and signature
sig_data.emplace_back(msg, sig_bytes);
}
return sig_data;
}
// Benchmark signature validation for equivalent of 4MB of data
double benchmark_signature_validation(secp256k1_context* ctx,
const std::vector<std::pair<std::vector<uint8_t>, std::vector<uint8_t>>>& sig_data,
const secp256k1_pubkey& pubkey) {
auto start = std::chrono::high_resolution_clock::now();
// Validate all signatures
for (const auto& [msg, sig] : sig_data) {
secp256k1_ecdsa_signature signature;
assert(secp256k1_ecdsa_signature_parse_compact(ctx, &signature, sig.data()) == 1);
// Verify signature
assert(secp256k1_ecdsa_verify(ctx, &signature, msg.data(), &pubkey) == 1);
}
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<double, std::milli> elapsed = end - start;
return elapsed.count();
}
int main() {
// Initialize libsecp256k1 context
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
// Generate random data for SHA-256 benchmark
std::cout << "Generating 4MB of random data for hashing..." << std::endl;
auto hash_data = generate_random_data(DATA_SIZE);
// Prepare signature data
std::cout << "Preparing " << NUM_SIGNATURES << " signatures for validation benchmark..." << std::endl;
// Generate a key pair for signature validation
uint8_t seckey[32];
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distrib(0, 255);
do {
for (int i = 0; i < 32; i++) {
seckey[i] = static_cast<uint8_t>(distrib(gen));
}
} while (!secp256k1_ec_seckey_verify(ctx, seckey));
// Derive public key
secp256k1_pubkey pubkey;
assert(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey) == 1);
// Create signatures using the same key pair
auto sig_data = prepare_signatures(ctx, seckey, &pubkey);
// Run SHA-256 benchmark
std::cout << "Running SHA-256 benchmark..." << std::endl;
double sha256_time = benchmark_sha256(hash_data);
// Run signature validation benchmark
std::cout << "Running signature validation benchmark..." << std::endl;
double sig_validation_time = benchmark_signature_validation(ctx, sig_data, pubkey);
// Output results
std::cout << std::fixed << std::setprecision(2);
std::cout << "\n=== BENCHMARK RESULTS ===\n";
std::cout << "Data size: " << (DATA_SIZE / MEGABYTE) << " MB\n";
std::cout << "Number of signatures: " << NUM_SIGNATURES << " (1MB)\n\n";
std::cout << "SHA-256 hashing time: " << sha256_time << " ms\n";
std::cout << "Signature validation time: " << sig_validation_time << " ms\n\n";
std::cout << "Ratio (validation/hashing): " << (sig_validation_time / sha256_time) << "x\n";
// Cleanup
secp256k1_context_destroy(ctx);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment