Last active
April 15, 2025 18:03
-
-
Save jamesob/02d3755b7bc34c10d57caef36535f637 to your computer and use it in GitHub Desktop.
Sig validation vs. hash time
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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