Appendix : Debugging & Development Tools
Introduction
This appendix provides practical tools and techniques for debugging cryptographic code in rippled, testing implementations, and developing new cryptographic features.
Logging Cryptographic Operations
Enable Debug Logging
# Edit rippled.cfg
[rpc_startup]
{ "command": "log_level", "severity": "trace" }
# Or via RPC
./rippled log_level partition=Transaction severity=traceMonitor Signature Verification
# Watch for signature verification
./rippled --conf rippled.cfg 2>&1 | grep -i "verify\|sign\|signature"
# Watch for failures
./rippled --conf rippled.cfg 2>&1 | grep -i "tefBAD_SIGNATURE\|temINVALID"Custom Logging
// Add debug logging to crypto code
#include <ripple/beast/core/Journal.h>
void debugSign(PublicKey const& pk, SecretKey const& sk, Slice const& m)
{
    JLOG(journal.trace()) << "Signing with key type: "
        << (publicKeyType(pk) == KeyType::ed25519 ? "ed25519" : "secp256k1");
    auto sig = sign(pk, sk, m);
    JLOG(journal.trace()) << "Signature size: " << sig.size();
    JLOG(journal.trace()) << "Signature (hex): " << strHex(sig);
}Standalone Mode for Testing
Start Standalone Node
# Start rippled in standalone mode (no network)
./rippled --standalone --conf rippled.cfgGenerate Test Accounts
# Generate new account with ed25519
./rippled wallet_propose ed25519
# Output:
# {
#   "account_id": "rN7n7otQDd6FczFgLdlqtyMVrn3LNU8B4C",
#   "key_type": "ed25519",
#   "master_key": "SNIT ARMY BOOM CALF ABLE ATOM CURE BARN FOWL ASIA HEAT TOUR",
#   "master_seed": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
#   "master_seed_hex": "DEDCE9CE67B451D852FD4E846FCDE31C",
#   "public_key": "aB44YfzW24VDEJQ2UuLPV2PvqcPCSoLnL7y5M1EzhdW4LnK5xMS3",
#   "public_key_hex": "ED9434799226374926EDA3B54B1B461B4ABF7237962EEB1144C10A7CA6A9D32C64"
# }
# Generate with secp256k1
./rippled wallet_propose secp256k1Test Transactions
# Submit test transaction
./rippled submit '{
  "tx_json": {
    "Account": "rN7n7otQDd6FczFgLdlqtyMVrn3LNU8B4C",
    "TransactionType": "Payment",
    "Destination": "rLHzPsX6oXkzU9w7fvQqJvGjzVtL5oJ47R",
    "Amount": "1000000"
  },
  "secret": "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9",
  "key_type": "ed25519"
}'
# Manually close ledger
./rippled ledger_acceptDebugging with GDB
Compile with Debug Symbols
# Build with debug info
cmake -DCMAKE_BUILD_TYPE=Debug ..
makeBasic GDB Commands
# Start rippled in gdb
gdb --args ./rippled --standalone --conf rippled.cfg
# Common commands:
(gdb) break SecretKey.cpp:randomSecretKey  # Set breakpoint
(gdb) run                                   # Run program
(gdb) next                                  # Step over
(gdb) step                                  # Step into
(gdb) continue                              # Continue execution
(gdb) print sk                              # Print variable
(gdb) backtrace                             # Show call stackInspect Cryptographic Data
# Examine secret key bytes
(gdb) x/32xb &secretKey  # Display 32 bytes in hex
# Examine signature
(gdb) x/64xb signature.data()
# Print public key
(gdb) print /x publicKey
# Check key type
(gdb) print publicKeyType(pk)Conditional Breakpoints
# Break only for ed25519 keys
(gdb) break sign if publicKeyType(pk) == KeyType::ed25519
# Break on signature verification failure
(gdb) break verify if $retval == falseMemory Debugging with Valgrind
Check for Memory Leaks
# Run with valgrind
valgrind --leak-check=full --show-leak-kinds=all ./rippled --standalone
# Look for:
# - Definitely lost: Memory leaks
# - Possibly lost: Potential leaks
# - Still reachable: OK (cleanup at exit)Check for Uninitialized Memory
# Detect uninitialized reads
valgrind --track-origins=yes ./rippled --standalone
# Look for:
# "Conditional jump or move depends on uninitialised value(s)"
# "Use of uninitialised value of size X"Unit Testing
Run Crypto Tests
# Run all tests
./rippled --unittest
# Run specific test suite
./rippled --unittest=ripple.protocol.SecretKey
# Run with specific algorithm
./rippled --unittest=ripple.protocol.SecretKey:Ed25519Write Custom Tests
// From src/test/protocol/SecretKey_test.cpp
class SecretKey_test : public beast::unit_test::suite
{
public:
    void testRandomGeneration()
    {
        // Generate keys
        auto sk1 = randomSecretKey();
        auto sk2 = randomSecretKey();
        // Should be different
        expect(sk1 != sk2, "Random keys should be unique");
        // Should be correct size
        expect(sk1.size() == 32, "Secret key should be 32 bytes");
    }
    void testSigning()
    {
        auto [pk, sk] = randomKeyPair(KeyType::ed25519);
        std::vector<uint8_t> message{0x01, 0x02, 0x03};
        auto sig = sign(pk, sk, makeSlice(message));
        // Verify signature
        bool valid = verify(pk, makeSlice(message), sig, true);
        expect(valid, "Signature should verify");
        // Modify message
        message[0] = 0xFF;
        valid = verify(pk, makeSlice(message), sig, true);
        expect(!valid, "Modified message should not verify");
    }
    void run() override
    {
        testRandomGeneration();
        testSigning();
    }
};
BEAST_DEFINE_TESTSUITE(SecretKey, protocol, ripple);Benchmarking
Measure Performance
#include <chrono>
void benchmarkSigning()
{
    auto [pk, sk] = randomKeyPair(KeyType::ed25519);
    std::vector<uint8_t> message(1000, 0xAA);
    constexpr int iterations = 1000;
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        auto sig = sign(pk, sk, makeSlice(message));
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << "Average signing time: "
              << (duration.count() / static_cast<double>(iterations))
              << " μs\n";
}Compare Algorithms
void compareAlgorithms()
{
    std::cout << "=== Signing Performance ===\n";
    // Ed25519
    {
        auto [pk, sk] = randomKeyPair(KeyType::ed25519);
        auto time = measureSign(pk, sk, 1000);
        std::cout << "Ed25519:   " << time << " μs/op\n";
    }
    // secp256k1
    {
        auto [pk, sk] = randomKeyPair(KeyType::secp256k1);
        auto time = measureSign(pk, sk, 1000);
        std::cout << "secp256k1: " << time << " μs/op\n";
    }
}Inspecting Key Material
View Public Key Details
void inspectPublicKey(PublicKey const& pk)
{
    std::cout << "=== Public Key Analysis ===\n";
    auto type = publicKeyType(pk);
    std::cout << "Type: ";
    if (!type) {
        std::cout << "INVALID\n";
        return;
    }
    if (*type == KeyType::secp256k1)
        std::cout << "secp256k1 (ECDSA)\n";
    else if (*type == KeyType::ed25519)
        std::cout << "ed25519 (EdDSA)\n";
    std::cout << "Size: " << pk.size() << " bytes\n";
    std::cout << "Hex: " << strHex(pk) << "\n";
    auto accountID = calcAccountID(pk);
    std::cout << "Account ID (hex): " << strHex(accountID) << "\n";
    std::cout << "Address: " << toBase58(accountID) << "\n";
}Verify Key Pair Consistency
bool verifyKeyPair(PublicKey const& pk, SecretKey const& sk)
{
    // Derive public key from secret
    auto derived = derivePublicKey(publicKeyType(pk).value(), sk);
    if (derived != pk) {
        std::cout << "ERROR: Public key doesn't match secret key!\n";
        std::cout << "Expected: " << strHex(pk) << "\n";
        std::cout << "Derived:  " << strHex(derived) << "\n";
        return false;
    }
    std::cout << "✓ Key pair is consistent\n";
    return true;
}Testing Signature Canonicality
Check secp256k1 Canonicality
void testCanonicality(Slice const& signature)
{
    auto canon = ecdsaCanonicality(signature);
    if (!canon) {
        std::cout << "ERROR: Invalid signature format\n";
        return;
    }
    switch (*canon) {
        case ECDSACanonicality::fullyCanonical:
            std::cout << "✓ Fully canonical (S ≤ order/2)\n";
            break;
        case ECDSACanonicality::canonical:
            std::cout << "⚠ Canonical but not fully (S > order/2)\n";
            std::cout << "  Should normalize for malleability prevention\n";
            break;
    }
}Hex Dump Utility
void hexDump(void const* data, size_t size, std::string const& label = "")
{
    if (!label.empty())
        std::cout << label << ":\n";
    auto const* bytes = static_cast<uint8_t const*>(data);
    for (size_t i = 0; i < size; ++i) {
        if (i % 16 == 0)
            std::cout << std::hex << std::setw(4) << std::setfill('0') << i << ": ";
        std::cout << std::hex << std::setw(2) << std::setfill('0')
                  << static_cast<int>(bytes[i]) << " ";
        if ((i + 1) % 16 == 0 || i + 1 == size)
            std::cout << "\n";
    }
    std::cout << std::dec;  // Reset to decimal
}
// Usage:
hexDump(signature.data(), signature.size(), "Signature");Address Sanitizer
Compile with AddressSanitizer
# Build with ASan
cmake -DCMAKE_BUILD_TYPE=Debug \
      -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer" \
      ..
make
# Run
./rippled --standaloneDetect Issues
Use-after-free
Heap buffer overflow
Stack buffer overflow
Memory leaks
Use of uninitialized memory
Common Debug Scenarios
Debug Signature Verification Failure
void debugVerificationFailure(
    PublicKey const& pk,
    Slice const& message,
    Slice const& signature)
{
    std::cout << "=== Debugging Signature Verification ===\n";
    // Check public key
    auto pkType = publicKeyType(pk);
    if (!pkType) {
        std::cout << "ERROR: Invalid public key format\n";
        return;
    }
    std::cout << "✓ Public key type: "
              << (*pkType == KeyType::ed25519 ? "ed25519" : "secp256k1")
              << "\n";
    // Check signature size
    if (*pkType == KeyType::ed25519 && signature.size() != 64) {
        std::cout << "ERROR: Ed25519 signature should be 64 bytes, got "
                  << signature.size() << "\n";
        return;
    }
    std::cout << "✓ Signature size: " << signature.size() << " bytes\n";
    // Check canonicality
    if (*pkType == KeyType::secp256k1) {
        auto canon = ecdsaCanonicality(signature);
        if (!canon) {
            std::cout << "ERROR: Invalid DER encoding\n";
            return;
        }
        if (*canon != ECDSACanonicality::fullyCanonical) {
            std::cout << "WARNING: Signature not fully canonical\n";
        }
    }
    // Try verification
    bool valid = verify(pk, message, signature, true);
    std::cout << "Verification result: " << (valid ? "✓ VALID" : "✗ INVALID") << "\n";
    if (!valid) {
        std::cout << "\nPossible causes:\n";
        std::cout << "- Wrong public key\n";
        std::cout << "- Wrong message\n";
        std::cout << "- Corrupted signature\n";
        std::cout << "- Algorithm mismatch\n";
    }
}Summary
Essential debugging tools and techniques:
Logging: Enable trace logging for crypto operations
Standalone mode: Test without network complexity
GDB: Step through code, inspect variables
Valgrind: Detect memory issues
Unit tests: Verify correctness
Benchmarking: Measure performance
Inspection tools: Examine keys, signatures, addresses
Sanitizers: Catch memory errors automatically
Best practices:
Test with both ed25519 and secp256k1
Verify canonicality for secp256k1
Check key pair consistency
Use hex dumps for visual inspection
Always check error returns
Last updated

