├── docs └── .gitkeep ├── src ├── app │ └── .gitkeep ├── protocol │ └── messages.c ├── consensus │ ├── sig_verify.c │ └── serialize.c ├── crypto │ ├── sha1.c │ └── sha256.c └── storage │ ├── blocks.c │ └── db.c ├── lib └── sqlite │ └── VERSION ├── .github ├── FUNDING.yml └── workflows │ └── test.yml ├── .gitignore ├── .editorconfig ├── LICENSE ├── generate_compile_commands.sh ├── check_quality.sh ├── include ├── sha1.h ├── ripemd160.h ├── echo_platform_config.h ├── sig_verify.h ├── sha256.h ├── echo_consensus.h ├── serialize.h ├── blocks_storage.h ├── echo_assert.h ├── merkle.h ├── echo_config.h ├── echo_types.h ├── block.h ├── tx.h ├── discovery.h ├── tx_validate.h ├── peer.h ├── utxo_db.h └── log.h ├── .clangd ├── test ├── unit │ ├── test_utils.h │ ├── README.md │ ├── test_sha256.c │ ├── test_ripemd160.c │ └── test_utils.c └── run_all_tests.sh ├── README.md ├── CONTRIBUTING.md └── CLAUDE.md /docs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/sqlite/VERSION: -------------------------------------------------------------------------------- 1 | 3.51.1 -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # Bitcoin Echo Funding 2 | # Adds a "Sponsor" button to the repository 3 | 4 | custom: ['https://bitcoinecho.org/funding'] 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build artifacts 2 | echo 3 | echo.exe 4 | *.o 5 | *.obj 6 | *.dSYM/ 7 | *.a 8 | *.so 9 | *.dylib 10 | 11 | # Test binaries 12 | test/unit/test_* 13 | !test/unit/test_*.c 14 | !test/unit/test_*.h 15 | 16 | # Editor files 17 | *.swp 18 | *~ 19 | .vscode/ 20 | .idea/ 21 | 22 | # clangd files 23 | .clangd/ 24 | .cache/ 25 | compile_commands.json 26 | 27 | # macOS 28 | .DS_Store 29 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Bitcoin Echo — Editor Configuration 2 | # Ensures consistent style across all editors and contributors 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.{c,h}] 13 | indent_style = space 14 | indent_size = 4 15 | max_line_length = 100 16 | 17 | [Makefile] 18 | indent_style = tab 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | max_line_length = off 23 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, macos-latest] 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | token: ${{ secrets.GITHUB_TOKEN }} 20 | 21 | - name: Build and run tests 22 | run: | 23 | set -e 24 | make clean 25 | make test 2>&1 | tee test_output.txt 26 | exit ${PIPESTATUS[0]} 27 | 28 | - name: Build main binary 29 | run: make all 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Bitcoin Echo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /generate_compile_commands.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Generate compile_commands.json for clangd 3 | # Includes all source files with proper flags 4 | 5 | cd "$(dirname "$0")" 6 | PROJECT_ROOT="$(pwd)" 7 | 8 | # Common compile flags 9 | FLAGS="-std=c11 -Wall -Wextra -Wpedantic -O2 -Iinclude -pthread" 10 | 11 | # Start JSON array 12 | echo "[" > compile_commands.json 13 | 14 | # Find all .c files in src/ and test/ 15 | files=$(find src test -type f -name "*.c" 2>/dev/null | sort) 16 | 17 | first=true 18 | for file in $files; do 19 | if [ "$first" = false ]; then 20 | echo "," >> compile_commands.json 21 | fi 22 | first=false 23 | 24 | echo " {" >> compile_commands.json 25 | echo " \"directory\": \"$PROJECT_ROOT\"," >> compile_commands.json 26 | echo " \"command\": \"cc $FLAGS -c $file\"," >> compile_commands.json 27 | echo " \"file\": \"$file\"" >> compile_commands.json 28 | echo -n " }" >> compile_commands.json 29 | done 30 | 31 | echo "" >> compile_commands.json 32 | echo "]" >> compile_commands.json 33 | 34 | count=$(echo "$files" | wc -l | tr -d ' ') 35 | echo "Generated compile_commands.json with $count entries" 36 | -------------------------------------------------------------------------------- /check_quality.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Quick code quality check for Bitcoin Echo 3 | # Run before committing to catch common issues 4 | 5 | set -e 6 | 7 | echo "Bitcoin Echo — Code Quality Check" 8 | echo "==================================" 9 | echo "" 10 | 11 | # Check for trailing whitespace 12 | echo "→ Checking for trailing whitespace..." 13 | if git ls-files '*.c' '*.h' | xargs grep -n ' $' 2>/dev/null; then 14 | echo " ⚠ Found trailing whitespace (see above)" 15 | else 16 | echo " ✓ No trailing whitespace" 17 | fi 18 | echo "" 19 | 20 | # Check for tabs (we use spaces) 21 | echo "→ Checking for tabs in source files..." 22 | if git ls-files '*.c' '*.h' | xargs grep -P '\t' 2>/dev/null; then 23 | echo " ⚠ Found tabs (we use 4 spaces)" 24 | else 25 | echo " ✓ No tabs found" 26 | fi 27 | echo "" 28 | 29 | # Check for TODO/FIXME/XXX comments 30 | echo "→ Checking for TODO/FIXME markers..." 31 | count=$(git ls-files '*.c' '*.h' | xargs grep -i 'TODO\|FIXME\|XXX' 2>/dev/null | wc -l | tr -d ' ') 32 | if [ "$count" -gt 0 ]; then 33 | echo " ⚠ Found $count TODO/FIXME markers" 34 | git ls-files '*.c' '*.h' | xargs grep -n -i 'TODO\|FIXME\|XXX' 2>/dev/null | head -5 35 | else 36 | echo " ✓ No pending TODO markers" 37 | fi 38 | echo "" 39 | 40 | # Check build 41 | echo "→ Testing build..." 42 | if make -j4 > /dev/null 2>&1; then 43 | echo " ✓ Build successful" 44 | else 45 | echo " ✗ Build failed" 46 | exit 1 47 | fi 48 | echo "" 49 | 50 | echo "==================================" 51 | echo "Quality check complete!" 52 | -------------------------------------------------------------------------------- /include/sha1.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — SHA-1 Implementation 3 | * 4 | * SHA-1 as specified in FIPS 180-4. 5 | * Used in Bitcoin Script for the OP_SHA1 opcode. 6 | * 7 | * Note: SHA-1 is cryptographically broken and should not be used 8 | * for new applications. It is included here only for compatibility 9 | * with the Bitcoin Script instruction set. 10 | * 11 | * Build once. Build right. Stop. 12 | */ 13 | 14 | #ifndef ECHO_SHA1_H 15 | #define ECHO_SHA1_H 16 | 17 | #include 18 | #include 19 | 20 | /* 21 | * SHA-1 produces a 160-bit (20-byte) digest. 22 | */ 23 | #define SHA1_DIGEST_SIZE 20 24 | 25 | /* 26 | * SHA-1 processes data in 512-bit (64-byte) blocks. 27 | */ 28 | #define SHA1_BLOCK_SIZE 64 29 | 30 | /* 31 | * SHA-1 context for incremental hashing. 32 | */ 33 | typedef struct { 34 | uint32_t state[5]; /* Current hash state (H0-H4) */ 35 | uint64_t count; /* Number of bytes processed */ 36 | uint8_t buffer[SHA1_BLOCK_SIZE]; /* Partial block buffer */ 37 | } sha1_ctx_t; 38 | 39 | /* 40 | * Initialize SHA-1 context. 41 | */ 42 | void sha1_init(sha1_ctx_t *ctx); 43 | 44 | /* 45 | * Feed data into SHA-1 context. 46 | */ 47 | void sha1_update(sha1_ctx_t *ctx, const uint8_t *data, size_t len); 48 | 49 | /* 50 | * Finalize SHA-1 and retrieve digest. 51 | */ 52 | void sha1_final(sha1_ctx_t *ctx, uint8_t out[SHA1_DIGEST_SIZE]); 53 | 54 | /* 55 | * Compute SHA-1 of data in one call. 56 | */ 57 | void sha1(const uint8_t *data, size_t len, uint8_t out[SHA1_DIGEST_SIZE]); 58 | 59 | #endif /* ECHO_SHA1_H */ 60 | -------------------------------------------------------------------------------- /.clangd: -------------------------------------------------------------------------------- 1 | CompileFlags: 2 | Add: 3 | - -Iinclude 4 | - -std=c11 5 | - -pedantic 6 | - -Wall 7 | - -Wextra 8 | - -Werror=implicit-function-declaration 9 | - -Werror=incompatible-pointer-types 10 | - -Werror=int-conversion 11 | - -Wconversion 12 | - -Wsign-conversion 13 | - -Wcast-qual 14 | - -Wcast-align 15 | - -Wwrite-strings 16 | - -Wundef 17 | - -Wshadow 18 | - -Wstrict-prototypes 19 | - -Wmissing-prototypes 20 | Remove: 21 | - -std=c++* 22 | - -std=gnu* 23 | 24 | Diagnostics: 25 | UnusedIncludes: Strict 26 | MissingIncludes: Strict 27 | # Suppress common noise from system headers 28 | Suppress: 29 | - 'bugprone-reserved-identifier' 30 | ClangTidy: 31 | Add: 32 | - bugprone-* 33 | - cert-* 34 | - clang-analyzer-* 35 | - misc-* 36 | - portability-* 37 | - readability-identifier-naming 38 | - readability-magic-numbers 39 | - readability-simplify-boolean-expr 40 | Remove: 41 | - bugprone-easily-swappable-parameters 42 | - readability-magic-numbers # We use constants appropriately 43 | - cert-err33-c # Too noisy for our use case 44 | 45 | Index: 46 | Background: Build 47 | 48 | # clangd include-cleaner on macOS may attribute symbols like pthread_t/socklen_t 49 | # to private system headers (e.g. sys/_pthread/*), producing noisy 50 | # clangd(missing-includes) diagnostics. We keep the source clean and suppress 51 | # MissingIncludes for these translation units instead of including private 52 | # headers. 53 | --- 54 | If: 55 | PathMatch: '(^|.*/)src/platform/posix\.c$' 56 | Diagnostics: 57 | MissingIncludes: None 58 | --- 59 | If: 60 | PathMatch: '(^|.*/)src/app/log\.c$' 61 | Diagnostics: 62 | MissingIncludes: None 63 | -------------------------------------------------------------------------------- /include/ripemd160.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — RIPEMD-160 Hash Function 3 | * 4 | * RIPEMD-160 as specified by Dobbertin, Bosselaers, and Preneel (1996). 5 | * 6 | * Used in Bitcoin for: 7 | * - HASH160 = RIPEMD160(SHA256(x)) for address generation 8 | * - OP_RIPEMD160 opcode 9 | * 10 | * Build once. Build right. Stop. 11 | */ 12 | 13 | #ifndef ECHO_RIPEMD160_H 14 | #define ECHO_RIPEMD160_H 15 | 16 | #include 17 | #include 18 | 19 | /* 20 | * RIPEMD-160 produces a 160-bit (20-byte) digest. 21 | */ 22 | #define RIPEMD160_DIGEST_SIZE 20 23 | 24 | /* 25 | * RIPEMD-160 processes data in 512-bit (64-byte) blocks. 26 | */ 27 | #define RIPEMD160_BLOCK_SIZE 64 28 | 29 | /* 30 | * RIPEMD-160 context for streaming interface. 31 | * 32 | * Allows hashing data incrementally via init/update/final. 33 | */ 34 | typedef struct { 35 | uint32_t state[5]; /* Current hash state */ 36 | uint64_t count; /* Total bytes processed */ 37 | uint8_t buffer[RIPEMD160_BLOCK_SIZE]; /* Partial block buffer */ 38 | } ripemd160_ctx_t; 39 | 40 | /* 41 | * Initialize RIPEMD-160 context. 42 | * 43 | * Must be called before first update. 44 | */ 45 | void ripemd160_init(ripemd160_ctx_t *ctx); 46 | 47 | /* 48 | * Update hash with additional data. 49 | * 50 | * May be called multiple times to hash data incrementally. 51 | * Data is buffered internally until a complete block is available. 52 | * 53 | * @param ctx Initialized context 54 | * @param data Data to hash 55 | * @param len Length of data in bytes 56 | */ 57 | void ripemd160_update(ripemd160_ctx_t *ctx, const uint8_t *data, size_t len); 58 | 59 | /* 60 | * Finalize hash and output digest. 61 | * 62 | * Applies padding and outputs the final 20-byte hash. 63 | * Context should not be used after this call without re-initialization. 64 | * 65 | * @param ctx Context with all data added 66 | * @param out Output buffer (must be at least 20 bytes) 67 | */ 68 | void ripemd160_final(ripemd160_ctx_t *ctx, uint8_t out[RIPEMD160_DIGEST_SIZE]); 69 | 70 | /* 71 | * Compute RIPEMD-160 hash in one shot. 72 | * 73 | * Convenience function for hashing complete data. 74 | * 75 | * @param data Data to hash 76 | * @param len Length of data in bytes 77 | * @param out Output buffer (must be at least 20 bytes) 78 | */ 79 | void ripemd160(const uint8_t *data, size_t len, 80 | uint8_t out[RIPEMD160_DIGEST_SIZE]); 81 | 82 | /* 83 | * Compute HASH160 = RIPEMD160(SHA256(x)). 84 | * 85 | * This is the standard Bitcoin hash for addresses. 86 | * Used for P2PKH and P2SH address generation. 87 | * 88 | * @param data Data to hash 89 | * @param len Length of data in bytes 90 | * @param out Output buffer (must be at least 20 bytes) 91 | */ 92 | void hash160(const uint8_t *data, size_t len, 93 | uint8_t out[RIPEMD160_DIGEST_SIZE]); 94 | 95 | #endif /* ECHO_RIPEMD160_H */ 96 | -------------------------------------------------------------------------------- /test/unit/test_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Test Utility Framework 3 | * 4 | * Provides unified test output formatting with: 5 | * - ANSI color support (green PASS, red FAIL) 6 | * - Consistent formatting across all test suites 7 | * - Detailed failure reporting 8 | * - Global test tracking and summary 9 | * 10 | * Build once. Build right. Stop. 11 | */ 12 | 13 | #ifndef BITCOIN_ECHO_TEST_UTILS_H 14 | #define BITCOIN_ECHO_TEST_UTILS_H 15 | 16 | #include 17 | #include 18 | 19 | /* Compare two byte arrays (returns 1 if equal, 0 otherwise) */ 20 | int bytes_equal(const uint8_t *a, const uint8_t *b, size_t len); 21 | 22 | /* 23 | * Test Suite Management 24 | * ===================== 25 | * Call test_suite_begin() at the start of your test suite, 26 | * then test_suite_end() at the end to print results. 27 | */ 28 | 29 | /* Begin a new test suite with the given name */ 30 | void test_suite_begin(const char *suite_name); 31 | 32 | /* End the current test suite and print results */ 33 | void test_suite_end(void); 34 | 35 | /* 36 | * Test Case Management 37 | * ==================== 38 | * Each test case should: 39 | * 1. Call test_case() with a descriptive name 40 | * 2. Perform the test 41 | * 3. Call test_pass() or one of the test_fail_*() functions 42 | */ 43 | 44 | /* Begin a new test case with the given name */ 45 | void test_case(const char *test_name); 46 | 47 | /* Mark the current test case as passed */ 48 | void test_pass(void); 49 | 50 | /* Mark the current test case as failed with a simple message */ 51 | void test_fail(const char *message); 52 | 53 | /* Mark the current test case as failed with integer mismatch details */ 54 | void test_fail_int(const char *message, long expected, long actual); 55 | 56 | /* Mark the current test case as failed with unsigned integer mismatch details */ 57 | void test_fail_uint(const char *message, unsigned long expected, unsigned long actual); 58 | 59 | /* Mark the current test case as failed with string mismatch details */ 60 | void test_fail_str(const char *message, const char *expected, const char *actual); 61 | 62 | /* Mark the current test case as failed with byte array mismatch details */ 63 | void test_fail_bytes(const char *message, const uint8_t *expected, const uint8_t *actual, size_t len); 64 | 65 | /* 66 | * Global Test Summary 67 | * =================== 68 | * Call test_global_summary() at the very end of your test runner 69 | * to print a global summary of all tests run across all suites. 70 | * Returns 0 if all tests passed, 1 if any failed. 71 | */ 72 | 73 | /* Print global summary and return exit code (0 = success, 1 = failure) */ 74 | int test_global_summary(void); 75 | 76 | /* 77 | * Section Headers 78 | * =============== 79 | * Optional visual separator for grouping related tests within a suite. 80 | */ 81 | 82 | /* Print a section header */ 83 | void test_section(const char *section_name); 84 | 85 | #endif /* BITCOIN_ECHO_TEST_UTILS_H */ 86 | -------------------------------------------------------------------------------- /include/echo_platform_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Platform Configuration 3 | * 4 | * These settings control platform-specific behavior: networking, storage, 5 | * resource limits, and operational parameters. Unlike consensus rules (which 6 | * must never change) and policy rules (which reflect philosophical choices), 7 | * platform settings are pragmatic operational decisions. 8 | * 9 | * These values may need adjustment as: 10 | * - Network conditions change 11 | * - Hardware capabilities evolve 12 | * - Operating systems update 13 | * 14 | * Platform changes do not affect consensus or policy. They affect performance, 15 | * resource usage, and operational characteristics. 16 | */ 17 | 18 | #ifndef ECHO_PLATFORM_CONFIG_H 19 | #define ECHO_PLATFORM_CONFIG_H 20 | 21 | /* 22 | * Peer-to-peer connection limits. 23 | * 24 | * Bitcoin nodes maintain connections to other nodes for block/transaction 25 | * relay. More connections increase redundancy and network health but consume 26 | * more bandwidth and memory. 27 | */ 28 | 29 | /* Maximum outbound connections (connections we initiate) */ 30 | #define PLATFORM_MAX_OUTBOUND_PEERS 8 31 | 32 | /* Maximum inbound connections (connections others initiate to us) */ 33 | #define PLATFORM_MAX_INBOUND_PEERS 117 34 | 35 | /* Total maximum connections */ 36 | #define PLATFORM_MAX_TOTAL_PEERS \ 37 | (PLATFORM_MAX_OUTBOUND_PEERS + PLATFORM_MAX_INBOUND_PEERS) 38 | 39 | /* 40 | * Network timeouts and intervals (milliseconds). 41 | * 42 | * These control how long we wait for responses and how often we perform 43 | * periodic network maintenance. 44 | */ 45 | 46 | /* Connection establishment timeout */ 47 | #define PLATFORM_CONNECT_TIMEOUT_MS 5000 48 | 49 | /* Ping interval (send keepalive to detect dead connections) */ 50 | #define PLATFORM_PING_INTERVAL_MS 120000 51 | 52 | /* Inactivity timeout (disconnect if no data received) */ 53 | #define PLATFORM_INACTIVITY_TIMEOUT_MS 600000 54 | 55 | /* 56 | * Data directory structure. 57 | * 58 | * All blockchain data is stored under a data directory root. 59 | * These paths are relative to that root. 60 | */ 61 | 62 | /* Block data storage directory */ 63 | #define PLATFORM_BLOCKS_DIR "blocks" 64 | 65 | /* UTXO set and chain state directory */ 66 | #define PLATFORM_CHAINSTATE_DIR "chainstate" 67 | 68 | /* Main database file */ 69 | #define PLATFORM_DATABASE_FILE "echo.db" 70 | 71 | /* 72 | * RPC server configuration. 73 | * 74 | * The JSON-RPC interface allows external programs to query blockchain 75 | * state and submit transactions. 76 | */ 77 | 78 | /* Maximum simultaneous RPC connections */ 79 | #define PLATFORM_RPC_MAX_CONNECTIONS 16 80 | 81 | /* RPC request timeout (milliseconds) */ 82 | #define PLATFORM_RPC_TIMEOUT_MS 30000 83 | 84 | /* 85 | * Logging configuration. 86 | */ 87 | 88 | /* Log level: 0=ERROR, 1=WARN, 2=INFO, 3=DEBUG */ 89 | #define PLATFORM_LOG_LEVEL 2 90 | 91 | /* Maximum log file size (bytes) before rotation */ 92 | #define PLATFORM_LOG_MAX_SIZE_BYTES 10485760 /* 10 MB */ 93 | 94 | /* Number of rotated log files to keep */ 95 | #define PLATFORM_LOG_ROTATION_COUNT 5 96 | 97 | #endif /* ECHO_PLATFORM_CONFIG_H */ 98 | -------------------------------------------------------------------------------- /include/sig_verify.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Signature Verification Interface 3 | * 4 | * sig_verify.h — the quantum succession seam 5 | * 6 | * This interface exists to document the succession boundary. 7 | * All signature verification in the consensus engine flows 8 | * through this interface. A post-quantum successor would: 9 | * 10 | * 1. Fork Bitcoin Echo at its final frozen state 11 | * 2. Add new signature types to sig_type_t 12 | * 3. Implement verification for the new scheme 13 | * 4. Leave all other consensus logic untouched 14 | * 15 | * Estimated modification: <500 lines of code. 16 | * 17 | * This is not a plugin system. There is no runtime extensibility, 18 | * no configuration, no abstraction for its own sake. It is a 19 | * documented seam—the single point where a successor implementation 20 | * would diverge. 21 | * 22 | * Build once. Build right. Stop. 23 | */ 24 | 25 | #ifndef ECHO_SIG_VERIFY_H 26 | #define ECHO_SIG_VERIFY_H 27 | 28 | #include 29 | #include 30 | 31 | /* 32 | * Signature type enumeration. 33 | * 34 | * Current Bitcoin protocol uses: 35 | * - ECDSA for pre-SegWit and SegWit v0 (P2PKH, P2WPKH, P2SH-P2WPKH) 36 | * - Schnorr for Taproot (SegWit v1) 37 | * 38 | * A post-quantum successor would add: 39 | * - SIG_DILITHIUM, SIG_SPHINCS, etc. 40 | */ 41 | typedef enum { 42 | SIG_ECDSA, /* Pre-SegWit and SegWit v0 */ 43 | SIG_SCHNORR /* Taproot (SegWit v1) */ 44 | /* Successor adds: SIG_DILITHIUM, SIG_SPHINCS, etc. */ 45 | } sig_type_t; 46 | 47 | /* 48 | * Verify a signature against a message hash and public key. 49 | * 50 | * This is the unified entry point for all signature verification 51 | * in the consensus engine. Script interpretation, transaction 52 | * validation, block validation—all cryptographic verification 53 | * flows through this function. 54 | * 55 | * Parameters: 56 | * type - signature scheme identifier 57 | * sig - signature bytes 58 | * sig_len - signature length 59 | * hash - 32-byte message hash (sighash) 60 | * pubkey - public key bytes 61 | * pubkey_len - public key length 62 | * 63 | * Returns: 64 | * 1 if signature is valid 65 | * 0 if signature is invalid 66 | * 67 | * This function is pure: no side effects, no I/O, deterministic. 68 | * 69 | * For ECDSA (SIG_ECDSA): 70 | * - sig: DER-encoded signature (variable length, typically 70-72 bytes) 71 | * - pubkey: compressed (33 bytes) or uncompressed (65 bytes) 72 | * 73 | * For Schnorr (SIG_SCHNORR): 74 | * - sig: 64-byte signature (r || s) 75 | * - pubkey: 32-byte x-only public key 76 | */ 77 | int sig_verify(sig_type_t type, const uint8_t *sig, size_t sig_len, 78 | const uint8_t *hash, const uint8_t *pubkey, size_t pubkey_len); 79 | 80 | /* 81 | * Check whether a signature type is known. 82 | * 83 | * Unknown types in consensus-critical paths cause validation failure. 84 | * This function allows callers to distinguish between "unknown type" 85 | * and "known type, invalid signature" conditions. 86 | * 87 | * Returns: 88 | * 1 if type is known (SIG_ECDSA or SIG_SCHNORR) 89 | * 0 if type is unknown 90 | */ 91 | int sig_type_known(sig_type_t type); 92 | 93 | #endif /* ECHO_SIG_VERIFY_H */ 94 | -------------------------------------------------------------------------------- /include/sha256.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — SHA-256 Implementation 3 | * 4 | * SHA-256 as specified in FIPS 180-4. 5 | * Used throughout Bitcoin for: 6 | * - Block header hashing (SHA256d) 7 | * - Transaction ID computation (SHA256d) 8 | * - Merkle tree construction (SHA256d) 9 | * - Address generation (as part of HASH160) 10 | * - Script opcodes (OP_SHA256, OP_HASH256) 11 | * 12 | * This implementation prioritizes correctness and clarity over speed. 13 | * No assembly, no SIMD, no hardware acceleration. 14 | * 15 | * Build once. Build right. Stop. 16 | */ 17 | 18 | #ifndef ECHO_SHA256_H 19 | #define ECHO_SHA256_H 20 | 21 | #include 22 | #include 23 | 24 | /* 25 | * SHA-256 produces a 256-bit (32-byte) digest. 26 | */ 27 | #define SHA256_DIGEST_SIZE 32 28 | 29 | /* 30 | * SHA-256 processes data in 512-bit (64-byte) blocks. 31 | */ 32 | #define SHA256_BLOCK_SIZE 64 33 | 34 | /* 35 | * SHA-256 context for incremental hashing. 36 | * 37 | * Use sha256_init() to initialize, sha256_update() to feed data, 38 | * and sha256_final() to retrieve the digest. 39 | */ 40 | typedef struct { 41 | uint32_t state[8]; /* Current hash state (H0-H7) */ 42 | uint64_t count; /* Number of bytes processed */ 43 | uint8_t buffer[SHA256_BLOCK_SIZE]; /* Partial block buffer */ 44 | } sha256_ctx_t; 45 | 46 | /* 47 | * Initialize SHA-256 context. 48 | * 49 | * Must be called before sha256_update(). 50 | */ 51 | void sha256_init(sha256_ctx_t *ctx); 52 | 53 | /* 54 | * Feed data into SHA-256 context. 55 | * 56 | * Can be called multiple times with arbitrary-sized chunks. 57 | * Data is buffered internally until a complete block is available. 58 | */ 59 | void sha256_update(sha256_ctx_t *ctx, const uint8_t *data, size_t len); 60 | 61 | /* 62 | * Finalize SHA-256 and retrieve digest. 63 | * 64 | * Applies padding and writes the 32-byte digest to `out`. 65 | * The context should not be used after this call without re-initialization. 66 | */ 67 | void sha256_final(sha256_ctx_t *ctx, uint8_t out[SHA256_DIGEST_SIZE]); 68 | 69 | /* 70 | * Compute SHA-256 of data in one call. 71 | * 72 | * Convenience function equivalent to init/update/final sequence. 73 | */ 74 | void sha256(const uint8_t *data, size_t len, uint8_t out[SHA256_DIGEST_SIZE]); 75 | 76 | /* 77 | * Compute SHA-256d (double SHA-256) of data. 78 | * 79 | * SHA256d(x) = SHA256(SHA256(x)) 80 | * 81 | * This is the standard hash used in Bitcoin for: 82 | * - Block header hash 83 | * - Transaction ID (txid) 84 | * - Merkle tree nodes 85 | */ 86 | void sha256d(const uint8_t *data, size_t len, uint8_t out[SHA256_DIGEST_SIZE]); 87 | 88 | /* 89 | * Compute SHA-256 midstate. 90 | * 91 | * For mining optimization: pre-compute the hash state after processing 92 | * the first 64 bytes of an 80-byte block header. The remaining 16 bytes 93 | * (which contain the nonce) can then be hashed incrementally. 94 | * 95 | * Parameters: 96 | * data - Exactly 64 bytes (one SHA-256 block) 97 | * out - 32 bytes to receive the midstate (raw state, not finalized) 98 | */ 99 | void sha256_midstate(const uint8_t data[SHA256_BLOCK_SIZE], 100 | uint8_t out[SHA256_DIGEST_SIZE]); 101 | 102 | #endif /* ECHO_SHA256_H */ 103 | -------------------------------------------------------------------------------- /src/protocol/messages.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Bitcoin Echo — Protocol Message Utilities 3 | * 4 | * Implementation of protocol message parsing and utility functions. 5 | */ 6 | 7 | #include "echo_types.h" 8 | #include "protocol.h" 9 | #include "sha256.h" 10 | #include 11 | #include 12 | 13 | /* Command name mapping table */ 14 | static const struct { 15 | msg_type_t type; 16 | const char *command; 17 | } command_table[] = { 18 | {MSG_VERSION, "version"}, 19 | {MSG_VERACK, "verack"}, 20 | {MSG_PING, "ping"}, 21 | {MSG_PONG, "pong"}, 22 | {MSG_INV, "inv"}, 23 | {MSG_GETDATA, "getdata"}, 24 | {MSG_NOTFOUND, "notfound"}, 25 | {MSG_BLOCK, "block"}, 26 | {MSG_TX, "tx"}, 27 | {MSG_ADDR, "addr"}, 28 | {MSG_GETADDR, "getaddr"}, 29 | {MSG_GETHEADERS, "getheaders"}, 30 | {MSG_GETBLOCKS, "getblocks"}, 31 | {MSG_HEADERS, "headers"}, 32 | {MSG_REJECT, "reject"}, 33 | {MSG_SENDHEADERS, "sendheaders"}, 34 | {MSG_FEEFILTER, "feefilter"}, 35 | {MSG_SENDCMPCT, "sendcmpct"}, 36 | {MSG_WTXIDRELAY, "wtxidrelay"}, 37 | }; 38 | 39 | #define COMMAND_TABLE_SIZE (sizeof(command_table) / sizeof(command_table[0])) 40 | 41 | /** 42 | * Parse message type from command string. 43 | */ 44 | msg_type_t msg_parse_command(const char *command) { 45 | /* Command string must be null-terminated within COMMAND_LEN */ 46 | size_t cmd_len = 0; 47 | for (size_t i = 0; i < COMMAND_LEN; i++) { 48 | if (command[i] == '\0') { 49 | cmd_len = i; 50 | break; 51 | } 52 | } 53 | 54 | /* If no null terminator found, invalid command */ 55 | if (cmd_len == 0 || cmd_len == COMMAND_LEN) { 56 | return MSG_UNKNOWN; 57 | } 58 | 59 | /* Look up in command table */ 60 | for (size_t i = 0; i < COMMAND_TABLE_SIZE; i++) { 61 | if (strcmp(command, command_table[i].command) == 0) { 62 | return command_table[i].type; 63 | } 64 | } 65 | 66 | return MSG_UNKNOWN; 67 | } 68 | 69 | /** 70 | * Get command string for message type. 71 | */ 72 | const char *msg_command_string(msg_type_t type) { 73 | for (size_t i = 0; i < COMMAND_TABLE_SIZE; i++) { 74 | if (command_table[i].type == type) { 75 | return command_table[i].command; 76 | } 77 | } 78 | return NULL; 79 | } 80 | 81 | /** 82 | * Compute message checksum. 83 | * 84 | * Checksum is first 4 bytes of SHA256d(payload). 85 | */ 86 | uint32_t msg_checksum(const uint8_t *payload, size_t len) { 87 | hash256_t hash; 88 | sha256d(payload, len, hash.bytes); 89 | 90 | /* Extract first 4 bytes as little-endian uint32 */ 91 | uint32_t checksum = 0; 92 | checksum |= ((uint32_t)hash.bytes[0]) << 0; 93 | checksum |= ((uint32_t)hash.bytes[1]) << 8; 94 | checksum |= ((uint32_t)hash.bytes[2]) << 16; 95 | checksum |= ((uint32_t)hash.bytes[3]) << 24; 96 | 97 | return checksum; 98 | } 99 | 100 | /** 101 | * Validate message header. 102 | */ 103 | echo_bool_t msg_header_valid(const msg_header_t *header, 104 | uint32_t expected_magic) { 105 | /* Check magic bytes */ 106 | if (header->magic != expected_magic) { 107 | return ECHO_FALSE; 108 | } 109 | 110 | /* Verify command is null-terminated */ 111 | echo_bool_t has_null = ECHO_FALSE; 112 | for (size_t i = 0; i < COMMAND_LEN; i++) { 113 | if (header->command[i] == '\0') { 114 | has_null = ECHO_TRUE; 115 | break; 116 | } 117 | } 118 | if (!has_null) { 119 | return ECHO_FALSE; 120 | } 121 | 122 | /* Check length is within bounds */ 123 | if (header->length > MAX_MESSAGE_SIZE) { 124 | return ECHO_FALSE; 125 | } 126 | 127 | return ECHO_TRUE; 128 | } 129 | -------------------------------------------------------------------------------- /include/echo_consensus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Consensus Configuration 3 | * 4 | * These parameters define Bitcoin's consensus rules. They are FROZEN and must 5 | * never be changed. Any modification to these values creates a hard fork. 6 | * 7 | * These rules determine: 8 | * - What makes a block valid 9 | * - What makes a transaction valid 10 | * - How difficulty adjusts 11 | * - When soft forks activate 12 | * 13 | * All Bitcoin Echo nodes, regardless of policy configuration, use identical 14 | * consensus rules and will therefore agree on the valid chain. 15 | */ 16 | 17 | #ifndef ECHO_CONSENSUS_H 18 | #define ECHO_CONSENSUS_H 19 | 20 | /* 21 | * Block subsidy and halving. 22 | */ 23 | 24 | /* Initial block subsidy in satoshis (50 BTC) */ 25 | #define CONSENSUS_INITIAL_SUBSIDY 5000000000LL 26 | 27 | /* Block subsidy halving interval (blocks) */ 28 | #define CONSENSUS_HALVING_INTERVAL 210000 29 | 30 | /* 31 | * Difficulty adjustment. 32 | */ 33 | 34 | /* Difficulty adjustment interval (blocks) */ 35 | #define CONSENSUS_DIFFICULTY_INTERVAL 2016 36 | 37 | /* Target time per block (seconds) */ 38 | #define CONSENSUS_TARGET_BLOCK_TIME 600 39 | 40 | /* Target time per difficulty period (seconds) */ 41 | #define CONSENSUS_TARGET_PERIOD_TIME \ 42 | (CONSENSUS_DIFFICULTY_INTERVAL * CONSENSUS_TARGET_BLOCK_TIME) 43 | 44 | /* 45 | * Transaction and block size limits. 46 | */ 47 | 48 | /* Maximum block weight (weight units, post-SegWit) */ 49 | #define CONSENSUS_MAX_BLOCK_WEIGHT 4000000 50 | 51 | /* Maximum block size for legacy calculations (bytes, pre-SegWit) */ 52 | #define CONSENSUS_MAX_BLOCK_SIZE 1000000 53 | 54 | /* Maximum transaction size (bytes) */ 55 | #define CONSENSUS_MAX_TX_SIZE 400000 56 | 57 | /* 58 | * Script limits. 59 | */ 60 | 61 | /* Maximum script size (bytes) */ 62 | #define CONSENSUS_MAX_SCRIPT_SIZE 10000 63 | 64 | /* Maximum script element size (bytes) */ 65 | #define CONSENSUS_MAX_SCRIPT_ELEMENT 520 66 | 67 | /* Maximum number of operations per script */ 68 | #define CONSENSUS_MAX_SCRIPT_OPS 201 69 | 70 | /* Maximum stack size during script execution */ 71 | #define CONSENSUS_MAX_STACK_SIZE 1000 72 | 73 | /* Maximum number of public keys in multisig */ 74 | #define CONSENSUS_MAX_MULTISIG_KEYS 20 75 | 76 | /* Maximum number of signature operations per block */ 77 | #define CONSENSUS_MAX_BLOCK_SIGOPS 80000 78 | 79 | /* 80 | * Timelock parameters. 81 | */ 82 | 83 | /* Coinbase maturity (blocks before spendable) */ 84 | #define CONSENSUS_COINBASE_MATURITY 100 85 | 86 | /* Locktime threshold: values below are block heights, above are timestamps */ 87 | #define CONSENSUS_LOCKTIME_THRESHOLD 500000000 88 | 89 | /* Sequence number that disables locktime */ 90 | #define CONSENSUS_SEQUENCE_FINAL 0xFFFFFFFF 91 | 92 | /* BIP-68 sequence lock flags */ 93 | #define CONSENSUS_SEQUENCE_LOCKTIME_DISABLE (1U << 31) 94 | #define CONSENSUS_SEQUENCE_LOCKTIME_TYPE (1U << 22) 95 | #define CONSENSUS_SEQUENCE_LOCKTIME_MASK 0x0000FFFF 96 | 97 | /* 98 | * Soft fork activation heights. 99 | * 100 | * After these block heights, the corresponding consensus rules are enforced. 101 | * Before these heights, the rules are not active. 102 | * 103 | * These are network-specific and defined in echo_config.h based on 104 | * ECHO_NETWORK_MAINNET, ECHO_NETWORK_TESTNET, or ECHO_NETWORK_REGTEST. 105 | */ 106 | 107 | /* Network-specific activation heights are defined in echo_config.h: 108 | * - CONSENSUS_BIP16_HEIGHT (P2SH) 109 | * - CONSENSUS_BIP34_HEIGHT (Height in coinbase) 110 | * - CONSENSUS_BIP65_HEIGHT (CHECKLOCKTIMEVERIFY) 111 | * - CONSENSUS_BIP66_HEIGHT (Strict DER signatures) 112 | * - CONSENSUS_BIP68_HEIGHT (Relative lock-time) 113 | * - CONSENSUS_SEGWIT_HEIGHT (Segregated Witness) 114 | * - CONSENSUS_TAPROOT_HEIGHT (Taproot/Schnorr) 115 | */ 116 | 117 | #endif /* ECHO_CONSENSUS_H */ 118 | -------------------------------------------------------------------------------- /include/serialize.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Serialization Primitives 3 | * 4 | * This header defines functions for encoding and decoding Bitcoin's 5 | * variable-length integer format (CompactSize). 6 | * 7 | * CompactSize encoding: 8 | * 0x00-0xFC: 1 byte (value as-is) 9 | * 0xFD + 2 bytes: 3 bytes (for values 253 to 65535) 10 | * 0xFE + 4 bytes: 5 bytes (for values 65536 to 4294967295) 11 | * 0xFF + 8 bytes: 9 bytes (for values > 4294967295) 12 | * 13 | * All multi-byte values are little-endian. 14 | * 15 | * Build once. Build right. Stop. 16 | */ 17 | 18 | #ifndef ECHO_SERIALIZE_H 19 | #define ECHO_SERIALIZE_H 20 | 21 | #include "echo_types.h" 22 | #include 23 | 24 | /* 25 | * CompactSize prefix bytes. 26 | */ 27 | #define VARINT_PREFIX_16 0xFD /* Followed by 2-byte value */ 28 | #define VARINT_PREFIX_32 0xFE /* Followed by 4-byte value */ 29 | #define VARINT_PREFIX_64 0xFF /* Followed by 8-byte value */ 30 | 31 | /* 32 | * Maximum CompactSize value (for transaction counts, etc.) 33 | * Some contexts limit this further, but the format supports full uint64_t. 34 | */ 35 | #define VARINT_MAX UINT64_MAX 36 | 37 | /* 38 | * Compute the encoded size of a varint. 39 | * 40 | * Parameters: 41 | * value - The value to encode 42 | * 43 | * Returns: 44 | * The number of bytes required to encode this value (1, 3, 5, or 9). 45 | */ 46 | size_t varint_size(uint64_t value); 47 | 48 | /* 49 | * Write a varint to a buffer. 50 | * 51 | * Parameters: 52 | * buf - Output buffer (must have capacity for varint_size(value) bytes) 53 | * buf_len - Size of output buffer 54 | * value - The value to encode 55 | * written - Output: number of bytes written (may be NULL) 56 | * 57 | * Returns: 58 | * ECHO_OK on success 59 | * ECHO_ERR_NULL_PARAM if buf is NULL 60 | * ECHO_ERR_BUFFER_TOO_SMALL if buf_len is insufficient 61 | */ 62 | echo_result_t varint_write(uint8_t *buf, size_t buf_len, uint64_t value, 63 | size_t *written); 64 | 65 | /* 66 | * Read a varint from a buffer. 67 | * 68 | * This function performs strict validation: 69 | * - Values 0-252 must NOT use a prefix (non-canonical encoding rejected) 70 | * - Values 253-65535 must use 0xFD prefix (non-canonical encoding rejected) 71 | * - Values 65536-4294967295 must use 0xFE prefix 72 | * - Larger values must use 0xFF prefix 73 | * 74 | * Parameters: 75 | * buf - Input buffer 76 | * buf_len - Size of input buffer 77 | * value - Output: the decoded value 78 | * consumed - Output: number of bytes consumed (may be NULL) 79 | * 80 | * Returns: 81 | * ECHO_OK on success 82 | * ECHO_ERR_NULL_PARAM if buf or value is NULL 83 | * ECHO_ERR_TRUNCATED if buffer too short for complete varint 84 | * ECHO_ERR_INVALID_FORMAT if non-canonical encoding detected 85 | */ 86 | echo_result_t varint_read(const uint8_t *buf, size_t buf_len, uint64_t *value, 87 | size_t *consumed); 88 | 89 | /* 90 | * Read a varint without strict canonical checking. 91 | * 92 | * This is provided for compatibility with potentially malformed data 93 | * that may exist in historical blocks. The consensus engine uses 94 | * varint_read() for strict validation; this function may be used 95 | * when parsing data where non-canonical encoding must be tolerated. 96 | * 97 | * Parameters: 98 | * buf - Input buffer 99 | * buf_len - Size of input buffer 100 | * value - Output: the decoded value 101 | * consumed - Output: number of bytes consumed (may be NULL) 102 | * 103 | * Returns: 104 | * ECHO_OK on success 105 | * ECHO_ERR_NULL_PARAM if buf or value is NULL 106 | * ECHO_ERR_TRUNCATED if buffer too short for complete varint 107 | */ 108 | echo_result_t varint_read_nonstrict(const uint8_t *buf, size_t buf_len, 109 | uint64_t *value, size_t *consumed); 110 | 111 | #endif /* ECHO_SERIALIZE_H */ 112 | -------------------------------------------------------------------------------- /src/consensus/sig_verify.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Signature Verification Implementation 3 | * 4 | * sig_verify.c — the quantum succession seam implementation 5 | * 6 | * This file contains the unified signature verification dispatcher. 7 | * All signature verification in the consensus engine flows through 8 | * this single point. 9 | * 10 | * SUCCESSION GUIDANCE FOR FUTURE DEVELOPERS: 11 | * 12 | * When the Bitcoin network adopts a quantum-resistant signature scheme, 13 | * modify this file (and only this file for signature changes): 14 | * 15 | * 1. Add new value to sig_type_t in sig_verify.h (e.g., SIG_DILITHIUM) 16 | * 2. Add verification case in sig_verify() below 17 | * 3. Update sig_type_known() to recognize the new type 18 | * 4. Add test vectors for the new scheme 19 | * 5. Verify all existing tests still pass 20 | * 21 | * Do not modify other files unless the Bitcoin protocol requires it. 22 | * The signature verification seam is intentionally narrow. 23 | * 24 | * Build once. Build right. Stop. 25 | */ 26 | 27 | #include "sig_verify.h" 28 | #include "secp256k1.h" 29 | #include 30 | #include 31 | 32 | /* 33 | * Verify a signature against a message hash and public key. 34 | * 35 | * Dispatches to the appropriate verification function based on type. 36 | */ 37 | int sig_verify(sig_type_t type, const uint8_t *sig, size_t sig_len, 38 | const uint8_t *hash, const uint8_t *pubkey, size_t pubkey_len) { 39 | /* Validate inputs */ 40 | if (sig == NULL || hash == NULL || pubkey == NULL) { 41 | return 0; 42 | } 43 | 44 | switch (type) { 45 | case SIG_ECDSA: { 46 | /* 47 | * ECDSA verification (pre-SegWit and SegWit v0) 48 | * 49 | * Signature: DER-encoded (typically 70-72 bytes) 50 | * Public key: compressed (33) or uncompressed (65) bytes 51 | * Hash: 32-byte sighash 52 | */ 53 | secp256k1_ecdsa_sig_t ecdsa_sig; 54 | secp256k1_point_t pk_point; 55 | 56 | /* Validate signature length (DER: min 8, max 73 bytes) */ 57 | if (sig_len < 8 || sig_len > 73) { 58 | return 0; 59 | } 60 | 61 | /* Validate public key length */ 62 | if (pubkey_len != 33 && pubkey_len != 65) { 63 | return 0; 64 | } 65 | 66 | /* Parse DER-encoded signature */ 67 | if (!secp256k1_ecdsa_sig_parse_der(&ecdsa_sig, sig, sig_len)) { 68 | return 0; 69 | } 70 | 71 | /* Parse public key */ 72 | if (!secp256k1_pubkey_parse(&pk_point, pubkey, pubkey_len)) { 73 | return 0; 74 | } 75 | 76 | /* Verify signature */ 77 | return secp256k1_ecdsa_verify(&ecdsa_sig, hash, &pk_point); 78 | } 79 | 80 | case SIG_SCHNORR: { 81 | /* 82 | * Schnorr verification (Taproot / SegWit v1) 83 | * 84 | * Signature: 64 bytes (r || s) 85 | * Public key: 32 bytes (x-only) 86 | * Hash: 32-byte sighash (note: Schnorr uses the raw message, 87 | * but in Bitcoin context this is always the sighash) 88 | */ 89 | 90 | /* Validate signature length (exactly 64 bytes) */ 91 | if (sig_len != 64) { 92 | return 0; 93 | } 94 | 95 | /* Validate public key length (exactly 32 bytes for x-only) */ 96 | if (pubkey_len != 32) { 97 | return 0; 98 | } 99 | 100 | /* 101 | * Note: For BIP-340 Schnorr, the 'hash' parameter is passed 102 | * as the message. In Bitcoin's Taproot context, this would 103 | * be the 32-byte sighash. The secp256k1_schnorr_verify function 104 | * handles the tagged hash internally. 105 | */ 106 | return secp256k1_schnorr_verify(sig, hash, 32, pubkey); 107 | } 108 | 109 | default: 110 | /* Unknown signature type */ 111 | return 0; 112 | } 113 | } 114 | 115 | /* 116 | * Check whether a signature type is known. 117 | * 118 | * Returns 1 for known types, 0 for unknown. 119 | */ 120 | int sig_type_known(sig_type_t type) { 121 | switch (type) { 122 | case SIG_ECDSA: 123 | case SIG_SCHNORR: 124 | return 1; 125 | default: 126 | return 0; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /include/blocks_storage.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Block File Storage 3 | * 4 | * Manages persistent storage of raw blocks in append-only files. 5 | * Format is compatible with Bitcoin Core's blk*.dat files: 6 | * - Network magic (4 bytes) 7 | * - Block size (4 bytes, little-endian) 8 | * - Raw block data (header + transactions) 9 | * 10 | * Files are named blk00000.dat, blk00001.dat, etc., and stored in the 11 | * blocks/ directory. Each file grows to ~128 MB before a new file is created. 12 | * 13 | * Block positions are tracked externally (in the block index database). 14 | * This module only handles raw block storage and retrieval. 15 | * 16 | * Build once. Build right. Stop. 17 | */ 18 | 19 | #ifndef ECHO_BLOCKS_STORAGE_H 20 | #define ECHO_BLOCKS_STORAGE_H 21 | 22 | #include "echo_types.h" 23 | #include 24 | 25 | /* 26 | * Block file position. 27 | * Identifies a block's location on disk. 28 | */ 29 | typedef struct { 30 | uint32_t file_index; /* File number (0 = blk00000.dat, 1 = blk00001.dat) */ 31 | uint32_t file_offset; /* Byte offset within file to start of magic bytes */ 32 | } block_file_pos_t; 33 | 34 | /* 35 | * Block file manager. 36 | * Manages writing and reading blocks to/from disk. 37 | */ 38 | typedef struct { 39 | char data_dir[256]; /* Data directory path */ 40 | uint32_t current_file_index; /* Current file being written */ 41 | uint32_t current_file_offset; /* Current offset in current file */ 42 | } block_file_manager_t; 43 | 44 | /* 45 | * Maximum size of a single block file (128 MB). 46 | * When a file reaches this size, we start a new file. 47 | */ 48 | #define BLOCK_FILE_MAX_SIZE (128 * 1024 * 1024) 49 | 50 | /* 51 | * Block file record header size (magic + size). 52 | */ 53 | #define BLOCK_FILE_RECORD_HEADER_SIZE 8 54 | 55 | /* 56 | * Initialize block file manager. 57 | * 58 | * Parameters: 59 | * mgr - Manager to initialize 60 | * data_dir - Path to data directory (blocks/ will be created inside) 61 | * 62 | * Returns: 63 | * ECHO_OK on success, error code on failure 64 | * 65 | * Notes: 66 | * - Creates blocks/ directory if it doesn't exist 67 | * - Scans existing block files to find current write position 68 | */ 69 | echo_result_t block_storage_init(block_file_manager_t *mgr, 70 | const char *data_dir); 71 | 72 | /* 73 | * Write a block to disk. 74 | * 75 | * Parameters: 76 | * mgr - Block file manager 77 | * block_data - Raw block data (header + transactions) 78 | * block_size - Size of block in bytes 79 | * pos_out - Output: position where block was written 80 | * 81 | * Returns: 82 | * ECHO_OK on success, error code on failure 83 | * 84 | * Notes: 85 | * - Writes network magic + size prefix + block data 86 | * - Automatically creates new file if current file would exceed max size 87 | * - Position is returned for storage in block index 88 | */ 89 | echo_result_t block_storage_write(block_file_manager_t *mgr, 90 | const uint8_t *block_data, 91 | uint32_t block_size, 92 | block_file_pos_t *pos_out); 93 | 94 | /* 95 | * Read a block from disk. 96 | * 97 | * Parameters: 98 | * mgr - Block file manager 99 | * pos - Position of block (from block index) 100 | * block_out - Output: pointer to allocated buffer with block data 101 | * size_out - Output: size of block in bytes 102 | * 103 | * Returns: 104 | * ECHO_OK on success, error code on failure 105 | * 106 | * Notes: 107 | * - Allocates buffer with malloc(); caller must free() 108 | * - Verifies magic bytes match network 109 | * - Returns raw block data (without magic/size prefix) 110 | */ 111 | echo_result_t block_storage_read(block_file_manager_t *mgr, 112 | block_file_pos_t pos, uint8_t **block_out, 113 | uint32_t *size_out); 114 | 115 | /* 116 | * Get path to block file. 117 | * 118 | * Parameters: 119 | * mgr - Block file manager 120 | * file_index - File index (0 = blk00000.dat, etc.) 121 | * path_out - Output buffer for path (at least 512 bytes) 122 | * 123 | * Notes: 124 | * - Helper function to construct block file paths 125 | */ 126 | void block_storage_get_path(const block_file_manager_t *mgr, 127 | uint32_t file_index, char *path_out); 128 | 129 | #endif /* ECHO_BLOCKS_STORAGE_H */ 130 | -------------------------------------------------------------------------------- /include/echo_assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Debug Assertions 3 | * 4 | * Assertions for catching programming errors during development. 5 | * In release builds (NDEBUG defined), assertions compile to nothing. 6 | * 7 | * These assertions are for invariants that should NEVER be violated 8 | * if the code is correct. They are NOT for validating external input 9 | * (blocks, transactions, network data) — use proper validation for those. 10 | * 11 | * Build once. Build right. Stop. 12 | */ 13 | 14 | #ifndef ECHO_ASSERT_H 15 | #define ECHO_ASSERT_H 16 | 17 | #include 18 | #include // IWYU pragma: keep 19 | 20 | /* 21 | * ECHO_ASSERT(condition) 22 | * 23 | * Assert that a condition is true. If false in debug builds, 24 | * prints diagnostic information and aborts. 25 | * 26 | * Example: 27 | * ECHO_ASSERT(ptr != NULL); 28 | * ECHO_ASSERT(index < array_len); 29 | */ 30 | #ifdef NDEBUG 31 | #define ECHO_ASSERT(cond) ((void)0) 32 | #else 33 | #define ECHO_ASSERT(cond) \ 34 | do { \ 35 | if (!(cond)) { \ 36 | fprintf(stderr, \ 37 | "ECHO_ASSERT failed: %s\n" \ 38 | " File: %s\n" \ 39 | " Line: %d\n" \ 40 | " Function: %s\n", \ 41 | #cond, __FILE__, __LINE__, __func__); \ 42 | abort(); \ 43 | } \ 44 | } while (0) 45 | #endif 46 | 47 | /* 48 | * ECHO_ASSERT_MSG(condition, message) 49 | * 50 | * Assert with a custom message for additional context. 51 | * 52 | * Example: 53 | * ECHO_ASSERT_MSG(len <= MAX_SIZE, "Buffer length exceeds maximum"); 54 | */ 55 | #ifdef NDEBUG 56 | #define ECHO_ASSERT_MSG(cond, msg) ((void)0) 57 | #else 58 | #define ECHO_ASSERT_MSG(cond, msg) \ 59 | do { \ 60 | if (!(cond)) { \ 61 | fprintf(stderr, \ 62 | "ECHO_ASSERT failed: %s\n" \ 63 | " Message: %s\n" \ 64 | " File: %s\n" \ 65 | " Line: %d\n" \ 66 | " Function: %s\n", \ 67 | #cond, (msg), __FILE__, __LINE__, __func__); \ 68 | abort(); \ 69 | } \ 70 | } while (0) 71 | #endif 72 | 73 | /* 74 | * ECHO_UNREACHABLE() 75 | * 76 | * Mark code paths that should never be reached. 77 | * If reached in debug builds, prints diagnostic and aborts. 78 | * In release builds, provides a hint to the compiler for optimization. 79 | * 80 | * Example: 81 | * switch (type) { 82 | * case TYPE_A: return handle_a(); 83 | * case TYPE_B: return handle_b(); 84 | * default: 85 | * ECHO_UNREACHABLE(); 86 | * } 87 | */ 88 | #ifdef NDEBUG 89 | #if defined(__GNUC__) || defined(__clang__) 90 | #define ECHO_UNREACHABLE() __builtin_unreachable() 91 | #elif defined(_MSC_VER) 92 | #define ECHO_UNREACHABLE() __assume(0) 93 | #else 94 | #define ECHO_UNREACHABLE() ((void)0) 95 | #endif 96 | #else 97 | #define ECHO_UNREACHABLE() \ 98 | do { \ 99 | fprintf(stderr, \ 100 | "ECHO_UNREACHABLE reached\n" \ 101 | " File: %s\n" \ 102 | " Line: %d\n" \ 103 | " Function: %s\n", \ 104 | __FILE__, __LINE__, __func__); \ 105 | abort(); \ 106 | } while (0) 107 | #endif 108 | 109 | /* 110 | * ECHO_STATIC_ASSERT(condition, message) 111 | * 112 | * Compile-time assertion. Fails at compile time if condition is false. 113 | * Use for type sizes, struct layout assumptions, etc. 114 | * 115 | * Example: 116 | * ECHO_STATIC_ASSERT(sizeof(hash256_t) == 32, "hash256_t must be 32 bytes"); 117 | */ 118 | #define ECHO_STATIC_ASSERT(cond, msg) _Static_assert(cond, msg) 119 | 120 | #endif /* ECHO_ASSERT_H */ 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bitcoin Echo 2 | 3 | [![Version](https://img.shields.io/badge/version-0.1-orange.svg)](https://github.com/bitcoinecho/bitcoin-echo/releases) 4 | [![Tests](https://github.com/bitcoinecho/bitcoin-echo/actions/workflows/test.yml/badge.svg)](https://github.com/bitcoinecho/bitcoin-echo/actions/workflows/test.yml) 5 | [![C Standard](https://img.shields.io/badge/C-C11-blue.svg)](https://en.wikipedia.org/wiki/C11_(C_standard_revision)) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 7 | [![Dependencies](https://img.shields.io/badge/dependencies-none-success.svg)]() 8 | 9 | > **⚠️ Development Status**: Bitcoin Echo is currently under active development (v0.1). The codebase is not yet feature-complete and should not be used in production. See [Status](#status) below for implementation progress. 10 | 11 | A complete, ossified implementation of the Bitcoin protocol in pure C. 12 | 13 | *Build once. Build right. Stop.* 14 | 15 | ## Status 16 | 17 | **Phase 9: Application Layer** — Complete 18 | 19 | | Component | Status | 20 | |-----------|--------| 21 | | **Cryptography** | | 22 | | SHA-256 | Complete | 23 | | RIPEMD-160 | Complete | 24 | | secp256k1 (Field/Group) | Complete | 25 | | ECDSA Verification | Complete | 26 | | Schnorr (BIP-340) | Complete | 27 | | **Data Structures** | | 28 | | Serialization | Complete | 29 | | Transactions | Complete | 30 | | Blocks | Complete | 31 | | Merkle Trees | Complete | 32 | | **Script Interpreter** | | 33 | | Stack Operations | Complete | 34 | | Arithmetic/Logic Opcodes | Complete | 35 | | Crypto Opcodes | Complete | 36 | | Flow Control | Complete | 37 | | P2SH Support | Complete | 38 | | Timelocks (BIP-65/68/112) | Complete | 39 | | Signature Verification | Complete | 40 | | **Transaction Validation** | | 41 | | Syntactic Validation | Complete | 42 | | Script Execution | Complete | 43 | | UTXO Context | Complete | 44 | | **Block Validation** | | 45 | | Header Validation (PoW, MTP) | Complete | 46 | | Difficulty Adjustment | Complete | 47 | | Coinbase Validation | Complete | 48 | | Full Block Validation | Complete | 49 | | **Chain Selection** | | 50 | | UTXO Set | Complete | 51 | | Chain State | Complete | 52 | | Chain Selection Algorithm | Complete | 53 | | Consensus Engine Integration | Complete | 54 | | **Storage Layer** | | 55 | | Block File Storage | Complete | 56 | | SQLite Integration | Complete | 57 | | UTXO Database | Complete | 58 | | Block Index Database | Complete | 59 | | **Protocol Layer** | | 60 | | P2P Message Structures | Complete | 61 | | Message Serialization | Complete | 62 | | Peer Connection Management | Complete | 63 | | Peer Discovery | Complete | 64 | | Inventory/Data Relay | Complete | 65 | | Headers-First Sync | Complete | 66 | | Transaction Mempool | Complete | 67 | | **Application Layer** | | 68 | | Node Initialization | Complete | 69 | | Main Event Loop | Complete | 70 | | RPC Interface | Complete | 71 | | Logging System | Complete | 72 | 73 | Next: [Phase 10 — Mining Interface](https://github.com/bitcoinecho/bitcoinecho-org/blob/main/ROADMAP.md#phase-10-mining-interface) 74 | 75 | ## Building 76 | 77 | ### POSIX (Linux, macOS, BSD) 78 | 79 | ```sh 80 | make 81 | ./echo 82 | ``` 83 | 84 | ### Windows 85 | 86 | ```cmd 87 | build.bat 88 | echo.exe 89 | ``` 90 | 91 | ## Running Observer Mode 92 | 93 | Observer mode connects to Bitcoin mainnet and watches live network traffic without validation or chain sync. The "Pinocchio moment." 94 | 95 | ```sh 96 | ./echo --observe 97 | ``` 98 | 99 | This starts the node in observer mode with: 100 | - RPC server on `localhost:8332` 101 | - P2P port on `8333` 102 | - Data directory at `~/.bitcoin-echo` 103 | 104 | **Optional flags:** 105 | ```sh 106 | ./echo --observe --datadir /path/to/data --port 8333 --rpcport 8332 107 | ``` 108 | 109 | **Testing the RPC API:** 110 | ```sh 111 | # Get observer statistics 112 | curl -X POST http://localhost:8332/ \ 113 | -H "Content-Type: application/json" \ 114 | -d '{"method":"getobserverstats","params":[],"id":1}' 115 | 116 | # Get recent block announcements 117 | curl -X POST http://localhost:8332/ \ 118 | -H "Content-Type: application/json" \ 119 | -d '{"method":"getobservedblocks","params":[],"id":1}' 120 | 121 | # Get recent transaction announcements 122 | curl -X POST http://localhost:8332/ \ 123 | -H "Content-Type: application/json" \ 124 | -d '{"method":"getobservedtxs","params":[],"id":1}' 125 | ``` 126 | 127 | **Using with the GUI:** 128 | 129 | See [bitcoinecho-gui](https://github.com/bitcoinecho/bitcoinecho-gui) for a browser-based interface to observer mode. 130 | 131 | ## Testing 132 | 133 | ```sh 134 | make test 135 | ``` 136 | 137 | Runs all unit tests for cryptographic primitives, data structures, and script execution. 138 | 139 | ## Requirements 140 | 141 | - C11 compiler (GCC, Clang, or MSVC) 142 | - No external dependencies 143 | 144 | ## Documentation 145 | 146 | - [Whitepaper](https://bitcoinecho.org/docs/whitepaper) — Technical specification 147 | - [Manifesto](https://bitcoinecho.org/docs/manifesto) — Philosophical foundation 148 | - [Bitcoin Primer](https://bitcoinecho.org/docs/primer) — What is Bitcoin? 149 | - [Building Guide](https://bitcoinecho.org/docs/building) — Compilation for the future 150 | - [Roadmap](https://github.com/bitcoinecho/bitcoinecho-org/blob/main/ROADMAP.md) — Detailed implementation progress 151 | 152 | ## License 153 | 154 | MIT License — see [LICENSE](LICENSE) 155 | -------------------------------------------------------------------------------- /src/crypto/sha1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — SHA-1 Implementation 3 | * 4 | * SHA-1 as specified in FIPS 180-4. 5 | * 6 | * Note: SHA-1 is cryptographically broken. This implementation is 7 | * provided only for Bitcoin Script compatibility (OP_SHA1). 8 | * 9 | * Build once. Build right. Stop. 10 | */ 11 | 12 | #include "sha1.h" 13 | #include 14 | #include 15 | 16 | /* 17 | * SHA-1 initial hash values (FIPS 180-4 section 5.3.1). 18 | */ 19 | static const uint32_t sha1_init_state[5] = {0x67452301, 0xefcdab89, 0x98badcfe, 20 | 0x10325476, 0xc3d2e1f0}; 21 | 22 | /* 23 | * Rotate left (circular left shift). 24 | */ 25 | static inline uint32_t rotl32(uint32_t x, int n) { 26 | return (x << n) | (x >> (32 - n)); 27 | } 28 | 29 | /* 30 | * Process a single 512-bit block. 31 | */ 32 | static void sha1_transform(uint32_t state[5], const uint8_t block[64]) { 33 | uint32_t w[80]; 34 | uint32_t a, b, c, d, e; 35 | 36 | /* Prepare message schedule */ 37 | for (int i = 0; i < 16; i++) { 38 | w[i] = ((uint32_t)block[i * 4 + 0] << 24) | 39 | ((uint32_t)block[i * 4 + 1] << 16) | 40 | ((uint32_t)block[i * 4 + 2] << 8) | ((uint32_t)block[i * 4 + 3]); 41 | } 42 | for (int i = 16; i < 80; i++) { 43 | w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); 44 | } 45 | 46 | /* Initialize working variables */ 47 | a = state[0]; 48 | b = state[1]; 49 | c = state[2]; 50 | d = state[3]; 51 | e = state[4]; 52 | 53 | /* 80 rounds */ 54 | for (int i = 0; i < 80; i++) { 55 | uint32_t f, k; 56 | 57 | if (i < 20) { 58 | f = (b & c) | ((~b) & d); 59 | k = 0x5a827999; 60 | } else if (i < 40) { 61 | f = b ^ c ^ d; 62 | k = 0x6ed9eba1; 63 | } else if (i < 60) { 64 | f = (b & c) | (b & d) | (c & d); 65 | k = 0x8f1bbcdc; 66 | } else { 67 | f = b ^ c ^ d; 68 | k = 0xca62c1d6; 69 | } 70 | 71 | uint32_t temp = rotl32(a, 5) + f + e + k + w[i]; 72 | e = d; 73 | d = c; 74 | c = rotl32(b, 30); 75 | b = a; 76 | a = temp; 77 | } 78 | 79 | /* Add to hash state */ 80 | state[0] += a; 81 | state[1] += b; 82 | state[2] += c; 83 | state[3] += d; 84 | state[4] += e; 85 | } 86 | 87 | /* 88 | * Initialize SHA-1 context. 89 | */ 90 | void sha1_init(sha1_ctx_t *ctx) { 91 | if (ctx == NULL) 92 | return; 93 | memcpy(ctx->state, sha1_init_state, sizeof(sha1_init_state)); 94 | ctx->count = 0; 95 | memset(ctx->buffer, 0, SHA1_BLOCK_SIZE); 96 | } 97 | 98 | /* 99 | * Feed data into SHA-1 context. 100 | */ 101 | void sha1_update(sha1_ctx_t *ctx, const uint8_t *data, size_t len) { 102 | if (ctx == NULL || (data == NULL && len > 0)) 103 | return; 104 | 105 | size_t buffer_used = (size_t)(ctx->count % SHA1_BLOCK_SIZE); 106 | ctx->count += len; 107 | 108 | /* Fill buffer if we have partial data */ 109 | if (buffer_used > 0) { 110 | size_t buffer_free = SHA1_BLOCK_SIZE - buffer_used; 111 | if (len < buffer_free) { 112 | memcpy(ctx->buffer + buffer_used, data, len); 113 | return; 114 | } 115 | memcpy(ctx->buffer + buffer_used, data, buffer_free); 116 | sha1_transform(ctx->state, ctx->buffer); 117 | data += buffer_free; 118 | len -= buffer_free; 119 | } 120 | 121 | /* Process complete blocks */ 122 | while (len >= SHA1_BLOCK_SIZE) { 123 | sha1_transform(ctx->state, data); 124 | data += SHA1_BLOCK_SIZE; 125 | len -= SHA1_BLOCK_SIZE; 126 | } 127 | 128 | /* Save remaining data */ 129 | if (len > 0) { 130 | memcpy(ctx->buffer, data, len); 131 | } 132 | } 133 | 134 | /* 135 | * Finalize SHA-1 and retrieve digest. 136 | */ 137 | void sha1_final(sha1_ctx_t *ctx, uint8_t out[SHA1_DIGEST_SIZE]) { 138 | if (ctx == NULL || out == NULL) 139 | return; 140 | 141 | size_t buffer_used = (size_t)(ctx->count % SHA1_BLOCK_SIZE); 142 | uint64_t bit_count = ctx->count * 8; 143 | 144 | /* Padding: append 1 bit, then zeros, then 64-bit length */ 145 | ctx->buffer[buffer_used++] = 0x80; 146 | 147 | if (buffer_used > 56) { 148 | /* Not enough room for length - need extra block */ 149 | memset(ctx->buffer + buffer_used, 0, SHA1_BLOCK_SIZE - buffer_used); 150 | sha1_transform(ctx->state, ctx->buffer); 151 | buffer_used = 0; 152 | } 153 | 154 | memset(ctx->buffer + buffer_used, 0, 56 - buffer_used); 155 | 156 | /* Append length in big-endian */ 157 | ctx->buffer[56] = (uint8_t)(bit_count >> 56); 158 | ctx->buffer[57] = (uint8_t)(bit_count >> 48); 159 | ctx->buffer[58] = (uint8_t)(bit_count >> 40); 160 | ctx->buffer[59] = (uint8_t)(bit_count >> 32); 161 | ctx->buffer[60] = (uint8_t)(bit_count >> 24); 162 | ctx->buffer[61] = (uint8_t)(bit_count >> 16); 163 | ctx->buffer[62] = (uint8_t)(bit_count >> 8); 164 | ctx->buffer[63] = (uint8_t)(bit_count); 165 | 166 | sha1_transform(ctx->state, ctx->buffer); 167 | 168 | /* Output in big-endian */ 169 | for (int i = 0; i < 5; i++) { 170 | out[i * 4 + 0] = (uint8_t)(ctx->state[i] >> 24); 171 | out[i * 4 + 1] = (uint8_t)(ctx->state[i] >> 16); 172 | out[i * 4 + 2] = (uint8_t)(ctx->state[i] >> 8); 173 | out[i * 4 + 3] = (uint8_t)(ctx->state[i]); 174 | } 175 | } 176 | 177 | /* 178 | * Compute SHA-1 of data in one call. 179 | */ 180 | void sha1(const uint8_t *data, size_t len, uint8_t out[SHA1_DIGEST_SIZE]) { 181 | sha1_ctx_t ctx; 182 | sha1_init(&ctx); 183 | sha1_update(&ctx, data, len); 184 | sha1_final(&ctx, out); 185 | } 186 | -------------------------------------------------------------------------------- /include/merkle.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Merkle Tree Computation 3 | * 4 | * This header defines functions for computing Merkle roots from 5 | * transaction lists. Bitcoin uses a binary Merkle tree with SHA256d 6 | * hashing. 7 | * 8 | * Algorithm: 9 | * 1. Hash each transaction to get leaves (txids or wtxids) 10 | * 2. If odd number of elements, duplicate the last one 11 | * 3. Pair elements and hash each pair: SHA256d(left || right) 12 | * 4. Repeat until single root remains 13 | * 14 | * Build once. Build right. Stop. 15 | */ 16 | 17 | #ifndef ECHO_MERKLE_H 18 | #define ECHO_MERKLE_H 19 | 20 | #include "echo_types.h" 21 | #include "tx.h" 22 | 23 | /* 24 | * Compute Merkle root from an array of 32-byte hashes. 25 | * 26 | * This is the core Merkle tree algorithm. Given N hashes (leaves), 27 | * computes the root by: 28 | * - If N == 0, returns all zeros 29 | * - If N == 1, returns the single hash 30 | * - Otherwise, pairs hashes (duplicating last if odd), computes 31 | * SHA256d of each pair, and recurses 32 | * 33 | * Parameters: 34 | * hashes - Array of 32-byte hashes (leaves) 35 | * count - Number of hashes 36 | * root - Output: 32-byte Merkle root 37 | * 38 | * Returns: 39 | * ECHO_OK on success 40 | * ECHO_ERR_NULL_PARAM if hashes (when count > 0) or root is NULL 41 | * ECHO_ERR_OUT_OF_MEMORY if allocation fails 42 | */ 43 | echo_result_t merkle_root(const hash256_t *hashes, size_t count, 44 | hash256_t *root); 45 | 46 | /* 47 | * Compute Merkle root from an array of transactions (using txids). 48 | * 49 | * This computes the txid of each transaction, then builds the 50 | * Merkle tree. This is used for the block header's merkle_root field. 51 | * 52 | * Parameters: 53 | * txs - Array of transactions 54 | * count - Number of transactions 55 | * root - Output: 32-byte Merkle root 56 | * 57 | * Returns: 58 | * ECHO_OK on success 59 | * ECHO_ERR_NULL_PARAM if txs (when count > 0) or root is NULL 60 | * ECHO_ERR_OUT_OF_MEMORY if allocation fails 61 | */ 62 | echo_result_t merkle_root_txids(const tx_t *txs, size_t count, hash256_t *root); 63 | 64 | /* 65 | * Compute witness commitment Merkle root (using wtxids). 66 | * 67 | * For SegWit blocks, a separate Merkle tree is computed from wtxids. 68 | * The coinbase wtxid is replaced with 32 zero bytes per BIP-141. 69 | * 70 | * Parameters: 71 | * txs - Array of transactions (first must be coinbase) 72 | * count - Number of transactions 73 | * root - Output: 32-byte witness Merkle root 74 | * 75 | * Returns: 76 | * ECHO_OK on success 77 | * ECHO_ERR_NULL_PARAM if txs (when count > 0) or root is NULL 78 | * ECHO_ERR_OUT_OF_MEMORY if allocation fails 79 | */ 80 | echo_result_t merkle_root_wtxids(const tx_t *txs, size_t count, 81 | hash256_t *root); 82 | 83 | /* 84 | * Compute the witness commitment hash. 85 | * 86 | * The witness commitment is: SHA256d(witness_root || witness_nonce) 87 | * where witness_nonce is a 32-byte value from the coinbase. 88 | * 89 | * Parameters: 90 | * witness_root - Witness Merkle root (from merkle_root_wtxids) 91 | * witness_nonce - 32-byte nonce from coinbase witness 92 | * commitment - Output: 32-byte witness commitment 93 | * 94 | * Returns: 95 | * ECHO_OK on success 96 | * ECHO_ERR_NULL_PARAM if any parameter is NULL 97 | */ 98 | echo_result_t witness_commitment(const hash256_t *witness_root, 99 | const hash256_t *witness_nonce, 100 | hash256_t *commitment); 101 | 102 | /* 103 | * Generate a Merkle proof for a transaction at a given index. 104 | * 105 | * A Merkle proof consists of sibling hashes needed to recompute 106 | * the root. This allows SPV clients to verify transaction inclusion 107 | * without downloading the full block. 108 | * 109 | * Parameters: 110 | * hashes - Array of 32-byte hashes (leaves) 111 | * count - Number of hashes 112 | * index - Index of hash to prove 113 | * proof - Output: array of sibling hashes (caller allocates) 114 | * proof_len - Output: number of hashes in proof 115 | * max_proof - Maximum proof length (caller's buffer size) 116 | * 117 | * Returns: 118 | * ECHO_OK on success 119 | * ECHO_ERR_NULL_PARAM if required parameters are NULL 120 | * ECHO_ERR_OUT_OF_RANGE if index >= count 121 | * ECHO_ERR_BUFFER_TOO_SMALL if proof buffer too small 122 | * ECHO_ERR_OUT_OF_MEMORY if allocation fails 123 | */ 124 | echo_result_t merkle_proof(const hash256_t *hashes, size_t count, size_t index, 125 | hash256_t *proof, size_t *proof_len, 126 | size_t max_proof); 127 | 128 | /* 129 | * Verify a Merkle proof. 130 | * 131 | * Given a leaf hash, its index, and a proof, verify that it 132 | * hashes to the expected root. 133 | * 134 | * Parameters: 135 | * leaf - The hash being proved 136 | * index - Position of leaf in original tree 137 | * count - Total number of leaves in tree 138 | * proof - Array of sibling hashes 139 | * proof_len - Number of hashes in proof 140 | * root - Expected Merkle root 141 | * 142 | * Returns: 143 | * ECHO_TRUE if proof is valid 144 | * ECHO_FALSE if proof is invalid or parameters are NULL 145 | */ 146 | echo_bool_t merkle_verify(const hash256_t *leaf, size_t index, size_t count, 147 | const hash256_t *proof, size_t proof_len, 148 | const hash256_t *root); 149 | 150 | #endif /* ECHO_MERKLE_H */ 151 | -------------------------------------------------------------------------------- /test/unit/README.md: -------------------------------------------------------------------------------- 1 | # Bitcoin Echo — Test Suite 2 | 3 | ## Overview 4 | 5 | Bitcoin Echo uses a unified test framework (`test_utils.h` / `test_utils.c`) that provides: 6 | 7 | - **Color-coded output**: Green `[PASS]`, Red `[FAIL]` 8 | - **Consistent formatting**: Uniform indentation and test case presentation 9 | - **Detailed failure reporting**: Shows expected vs actual with hex dumps 10 | - **Global summary**: Accumulates totals across all test suites 11 | - **Suite tracking**: Reports both per-suite and global statistics 12 | 13 | ## Test Coverage 14 | 15 | All unit tests are organized by architectural layer: 16 | 17 | ### Crypto Layer 18 | Cryptographic primitives: 19 | - SHA-256, RIPEMD-160 20 | - secp256k1 field arithmetic and group operations 21 | - ECDSA and Schnorr signature verification 22 | 23 | ### Consensus Layer 24 | Bitcoin consensus rules: 25 | - Serialization, transactions, blocks, merkle trees 26 | - Script engine, stack operations, opcodes 27 | - P2SH, timelocks (CLTV/CSV) 28 | - Transaction validation, block validation, coinbase validation 29 | 30 | ### State Management 31 | UTXO and chain state: 32 | - UTXO set operations 33 | - Chain state management 34 | - Consensus engine integration 35 | 36 | ### Storage Layer 37 | Persistent storage: 38 | - Block storage and retrieval 39 | - Database integration (SQLite) 40 | - UTXO database, block index database 41 | 42 | ### Protocol Layer 43 | P2P networking: 44 | - Protocol messages and serialization 45 | - Peer management and discovery 46 | - Block relay, synchronization, mempool 47 | - Event loop 48 | 49 | ### Application Layer 50 | Node operations: 51 | - Node lifecycle management 52 | - RPC interface 53 | - Logging system 54 | 55 | ### Special Cases 56 | - `test_script_vectors.c` - JSON test vector harness (uses different architecture) 57 | 58 | ## Running Tests 59 | 60 | ```bash 61 | # Run all tests with colored output and summary 62 | make test 63 | 64 | # Run individual test suite 65 | ./test/unit/test_sha256 66 | 67 | # Run global test runner directly 68 | ./test/run_all_tests.sh 69 | ``` 70 | 71 | Expected output format: 72 | ``` 73 | ================================================================================ 74 | BITCOIN ECHO — GLOBAL TEST SUMMARY 75 | ================================================================================ 76 | 77 | Test Suite Results: 78 | ✓ SHA-256 tests 9/ 9 passed 79 | ✓ RIPEMD-160 tests 17/ 17 passed 80 | ... 81 | 82 | Summary: 83 | Test Suites: X/X passed 84 | Test Cases: Y/Y passed 85 | 86 | ALL TESTS PASSED! 87 | ================================================================================ 88 | ``` 89 | 90 | ## Continuous Integration 91 | 92 | Tests run automatically on GitHub Actions for: 93 | - **macOS** (latest) 94 | - **Ubuntu** (latest) 95 | 96 | CI workflow: [`.github/workflows/test.yml`](../../.github/workflows/test.yml) 97 | 98 | Both platforms must pass before merge. 99 | 100 | ## Writing Tests 101 | 102 | ### Structure 103 | 104 | All test files follow this pattern: 105 | 106 | 1. **Add includes:** 107 | ```c 108 | #include 109 | #include 110 | #include "test_utils.h" 111 | ``` 112 | 113 | 2. **Structure main() function:** 114 | ```c 115 | int main(void) 116 | { 117 | test_suite_begin("Your Test Suite Name"); 118 | 119 | // ... your tests ... 120 | 121 | test_suite_end(); 122 | return test_global_summary(); 123 | } 124 | ``` 125 | 126 | 3. **Write test functions:** 127 | ```c 128 | static void run_something_test(const char *name, ...) 129 | { 130 | // ... perform test ... 131 | 132 | test_case(name); 133 | if (condition) { 134 | test_pass(); 135 | } else { 136 | test_fail_bytes("mismatch description", expected, actual, len); 137 | } 138 | } 139 | ``` 140 | 141 | 4. **Use section headers (optional):** 142 | ```c 143 | test_section("Advanced Features"); 144 | ``` 145 | 146 | ### API Reference 147 | 148 | #### Suite Management 149 | 150 | ```c 151 | void test_suite_begin(const char *suite_name); 152 | void test_suite_end(void); 153 | int test_global_summary(void); // Returns 0 on success, 1 on failure 154 | ``` 155 | 156 | #### Test Case Management 157 | 158 | ```c 159 | void test_case(const char *test_name); 160 | void test_pass(void); 161 | ``` 162 | 163 | #### Failure Reporting 164 | 165 | ```c 166 | void test_fail(const char *message); 167 | void test_fail_int(const char *message, long expected, long actual); 168 | void test_fail_uint(const char *message, unsigned long expected, unsigned long actual); 169 | void test_fail_str(const char *message, const char *expected, const char *actual); 170 | void test_fail_bytes(const char *message, const uint8_t *expected, const uint8_t *actual, size_t len); 171 | ``` 172 | 173 | #### Visual Organization 174 | 175 | ```c 176 | void test_section(const char *section_name); // Prints cyan section header 177 | ``` 178 | 179 | ### Examples 180 | 181 | See any test file in this directory for complete working examples: 182 | - [`test_sha256.c`](test_sha256.c) - Simple hash function tests 183 | - [`test_script.c`](test_script.c) - Complex script execution tests 184 | - [`test_consensus.c`](test_consensus.c) - Engine integration tests 185 | 186 | ### Makefile Integration 187 | 188 | All test rules in the Makefile link against `$(TEST_UTILS_OBJ)` automatically. 189 | 190 | ## Philosophy 191 | 192 | The test framework embodies Bitcoin Echo's core principles: 193 | 194 | - **Build once, build right:** Tests are the proof 195 | - **Simplicity:** No macros, no magic, just clean function calls 196 | - **Quality:** Professional output that makes a statement about code quality 197 | - **Ossification:** Once the framework is complete, it freezes with the codebase 198 | 199 | --- 200 | 201 | *Build once. Build right. Stop.* 202 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Bitcoin Echo 2 | 3 | Thank you for your interest in Bitcoin Echo! 4 | 5 | ## Current Status 6 | 7 | Bitcoin Echo is in active development (Phase 9 of 12). We're not yet accepting large-scale external code contributions, but we **absolutely welcome**: 8 | 9 | - **Code contributions** for bug fixes and test improvements 10 | - **Technical review** of completed phases 11 | - **Test case suggestions** from Bitcoin Core test vectors 12 | - **Documentation improvements** 13 | - **Bug reports** if you find consensus discrepancies 14 | 15 | ## How to Help 16 | 17 | ### Contribute Code 18 | 19 | We welcome contributions! Here's how: 20 | 21 | 1. **Open an issue first** - Describe the bug or improvement you want to make 22 | 2. **Wait for feedback** - We'll discuss approach and feasibility 23 | 3. **Submit a PR** - Include tests and follow our code style 24 | 4. **Be patient** - We review carefully since this code is designed to ossify 25 | 26 | **What makes a good contribution:** 27 | - Bug fixes with test cases demonstrating the issue 28 | - Additional test coverage from Bitcoin Core test vectors 29 | - Performance improvements with benchmarks 30 | - Documentation clarifications 31 | 32 | **What we're NOT accepting:** 33 | - New features beyond the roadmap 34 | - Refactoring for style preferences 35 | - External dependencies 36 | - Breaking changes to the API 37 | 38 | ### Review the Code 39 | 40 | The best contribution right now is careful review of consensus-critical code: 41 | - Cryptographic primitives (`src/crypto/`) 42 | - Script interpreter (`src/consensus/script.c`) 43 | - Transaction validation (`src/consensus/tx_validate.c`) 44 | - Block validation (`src/consensus/block_validate.c`) 45 | 46 | ### Report Issues 47 | 48 | If you find a consensus bug or discrepancy with Bitcoin Core: 49 | 1. Create an issue with a minimal test case 50 | 2. Reference Bitcoin Core behavior 51 | 3. Include relevant BIP numbers if applicable 52 | 4. Provide steps to reproduce 53 | 54 | ## Code Style 55 | 56 | - **Language:** Pure C11, zero dependencies 57 | - **Style:** Follow existing code conventions 58 | - **Tests:** Every PR must include tests 59 | - **Comments:** Explain *why*, not *what* 60 | - **Commits:** Atomic, conventional commit messages 61 | 62 | ## Testing 63 | 64 | Before submitting a PR: 65 | 66 | ```bash 67 | # Run all tests 68 | make test 69 | 70 | # Run specific test suites 71 | make test/unit/test_ 72 | ./test/unit/test_ 73 | ``` 74 | 75 | All tests must pass. See `test/unit/README.md` for test framework documentation. 76 | 77 | ## Understanding Ossification 78 | 79 | **"But what about bugs? Security issues? Platform changes?"** 80 | 81 | Let's be clear about what ossification means for Bitcoin Echo: 82 | 83 | ### What Ossifies (Frozen Forever) 84 | 85 | - **Consensus rules** - How blocks and transactions are validated 86 | - **Feature set** - No new capabilities, no optimization rewrites 87 | - **Architecture** - The design is complete 88 | - **v2.0 will never exist** - No major revisions, no roadmap beyond v1.0 89 | 90 | ### What Doesn't Ossify (Responsible Maintenance) 91 | 92 | - **Critical consensus bugs** - Fixed via minimal errata (v1.0.1, v1.0.2, etc.) 93 | - **Security vulnerabilities** - Patched immediately with documented errata 94 | - **Platform compatibility** - OS updates may require platform layer maintenance 95 | - **Documentation & comments** - Clarifications, improvements, and expansions welcome 96 | - **Test coverage** - Additional test vectors and edge case coverage encouraged 97 | - **Educational materials** - Explanatory documentation, guides, and curriculum 98 | 99 | ### What About Protocol Changes? 100 | 101 | If Bitcoin adopts quantum-resistant signatures or other soft forks: 102 | - **We don't modify Bitcoin Echo v1.0** - It remains frozen 103 | - **We create a successor** - Bitcoin Echo-Q, Bitcoin Echo-R, etc. 104 | - **Each successor is also frozen** - Upon completion, it too becomes immutable 105 | - **The chain of succession is linear** - Each validates all historical blocks 106 | 107 | **Version numbers tell the story:** 108 | - v1.0.0 → v1.0.47 (errata/patches) ✅ Acceptable 109 | - v1.0.0 → v2.0.0 (new features) ❌ Will never happen 110 | - Bitcoin Echo v1.0 → Bitcoin Echo-Q v1.0 (successor) ✅ How we evolve 111 | 112 | This is **pragmatic ossification**: Strong intention to freeze consensus and features, with responsible governance for defects and succession. 113 | 114 | See [Whitepaper §15: Cryptographic Succession](https://github.com/bitcoinecho/bitcoinecho-org/blob/main/bitcoin-echo-whitepaper.md#15-cryptographic-succession) and [Errata Policy](https://github.com/bitcoinecho/bitcoinecho-org/blob/main/ERRATA_POLICY.md) for complete details. 115 | 116 | ### Your Contribution Lives Forever 117 | 118 | - Your code becomes a **permanent artifact** 119 | - Your name in git history will be there for centuries 120 | - No future maintainer can change or remove your work 121 | - You're contributing to Bitcoin's permanent archaeological record 122 | 123 | **This is your chance to be part of Bitcoin history.** 124 | 125 | Every contributor during this development phase will be forever preserved in the codebase that scholars and historians will study for generations. 126 | 127 | ## After v1.0 128 | 129 | Once Bitcoin Echo reaches v1.0 and is audited, the codebase freezes. No further feature contributions will be accepted—by design. The code becomes a permanent artifact, a snapshot of Bitcoin consensus frozen in time. 130 | 131 | Bug fixes for critical consensus issues may be accepted post-freeze, but only after extensive review and only if they represent genuine Bitcoin protocol bugs. 132 | 133 | ## Funding 134 | 135 | Bitcoin Echo is seeking funding for professional audit and development completion. See [bitcoinecho.org/funding](https://bitcoinecho.org/funding) for details. 136 | 137 | ## Questions? 138 | 139 | - **Documentation:** [bitcoinecho.org](https://bitcoinecho.org) 140 | - **Whitepaper:** [Technical Specification](https://bitcoinecho.org/docs/whitepaper) 141 | - **Manifesto:** [Why Permanence Matters](https://bitcoinecho.org/docs/manifesto) 142 | - **Email:** echo@bitcoinecho.org 143 | - **X/Twitter:** [@bitcoinechoorg](https://twitter.com/bitcoinechoorg) 144 | 145 | --- 146 | 147 | *Build once. Build right. Stop.* 148 | -------------------------------------------------------------------------------- /src/consensus/serialize.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Serialization Primitives 3 | * 4 | * Implementation of Bitcoin's variable-length integer encoding (CompactSize). 5 | * 6 | * Build once. Build right. Stop. 7 | */ 8 | 9 | #include "serialize.h" 10 | #include "echo_types.h" 11 | #include 12 | #include 13 | 14 | /* 15 | * Compute the encoded size of a varint. 16 | */ 17 | size_t varint_size(uint64_t value) { 18 | if (value <= 0xFC) { 19 | return 1; 20 | } else if (value <= 0xFFFF) { 21 | return 3; 22 | } else if (value <= 0xFFFFFFFF) { 23 | return 5; 24 | } else { 25 | return 9; 26 | } 27 | } 28 | 29 | /* 30 | * Write a varint to a buffer. 31 | */ 32 | echo_result_t varint_write(uint8_t *buf, size_t buf_len, uint64_t value, 33 | size_t *written) { 34 | size_t size; 35 | 36 | if (buf == NULL) { 37 | return ECHO_ERR_NULL_PARAM; 38 | } 39 | 40 | size = varint_size(value); 41 | 42 | if (buf_len < size) { 43 | return ECHO_ERR_BUFFER_TOO_SMALL; 44 | } 45 | 46 | if (value <= 0xFC) { 47 | /* Single byte encoding */ 48 | buf[0] = (uint8_t)value; 49 | } else if (value <= 0xFFFF) { 50 | /* 0xFD prefix + 2 bytes little-endian */ 51 | buf[0] = VARINT_PREFIX_16; 52 | buf[1] = (uint8_t)(value & 0xFF); 53 | buf[2] = (uint8_t)((value >> 8) & 0xFF); 54 | } else if (value <= 0xFFFFFFFF) { 55 | /* 0xFE prefix + 4 bytes little-endian */ 56 | buf[0] = VARINT_PREFIX_32; 57 | buf[1] = (uint8_t)(value & 0xFF); 58 | buf[2] = (uint8_t)((value >> 8) & 0xFF); 59 | buf[3] = (uint8_t)((value >> 16) & 0xFF); 60 | buf[4] = (uint8_t)((value >> 24) & 0xFF); 61 | } else { 62 | /* 0xFF prefix + 8 bytes little-endian */ 63 | buf[0] = VARINT_PREFIX_64; 64 | buf[1] = (uint8_t)(value & 0xFF); 65 | buf[2] = (uint8_t)((value >> 8) & 0xFF); 66 | buf[3] = (uint8_t)((value >> 16) & 0xFF); 67 | buf[4] = (uint8_t)((value >> 24) & 0xFF); 68 | buf[5] = (uint8_t)((value >> 32) & 0xFF); 69 | buf[6] = (uint8_t)((value >> 40) & 0xFF); 70 | buf[7] = (uint8_t)((value >> 48) & 0xFF); 71 | buf[8] = (uint8_t)((value >> 56) & 0xFF); 72 | } 73 | 74 | if (written != NULL) { 75 | *written = size; 76 | } 77 | 78 | return ECHO_OK; 79 | } 80 | 81 | /* 82 | * Read a varint from a buffer (strict canonical checking). 83 | */ 84 | echo_result_t varint_read(const uint8_t *buf, size_t buf_len, uint64_t *value, 85 | size_t *consumed) { 86 | uint64_t result; 87 | size_t size; 88 | 89 | if (buf == NULL || value == NULL) { 90 | return ECHO_ERR_NULL_PARAM; 91 | } 92 | 93 | if (buf_len < 1) { 94 | return ECHO_ERR_TRUNCATED; 95 | } 96 | 97 | if (buf[0] <= 0xFC) { 98 | /* Single byte value */ 99 | result = buf[0]; 100 | size = 1; 101 | } else if (buf[0] == VARINT_PREFIX_16) { 102 | /* 2-byte value */ 103 | if (buf_len < 3) { 104 | return ECHO_ERR_TRUNCATED; 105 | } 106 | result = (uint64_t)buf[1] | ((uint64_t)buf[2] << 8); 107 | size = 3; 108 | 109 | /* Canonical check: value must require this encoding */ 110 | if (result < 0xFD) { 111 | return ECHO_ERR_INVALID_FORMAT; 112 | } 113 | } else if (buf[0] == VARINT_PREFIX_32) { 114 | /* 4-byte value */ 115 | if (buf_len < 5) { 116 | return ECHO_ERR_TRUNCATED; 117 | } 118 | result = (uint64_t)buf[1] | ((uint64_t)buf[2] << 8) | 119 | ((uint64_t)buf[3] << 16) | ((uint64_t)buf[4] << 24); 120 | size = 5; 121 | 122 | /* Canonical check: value must require this encoding */ 123 | if (result <= 0xFFFF) { 124 | return ECHO_ERR_INVALID_FORMAT; 125 | } 126 | } else { 127 | /* 8-byte value (buf[0] == VARINT_PREFIX_64) */ 128 | if (buf_len < 9) { 129 | return ECHO_ERR_TRUNCATED; 130 | } 131 | result = (uint64_t)buf[1] | ((uint64_t)buf[2] << 8) | 132 | ((uint64_t)buf[3] << 16) | ((uint64_t)buf[4] << 24) | 133 | ((uint64_t)buf[5] << 32) | ((uint64_t)buf[6] << 40) | 134 | ((uint64_t)buf[7] << 48) | ((uint64_t)buf[8] << 56); 135 | size = 9; 136 | 137 | /* Canonical check: value must require this encoding */ 138 | if (result <= 0xFFFFFFFF) { 139 | return ECHO_ERR_INVALID_FORMAT; 140 | } 141 | } 142 | 143 | *value = result; 144 | if (consumed != NULL) { 145 | *consumed = size; 146 | } 147 | 148 | return ECHO_OK; 149 | } 150 | 151 | /* 152 | * Read a varint from a buffer (non-strict, for historical compatibility). 153 | */ 154 | echo_result_t varint_read_nonstrict(const uint8_t *buf, size_t buf_len, 155 | uint64_t *value, size_t *consumed) { 156 | uint64_t result; 157 | size_t size; 158 | 159 | if (buf == NULL || value == NULL) { 160 | return ECHO_ERR_NULL_PARAM; 161 | } 162 | 163 | if (buf_len < 1) { 164 | return ECHO_ERR_TRUNCATED; 165 | } 166 | 167 | if (buf[0] <= 0xFC) { 168 | /* Single byte value */ 169 | result = buf[0]; 170 | size = 1; 171 | } else if (buf[0] == VARINT_PREFIX_16) { 172 | /* 2-byte value */ 173 | if (buf_len < 3) { 174 | return ECHO_ERR_TRUNCATED; 175 | } 176 | result = (uint64_t)buf[1] | ((uint64_t)buf[2] << 8); 177 | size = 3; 178 | /* No canonical check in non-strict mode */ 179 | } else if (buf[0] == VARINT_PREFIX_32) { 180 | /* 4-byte value */ 181 | if (buf_len < 5) { 182 | return ECHO_ERR_TRUNCATED; 183 | } 184 | result = (uint64_t)buf[1] | ((uint64_t)buf[2] << 8) | 185 | ((uint64_t)buf[3] << 16) | ((uint64_t)buf[4] << 24); 186 | size = 5; 187 | /* No canonical check in non-strict mode */ 188 | } else { 189 | /* 8-byte value (buf[0] == VARINT_PREFIX_64) */ 190 | if (buf_len < 9) { 191 | return ECHO_ERR_TRUNCATED; 192 | } 193 | result = (uint64_t)buf[1] | ((uint64_t)buf[2] << 8) | 194 | ((uint64_t)buf[3] << 16) | ((uint64_t)buf[4] << 24) | 195 | ((uint64_t)buf[5] << 32) | ((uint64_t)buf[6] << 40) | 196 | ((uint64_t)buf[7] << 48) | ((uint64_t)buf[8] << 56); 197 | size = 9; 198 | /* No canonical check in non-strict mode */ 199 | } 200 | 201 | *value = result; 202 | if (consumed != NULL) { 203 | *consumed = size; 204 | } 205 | 206 | return ECHO_OK; 207 | } 208 | -------------------------------------------------------------------------------- /include/echo_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Master Configuration 3 | * 4 | * All configuration is compile-time. There are no configuration files, 5 | * no command-line flags, no runtime policy changes. The node behaves 6 | * identically on every execution. 7 | * 8 | * This file coordinates all configuration layers: 9 | * - Consensus rules (frozen, never change) 10 | * - Policy settings (configurable, reflect operational choices) 11 | * - Platform settings (pragmatic, may need updates as systems evolve) 12 | * 13 | * To build for a different network, define one of: 14 | * ECHO_NETWORK_MAINNET (default) 15 | * ECHO_NETWORK_TESTNET 16 | * ECHO_NETWORK_REGTEST 17 | * 18 | * Build once. Build right. Stop. 19 | */ 20 | 21 | #ifndef ECHO_CONFIG_H 22 | #define ECHO_CONFIG_H 23 | 24 | /* 25 | * Version information. 26 | */ 27 | #define ECHO_VERSION_MAJOR 0 28 | #define ECHO_VERSION_MINOR 0 29 | #define ECHO_VERSION_PATCH 1 30 | #define ECHO_VERSION_STRING "0.0.1" 31 | 32 | /* Protocol version we speak */ 33 | #define ECHO_PROTOCOL_VERSION 70016 34 | 35 | /* User agent string for version messages */ 36 | #define ECHO_USER_AGENT "/BitcoinEcho:0.0.1/" 37 | 38 | /* 39 | * Network selection. 40 | * Default to mainnet if nothing specified. 41 | */ 42 | #if !defined(ECHO_NETWORK_MAINNET) && !defined(ECHO_NETWORK_TESTNET) && \ 43 | !defined(ECHO_NETWORK_REGTEST) 44 | #define ECHO_NETWORK_MAINNET 45 | #endif 46 | 47 | /* 48 | * Network magic bytes. 49 | * Four bytes that prefix every P2P message to identify the network. 50 | */ 51 | #if defined(ECHO_NETWORK_MAINNET) 52 | #define ECHO_NETWORK_MAGIC 0xD9B4BEF9 /* Little-endian: F9 BE B4 D9 */ 53 | #define ECHO_NETWORK_NAME "mainnet" 54 | #elif defined(ECHO_NETWORK_TESTNET) 55 | #define ECHO_NETWORK_MAGIC 0x0709110B /* Little-endian: 0B 11 09 07 */ 56 | #define ECHO_NETWORK_NAME "testnet3" 57 | #elif defined(ECHO_NETWORK_REGTEST) 58 | #define ECHO_NETWORK_MAGIC 0xDAB5BFFA /* Little-endian: FA BF B5 DA */ 59 | #define ECHO_NETWORK_NAME "regtest" 60 | #endif 61 | 62 | /* 63 | * Network ports. 64 | */ 65 | #if defined(ECHO_NETWORK_MAINNET) 66 | #define ECHO_DEFAULT_PORT 8333 67 | #define ECHO_DEFAULT_RPC_PORT 8332 68 | #elif defined(ECHO_NETWORK_TESTNET) 69 | #define ECHO_DEFAULT_PORT 18333 70 | #define ECHO_DEFAULT_RPC_PORT 18332 71 | #elif defined(ECHO_NETWORK_REGTEST) 72 | #define ECHO_DEFAULT_PORT 18444 73 | #define ECHO_DEFAULT_RPC_PORT 18443 74 | #endif 75 | 76 | /* 77 | * Soft fork activation heights. 78 | * Network-specific, defined here then used by consensus layer. 79 | */ 80 | #if defined(ECHO_NETWORK_MAINNET) 81 | #define CONSENSUS_BIP16_HEIGHT 173805 /* P2SH */ 82 | #define CONSENSUS_BIP34_HEIGHT 227931 /* Height in coinbase */ 83 | #define CONSENSUS_BIP65_HEIGHT 388381 /* CHECKLOCKTIMEVERIFY */ 84 | #define CONSENSUS_BIP66_HEIGHT 363725 /* Strict DER signatures */ 85 | #define CONSENSUS_BIP68_HEIGHT 419328 /* Relative lock-time */ 86 | #define CONSENSUS_SEGWIT_HEIGHT 481824 /* Segregated Witness */ 87 | #define CONSENSUS_TAPROOT_HEIGHT 709632 /* Taproot/Schnorr */ 88 | #elif defined(ECHO_NETWORK_TESTNET) 89 | #define CONSENSUS_BIP16_HEIGHT 514 90 | #define CONSENSUS_BIP34_HEIGHT 21111 91 | #define CONSENSUS_BIP65_HEIGHT 581885 92 | #define CONSENSUS_BIP66_HEIGHT 330776 93 | #define CONSENSUS_BIP68_HEIGHT 770112 94 | #define CONSENSUS_SEGWIT_HEIGHT 834624 95 | #define CONSENSUS_TAPROOT_HEIGHT 2000000 /* Approximate */ 96 | #elif defined(ECHO_NETWORK_REGTEST) 97 | #define CONSENSUS_BIP16_HEIGHT 0 /* Always active */ 98 | #define CONSENSUS_BIP34_HEIGHT 500 99 | #define CONSENSUS_BIP65_HEIGHT 1351 100 | #define CONSENSUS_BIP66_HEIGHT 1251 101 | #define CONSENSUS_BIP68_HEIGHT 432 102 | #define CONSENSUS_SEGWIT_HEIGHT 0 /* Always active */ 103 | #define CONSENSUS_TAPROOT_HEIGHT 0 /* Always active */ 104 | #endif 105 | 106 | /* 107 | * Include configuration layers. 108 | */ 109 | #include "echo_consensus.h" 110 | #include "echo_policy.h" 111 | #include "echo_platform_config.h" 112 | 113 | /* 114 | * Backward compatibility aliases. 115 | * These maintain compatibility with existing code while we transition 116 | * to the new layered configuration structure. 117 | * 118 | * TODO: Remove these aliases once all code uses new constant names. 119 | */ 120 | 121 | /* Consensus aliases (ECHO_* -> CONSENSUS_*) */ 122 | #define ECHO_HALVING_INTERVAL CONSENSUS_HALVING_INTERVAL 123 | #define ECHO_INITIAL_SUBSIDY CONSENSUS_INITIAL_SUBSIDY 124 | #define ECHO_DIFFICULTY_INTERVAL CONSENSUS_DIFFICULTY_INTERVAL 125 | #define ECHO_TARGET_BLOCK_TIME CONSENSUS_TARGET_BLOCK_TIME 126 | #define ECHO_TARGET_PERIOD_TIME CONSENSUS_TARGET_PERIOD_TIME 127 | #define ECHO_COINBASE_MATURITY CONSENSUS_COINBASE_MATURITY 128 | #define ECHO_MAX_BLOCK_WEIGHT CONSENSUS_MAX_BLOCK_WEIGHT 129 | #define ECHO_MAX_BLOCK_SIZE CONSENSUS_MAX_BLOCK_SIZE 130 | #define ECHO_MAX_SCRIPT_SIZE CONSENSUS_MAX_SCRIPT_SIZE 131 | #define ECHO_MAX_SCRIPT_ELEMENT CONSENSUS_MAX_SCRIPT_ELEMENT 132 | #define ECHO_MAX_SCRIPT_OPS CONSENSUS_MAX_SCRIPT_OPS 133 | #define ECHO_MAX_STACK_SIZE CONSENSUS_MAX_STACK_SIZE 134 | #define ECHO_MAX_MULTISIG_KEYS CONSENSUS_MAX_MULTISIG_KEYS 135 | #define ECHO_MAX_BLOCK_SIGOPS CONSENSUS_MAX_BLOCK_SIGOPS 136 | #define ECHO_MAX_TX_SIZE CONSENSUS_MAX_TX_SIZE 137 | #define ECHO_LOCKTIME_THRESHOLD CONSENSUS_LOCKTIME_THRESHOLD 138 | #define ECHO_SEQUENCE_FINAL CONSENSUS_SEQUENCE_FINAL 139 | #define ECHO_SEQUENCE_LOCKTIME_DISABLE CONSENSUS_SEQUENCE_LOCKTIME_DISABLE 140 | #define ECHO_SEQUENCE_LOCKTIME_TYPE CONSENSUS_SEQUENCE_LOCKTIME_TYPE 141 | #define ECHO_SEQUENCE_LOCKTIME_MASK CONSENSUS_SEQUENCE_LOCKTIME_MASK 142 | #define ECHO_BIP16_HEIGHT CONSENSUS_BIP16_HEIGHT 143 | #define ECHO_BIP34_HEIGHT CONSENSUS_BIP34_HEIGHT 144 | #define ECHO_BIP65_HEIGHT CONSENSUS_BIP65_HEIGHT 145 | #define ECHO_BIP66_HEIGHT CONSENSUS_BIP66_HEIGHT 146 | #define ECHO_BIP68_HEIGHT CONSENSUS_BIP68_HEIGHT 147 | #define ECHO_SEGWIT_HEIGHT CONSENSUS_SEGWIT_HEIGHT 148 | #define ECHO_TAPROOT_HEIGHT CONSENSUS_TAPROOT_HEIGHT 149 | 150 | /* Platform aliases (ECHO_* -> PLATFORM_*) */ 151 | #define ECHO_MAX_OUTBOUND_PEERS PLATFORM_MAX_OUTBOUND_PEERS 152 | #define ECHO_MAX_INBOUND_PEERS PLATFORM_MAX_INBOUND_PEERS 153 | #define ECHO_MAX_TOTAL_PEERS PLATFORM_MAX_TOTAL_PEERS 154 | #define ECHO_CONNECT_TIMEOUT_MS PLATFORM_CONNECT_TIMEOUT_MS 155 | #define ECHO_PING_INTERVAL_MS PLATFORM_PING_INTERVAL_MS 156 | #define ECHO_INACTIVITY_TIMEOUT_MS PLATFORM_INACTIVITY_TIMEOUT_MS 157 | #define ECHO_BLOCKS_DIR PLATFORM_BLOCKS_DIR 158 | #define ECHO_CHAINSTATE_DIR PLATFORM_CHAINSTATE_DIR 159 | #define ECHO_DATABASE_FILE PLATFORM_DATABASE_FILE 160 | 161 | #endif /* ECHO_CONFIG_H */ 162 | -------------------------------------------------------------------------------- /include/echo_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Fundamental Types 3 | * 4 | * This header defines the core types used throughout Bitcoin Echo. 5 | * All types are fixed-width for portability and protocol correctness. 6 | * 7 | * Build once. Build right. Stop. 8 | */ 9 | 10 | #ifndef ECHO_TYPES_H 11 | #define ECHO_TYPES_H 12 | 13 | #include 14 | #include 15 | 16 | /* 17 | * Boolean type. 18 | * C11 provides _Bool and stdbool.h, but we define our own for clarity. 19 | */ 20 | typedef int echo_bool_t; 21 | #define ECHO_TRUE 1 22 | #define ECHO_FALSE 0 23 | 24 | /* 25 | * Fixed-width integer types. 26 | * These come from stdint.h but we document their use here: 27 | * uint8_t - single byte (opcodes, flags) 28 | * uint16_t - port numbers, version fields 29 | * uint32_t - block height, timestamps, nonce, sequence 30 | * uint64_t - services flags, compact size integers 31 | * int32_t - version numbers (signed per protocol) 32 | * int64_t - satoshi amounts (signed to detect underflow) 33 | */ 34 | 35 | /* 36 | * Satoshi amount type. 37 | * Signed 64-bit to allow detection of underflow during validation. 38 | * Valid range: 0 to 21,000,000 * 100,000,000 (2.1 quadrillion satoshis). 39 | */ 40 | typedef int64_t satoshi_t; 41 | 42 | /* Maximum possible satoshis (21 million BTC) */ 43 | #define ECHO_MAX_SATOSHIS ((satoshi_t)2100000000000000LL) 44 | 45 | /* Satoshis per bitcoin */ 46 | #define ECHO_SATOSHIS_PER_BTC ((satoshi_t)100000000LL) 47 | 48 | /* 49 | * Hash types. 50 | * Bitcoin uses two hash sizes: 256-bit (SHA256d) and 160-bit (HASH160). 51 | */ 52 | 53 | /* 256-bit hash (32 bytes) - used for block hashes, txids, merkle nodes */ 54 | typedef struct { 55 | uint8_t bytes[32]; 56 | } hash256_t; 57 | 58 | /* 160-bit hash (20 bytes) - used for addresses (RIPEMD160(SHA256(x))) */ 59 | typedef struct { 60 | uint8_t bytes[20]; 61 | } hash160_t; 62 | 63 | /* Null hash constant (32 zero bytes) - used for coinbase prevout */ 64 | #define ECHO_NULL_HASH256 ((hash256_t){{0}}) 65 | 66 | /* 67 | * Result codes. 68 | * Functions return these to indicate success or specific failure reasons. 69 | * Zero is success; negative values indicate errors. 70 | */ 71 | typedef enum { 72 | ECHO_OK = 0, 73 | ECHO_DONE = 1, /* Iterator/query has no more items */ 74 | 75 | /* General errors */ 76 | ECHO_ERR_NULL_PARAM = -1, /* Required parameter was NULL */ 77 | ECHO_ERR_BUFFER_TOO_SMALL = -2, /* Output buffer insufficient */ 78 | ECHO_ERR_OUT_OF_MEMORY = -3, /* Memory allocation failed */ 79 | ECHO_ERR_OUT_OF_RANGE = -4, /* Index out of valid range */ 80 | ECHO_ERR_NOT_FOUND = -5, /* Item not found */ 81 | ECHO_ERR_INVALID = -6, /* Invalid operation or state */ 82 | 83 | /* Parsing errors */ 84 | ECHO_ERR_PARSE_FAILED = -10, /* General parse failure */ 85 | ECHO_ERR_TRUNCATED = -11, /* Unexpected end of data */ 86 | ECHO_ERR_INVALID_FORMAT = -12, /* Data format invalid */ 87 | ECHO_ERR_VARINT_TOO_LARGE = -13, /* CompactSize exceeds maximum */ 88 | 89 | /* Validation errors - transactions */ 90 | ECHO_ERR_TX_EMPTY_INPUTS = -20, /* Transaction has no inputs */ 91 | ECHO_ERR_TX_EMPTY_OUTPUTS = -21, /* Transaction has no outputs */ 92 | ECHO_ERR_TX_DUPLICATE_INPUT = -22, /* Same outpoint spent twice */ 93 | ECHO_ERR_TX_NEGATIVE_VALUE = -23, /* Output value negative */ 94 | ECHO_ERR_TX_VALUE_OVERFLOW = -24, /* Total output exceeds max */ 95 | ECHO_ERR_TX_SCRIPT_FAILED = -25, /* Script execution failed */ 96 | 97 | /* Validation errors - blocks */ 98 | ECHO_ERR_BLOCK_POW_FAILED = -30, /* Proof-of-work invalid */ 99 | ECHO_ERR_BLOCK_MERKLE = -31, /* Merkle root mismatch */ 100 | ECHO_ERR_BLOCK_TIMESTAMP = -32, /* Timestamp out of range */ 101 | ECHO_ERR_BLOCK_SIZE = -33, /* Block exceeds size limit */ 102 | ECHO_ERR_BLOCK_NO_COINBASE = -34, /* First tx not coinbase */ 103 | ECHO_ERR_BLOCK_MULTI_COINBASE = -35, /* Multiple coinbase txs */ 104 | 105 | /* Validation errors - scripts */ 106 | ECHO_ERR_SCRIPT_SIZE = -40, /* Script exceeds size limit */ 107 | ECHO_ERR_SCRIPT_OPCODE = -41, /* Invalid or disabled opcode */ 108 | ECHO_ERR_SCRIPT_STACK = -42, /* Stack overflow/underflow */ 109 | ECHO_ERR_SCRIPT_VERIFY = -43, /* OP_VERIFY failed */ 110 | ECHO_ERR_SCRIPT_SIG = -44, /* Signature verification failed */ 111 | ECHO_ERR_SCRIPT_ERROR = -45, /* General script error (check ctx->error) */ 112 | 113 | /* Consensus errors */ 114 | ECHO_ERR_CONSENSUS_UTXO = -50, /* UTXO not found */ 115 | ECHO_ERR_CONSENSUS_SPENT = -51, /* UTXO already spent */ 116 | ECHO_ERR_CONSENSUS_MATURITY = -52, /* Coinbase not mature */ 117 | 118 | /* UTXO errors */ 119 | ECHO_ERR_EXISTS = -55, /* Item already exists */ 120 | ECHO_ERR_NOMEM = -56, /* Memory allocation failed */ 121 | 122 | /* Chain state errors */ 123 | ECHO_ERR_INVALID_BLOCK = -60, /* Block invalid or doesn't connect */ 124 | ECHO_ERR_UNDERFLOW = -61, /* Arithmetic underflow */ 125 | ECHO_ERR_OVERFLOW = -62, /* Arithmetic overflow */ 126 | 127 | /* Platform errors */ 128 | ECHO_ERR_PLATFORM_IO = -70, /* I/O operation failed */ 129 | ECHO_ERR_PLATFORM_NET = -71, /* Network operation failed */ 130 | 131 | /* Database errors */ 132 | ECHO_ERR_DB = -80, /* Database operation failed */ 133 | ECHO_ERR_IO = -81, /* File I/O error */ 134 | 135 | /* Network/protocol errors */ 136 | ECHO_ERR_NETWORK = -90, /* Network operation failed */ 137 | ECHO_ERR_PROTOCOL = -91, /* Protocol violation */ 138 | ECHO_ERR_WOULD_BLOCK = -92, /* Operation would block (not an error) */ 139 | ECHO_ERR_INVALID_STATE = -93, /* Invalid state for operation */ 140 | ECHO_ERR_FULL = -94, /* Queue or buffer full */ 141 | ECHO_ERR_INVALID_PARAM = -95, /* Invalid parameter value */ 142 | ECHO_ERR_RATE_LIMIT = -96, /* Rate limit exceeded */ 143 | ECHO_ERR_MEMORY = -97, /* Memory operation failed */ 144 | ECHO_ERR_DUPLICATE = -98, /* Duplicate item */ 145 | 146 | /* Success codes */ 147 | ECHO_SUCCESS = 0 /* Operation succeeded (alias for ECHO_OK) */ 148 | 149 | } echo_result_t; 150 | 151 | /* 152 | * Byte buffer with length. 153 | * Used for variable-length data like scripts and signatures. 154 | */ 155 | typedef struct { 156 | const uint8_t *data; 157 | size_t len; 158 | } echo_buffer_t; 159 | 160 | /* 161 | * Mutable byte buffer for output. 162 | */ 163 | typedef struct { 164 | uint8_t *data; 165 | size_t len; 166 | size_t capacity; 167 | } echo_buffer_mut_t; 168 | 169 | #endif /* ECHO_TYPES_H */ 170 | -------------------------------------------------------------------------------- /test/run_all_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Bitcoin Echo — Global Test Runner 3 | # Runs all unit tests and displays grand finale summary 4 | # Build once. Build right. Stop. 5 | 6 | # ANSI color codes 7 | BOLD='\033[1m' 8 | GREEN='\033[32m' 9 | RED='\033[31m' 10 | CYAN='\033[36m' 11 | RESET='\033[0m' 12 | 13 | # Temporary files for collecting results 14 | RESULTS_FILE=$(mktemp) 15 | COUNTS_FILE=$(mktemp) 16 | TEST_OUTPUT_FILE=$(mktemp) 17 | 18 | # Initialize counters file 19 | echo "0 0 0 0 0 0" > "$COUNTS_FILE" 20 | 21 | # Test definitions: "executable|description" 22 | run_test() { 23 | test_exec="$1" 24 | test_desc="$2" 25 | 26 | # Read current counts 27 | read total_suites passed_suites failed_suites total_tests passed_tests failed_tests < "$COUNTS_FILE" 28 | 29 | total_suites=$((total_suites + 1)) 30 | 31 | echo "Running ${test_desc}..." 32 | 33 | # Run test and capture output 34 | if ./"$test_exec" > "$TEST_OUTPUT_FILE" 2>&1; then 35 | exit_code=0 36 | else 37 | exit_code=$? 38 | fi 39 | 40 | # Display the test output 41 | cat "$TEST_OUTPUT_FILE" 42 | echo "" 43 | 44 | # Extract test counts from output 45 | # Look for pattern: "Results: X/Y tests passed" 46 | suite_passed=$(grep -o '[0-9]\+/[0-9]\+ tests passed' "$TEST_OUTPUT_FILE" | head -1 | cut -d'/' -f1) 47 | suite_total=$(grep -o '[0-9]\+/[0-9]\+ tests passed' "$TEST_OUTPUT_FILE" | head -1 | cut -d'/' -f2 | cut -d' ' -f1) 48 | 49 | if [ -n "$suite_passed" ] && [ -n "$suite_total" ]; then 50 | # Record results 51 | echo "${test_desc}|${suite_passed}|${suite_total}|${exit_code}" >> "$RESULTS_FILE" 52 | 53 | total_tests=$((total_tests + suite_total)) 54 | passed_tests=$((passed_tests + suite_passed)) 55 | 56 | if [ "$exit_code" -eq 0 ]; then 57 | passed_suites=$((passed_suites + 1)) 58 | else 59 | failed_suites=$((failed_suites + 1)) 60 | fi 61 | 62 | # Write updated counts 63 | echo "$total_suites $passed_suites $failed_suites $total_tests $passed_tests $failed_tests" > "$COUNTS_FILE" 64 | fi 65 | } 66 | 67 | # Run all tests 68 | run_test "test/unit/test_sha256" "SHA-256 tests" 69 | run_test "test/unit/test_ripemd160" "RIPEMD-160 tests" 70 | run_test "test/unit/test_secp256k1_fe" "secp256k1 field tests" 71 | run_test "test/unit/test_secp256k1_group" "secp256k1 group tests" 72 | run_test "test/unit/test_ecdsa" "ECDSA tests" 73 | run_test "test/unit/test_schnorr" "Schnorr tests" 74 | run_test "test/unit/test_sig_verify" "Signature Interface tests" 75 | run_test "test/unit/test_serialize" "Serialization tests" 76 | run_test "test/unit/test_tx" "Transaction tests" 77 | run_test "test/unit/test_block" "Block tests" 78 | run_test "test/unit/test_merkle" "Merkle tree tests" 79 | run_test "test/unit/test_script" "Script tests" 80 | run_test "test/unit/test_stack" "Stack tests" 81 | run_test "test/unit/test_opcodes" "Opcode tests" 82 | run_test "test/unit/test_p2sh" "P2SH tests" 83 | run_test "test/unit/test_timelock" "Timelock tests" 84 | run_test "test/unit/test_tx_validate" "Transaction Validation tests" 85 | run_test "test/unit/test_block_validate" "Block Header Validation tests" 86 | run_test "test/unit/test_coinbase" "Coinbase Validation tests" 87 | run_test "test/unit/test_utxo" "UTXO tests" 88 | run_test "test/unit/test_chainstate" "Chain State tests" 89 | run_test "test/unit/test_consensus" "Consensus Engine tests" 90 | run_test "test/unit/test_block_storage" "Block Storage tests" 91 | run_test "test/unit/test_db" "Database Integration tests" 92 | run_test "test/unit/test_utxo_db" "UTXO Database tests" 93 | run_test "test/unit/test_block_index_db" "Block Index Database tests" 94 | run_test "test/unit/test_protocol" "Protocol Message tests" 95 | run_test "test/unit/test_protocol_serialize" "Protocol Serialization tests" 96 | run_test "test/unit/test_peer" "Peer Management tests" 97 | run_test "test/unit/test_discovery" "Peer Discovery tests" 98 | run_test "test/unit/test_relay" "Relay tests" 99 | run_test "test/unit/test_sync" "Sync tests" 100 | run_test "test/unit/test_mempool" "Mempool tests" 101 | run_test "test/unit/test_node" "Node Lifecycle tests" 102 | run_test "test/unit/test_event_loop" "Event Loop tests" 103 | run_test "test/unit/test_rpc" "RPC Interface tests" 104 | run_test "test/unit/test_log" "Logging System tests" 105 | 106 | # Read final counts 107 | read total_suites passed_suites failed_suites total_tests passed_tests failed_tests < "$COUNTS_FILE" 108 | 109 | # Calculate failures 110 | failed_tests=$((total_tests - passed_tests)) 111 | 112 | # Display grand finale 113 | printf "${BOLD}================================================================================\n" 114 | printf " BITCOIN ECHO — GLOBAL TEST SUMMARY\n" 115 | printf "================================================================================\n${RESET}\n" 116 | 117 | # Display individual suite results 118 | printf "${CYAN}Test Suite Results:${RESET}\n" 119 | while IFS='|' read -r desc passed total exit_code; do 120 | if [ "$exit_code" -eq 0 ]; then 121 | printf " ${GREEN}✓${RESET} %-40s ${GREEN}%3s/%3s passed${RESET}\n" "$desc" "$passed" "$total" 122 | else 123 | printf " ${RED}✗${RESET} %-40s ${RED}%3s/%3s passed${RESET}\n" "$desc" "$passed" "$total" 124 | fi 125 | done < "$RESULTS_FILE" 126 | 127 | printf "\n${BOLD}Summary:${RESET}\n" 128 | printf " Test Suites: " 129 | if [ "$failed_suites" -eq 0 ]; then 130 | printf "${GREEN}%d/%d passed${RESET}\n" "$passed_suites" "$total_suites" 131 | else 132 | printf "${RED}%d/%d passed${RESET}, ${RED}%d failed${RESET}\n" "$passed_suites" "$total_suites" "$failed_suites" 133 | fi 134 | 135 | printf " Test Cases: " 136 | if [ "$failed_tests" -eq 0 ]; then 137 | printf "${GREEN}%d/%d passed${RESET}\n" "$passed_tests" "$total_tests" 138 | else 139 | printf "${RED}%d/%d passed${RESET}, ${RED}%d failed${RESET}\n" "$passed_tests" "$total_tests" "$failed_tests" 140 | fi 141 | 142 | printf "\n" 143 | 144 | # Grand finale 145 | if [ "$failed_tests" -eq 0 ] && [ "$failed_suites" -eq 0 ]; then 146 | printf "${BOLD}${GREEN} ALL %d TESTS PASSED!${RESET}\n" "$total_tests" 147 | printf "${BOLD}================================================================================${RESET}\n" 148 | exit_status=0 149 | else 150 | printf "${BOLD}${RED} %d TEST(S) FAILED${RESET}\n" "$failed_tests" 151 | printf "${BOLD}================================================================================${RESET}\n" 152 | exit_status=1 153 | fi 154 | 155 | # Cleanup 156 | rm -f "$RESULTS_FILE" "$COUNTS_FILE" "$TEST_OUTPUT_FILE" 157 | 158 | exit $exit_status 159 | -------------------------------------------------------------------------------- /CLAUDE.md: -------------------------------------------------------------------------------- 1 | # Bitcoin Echo — C Implementation Context 2 | 3 | ## Critical Constraints 4 | 5 | **You must follow these constraints in all implementation work:** 6 | 7 | 1. **Pure C11** — No C++ features, no extensions beyond rotation intrinsics 8 | 2. **Zero external dependencies** — Only C compiler and standard library (+ SQLite, which is embedded) 9 | 3. **Consensus engine purity** — No I/O, no system calls, no dynamic allocation during validation 10 | 4. **Simplicity over optimization** — Correct and clear beats fast and clever 11 | 5. **15,000–25,000 lines target** — Every line must justify its existence 12 | 6. **Heavy commenting** — Code must be understandable by auditor in 2125 13 | 7. **No future features** — Implement what exists in Bitcoin today, nothing speculative 14 | 15 | ## Code Style 16 | 17 | - Functions are obvious in purpose 18 | - Every branch justifiable by protocol specification 19 | - No clever tricks 20 | - Bounds checking on all buffers 21 | - Constants over magic numbers 22 | - Descriptive names over abbreviations 23 | 24 | **Include Hygiene (Critical for Ossification):** 25 | - Every symbol used must have its header explicitly included 26 | - No transitive include dependencies (don't rely on A→B→C chains) 27 | - Headers must be self-contained and independently compilable 28 | - Remove unused includes - they mislead future maintainers 29 | - If you use `uint32_t`, explicitly include `` even if another header does 30 | 31 | ## Code Quality Enforcement (Added Phase 8) 32 | 33 | **As of Phase 8, all code must pass clangd + clang-tidy analysis before commit:** 34 | 35 | ### Active Tooling 36 | - **clangd LSP server** configured via `.clangd` in project root 37 | - **Format on save** enabled in VSCode 38 | - **clang-tidy checks** enforce cert-*, bugprone-*, misc-*, portability-*, readability-* rules 39 | - **Include-cleaner** validates explicit include hygiene (see MissingIncludes/UnusedIncludes diagnostics) 40 | 41 | ### Writing New Code 42 | 1. Write code and save file - auto-formatting applies immediately 43 | 2. Address ALL clangd warnings shown in Problems panel 44 | 3. Fix include issues: add missing headers, remove unused ones 45 | 4. Only use NOLINT suppressions when absolutely necessary with explanatory comment 46 | 47 | ### NOLINT Usage Guidelines 48 | Use NOLINT suppressions **sparingly** and **only** with clear justification: 49 | 50 | ```c 51 | // NOLINTBEGIN(cert-err34-c) - sscanf is correct here: we check return value 52 | // and need exactly 2 hex chars, not all available hex like strtoul would read 53 | if (sscanf(hex + i * 2, "%02x", &byte) != 1) 54 | return 0; 55 | // NOLINTEND(cert-err34-c) 56 | ``` 57 | 58 | **Valid reasons for NOLINT:** 59 | - Platform-specific false positives (see `posix.c` in `.clangd` config) 60 | - Algorithm requires specific pattern clang-tidy flags incorrectly 61 | - Bitcoin protocol quirk that appears wrong but is intentional 62 | 63 | **Invalid reasons:** 64 | - "It's too hard to fix properly" 65 | - "The warning is annoying" 66 | - "I don't understand the warning" 67 | 68 | ### Compliance Status 69 | - **`include/` and `src/` folders**: ✅ Fully compliant - use as reference examples 70 | - **`test/` folder**: ⚠️ Partially compliant - being incrementally updated 71 | - `test/unit/test_block.c` - proper NOLINT usage for sscanf 72 | - `test/unit/test_block_validate.c` - include hygiene example 73 | 74 | **Key reference files:** 75 | - `src/platform/posix.c` - platform-specific suppression via .clangd config 76 | - `.clangd` - project-wide configuration with documented exceptions 77 | 78 | ## Context Loading Strategy 79 | 80 | To avoid context window limits and ensure focused implementation: 81 | 1. **DO NOT read full source files (.c)** for completed modules unless you are modifying them. 82 | 2. **Read `include/*.h`** to understand available APIs and types. 83 | 3. **Read ONLY the specific `.c` file** you are currently working on. 84 | 4. Treat other modules as **black boxes** based on their headers. 85 | 86 | ## Architecture Layers 87 | 88 | ``` 89 | Application Layer — Node operation, RPC, logging 90 | Protocol Layer — P2P messages, peers, mempool 91 | Consensus Engine — FROZEN CORE (block/tx validation, chain selection) 92 | Platform Abstraction — OS interface (sockets, threads, files, time, entropy) 93 | ``` 94 | 95 | Information flows down as function calls, up as return values. Lower layers know nothing of higher layers. 96 | 97 | ## Directory Structure 98 | 99 | ``` 100 | bitcoin-echo/ 101 | ├── src/ 102 | │ ├── platform/ ← OS abstraction (POSIX, Windows) 103 | │ ├── crypto/ ← SHA-256, RIPEMD-160, secp256k1 104 | │ ├── consensus/ ← FROZEN CORE - block/tx validation 105 | │ ├── protocol/ ← P2P networking, message handling 106 | │ └── app/ ← Node orchestration, RPC, logging 107 | ├── include/ ← Public headers 108 | ├── test/ 109 | │ ├── vectors/ ← Bitcoin Core test vectors 110 | │ └── unit/ ← Unit tests 111 | ├── docs/ ← Implementation notes 112 | ├── Makefile ← POSIX build 113 | └── build.bat ← Windows build 114 | ``` 115 | 116 | ## What NOT To Do 117 | 118 | - Don't add features beyond Bitcoin protocol as it exists today 119 | - Don't optimize at the expense of clarity 120 | - Don't use external libraries (embed everything) 121 | - Don't make the consensus engine touch I/O 122 | - Don't create abstractions for one-time operations 123 | - Don't add configuration options or runtime flags 124 | - Don't write wallet functionality 125 | 126 | ## Quick Reference 127 | 128 | **Signature verification seam:** `sig_verify.h` / `sig_verify.c` — the quantum succession boundary 129 | 130 | **Platform interface:** See whitepaper Appendix A for complete API 131 | 132 | **Supported soft forks:** P2SH, BIP-66, CLTV, CSV, SegWit, Taproot 133 | 134 | **Test vectors:** Embed Bitcoin Core's consensus test suite; 100% pass required 135 | 136 | ## Pre-Commit Quality Checklist (MANDATORY - Phase 8+) 137 | 138 | **Before committing ANY code, verify ALL of the following:** 139 | 140 | - [ ] **File saved** — Auto-formatting applied 141 | - [ ] **Zero clangd warnings** — Check VSCode Problems panel, address every warning 142 | - [ ] **Include hygiene verified** — All used symbols have headers explicitly included 143 | - [ ] **No unused includes** — Remove headers that aren't needed 144 | - [ ] **NOLINT justified** — Every suppression has clear explanatory comment 145 | - [ ] **Tests pass** — All unit tests passing 146 | 147 | **If you skip this checklist, the commit is INVALID and must be redone.** 148 | 149 | ## Session Completion Workflow 150 | 151 | **After completing each session and all tests pass:** 152 | 153 | 1. **Run quality checklist** — Verify all items in Pre-Commit Quality Checklist above 154 | 2. **Update roadmap** — Mark session complete in `bitcoinecho-org/ROADMAP.md` with status and test count 155 | 3. **Commit bitcoin-echo** — Commit implementation changes with descriptive message 156 | 4. **Commit bitcoinecho-org** — Commit roadmap update 157 | 5. **Push both repos** — Push changes to GitHub 158 | 6. **Pause and check in** — Wait for user confirmation before starting next session 159 | 160 | ## When Uncertain 161 | 162 | Consult in order: 163 | 1. The whitepaper (authoritative specification) 164 | 2. Bitcoin Core source (reference implementation) 165 | 3. BIPs (protocol documentation) 166 | 167 | When the whitepaper is silent, choose simplicity. 168 | -------------------------------------------------------------------------------- /test/unit/test_sha256.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — SHA-256 Test Vectors 3 | * 4 | * Test vectors from: 5 | * - NIST FIPS 180-4 examples 6 | * - NIST CAVP (Cryptographic Algorithm Validation Program) 7 | * - Bitcoin-specific test cases 8 | * 9 | * Build once. Build right. Stop. 10 | */ 11 | 12 | #include 13 | #include 14 | #include "sha256.h" 15 | #include "test_utils.h" 16 | 17 | 18 | /* 19 | * Run a single SHA-256 test. 20 | */ 21 | static void run_sha256_test(const char *name, 22 | const uint8_t *input, size_t input_len, 23 | const uint8_t expected[32]) 24 | { 25 | uint8_t result[32]; 26 | 27 | sha256(input, input_len, result); 28 | 29 | test_case(name); 30 | if (bytes_equal(result, expected, 32)) { 31 | test_pass(); 32 | } else { 33 | test_fail_bytes("hash mismatch", expected, result, 32); 34 | } 35 | } 36 | 37 | /* 38 | * Run a single SHA-256d test. 39 | */ 40 | static void run_sha256d_test(const char *name, 41 | const uint8_t *input, size_t input_len, 42 | const uint8_t expected[32]) 43 | { 44 | uint8_t result[32]; 45 | 46 | sha256d(input, input_len, result); 47 | 48 | test_case(name); 49 | if (bytes_equal(result, expected, 32)) { 50 | test_pass(); 51 | } else { 52 | test_fail_bytes("hash mismatch", expected, result, 32); 53 | } 54 | } 55 | 56 | /* 57 | * Test streaming interface produces same result as one-shot. 58 | */ 59 | static void run_streaming_test(const char *name, 60 | const uint8_t *input, size_t input_len) 61 | { 62 | uint8_t oneshot[32]; 63 | uint8_t streaming[32]; 64 | sha256_ctx_t ctx; 65 | size_t i; 66 | char test_name[256]; 67 | 68 | /* One-shot */ 69 | sha256(input, input_len, oneshot); 70 | 71 | /* Streaming: feed one byte at a time */ 72 | sha256_init(&ctx); 73 | for (i = 0; i < input_len; i++) { 74 | sha256_update(&ctx, input + i, 1); 75 | } 76 | sha256_final(&ctx, streaming); 77 | 78 | snprintf(test_name, sizeof(test_name), "%s (streaming)", name); 79 | test_case(test_name); 80 | if (bytes_equal(oneshot, streaming, 32)) { 81 | test_pass(); 82 | } else { 83 | test_fail_bytes("streaming mismatch", oneshot, streaming, 32); 84 | } 85 | } 86 | 87 | int main(void) 88 | { 89 | test_suite_begin("SHA-256 Test Suite"); 90 | 91 | /* 92 | * NIST FIPS 180-4 Example: "abc" 93 | */ 94 | { 95 | const uint8_t input[] = "abc"; 96 | const uint8_t expected[32] = { 97 | 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 98 | 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 99 | 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 100 | 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad 101 | }; 102 | run_sha256_test("NIST abc", input, 3, expected); 103 | run_streaming_test("NIST abc", input, 3); 104 | } 105 | 106 | /* 107 | * NIST FIPS 180-4 Example: empty string 108 | */ 109 | { 110 | const uint8_t *input = (const uint8_t *)""; 111 | const uint8_t expected[32] = { 112 | 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 113 | 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 114 | 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 115 | 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 116 | }; 117 | run_sha256_test("Empty string", input, 0, expected); 118 | } 119 | 120 | /* 121 | * NIST FIPS 180-4 Example: 448 bits (56 bytes) 122 | * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 123 | */ 124 | { 125 | const uint8_t input[] = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; 126 | const uint8_t expected[32] = { 127 | 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 128 | 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, 129 | 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 130 | 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 131 | }; 132 | run_sha256_test("NIST 448 bits", input, 56, expected); 133 | run_streaming_test("NIST 448 bits", input, 56); 134 | } 135 | 136 | /* 137 | * NIST FIPS 180-4 Example: 896 bits (112 bytes) 138 | * "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn 139 | * hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" 140 | */ 141 | { 142 | const uint8_t input[] = 143 | "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" 144 | "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"; 145 | const uint8_t expected[32] = { 146 | 0xcf, 0x5b, 0x16, 0xa7, 0x78, 0xaf, 0x83, 0x80, 147 | 0x03, 0x6c, 0xe5, 0x9e, 0x7b, 0x04, 0x92, 0x37, 148 | 0x0b, 0x24, 0x9b, 0x11, 0xe8, 0xf0, 0x7a, 0x51, 149 | 0xaf, 0xac, 0x45, 0x03, 0x7a, 0xfe, 0xe9, 0xd1 150 | }; 151 | run_sha256_test("NIST 896 bits", input, 112, expected); 152 | run_streaming_test("NIST 896 bits", input, 112); 153 | } 154 | 155 | /* 156 | * SHA-256d test: empty string 157 | * SHA256d("") = SHA256(SHA256("")) 158 | */ 159 | { 160 | const uint8_t *input = (const uint8_t *)""; 161 | const uint8_t expected[32] = { 162 | 0x5d, 0xf6, 0xe0, 0xe2, 0x76, 0x13, 0x59, 0xd3, 163 | 0x0a, 0x82, 0x75, 0x05, 0x8e, 0x29, 0x9f, 0xcc, 164 | 0x03, 0x81, 0x53, 0x45, 0x45, 0xf5, 0x5c, 0xf4, 165 | 0x3e, 0x41, 0x98, 0x3f, 0x5d, 0x4c, 0x94, 0x56 166 | }; 167 | run_sha256d_test("SHA256d empty", input, 0, expected); 168 | } 169 | 170 | /* 171 | * Bitcoin Genesis Block Header Hash 172 | * The genesis block header (80 bytes) hashes to the famous 173 | * 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f 174 | * (displayed in little-endian as is convention) 175 | */ 176 | { 177 | /* Genesis block header (80 bytes) */ 178 | const uint8_t genesis_header[80] = { 179 | 0x01, 0x00, 0x00, 0x00, /* version */ 180 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 181 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 182 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 183 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* prev block (zeros) */ 184 | 0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, 185 | 0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, 186 | 0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, 187 | 0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, /* merkle root */ 188 | 0x29, 0xab, 0x5f, 0x49, /* timestamp */ 189 | 0xff, 0xff, 0x00, 0x1d, /* bits */ 190 | 0x1d, 0xac, 0x2b, 0x7c /* nonce */ 191 | }; 192 | /* Expected hash (big-endian, as computed) */ 193 | const uint8_t expected[32] = { 194 | 0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, 195 | 0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, 196 | 0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, 197 | 0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00 198 | }; 199 | run_sha256d_test("Bitcoin Genesis Block", genesis_header, 80, expected); 200 | } 201 | 202 | test_suite_end(); 203 | 204 | return test_global_summary(); 205 | } 206 | -------------------------------------------------------------------------------- /test/unit/test_ripemd160.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — RIPEMD-160 Test Vectors 3 | * 4 | * Test vectors from: 5 | * - Original RIPEMD-160 publication (Dobbertin et al., 1996) 6 | * - Bitcoin-specific HASH160 test cases 7 | * 8 | * Build once. Build right. Stop. 9 | */ 10 | 11 | #include 12 | #include 13 | #include "ripemd160.h" 14 | #include "test_utils.h" 15 | 16 | 17 | /* 18 | * Run a single RIPEMD-160 test. 19 | */ 20 | static void run_ripemd160_test(const char *name, 21 | const uint8_t *input, size_t input_len, 22 | const uint8_t expected[20]) 23 | { 24 | uint8_t result[20]; 25 | 26 | ripemd160(input, input_len, result); 27 | 28 | test_case(name); 29 | if (bytes_equal(result, expected, 20)) { 30 | test_pass(); 31 | } else { 32 | test_fail_bytes("hash mismatch", expected, result, 20); 33 | } 34 | } 35 | 36 | /* 37 | * Run a single HASH160 test. 38 | */ 39 | static void run_hash160_test(const char *name, 40 | const uint8_t *input, size_t input_len, 41 | const uint8_t expected[20]) 42 | { 43 | uint8_t result[20]; 44 | 45 | hash160(input, input_len, result); 46 | 47 | test_case(name); 48 | if (bytes_equal(result, expected, 20)) { 49 | test_pass(); 50 | } else { 51 | test_fail_bytes("hash mismatch", expected, result, 20); 52 | } 53 | } 54 | 55 | /* 56 | * Test streaming interface produces same result as one-shot. 57 | */ 58 | static void run_streaming_test(const char *name, 59 | const uint8_t *input, size_t input_len) 60 | { 61 | uint8_t oneshot[20]; 62 | uint8_t streaming[20]; 63 | ripemd160_ctx_t ctx; 64 | size_t i; 65 | char test_name[256]; 66 | 67 | /* One-shot */ 68 | ripemd160(input, input_len, oneshot); 69 | 70 | /* Streaming: feed one byte at a time */ 71 | ripemd160_init(&ctx); 72 | for (i = 0; i < input_len; i++) { 73 | ripemd160_update(&ctx, input + i, 1); 74 | } 75 | ripemd160_final(&ctx, streaming); 76 | 77 | snprintf(test_name, sizeof(test_name), "%s (streaming)", name); 78 | test_case(test_name); 79 | if (bytes_equal(oneshot, streaming, 20)) { 80 | test_pass(); 81 | } else { 82 | test_fail_bytes("streaming mismatch", oneshot, streaming, 20); 83 | } 84 | } 85 | 86 | int main(void) 87 | { 88 | test_suite_begin("RIPEMD-160 Test Suite"); 89 | 90 | /* 91 | * Official RIPEMD-160 test vectors from the original publication. 92 | * Source: https://homes.esat.kuleuven.be/~bosselae/ripemd160.html 93 | */ 94 | 95 | /* Empty string */ 96 | { 97 | const uint8_t *input = (const uint8_t *)""; 98 | const uint8_t expected[20] = { 99 | 0x9c, 0x11, 0x85, 0xa5, 0xc5, 0xe9, 0xfc, 0x54, 0x61, 0x28, 100 | 0x08, 0x97, 0x7e, 0xe8, 0xf5, 0x48, 0xb2, 0x25, 0x8d, 0x31 101 | }; 102 | run_ripemd160_test("Empty string", input, 0, expected); 103 | } 104 | 105 | /* "a" */ 106 | { 107 | const uint8_t input[] = "a"; 108 | const uint8_t expected[20] = { 109 | 0x0b, 0xdc, 0x9d, 0x2d, 0x25, 0x6b, 0x3e, 0xe9, 0xda, 0xae, 110 | 0x34, 0x7b, 0xe6, 0xf4, 0xdc, 0x83, 0x5a, 0x46, 0x7f, 0xfe 111 | }; 112 | run_ripemd160_test("Single 'a'", input, 1, expected); 113 | run_streaming_test("Single 'a'", input, 1); 114 | } 115 | 116 | /* "abc" */ 117 | { 118 | const uint8_t input[] = "abc"; 119 | const uint8_t expected[20] = { 120 | 0x8e, 0xb2, 0x08, 0xf7, 0xe0, 0x5d, 0x98, 0x7a, 0x9b, 0x04, 121 | 0x4a, 0x8e, 0x98, 0xc6, 0xb0, 0x87, 0xf1, 0x5a, 0x0b, 0xfc 122 | }; 123 | run_ripemd160_test("'abc'", input, 3, expected); 124 | run_streaming_test("'abc'", input, 3); 125 | } 126 | 127 | /* "message digest" */ 128 | { 129 | const uint8_t input[] = "message digest"; 130 | const uint8_t expected[20] = { 131 | 0x5d, 0x06, 0x89, 0xef, 0x49, 0xd2, 0xfa, 0xe5, 0x72, 0xb8, 132 | 0x81, 0xb1, 0x23, 0xa8, 0x5f, 0xfa, 0x21, 0x59, 0x5f, 0x36 133 | }; 134 | run_ripemd160_test("'message digest'", input, 14, expected); 135 | run_streaming_test("'message digest'", input, 14); 136 | } 137 | 138 | /* "abcdefghijklmnopqrstuvwxyz" */ 139 | { 140 | const uint8_t input[] = "abcdefghijklmnopqrstuvwxyz"; 141 | const uint8_t expected[20] = { 142 | 0xf7, 0x1c, 0x27, 0x10, 0x9c, 0x69, 0x2c, 0x1b, 0x56, 0xbb, 143 | 0xdc, 0xeb, 0x5b, 0x9d, 0x28, 0x65, 0xb3, 0x70, 0x8d, 0xbc 144 | }; 145 | run_ripemd160_test("Lowercase alphabet", input, 26, expected); 146 | run_streaming_test("Lowercase alphabet", input, 26); 147 | } 148 | 149 | /* "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" */ 150 | { 151 | const uint8_t input[] = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; 152 | const uint8_t expected[20] = { 153 | 0x12, 0xa0, 0x53, 0x38, 0x4a, 0x9c, 0x0c, 0x88, 0xe4, 0x05, 154 | 0xa0, 0x6c, 0x27, 0xdc, 0xf4, 0x9a, 0xda, 0x62, 0xeb, 0x2b 155 | }; 156 | run_ripemd160_test("448 bits", input, 56, expected); 157 | run_streaming_test("448 bits", input, 56); 158 | } 159 | 160 | /* "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" */ 161 | { 162 | const uint8_t input[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 163 | const uint8_t expected[20] = { 164 | 0xb0, 0xe2, 0x0b, 0x6e, 0x31, 0x16, 0x64, 0x02, 0x86, 0xed, 165 | 0x3a, 0x87, 0xa5, 0x71, 0x30, 0x79, 0xb2, 0x1f, 0x51, 0x89 166 | }; 167 | run_ripemd160_test("Alphanumeric", input, 62, expected); 168 | run_streaming_test("Alphanumeric", input, 62); 169 | } 170 | 171 | /* 8 copies of "1234567890" */ 172 | { 173 | const uint8_t input[] = "12345678901234567890123456789012345678901234567890123456789012345678901234567890"; 174 | const uint8_t expected[20] = { 175 | 0x9b, 0x75, 0x2e, 0x45, 0x57, 0x3d, 0x4b, 0x39, 0xf4, 0xdb, 176 | 0xd3, 0x32, 0x3c, 0xab, 0x82, 0xbf, 0x63, 0x32, 0x6b, 0xfb 177 | }; 178 | run_ripemd160_test("8x '1234567890'", input, 80, expected); 179 | run_streaming_test("8x '1234567890'", input, 80); 180 | } 181 | 182 | test_section("HASH160 Tests (RIPEMD160(SHA256(x)))"); 183 | 184 | /* 185 | * HASH160 test: empty string 186 | * SHA256("") = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 187 | * RIPEMD160(above) = b472a266d0bd89c13706a4132ccfb16f7c3b9fcb 188 | */ 189 | { 190 | const uint8_t *input = (const uint8_t *)""; 191 | const uint8_t expected[20] = { 192 | 0xb4, 0x72, 0xa2, 0x66, 0xd0, 0xbd, 0x89, 0xc1, 0x37, 0x06, 193 | 0xa4, 0x13, 0x2c, 0xcf, 0xb1, 0x6f, 0x7c, 0x3b, 0x9f, 0xcb 194 | }; 195 | run_hash160_test("HASH160 empty", input, 0, expected); 196 | } 197 | 198 | /* 199 | * HASH160 of a compressed public key (typical Bitcoin use case). 200 | * This is a made-up test key, not a real Bitcoin key. 201 | * Public key: 02 + 32 bytes of 0x01 202 | */ 203 | { 204 | const uint8_t pubkey[33] = { 205 | 0x02, 206 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 207 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 208 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 209 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 210 | }; 211 | /* Computed: HASH160(pubkey) = 9b596d772a3bfe0335f36c38357f026221212c90 */ 212 | const uint8_t expected[20] = { 213 | 0x9b, 0x59, 0x6d, 0x77, 0x2a, 0x3b, 0xfe, 0x03, 0x35, 0xf3, 214 | 0x6c, 0x38, 0x35, 0x7f, 0x02, 0x62, 0x21, 0x21, 0x2c, 0x90 215 | }; 216 | run_hash160_test("HASH160 pubkey", pubkey, 33, expected); 217 | } 218 | 219 | test_suite_end(); 220 | 221 | return test_global_summary(); 222 | } 223 | -------------------------------------------------------------------------------- /include/block.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Block Data Structures 3 | * 4 | * This header defines the structures for Bitcoin blocks, including 5 | * the 80-byte block header and full block with transactions. 6 | * 7 | * Block header structure (80 bytes): 8 | * - version (4 bytes, signed) 9 | * - previous block hash (32 bytes) 10 | * - merkle root (32 bytes) 11 | * - timestamp (4 bytes, Unix time) 12 | * - bits (4 bytes, compact difficulty target) 13 | * - nonce (4 bytes) 14 | * 15 | * Full block: 16 | * - header (80 bytes) 17 | * - transaction count (varint) 18 | * - transactions (variable) 19 | * 20 | * Build once. Build right. Stop. 21 | */ 22 | 23 | #ifndef ECHO_BLOCK_H 24 | #define ECHO_BLOCK_H 25 | 26 | #include "echo_types.h" 27 | #include "tx.h" 28 | #include 29 | 30 | /* 31 | * Block header size in bytes. 32 | */ 33 | #define BLOCK_HEADER_SIZE 80 34 | 35 | /* 36 | * Block size limits. 37 | */ 38 | #define BLOCK_MAX_SIZE 4000000 /* Max block size in bytes (4MB) */ 39 | #define BLOCK_MAX_WEIGHT 4000000 /* Max block weight (4M weight units) */ 40 | #define BLOCK_MAX_SIGOPS 80000 /* Max signature operations */ 41 | 42 | /* 43 | * Genesis block constants (mainnet). 44 | */ 45 | #define GENESIS_BLOCK_VERSION 1 46 | #define GENESIS_BLOCK_TIMESTAMP 1231006505 47 | #define GENESIS_BLOCK_BITS 0x1d00ffff 48 | #define GENESIS_BLOCK_NONCE 2083236893 49 | 50 | /* 51 | * Difficulty adjustment constants. 52 | */ 53 | #define DIFFICULTY_ADJUSTMENT_INTERVAL 2016 /* Blocks between adjustments */ 54 | #define TARGET_TIMESPAN 1209600 /* 2 weeks in seconds */ 55 | #define TARGET_SPACING 600 /* 10 minutes in seconds */ 56 | 57 | /* 58 | * Block header structure. 59 | * Exactly 80 bytes when serialized. 60 | */ 61 | typedef struct { 62 | int32_t version; /* Block version (signed per protocol) */ 63 | hash256_t prev_hash; /* Hash of previous block header */ 64 | hash256_t merkle_root; /* Merkle root of transactions */ 65 | uint32_t timestamp; /* Unix timestamp */ 66 | uint32_t bits; /* Compact difficulty target */ 67 | uint32_t nonce; /* Nonce for proof-of-work */ 68 | } block_header_t; 69 | 70 | /* 71 | * Full block structure. 72 | */ 73 | typedef struct { 74 | block_header_t header; /* 80-byte header */ 75 | tx_t *txs; /* Array of transactions (owned) */ 76 | size_t tx_count; /* Number of transactions */ 77 | } block_t; 78 | 79 | /* 80 | * Initialize a block structure to empty/safe state. 81 | * 82 | * Parameters: 83 | * block - Block to initialize 84 | */ 85 | void block_init(block_t *block); 86 | 87 | /* 88 | * Free all memory owned by a block. 89 | * 90 | * Parameters: 91 | * block - Block to free (structure itself is not freed) 92 | */ 93 | void block_free(block_t *block); 94 | 95 | /* 96 | * Parse a block header from raw bytes. 97 | * 98 | * Parameters: 99 | * data - Raw header bytes (must be at least 80 bytes) 100 | * data_len - Length of data 101 | * header - Output: parsed header 102 | * 103 | * Returns: 104 | * ECHO_OK on success 105 | * ECHO_ERR_NULL_PARAM if data or header is NULL 106 | * ECHO_ERR_TRUNCATED if data_len < 80 107 | */ 108 | echo_result_t block_header_parse(const uint8_t *data, size_t data_len, 109 | block_header_t *header); 110 | 111 | /* 112 | * Serialize a block header to bytes. 113 | * 114 | * Parameters: 115 | * header - Header to serialize 116 | * buf - Output buffer (must be at least 80 bytes) 117 | * buf_len - Size of output buffer 118 | * 119 | * Returns: 120 | * ECHO_OK on success 121 | * ECHO_ERR_NULL_PARAM if header or buf is NULL 122 | * ECHO_ERR_BUFFER_TOO_SMALL if buf_len < 80 123 | */ 124 | echo_result_t block_header_serialize(const block_header_t *header, uint8_t *buf, 125 | size_t buf_len); 126 | 127 | /* 128 | * Compute the block hash (hash of the 80-byte header). 129 | * 130 | * The block hash is SHA256d of the serialized header. 131 | * Note: The hash is stored in little-endian byte order internally, 132 | * but displayed in big-endian (reversed) format by convention. 133 | * 134 | * Parameters: 135 | * header - Block header 136 | * hash - Output: 32-byte block hash 137 | * 138 | * Returns: 139 | * ECHO_OK on success 140 | * ECHO_ERR_NULL_PARAM if header or hash is NULL 141 | */ 142 | echo_result_t block_header_hash(const block_header_t *header, hash256_t *hash); 143 | 144 | /* 145 | * Parse a full block from raw bytes. 146 | * 147 | * This function allocates memory for transactions. 148 | * Call block_free() when done. 149 | * 150 | * Parameters: 151 | * data - Raw block bytes 152 | * data_len - Length of data 153 | * block - Output: parsed block 154 | * consumed - Output: bytes consumed (may be NULL) 155 | * 156 | * Returns: 157 | * ECHO_OK on success 158 | * ECHO_ERR_NULL_PARAM if data or block is NULL 159 | * ECHO_ERR_TRUNCATED if data too short 160 | * ECHO_ERR_INVALID_FORMAT if block malformed 161 | * ECHO_ERR_OUT_OF_MEMORY if allocation fails 162 | */ 163 | echo_result_t block_parse(const uint8_t *data, size_t data_len, block_t *block, 164 | size_t *consumed); 165 | 166 | /* 167 | * Compute the serialized size of a full block. 168 | * 169 | * Parameters: 170 | * block - Block to measure 171 | * 172 | * Returns: 173 | * Serialized size in bytes, or 0 if block is NULL 174 | */ 175 | size_t block_serialize_size(const block_t *block); 176 | 177 | /* 178 | * Serialize a full block to bytes. 179 | * 180 | * Parameters: 181 | * block - Block to serialize 182 | * buf - Output buffer 183 | * buf_len - Size of output buffer 184 | * written - Output: bytes written (may be NULL) 185 | * 186 | * Returns: 187 | * ECHO_OK on success 188 | * ECHO_ERR_NULL_PARAM if block or buf is NULL 189 | * ECHO_ERR_BUFFER_TOO_SMALL if buffer insufficient 190 | */ 191 | echo_result_t block_serialize(const block_t *block, uint8_t *buf, 192 | size_t buf_len, size_t *written); 193 | 194 | /* 195 | * Compute block weight. 196 | * 197 | * Weight = (base_size * 3) + total_size 198 | * Where base_size excludes witness data from all transactions. 199 | * 200 | * Parameters: 201 | * block - Block to measure 202 | * 203 | * Returns: 204 | * Block weight in weight units, or 0 if block is NULL 205 | */ 206 | size_t block_weight(const block_t *block); 207 | 208 | /* 209 | * Convert compact "bits" field to 256-bit target. 210 | * 211 | * The bits field encodes the target as: 212 | * target = mantissa * 256^(exponent - 3) 213 | * where exponent is the high byte and mantissa is the low 3 bytes. 214 | * 215 | * Parameters: 216 | * bits - Compact target representation 217 | * target - Output: 256-bit target (32 bytes, little-endian) 218 | * 219 | * Returns: 220 | * ECHO_OK on success 221 | * ECHO_ERR_NULL_PARAM if target is NULL 222 | */ 223 | echo_result_t block_bits_to_target(uint32_t bits, hash256_t *target); 224 | 225 | /* 226 | * Convert 256-bit target to compact "bits" field. 227 | * 228 | * Parameters: 229 | * target - 256-bit target (32 bytes, little-endian) 230 | * bits - Output: compact target representation 231 | * 232 | * Returns: 233 | * ECHO_OK on success 234 | * ECHO_ERR_NULL_PARAM if target or bits is NULL 235 | */ 236 | echo_result_t block_target_to_bits(const hash256_t *target, uint32_t *bits); 237 | 238 | /* 239 | * Check if a block hash meets the target difficulty. 240 | * 241 | * The hash (interpreted as a little-endian 256-bit number) must be 242 | * less than or equal to the target. 243 | * 244 | * Parameters: 245 | * hash - Block hash 246 | * target - Target threshold 247 | * 248 | * Returns: 249 | * ECHO_TRUE if hash <= target, ECHO_FALSE otherwise 250 | */ 251 | echo_bool_t block_hash_meets_target(const hash256_t *hash, 252 | const hash256_t *target); 253 | 254 | /* 255 | * Get the genesis block header (mainnet). 256 | * 257 | * Parameters: 258 | * header - Output: genesis block header 259 | */ 260 | void block_genesis_header(block_header_t *header); 261 | 262 | #endif /* ECHO_BLOCK_H */ 263 | -------------------------------------------------------------------------------- /src/storage/blocks.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Block File Storage Implementation 3 | * 4 | * Append-only block files compatible with Bitcoin Core format. 5 | * 6 | * Build once. Build right. Stop. 7 | */ 8 | 9 | #include "blocks_storage.h" 10 | #include "echo_config.h" 11 | #include "echo_types.h" 12 | #include "platform.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | /* 19 | * Scan existing block files to find the current write position. 20 | * This is called during initialization to resume writing where we left off. 21 | */ 22 | static echo_result_t scan_block_files(block_file_manager_t *mgr) { 23 | uint32_t file_index = 0; 24 | char path[512]; 25 | 26 | /* Find the highest numbered block file */ 27 | while (1) { 28 | block_storage_get_path(mgr, file_index, path); 29 | 30 | if (!plat_file_exists(path)) { 31 | /* This file doesn't exist - we've found the end */ 32 | if (file_index == 0) { 33 | /* No files exist yet */ 34 | mgr->current_file_index = 0; 35 | mgr->current_file_offset = 0; 36 | return ECHO_OK; 37 | } else { 38 | /* Previous file is the last one */ 39 | file_index--; 40 | break; 41 | } 42 | } 43 | 44 | file_index++; 45 | 46 | /* Sanity check: don't scan more than 100,000 files */ 47 | if (file_index > 100000) { 48 | return ECHO_ERR_PLATFORM_IO; 49 | } 50 | } 51 | 52 | /* Open the last file and find its size */ 53 | block_storage_get_path(mgr, file_index, path); 54 | 55 | FILE *f = fopen(path, "rb"); 56 | if (!f) { 57 | return ECHO_ERR_PLATFORM_IO; 58 | } 59 | 60 | /* Seek to end to get file size */ 61 | if (fseek(f, 0, SEEK_END) != 0) { 62 | fclose(f); 63 | return ECHO_ERR_PLATFORM_IO; 64 | } 65 | 66 | long size = ftell(f); 67 | fclose(f); 68 | 69 | if (size < 0 || size > (long)BLOCK_FILE_MAX_SIZE) { 70 | return ECHO_ERR_PLATFORM_IO; 71 | } 72 | 73 | /* If the last file is at max size, start a new file */ 74 | if (size >= (long)BLOCK_FILE_MAX_SIZE) { 75 | mgr->current_file_index = file_index + 1; 76 | mgr->current_file_offset = 0; 77 | } else { 78 | mgr->current_file_index = file_index; 79 | mgr->current_file_offset = (uint32_t)size; 80 | } 81 | 82 | return ECHO_OK; 83 | } 84 | 85 | /* 86 | * Initialize block file manager. 87 | */ 88 | echo_result_t block_storage_init(block_file_manager_t *mgr, 89 | const char *data_dir) { 90 | if (!mgr || !data_dir) { 91 | return ECHO_ERR_NULL_PARAM; 92 | } 93 | 94 | /* Copy data directory path */ 95 | size_t len = strlen(data_dir); 96 | if (len >= sizeof(mgr->data_dir)) { 97 | return ECHO_ERR_BUFFER_TOO_SMALL; 98 | } 99 | memcpy(mgr->data_dir, data_dir, len + 1); 100 | 101 | /* Create blocks directory */ 102 | char blocks_dir[512]; 103 | snprintf(blocks_dir, sizeof(blocks_dir), "%s/%s", data_dir, ECHO_BLOCKS_DIR); 104 | 105 | if (plat_dir_create(blocks_dir) != PLAT_OK) { 106 | return ECHO_ERR_PLATFORM_IO; 107 | } 108 | 109 | /* Scan existing files to find current position */ 110 | return scan_block_files(mgr); 111 | } 112 | 113 | /* 114 | * Get path to block file. 115 | */ 116 | void block_storage_get_path(const block_file_manager_t *mgr, 117 | uint32_t file_index, char *path_out) { 118 | snprintf(path_out, 512, "%s/%s/blk%05u.dat", mgr->data_dir, ECHO_BLOCKS_DIR, 119 | file_index); 120 | } 121 | 122 | /* 123 | * Write a block to disk. 124 | */ 125 | echo_result_t block_storage_write(block_file_manager_t *mgr, 126 | const uint8_t *block_data, 127 | uint32_t block_size, 128 | block_file_pos_t *pos_out) { 129 | if (!mgr || !block_data || !pos_out) { 130 | return ECHO_ERR_NULL_PARAM; 131 | } 132 | 133 | /* Check if we need to start a new file */ 134 | uint32_t record_size = BLOCK_FILE_RECORD_HEADER_SIZE + block_size; 135 | 136 | if (mgr->current_file_offset + record_size > BLOCK_FILE_MAX_SIZE) { 137 | /* Start new file */ 138 | mgr->current_file_index++; 139 | mgr->current_file_offset = 0; 140 | } 141 | 142 | /* Get path to current file */ 143 | char path[512]; 144 | block_storage_get_path(mgr, mgr->current_file_index, path); 145 | 146 | /* Open file for appending */ 147 | FILE *f = fopen(path, "ab"); 148 | if (!f) { 149 | return ECHO_ERR_PLATFORM_IO; 150 | } 151 | 152 | /* Write magic bytes (little-endian) */ 153 | uint32_t magic = ECHO_NETWORK_MAGIC; 154 | uint8_t magic_bytes[4] = {(magic >> 0) & 0xFF, (magic >> 8) & 0xFF, 155 | (magic >> 16) & 0xFF, (magic >> 24) & 0xFF}; 156 | 157 | if (fwrite(magic_bytes, 1, 4, f) != 4) { 158 | fclose(f); 159 | return ECHO_ERR_PLATFORM_IO; 160 | } 161 | 162 | /* Write block size (little-endian) */ 163 | uint8_t size_bytes[4] = {(block_size >> 0) & 0xFF, (block_size >> 8) & 0xFF, 164 | (block_size >> 16) & 0xFF, 165 | (block_size >> 24) & 0xFF}; 166 | 167 | if (fwrite(size_bytes, 1, 4, f) != 4) { 168 | fclose(f); 169 | return ECHO_ERR_PLATFORM_IO; 170 | } 171 | 172 | /* Write block data */ 173 | if (fwrite(block_data, 1, block_size, f) != block_size) { 174 | fclose(f); 175 | return ECHO_ERR_PLATFORM_IO; 176 | } 177 | 178 | /* Flush to disk */ 179 | if (fflush(f) != 0) { 180 | fclose(f); 181 | return ECHO_ERR_PLATFORM_IO; 182 | } 183 | 184 | fclose(f); 185 | 186 | /* Return position */ 187 | pos_out->file_index = mgr->current_file_index; 188 | pos_out->file_offset = mgr->current_file_offset; 189 | 190 | /* Update current position */ 191 | mgr->current_file_offset += record_size; 192 | 193 | return ECHO_OK; 194 | } 195 | 196 | /* 197 | * Read a block from disk. 198 | */ 199 | echo_result_t block_storage_read(block_file_manager_t *mgr, 200 | block_file_pos_t pos, uint8_t **block_out, 201 | uint32_t *size_out) { 202 | if (!mgr || !block_out || !size_out) { 203 | return ECHO_ERR_NULL_PARAM; 204 | } 205 | 206 | /* Initialize outputs */ 207 | *block_out = NULL; 208 | *size_out = 0; 209 | 210 | /* Get path to file */ 211 | char path[512]; 212 | block_storage_get_path(mgr, pos.file_index, path); 213 | 214 | /* Open file for reading */ 215 | FILE *f = fopen(path, "rb"); 216 | if (!f) { 217 | return ECHO_ERR_NOT_FOUND; 218 | } 219 | 220 | /* Seek to position */ 221 | if (fseek(f, pos.file_offset, SEEK_SET) != 0) { 222 | fclose(f); 223 | return ECHO_ERR_PLATFORM_IO; 224 | } 225 | 226 | /* Read and verify magic bytes */ 227 | uint8_t magic_bytes[4]; 228 | if (fread(magic_bytes, 1, 4, f) != 4) { 229 | fclose(f); 230 | return ECHO_ERR_TRUNCATED; 231 | } 232 | 233 | uint32_t magic = 234 | ((uint32_t)magic_bytes[0] << 0) | ((uint32_t)magic_bytes[1] << 8) | 235 | ((uint32_t)magic_bytes[2] << 16) | ((uint32_t)magic_bytes[3] << 24); 236 | 237 | if (magic != ECHO_NETWORK_MAGIC) { 238 | fclose(f); 239 | return ECHO_ERR_INVALID_FORMAT; 240 | } 241 | 242 | /* Read block size */ 243 | uint8_t size_bytes[4]; 244 | if (fread(size_bytes, 1, 4, f) != 4) { 245 | fclose(f); 246 | return ECHO_ERR_TRUNCATED; 247 | } 248 | 249 | uint32_t block_size = 250 | ((uint32_t)size_bytes[0] << 0) | ((uint32_t)size_bytes[1] << 8) | 251 | ((uint32_t)size_bytes[2] << 16) | ((uint32_t)size_bytes[3] << 24); 252 | 253 | /* Sanity check block size */ 254 | if (block_size == 0 || block_size > ECHO_MAX_BLOCK_SIZE * 4) { 255 | fclose(f); 256 | return ECHO_ERR_INVALID_FORMAT; 257 | } 258 | 259 | /* Allocate buffer for block */ 260 | uint8_t *block = (uint8_t *)malloc(block_size); 261 | if (!block) { 262 | fclose(f); 263 | return ECHO_ERR_OUT_OF_MEMORY; 264 | } 265 | 266 | /* Read block data */ 267 | if (fread(block, 1, block_size, f) != block_size) { 268 | free(block); 269 | fclose(f); 270 | return ECHO_ERR_TRUNCATED; 271 | } 272 | 273 | fclose(f); 274 | 275 | /* Success */ 276 | *block_out = block; 277 | *size_out = block_size; 278 | return ECHO_OK; 279 | } 280 | -------------------------------------------------------------------------------- /test/unit/test_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Test Utility Framework Implementation 3 | * 4 | * Build once. Build right. Stop. 5 | */ 6 | 7 | #include "test_utils.h" 8 | #include 9 | #include 10 | 11 | /* 12 | * ANSI Color Codes 13 | * ================ 14 | * Terminal colors for beautiful test output. 15 | */ 16 | #define COLOR_RESET "\033[0m" 17 | #define COLOR_GREEN "\033[32m" 18 | #define COLOR_RED "\033[31m" 19 | #define COLOR_CYAN "\033[36m" 20 | #define COLOR_YELLOW "\033[33m" 21 | #define COLOR_BOLD "\033[1m" 22 | 23 | /* 24 | * Test State Tracking 25 | * =================== 26 | * Track current suite and global statistics. 27 | */ 28 | 29 | /* Current suite state */ 30 | static const char *current_suite = NULL; 31 | static int suite_tests_run = 0; 32 | static int suite_tests_passed = 0; 33 | 34 | /* Global state (across all suites) */ 35 | static int global_tests_run = 0; 36 | static int global_tests_passed = 0; 37 | static int global_suites_run = 0; 38 | static int global_suites_passed = 0; 39 | 40 | /* Current test case name (for deferred output) */ 41 | static const char *current_test = NULL; 42 | 43 | /* 44 | * Helper Functions 45 | * ================ 46 | */ 47 | 48 | /* Print bytes as hex (for failure output) */ 49 | static void print_hex(const uint8_t *data, size_t len) 50 | { 51 | size_t i; 52 | for (i = 0; i < len; i++) { 53 | printf("%02x", data[i]); 54 | } 55 | } 56 | 57 | /* 58 | * Compare two byte arrays and return 1 if equal, 0 otherwise. 59 | */ 60 | int bytes_equal(const uint8_t *a, const uint8_t *b, size_t len) 61 | { 62 | size_t i; 63 | for (i = 0; i < len; i++) { 64 | if (a[i] != b[i]) { 65 | return 0; 66 | } 67 | } 68 | return 1; 69 | } 70 | 71 | /* 72 | * Public API Implementation 73 | * ========================== 74 | */ 75 | 76 | void test_suite_begin(const char *suite_name) 77 | { 78 | current_suite = suite_name; 79 | suite_tests_run = 0; 80 | suite_tests_passed = 0; 81 | global_suites_run++; 82 | 83 | printf("%s%s%s\n", COLOR_BOLD, suite_name, COLOR_RESET); 84 | 85 | /* Print separator of equal length to suite name */ 86 | size_t i; 87 | size_t len = strlen(suite_name); 88 | for (i = 0; i < len; i++) { 89 | printf("="); 90 | } 91 | printf("\n\n"); 92 | } 93 | 94 | void test_suite_end(void) 95 | { 96 | if (current_suite == NULL) { 97 | return; 98 | } 99 | 100 | /* Update global counters */ 101 | global_tests_run += suite_tests_run; 102 | global_tests_passed += suite_tests_passed; 103 | 104 | if (suite_tests_passed == suite_tests_run) { 105 | global_suites_passed++; 106 | } 107 | 108 | /* Print suite results */ 109 | printf("\n"); 110 | if (suite_tests_passed == suite_tests_run) { 111 | printf("%sResults: %d/%d tests passed%s\n", 112 | COLOR_GREEN, suite_tests_passed, suite_tests_run, COLOR_RESET); 113 | } else { 114 | printf("%sResults: %d/%d tests passed (%d failed)%s\n", 115 | COLOR_RED, suite_tests_passed, suite_tests_run, 116 | suite_tests_run - suite_tests_passed, COLOR_RESET); 117 | } 118 | printf("\n"); 119 | 120 | current_suite = NULL; 121 | } 122 | 123 | void test_case(const char *test_name) 124 | { 125 | current_test = test_name; 126 | suite_tests_run++; 127 | } 128 | 129 | void test_pass(void) 130 | { 131 | if (current_test == NULL) { 132 | return; 133 | } 134 | 135 | suite_tests_passed++; 136 | printf(" %s[PASS]%s %s\n", COLOR_GREEN, COLOR_RESET, current_test); 137 | current_test = NULL; 138 | } 139 | 140 | void test_fail(const char *message) 141 | { 142 | if (current_test == NULL) { 143 | return; 144 | } 145 | 146 | printf(" %s[FAIL]%s %s\n", COLOR_RED, COLOR_RESET, current_test); 147 | if (message != NULL && message[0] != '\0') { 148 | printf(" %s%s%s\n", COLOR_YELLOW, message, COLOR_RESET); 149 | } 150 | current_test = NULL; 151 | } 152 | 153 | void test_fail_int(const char *message, long expected, long actual) 154 | { 155 | if (current_test == NULL) { 156 | return; 157 | } 158 | 159 | printf(" %s[FAIL]%s %s\n", COLOR_RED, COLOR_RESET, current_test); 160 | if (message != NULL && message[0] != '\0') { 161 | printf(" %s%s%s\n", COLOR_YELLOW, message, COLOR_RESET); 162 | } 163 | printf(" Expected: %ld\n", expected); 164 | printf(" Got: %ld\n", actual); 165 | current_test = NULL; 166 | } 167 | 168 | void test_fail_uint(const char *message, unsigned long expected, unsigned long actual) 169 | { 170 | if (current_test == NULL) { 171 | return; 172 | } 173 | 174 | printf(" %s[FAIL]%s %s\n", COLOR_RED, COLOR_RESET, current_test); 175 | if (message != NULL && message[0] != '\0') { 176 | printf(" %s%s%s\n", COLOR_YELLOW, message, COLOR_RESET); 177 | } 178 | printf(" Expected: %lu\n", expected); 179 | printf(" Got: %lu\n", actual); 180 | current_test = NULL; 181 | } 182 | 183 | void test_fail_str(const char *message, const char *expected, const char *actual) 184 | { 185 | if (current_test == NULL) { 186 | return; 187 | } 188 | 189 | printf(" %s[FAIL]%s %s\n", COLOR_RED, COLOR_RESET, current_test); 190 | if (message != NULL && message[0] != '\0') { 191 | printf(" %s%s%s\n", COLOR_YELLOW, message, COLOR_RESET); 192 | } 193 | printf(" Expected: \"%s\"\n", expected); 194 | printf(" Got: \"%s\"\n", actual); 195 | current_test = NULL; 196 | } 197 | 198 | void test_fail_bytes(const char *message, const uint8_t *expected, 199 | const uint8_t *actual, size_t len) 200 | { 201 | if (current_test == NULL) { 202 | return; 203 | } 204 | 205 | printf(" %s[FAIL]%s %s\n", COLOR_RED, COLOR_RESET, current_test); 206 | if (message != NULL && message[0] != '\0') { 207 | printf(" %s%s%s\n", COLOR_YELLOW, message, COLOR_RESET); 208 | } 209 | printf(" Expected: "); 210 | print_hex(expected, len); 211 | printf("\n"); 212 | printf(" Got: "); 213 | print_hex(actual, len); 214 | printf("\n"); 215 | current_test = NULL; 216 | } 217 | 218 | void test_section(const char *section_name) 219 | { 220 | printf("%s%s:%s\n", COLOR_CYAN, section_name, COLOR_RESET); 221 | } 222 | 223 | int test_global_summary(void) 224 | { 225 | int all_passed = (global_tests_passed == global_tests_run); 226 | 227 | printf("%s", COLOR_BOLD); 228 | printf("================================================================================\n"); 229 | printf(" GLOBAL TEST SUMMARY\n"); 230 | printf("================================================================================\n"); 231 | printf("%s", COLOR_RESET); 232 | printf("\n"); 233 | 234 | printf(" Test Suites: "); 235 | if (global_suites_passed == global_suites_run) { 236 | printf("%s%d/%d passed%s\n", COLOR_GREEN, 237 | global_suites_passed, global_suites_run, COLOR_RESET); 238 | } else { 239 | printf("%s%d/%d passed (%d failed)%s\n", COLOR_RED, 240 | global_suites_passed, global_suites_run, 241 | global_suites_run - global_suites_passed, COLOR_RESET); 242 | } 243 | 244 | printf(" Test Cases: "); 245 | if (all_passed) { 246 | printf("%s%d/%d passed%s\n", COLOR_GREEN, 247 | global_tests_passed, global_tests_run, COLOR_RESET); 248 | } else { 249 | printf("%s%d/%d passed (%d failed)%s\n", COLOR_RED, 250 | global_tests_passed, global_tests_run, 251 | global_tests_run - global_tests_passed, COLOR_RESET); 252 | } 253 | 254 | printf("\n"); 255 | printf("%s", COLOR_BOLD); 256 | if (all_passed) { 257 | printf("%s ALL TESTS PASSED!%s\n", 258 | COLOR_GREEN, COLOR_RESET); 259 | } else { 260 | printf("%s SOME TESTS FAILED%s\n", 261 | COLOR_RED, COLOR_RESET); 262 | } 263 | printf("%s", COLOR_BOLD); 264 | printf("================================================================================\n"); 265 | printf("%s", COLOR_RESET); 266 | 267 | return all_passed ? 0 : 1; 268 | } 269 | -------------------------------------------------------------------------------- /include/tx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Transaction Data Structures 3 | * 4 | * This header defines the structures for Bitcoin transactions, including 5 | * support for both legacy and SegWit (BIP-141) formats. 6 | * 7 | * Transaction structure (legacy): 8 | * - version (4 bytes, signed) 9 | * - input count (varint) 10 | * - inputs (variable) 11 | * - output count (varint) 12 | * - outputs (variable) 13 | * - locktime (4 bytes) 14 | * 15 | * Transaction structure (SegWit): 16 | * - version (4 bytes) 17 | * - marker (0x00) 18 | * - flag (0x01) 19 | * - input count (varint) 20 | * - inputs (variable) 21 | * - output count (varint) 22 | * - outputs (variable) 23 | * - witness data (variable) 24 | * - locktime (4 bytes) 25 | * 26 | * Build once. Build right. Stop. 27 | */ 28 | 29 | #ifndef ECHO_TX_H 30 | #define ECHO_TX_H 31 | 32 | #include "echo_types.h" 33 | #include 34 | 35 | /* 36 | * Transaction version constants. 37 | */ 38 | #define TX_VERSION_1 1 39 | #define TX_VERSION_2 2 /* BIP-68 relative locktime */ 40 | 41 | /* 42 | * Sequence number constants. 43 | */ 44 | #define TX_SEQUENCE_FINAL 0xFFFFFFFF 45 | #define TX_SEQUENCE_DISABLE_LOCKTIME 0xFFFFFFFF 46 | #define TX_SEQUENCE_DISABLE_RBF 0xFFFFFFFE 47 | 48 | /* 49 | * BIP-68 relative locktime sequence constants. 50 | * Sequence numbers are interpreted as relative locktimes when: 51 | * - Transaction version >= 2 52 | * - Bit 31 (SEQUENCE_LOCKTIME_DISABLE_FLAG) is NOT set 53 | */ 54 | #define SEQUENCE_LOCKTIME_DISABLE_FLAG \ 55 | (1U << 31) /* Bit 31: disable relative locktime */ 56 | #define SEQUENCE_LOCKTIME_TYPE_FLAG \ 57 | (1U << 22) /* Bit 22: 0=blocks, 1=time (512s units) */ 58 | #define SEQUENCE_LOCKTIME_MASK 0x0000FFFF /* Bits 0-15: locktime value */ 59 | 60 | /* 61 | * Locktime threshold. 62 | * Values below this are block heights, values >= are Unix timestamps. 63 | */ 64 | #define LOCKTIME_THRESHOLD 500000000 65 | 66 | /* 67 | * Coinbase constants. 68 | */ 69 | #define TX_COINBASE_VOUT 0xFFFFFFFF /* Previous output index for coinbase */ 70 | 71 | /* 72 | * Size limits (consensus). 73 | */ 74 | #define TX_MAX_SIZE 4000000 /* Max transaction size in bytes */ 75 | #define TX_MAX_INPUTS 100000 /* Practical limit */ 76 | #define TX_MAX_OUTPUTS 100000 /* Practical limit */ 77 | #define TX_MAX_SCRIPT_SIZE 10000 /* Max script size */ 78 | #define TX_MAX_WITNESS_SIZE 4000000 /* Max witness size */ 79 | 80 | /* 81 | * Outpoint — reference to a previous transaction output. 82 | * Used in transaction inputs to identify which output is being spent. 83 | */ 84 | typedef struct { 85 | hash256_t txid; /* Transaction ID (SHA256d of tx without witness) */ 86 | uint32_t vout; /* Output index within that transaction */ 87 | } outpoint_t; 88 | 89 | /* 90 | * Witness item — single item in a witness stack. 91 | */ 92 | typedef struct { 93 | uint8_t *data; /* Witness item data (owned, must be freed) */ 94 | size_t len; /* Length of data */ 95 | } witness_item_t; 96 | 97 | /* 98 | * Witness stack — all witness items for a single input. 99 | */ 100 | typedef struct { 101 | witness_item_t *items; /* Array of witness items (owned) */ 102 | size_t count; /* Number of items */ 103 | } witness_stack_t; 104 | 105 | /* 106 | * Transaction input. 107 | */ 108 | typedef struct { 109 | outpoint_t prevout; /* Reference to output being spent */ 110 | uint8_t *script_sig; /* Unlocking script (owned, must be freed) */ 111 | size_t script_sig_len; 112 | uint32_t sequence; /* Sequence number */ 113 | witness_stack_t witness; /* Witness data (empty for non-SegWit) */ 114 | } tx_input_t; 115 | 116 | /* 117 | * Transaction output. 118 | */ 119 | typedef struct { 120 | satoshi_t value; /* Amount in satoshis */ 121 | uint8_t *script_pubkey; /* Locking script (owned, must be freed) */ 122 | size_t script_pubkey_len; 123 | } tx_output_t; 124 | 125 | /* 126 | * Complete transaction. 127 | * Uses named struct for forward declaration compatibility. 128 | */ 129 | typedef struct tx_s { 130 | int32_t version; /* Transaction version (signed per protocol) */ 131 | tx_input_t *inputs; /* Array of inputs (owned) */ 132 | size_t input_count; 133 | tx_output_t *outputs; /* Array of outputs (owned) */ 134 | size_t output_count; 135 | uint32_t locktime; /* Lock time */ 136 | echo_bool_t has_witness; /* True if any input has witness data */ 137 | } tx_t; 138 | 139 | /* 140 | * Initialize a transaction structure to empty/safe state. 141 | * 142 | * Parameters: 143 | * tx - Transaction to initialize 144 | */ 145 | void tx_init(tx_t *tx); 146 | 147 | /* 148 | * Free all memory owned by a transaction. 149 | * 150 | * Parameters: 151 | * tx - Transaction to free (structure itself is not freed) 152 | */ 153 | void tx_free(tx_t *tx); 154 | 155 | /* 156 | * Parse a transaction from raw bytes. 157 | * 158 | * This function allocates memory for scripts and witness data. 159 | * Call tx_free() when done. 160 | * 161 | * Parameters: 162 | * data - Raw transaction bytes 163 | * data_len - Length of data 164 | * tx - Output: parsed transaction 165 | * consumed - Output: bytes consumed (may be NULL) 166 | * 167 | * Returns: 168 | * ECHO_OK on success 169 | * ECHO_ERR_NULL_PARAM if data or tx is NULL 170 | * ECHO_ERR_TRUNCATED if data too short 171 | * ECHO_ERR_INVALID_FORMAT if transaction malformed 172 | * ECHO_ERR_OUT_OF_MEMORY if allocation fails 173 | */ 174 | echo_result_t tx_parse(const uint8_t *data, size_t data_len, tx_t *tx, 175 | size_t *consumed); 176 | 177 | /* 178 | * Compute the serialized size of a transaction. 179 | * 180 | * Parameters: 181 | * tx - Transaction to measure 182 | * with_witness - Include witness data in size 183 | * 184 | * Returns: 185 | * Serialized size in bytes, or 0 if tx is NULL 186 | */ 187 | size_t tx_serialize_size(const tx_t *tx, echo_bool_t with_witness); 188 | 189 | /* 190 | * Serialize a transaction to bytes. 191 | * 192 | * Parameters: 193 | * tx - Transaction to serialize 194 | * with_witness - Include witness data 195 | * buf - Output buffer 196 | * buf_len - Size of output buffer 197 | * written - Output: bytes written (may be NULL) 198 | * 199 | * Returns: 200 | * ECHO_OK on success 201 | * ECHO_ERR_NULL_PARAM if tx or buf is NULL 202 | * ECHO_ERR_BUFFER_TOO_SMALL if buffer insufficient 203 | */ 204 | echo_result_t tx_serialize(const tx_t *tx, echo_bool_t with_witness, 205 | uint8_t *buf, size_t buf_len, size_t *written); 206 | 207 | /* 208 | * Compute the transaction ID (txid). 209 | * 210 | * The txid is SHA256d of the transaction serialized WITHOUT witness data. 211 | * This ensures txid stability for SegWit transactions. 212 | * 213 | * Parameters: 214 | * tx - Transaction 215 | * txid - Output: 32-byte transaction ID 216 | * 217 | * Returns: 218 | * ECHO_OK on success 219 | * ECHO_ERR_NULL_PARAM if tx or txid is NULL 220 | */ 221 | echo_result_t tx_compute_txid(const tx_t *tx, hash256_t *txid); 222 | 223 | /* 224 | * Compute the witness transaction ID (wtxid). 225 | * 226 | * The wtxid is SHA256d of the full transaction including witness data. 227 | * For non-witness transactions, wtxid equals txid. 228 | * 229 | * Parameters: 230 | * tx - Transaction 231 | * wtxid - Output: 32-byte witness transaction ID 232 | * 233 | * Returns: 234 | * ECHO_OK on success 235 | * ECHO_ERR_NULL_PARAM if tx or wtxid is NULL 236 | */ 237 | echo_result_t tx_compute_wtxid(const tx_t *tx, hash256_t *wtxid); 238 | 239 | /* 240 | * Check if a transaction is a coinbase transaction. 241 | * 242 | * A coinbase has exactly one input with null prevout (all zeros txid, 243 | * vout = 0xFFFFFFFF). 244 | * 245 | * Parameters: 246 | * tx - Transaction to check 247 | * 248 | * Returns: 249 | * ECHO_TRUE if coinbase, ECHO_FALSE otherwise 250 | */ 251 | echo_bool_t tx_is_coinbase(const tx_t *tx); 252 | 253 | /* 254 | * Compute transaction weight (for block weight limit). 255 | * 256 | * Weight = (base size * 3) + total size 257 | * Where base size excludes witness data. 258 | * 259 | * Parameters: 260 | * tx - Transaction 261 | * 262 | * Returns: 263 | * Transaction weight in weight units, or 0 if tx is NULL 264 | */ 265 | size_t tx_weight(const tx_t *tx); 266 | 267 | /* 268 | * Compute transaction virtual size (vsize). 269 | * 270 | * vsize = ceil(weight / 4) 271 | * 272 | * Parameters: 273 | * tx - Transaction 274 | * 275 | * Returns: 276 | * Virtual size in vbytes, or 0 if tx is NULL 277 | */ 278 | size_t tx_vsize(const tx_t *tx); 279 | 280 | #endif /* ECHO_TX_H */ 281 | -------------------------------------------------------------------------------- /src/crypto/sha256.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — SHA-256 Implementation 3 | * 4 | * SHA-256 as specified in FIPS 180-4 (Secure Hash Standard). 5 | * 6 | * Reference: https://csrc.nist.gov/publications/detail/fips/180/4/final 7 | * 8 | * This implementation prioritizes correctness and clarity. 9 | * Every operation maps directly to the specification. 10 | * 11 | * Build once. Build right. Stop. 12 | */ 13 | 14 | #include "sha256.h" 15 | #include 16 | #include 17 | 18 | /* 19 | * Initial hash values. 20 | * First 32 bits of the fractional parts of the square roots 21 | * of the first 8 prime numbers (2, 3, 5, 7, 11, 13, 17, 19). 22 | */ 23 | static const uint32_t H_INIT[8] = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 24 | 0xa54ff53a, 0x510e527f, 0x9b05688c, 25 | 0x1f83d9ab, 0x5be0cd19}; 26 | 27 | /* 28 | * Round constants. 29 | * First 32 bits of the fractional parts of the cube roots 30 | * of the first 64 prime numbers. 31 | */ 32 | static const uint32_t K[64] = { 33 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 34 | 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 35 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 36 | 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 37 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 38 | 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 39 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 40 | 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 41 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 42 | 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 43 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; 44 | 45 | /* 46 | * Right rotate a 32-bit value by n bits. 47 | */ 48 | static inline uint32_t rotr32(uint32_t x, int n) { 49 | return (x >> n) | (x << (32 - n)); 50 | } 51 | 52 | /* 53 | * SHA-256 logical functions (FIPS 180-4, Section 4.1.2). 54 | */ 55 | #define Ch(x, y, z) (((x) & (y)) ^ (~(x) & (z))) 56 | #define Maj(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) 57 | #define Sigma0(x) (rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22)) 58 | #define Sigma1(x) (rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25)) 59 | #define sigma0(x) (rotr32(x, 7) ^ rotr32(x, 18) ^ ((x) >> 3)) 60 | #define sigma1(x) (rotr32(x, 17) ^ rotr32(x, 19) ^ ((x) >> 10)) 61 | 62 | /* 63 | * Load 32-bit big-endian value from byte array. 64 | */ 65 | static inline uint32_t load_be32(const uint8_t *p) { 66 | return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | 67 | ((uint32_t)p[2] << 8) | ((uint32_t)p[3]); 68 | } 69 | 70 | /* 71 | * Store 32-bit value as big-endian bytes. 72 | */ 73 | static inline void store_be32(uint8_t *p, uint32_t x) { 74 | p[0] = (uint8_t)(x >> 24); 75 | p[1] = (uint8_t)(x >> 16); 76 | p[2] = (uint8_t)(x >> 8); 77 | p[3] = (uint8_t)(x); 78 | } 79 | 80 | /* 81 | * Store 64-bit value as big-endian bytes. 82 | */ 83 | static inline void store_be64(uint8_t *p, uint64_t x) { 84 | p[0] = (uint8_t)(x >> 56); 85 | p[1] = (uint8_t)(x >> 48); 86 | p[2] = (uint8_t)(x >> 40); 87 | p[3] = (uint8_t)(x >> 32); 88 | p[4] = (uint8_t)(x >> 24); 89 | p[5] = (uint8_t)(x >> 16); 90 | p[6] = (uint8_t)(x >> 8); 91 | p[7] = (uint8_t)(x); 92 | } 93 | 94 | /* 95 | * Process one 64-byte block. 96 | * 97 | * This is the SHA-256 compression function. 98 | * FIPS 180-4, Section 6.2.2. 99 | */ 100 | static void sha256_transform(uint32_t state[8], const uint8_t block[64]) { 101 | uint32_t W[64]; 102 | uint32_t a, b, c, d, e, f, g, h; 103 | uint32_t T1, T2; 104 | int t; 105 | 106 | /* 107 | * Step 1: Prepare the message schedule W[0..63]. 108 | * W[0..15] are loaded directly from the block. 109 | * W[16..63] are computed from earlier W values. 110 | */ 111 | for (t = 0; t < 16; t++) { 112 | W[t] = load_be32(block + (size_t)(t * 4)); 113 | } 114 | for (t = 16; t < 64; t++) { 115 | W[t] = sigma1(W[t - 2]) + W[t - 7] + sigma0(W[t - 15]) + W[t - 16]; 116 | } 117 | 118 | /* 119 | * Step 2: Initialize working variables. 120 | */ 121 | a = state[0]; 122 | b = state[1]; 123 | c = state[2]; 124 | d = state[3]; 125 | e = state[4]; 126 | f = state[5]; 127 | g = state[6]; 128 | h = state[7]; 129 | 130 | /* 131 | * Step 3: Perform 64 rounds of compression. 132 | */ 133 | for (t = 0; t < 64; t++) { 134 | T1 = h + Sigma1(e) + Ch(e, f, g) + K[t] + W[t]; 135 | T2 = Sigma0(a) + Maj(a, b, c); 136 | h = g; 137 | g = f; 138 | f = e; 139 | e = d + T1; 140 | d = c; 141 | c = b; 142 | b = a; 143 | a = T1 + T2; 144 | } 145 | 146 | /* 147 | * Step 4: Compute intermediate hash value. 148 | */ 149 | state[0] += a; 150 | state[1] += b; 151 | state[2] += c; 152 | state[3] += d; 153 | state[4] += e; 154 | state[5] += f; 155 | state[6] += g; 156 | state[7] += h; 157 | } 158 | 159 | void sha256_init(sha256_ctx_t *ctx) { 160 | ctx->state[0] = H_INIT[0]; 161 | ctx->state[1] = H_INIT[1]; 162 | ctx->state[2] = H_INIT[2]; 163 | ctx->state[3] = H_INIT[3]; 164 | ctx->state[4] = H_INIT[4]; 165 | ctx->state[5] = H_INIT[5]; 166 | ctx->state[6] = H_INIT[6]; 167 | ctx->state[7] = H_INIT[7]; 168 | ctx->count = 0; 169 | } 170 | 171 | void sha256_update(sha256_ctx_t *ctx, const uint8_t *data, size_t len) { 172 | size_t buffered; 173 | size_t remaining; 174 | 175 | if (len == 0) { 176 | return; 177 | } 178 | 179 | /* How many bytes are already buffered? */ 180 | buffered = (size_t)(ctx->count % SHA256_BLOCK_SIZE); 181 | 182 | /* Update total count */ 183 | ctx->count += len; 184 | 185 | /* If we have buffered data, try to complete a block */ 186 | if (buffered > 0) { 187 | remaining = SHA256_BLOCK_SIZE - buffered; 188 | if (len < remaining) { 189 | /* Not enough to complete a block, just buffer */ 190 | memcpy(ctx->buffer + buffered, data, len); 191 | return; 192 | } 193 | /* Complete the buffered block */ 194 | memcpy(ctx->buffer + buffered, data, remaining); 195 | sha256_transform(ctx->state, ctx->buffer); 196 | data += remaining; 197 | len -= remaining; 198 | } 199 | 200 | /* Process complete blocks directly from input */ 201 | while (len >= SHA256_BLOCK_SIZE) { 202 | sha256_transform(ctx->state, data); 203 | data += SHA256_BLOCK_SIZE; 204 | len -= SHA256_BLOCK_SIZE; 205 | } 206 | 207 | /* Buffer any remaining data */ 208 | if (len > 0) { 209 | memcpy(ctx->buffer, data, len); 210 | } 211 | } 212 | 213 | void sha256_final(sha256_ctx_t *ctx, uint8_t out[SHA256_DIGEST_SIZE]) { 214 | size_t buffered; 215 | uint64_t bit_count; 216 | int i; 217 | 218 | /* How many bytes in the buffer? */ 219 | buffered = (size_t)(ctx->count % SHA256_BLOCK_SIZE); 220 | 221 | /* Total message length in bits */ 222 | bit_count = ctx->count * 8; 223 | 224 | /* 225 | * Padding: append 1 bit, then zeros, then 64-bit length. 226 | * The padding must result in a message length that is a multiple of 512 bits. 227 | */ 228 | 229 | /* Append 0x80 (1 bit followed by 7 zero bits) */ 230 | ctx->buffer[buffered++] = 0x80; 231 | 232 | /* If not enough room for the length (need 8 bytes), pad and process */ 233 | if (buffered > 56) { 234 | /* Fill rest of block with zeros and process */ 235 | memset(ctx->buffer + buffered, 0, SHA256_BLOCK_SIZE - buffered); 236 | sha256_transform(ctx->state, ctx->buffer); 237 | buffered = 0; 238 | } 239 | 240 | /* Pad with zeros up to the length field */ 241 | memset(ctx->buffer + buffered, 0, 56 - buffered); 242 | 243 | /* Append the 64-bit message length in bits (big-endian) */ 244 | store_be64(ctx->buffer + 56, bit_count); 245 | 246 | /* Process the final block */ 247 | sha256_transform(ctx->state, ctx->buffer); 248 | 249 | /* Output the hash (big-endian) */ 250 | for (i = 0; i < 8; i++) { 251 | store_be32(out + (size_t)(i * 4), ctx->state[i]); 252 | } 253 | } 254 | 255 | void sha256(const uint8_t *data, size_t len, uint8_t out[SHA256_DIGEST_SIZE]) { 256 | sha256_ctx_t ctx; 257 | 258 | sha256_init(&ctx); 259 | sha256_update(&ctx, data, len); 260 | sha256_final(&ctx, out); 261 | } 262 | 263 | void sha256d(const uint8_t *data, size_t len, uint8_t out[SHA256_DIGEST_SIZE]) { 264 | uint8_t inner[SHA256_DIGEST_SIZE]; 265 | 266 | /* SHA256d(x) = SHA256(SHA256(x)) */ 267 | sha256(data, len, inner); 268 | sha256(inner, SHA256_DIGEST_SIZE, out); 269 | } 270 | 271 | void sha256_midstate(const uint8_t data[SHA256_BLOCK_SIZE], 272 | uint8_t out[SHA256_DIGEST_SIZE]) { 273 | sha256_ctx_t ctx; 274 | int i; 275 | 276 | sha256_init(&ctx); 277 | sha256_transform(ctx.state, data); 278 | 279 | /* Output the raw state (not finalized) */ 280 | for (i = 0; i < 8; i++) { 281 | store_be32(out + (size_t)(i * 4), ctx.state[i]); 282 | } 283 | } 284 | -------------------------------------------------------------------------------- /include/discovery.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Bitcoin Echo — Peer Discovery 3 | * 4 | * This module implements peer discovery mechanisms: 5 | * - DNS seed queries for bootstrap 6 | * - Hardcoded seed addresses as fallback 7 | * - addr/getaddr message handling for peer address relay 8 | * - Peer address storage and selection 9 | * 10 | * Peer discovery is essential for joining the Bitcoin network. A new node 11 | * uses DNS seeds or hardcoded addresses to find initial peers, then learns 12 | * about additional peers through addr messages. 13 | */ 14 | 15 | #ifndef ECHO_DISCOVERY_H 16 | #define ECHO_DISCOVERY_H 17 | 18 | #include "echo_types.h" 19 | #include "protocol.h" 20 | #include 21 | 22 | /* Maximum peer addresses to store */ 23 | #define MAX_PEER_ADDRESSES 10000 24 | 25 | /* Maximum addresses per DNS seed query */ 26 | #define MAX_DNS_RESULTS 128 27 | 28 | /* Minimum time between addr broadcasts (24 hours in milliseconds) */ 29 | #define ADDR_BROADCAST_INTERVAL_MS (24ULL * 60 * 60 * 1000) 30 | 31 | /* Maximum age for peer address to be considered fresh (3 hours in seconds) */ 32 | #define ADDR_FRESH_THRESHOLD_SEC (3ULL * 60 * 60) 33 | 34 | /* Maximum age for peer address to be considered valid (7 days in seconds) */ 35 | #define ADDR_MAX_AGE_SEC (7ULL * 24 * 60 * 60) 36 | 37 | /** 38 | * Network type for seed selection 39 | */ 40 | typedef enum { 41 | NETWORK_MAINNET, 42 | NETWORK_TESTNET, 43 | NETWORK_REGTEST 44 | } network_type_t; 45 | 46 | /** 47 | * Peer address entry 48 | * 49 | * Stores information about a known peer address. 50 | */ 51 | typedef struct { 52 | net_addr_t addr; /* Network address with timestamp */ 53 | uint64_t last_try; /* Last connection attempt (plat_time_ms) */ 54 | uint64_t last_success; /* Last successful connection (plat_time_ms) */ 55 | uint32_t attempts; /* Number of connection attempts */ 56 | echo_bool_t in_use; /* Whether address is currently being used */ 57 | echo_bool_t reachable; /* Whether we've successfully connected before */ 58 | uint8_t source; /* How we learned about this address */ 59 | } peer_addr_entry_t; 60 | 61 | /* Address source flags */ 62 | #define ADDR_SOURCE_DNS_SEED 0x01 63 | #define ADDR_SOURCE_HARDCODED 0x02 64 | #define ADDR_SOURCE_ADDR_MSG 0x04 65 | #define ADDR_SOURCE_MANUAL 0x08 66 | 67 | /** 68 | * Peer address manager 69 | * 70 | * Manages known peer addresses and selection. 71 | */ 72 | typedef struct { 73 | peer_addr_entry_t addresses[MAX_PEER_ADDRESSES]; 74 | size_t count; /* Number of known addresses */ 75 | uint64_t last_addr_broadcast; /* Last time we sent addr to peers */ 76 | network_type_t network; /* Which network we're on */ 77 | } peer_addr_manager_t; 78 | 79 | /** 80 | * Initialize peer address manager. 81 | * 82 | * Parameters: 83 | * manager - Address manager to initialize 84 | * network - Network type (mainnet/testnet/regtest) 85 | */ 86 | void discovery_init(peer_addr_manager_t *manager, network_type_t network); 87 | 88 | /** 89 | * Query DNS seeds for peer addresses. 90 | * 91 | * Queries all DNS seeds for the configured network and adds results to 92 | * address manager. 93 | * 94 | * This function uses the platform DNS resolution API (plat_dns_resolve). 95 | * DNS seeds are only available for mainnet and testnet, not regtest. 96 | * 97 | * Parameters: 98 | * manager - Address manager to populate 99 | * 100 | * Returns: 101 | * Number of addresses discovered from DNS seeds 102 | * 0 if DNS resolution fails or network doesn't have DNS seeds 103 | */ 104 | size_t discovery_query_dns_seeds(peer_addr_manager_t *manager); 105 | 106 | /** 107 | * Add hardcoded seed addresses. 108 | * 109 | * Adds hardcoded IP addresses as fallback when DNS seeds are unavailable. 110 | * These addresses are embedded in the binary and represent known stable nodes. 111 | * 112 | * Parameters: 113 | * manager - Address manager to populate 114 | * 115 | * Returns: 116 | * Number of addresses added 117 | */ 118 | size_t discovery_add_hardcoded_seeds(peer_addr_manager_t *manager); 119 | 120 | /** 121 | * Add peer address from addr message. 122 | * 123 | * Validates and adds address to manager. Rejects addresses that are: 124 | * - Too old (timestamp more than 10 minutes in future or very old) 125 | * - Invalid (0.0.0.0, loopback, etc.) 126 | * - Duplicate 127 | * 128 | * Parameters: 129 | * manager - Address manager 130 | * addr - Network address to add 131 | * 132 | * Returns: 133 | * ECHO_SUCCESS if added 134 | * ECHO_ERR_INVALID if address is invalid 135 | * ECHO_ERR_FULL if address manager is full 136 | * ECHO_ERR_EXISTS if address already known 137 | */ 138 | echo_result_t discovery_add_address(peer_addr_manager_t *manager, 139 | const net_addr_t *addr); 140 | 141 | /** 142 | * Add multiple addresses from addr message. 143 | * 144 | * Bulk version of discovery_add_address(). 145 | * 146 | * Parameters: 147 | * manager - Address manager 148 | * addresses - Array of network addresses 149 | * count - Number of addresses in array 150 | * 151 | * Returns: 152 | * Number of addresses successfully added 153 | */ 154 | size_t discovery_add_addresses(peer_addr_manager_t *manager, 155 | const net_addr_t *addresses, size_t count); 156 | 157 | /** 158 | * Select addresses to send in addr message. 159 | * 160 | * Selects up to max_count fresh addresses to advertise to a peer. 161 | * Prioritizes recently seen, reachable addresses. 162 | * 163 | * Parameters: 164 | * manager - Address manager 165 | * out_addrs - Output array (must hold max_count entries) 166 | * max_count - Maximum number of addresses to return 167 | * 168 | * Returns: 169 | * Number of addresses written to out_addrs 170 | */ 171 | size_t 172 | discovery_select_addresses_to_advertise(const peer_addr_manager_t *manager, 173 | net_addr_t *out_addrs, 174 | size_t max_count); 175 | 176 | /** 177 | * Select address for outbound connection. 178 | * 179 | * Chooses the best address for a new outbound connection. 180 | * Prioritizes: 181 | * - Addresses we haven't tried recently 182 | * - Addresses that worked before 183 | * - Fresh addresses 184 | * 185 | * Parameters: 186 | * manager - Address manager 187 | * out_addr - Output: selected address 188 | * 189 | * Returns: 190 | * ECHO_SUCCESS if address selected 191 | * ECHO_ERR_NOT_FOUND if no suitable addresses available 192 | */ 193 | echo_result_t discovery_select_outbound_address(peer_addr_manager_t *manager, 194 | net_addr_t *out_addr); 195 | 196 | /** 197 | * Mark address as currently in use. 198 | * 199 | * Prevents selecting the same address for multiple simultaneous connections. 200 | * 201 | * Parameters: 202 | * manager - Address manager 203 | * addr - Address to mark 204 | */ 205 | void discovery_mark_address_in_use(peer_addr_manager_t *manager, 206 | const net_addr_t *addr); 207 | 208 | /** 209 | * Mark address as no longer in use. 210 | * 211 | * Parameters: 212 | * manager - Address manager 213 | * addr - Address to unmark 214 | * success - Whether connection was successful 215 | */ 216 | void discovery_mark_address_free(peer_addr_manager_t *manager, 217 | const net_addr_t *addr, echo_bool_t success); 218 | 219 | /** 220 | * Mark connection attempt to address. 221 | * 222 | * Updates last_try timestamp and increments attempt counter. 223 | * 224 | * Parameters: 225 | * manager - Address manager 226 | * addr - Address that was attempted 227 | */ 228 | void discovery_mark_attempt(peer_addr_manager_t *manager, 229 | const net_addr_t *addr); 230 | 231 | /** 232 | * Mark successful connection to address. 233 | * 234 | * Updates last_success timestamp and sets reachable flag. 235 | * 236 | * Parameters: 237 | * manager - Address manager 238 | * addr - Address that succeeded 239 | */ 240 | void discovery_mark_success(peer_addr_manager_t *manager, 241 | const net_addr_t *addr); 242 | 243 | /** 244 | * Check if address is valid for connection. 245 | * 246 | * Validates that address is: 247 | * - Not loopback (unless regtest) 248 | * - Not unspecified (0.0.0.0 or ::) 249 | * - Not multicast 250 | * - Has valid port 251 | * 252 | * Parameters: 253 | * manager - Address manager (for network type) 254 | * addr - Address to validate 255 | * 256 | * Returns: 257 | * true if address is valid for outbound connection 258 | */ 259 | echo_bool_t discovery_is_address_valid(const peer_addr_manager_t *manager, 260 | const net_addr_t *addr); 261 | 262 | /** 263 | * Get count of known addresses. 264 | * 265 | * Returns: 266 | * Number of addresses in manager 267 | */ 268 | size_t discovery_get_address_count(const peer_addr_manager_t *manager); 269 | 270 | /** 271 | * Get count of reachable addresses. 272 | * 273 | * Returns: 274 | * Number of addresses we've successfully connected to before 275 | */ 276 | size_t discovery_get_reachable_count(const peer_addr_manager_t *manager); 277 | 278 | #endif /* ECHO_DISCOVERY_H */ 279 | -------------------------------------------------------------------------------- /include/tx_validate.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Transaction Validation 3 | * 4 | * This module implements complete transaction validation including: 5 | * - Syntactic validation (well-formed) 6 | * - Size and count limits 7 | * - Duplicate input detection 8 | * - Output value range validation 9 | * - Script validation for each input 10 | * - Locktime and sequence validation 11 | * 12 | * Build once. Build right. Stop. 13 | */ 14 | 15 | #ifndef ECHO_TX_VALIDATE_H 16 | #define ECHO_TX_VALIDATE_H 17 | 18 | #include "echo_types.h" 19 | #include "script.h" 20 | #include "tx.h" 21 | #include 22 | 23 | /* 24 | * ============================================================================ 25 | * VALIDATION CONSTANTS 26 | * ============================================================================ 27 | */ 28 | 29 | /* 30 | * Maximum transaction weight (in weight units). 31 | * Block weight limit is 4,000,000 WU; a single tx can't exceed it. 32 | */ 33 | #define TX_MAX_WEIGHT 4000000 34 | 35 | /* 36 | * Maximum transaction virtual size. 37 | */ 38 | #define TX_MAX_VSIZE 1000000 39 | 40 | /* 41 | * Minimum coinbase script length (BIP-34: height in coinbase). 42 | */ 43 | #define COINBASE_SCRIPT_MIN_LEN 2 44 | 45 | /* 46 | * Maximum coinbase script length. 47 | */ 48 | #define COINBASE_SCRIPT_MAX_LEN 100 49 | 50 | /* 51 | * ============================================================================ 52 | * VALIDATION RESULT STRUCTURE 53 | * ============================================================================ 54 | */ 55 | 56 | /* 57 | * Validation error codes (specific to tx validation). 58 | * More specific than the general echo_result_t codes. 59 | */ 60 | typedef enum { 61 | TX_VALIDATE_OK = 0, 62 | 63 | /* Structural errors */ 64 | TX_VALIDATE_ERR_NULL, 65 | TX_VALIDATE_ERR_EMPTY_INPUTS, 66 | TX_VALIDATE_ERR_EMPTY_OUTPUTS, 67 | TX_VALIDATE_ERR_TOO_MANY_INPUTS, 68 | TX_VALIDATE_ERR_TOO_MANY_OUTPUTS, 69 | TX_VALIDATE_ERR_SIZE_EXCEEDED, 70 | TX_VALIDATE_ERR_WEIGHT_EXCEEDED, 71 | 72 | /* Input errors */ 73 | TX_VALIDATE_ERR_DUPLICATE_INPUT, 74 | TX_VALIDATE_ERR_NULL_PREVOUT, /* Non-coinbase with null prevout */ 75 | TX_VALIDATE_ERR_SCRIPT_SIZE, /* scriptSig too large */ 76 | 77 | /* Output errors */ 78 | TX_VALIDATE_ERR_NEGATIVE_VALUE, 79 | TX_VALIDATE_ERR_VALUE_TOO_LARGE, /* Single output > 21M BTC */ 80 | TX_VALIDATE_ERR_TOTAL_OVERFLOW, /* Sum of outputs overflows */ 81 | TX_VALIDATE_ERR_TOTAL_TOO_LARGE, /* Sum of outputs > 21M BTC */ 82 | TX_VALIDATE_ERR_OUTPUT_SCRIPT_SIZE, /* scriptPubKey too large */ 83 | 84 | /* Coinbase errors */ 85 | TX_VALIDATE_ERR_COINBASE_SCRIPT_SIZE, 86 | 87 | /* Script execution errors */ 88 | TX_VALIDATE_ERR_SCRIPT_FAILED, 89 | TX_VALIDATE_ERR_SCRIPT_VERIFY, 90 | 91 | /* Locktime errors */ 92 | TX_VALIDATE_ERR_LOCKTIME, 93 | 94 | /* Context errors (need additional info) */ 95 | TX_VALIDATE_ERR_MISSING_UTXO, 96 | TX_VALIDATE_ERR_INPUT_VALUE_OVERFLOW, 97 | TX_VALIDATE_ERR_INSUFFICIENT_FUNDS, 98 | 99 | } tx_validate_error_t; 100 | 101 | /* 102 | * Detailed validation result. 103 | * Contains the specific error code and location of the error. 104 | */ 105 | typedef struct { 106 | tx_validate_error_t error; /* Specific error code */ 107 | size_t index; /* Input/output index where error occurred */ 108 | script_error_t script_error; /* Script-specific error (if applicable) */ 109 | } tx_validate_result_t; 110 | 111 | /* 112 | * UTXO information needed for input validation. 113 | * Caller must provide this for each input being validated. 114 | */ 115 | typedef struct { 116 | satoshi_t value; /* Value of the UTXO */ 117 | uint8_t *script_pubkey; /* The scriptPubKey of the UTXO */ 118 | size_t script_pubkey_len; 119 | uint32_t height; /* Block height where UTXO was created */ 120 | echo_bool_t is_coinbase; /* Whether this UTXO came from a coinbase */ 121 | } utxo_info_t; 122 | 123 | /* 124 | * Validation context. 125 | * Provides additional information needed for full validation. 126 | */ 127 | typedef struct { 128 | /* Block context (for locktime validation) */ 129 | uint32_t block_height; /* Current block height */ 130 | uint32_t block_time; /* Current block timestamp */ 131 | uint32_t median_time_past; /* Median time past (BIP-113) */ 132 | 133 | /* UTXO information for inputs */ 134 | const utxo_info_t *utxos; /* Array of UTXO info, one per input */ 135 | size_t utxo_count; 136 | 137 | /* Script verification flags */ 138 | uint32_t script_flags; /* SCRIPT_VERIFY_* flags */ 139 | 140 | } tx_validate_ctx_t; 141 | 142 | /* 143 | * ============================================================================ 144 | * VALIDATION FUNCTIONS 145 | * ============================================================================ 146 | */ 147 | 148 | /* 149 | * Initialize validation result to success state. 150 | */ 151 | void tx_validate_result_init(tx_validate_result_t *result); 152 | 153 | /* 154 | * Get a human-readable string for a validation error. 155 | * 156 | * Parameters: 157 | * error - The validation error code 158 | * 159 | * Returns: 160 | * Static string describing the error 161 | */ 162 | const char *tx_validate_error_string(tx_validate_error_t error); 163 | 164 | /* 165 | * Perform syntactic validation of a transaction. 166 | * 167 | * This validates the transaction structure without requiring UTXO context: 168 | * - Not NULL 169 | * - Has inputs (unless coinbase) 170 | * - Has outputs 171 | * - Input/output counts within limits 172 | * - No duplicate inputs 173 | * - Output values in valid range 174 | * - Total output value doesn't overflow 175 | * - Script sizes within limits 176 | * - Transaction size/weight within limits 177 | * 178 | * Parameters: 179 | * tx - Transaction to validate 180 | * result - Output: detailed validation result 181 | * 182 | * Returns: 183 | * ECHO_OK if transaction is syntactically valid 184 | * ECHO_ERR_* otherwise (result contains details) 185 | */ 186 | echo_result_t tx_validate_syntax(const tx_t *tx, tx_validate_result_t *result); 187 | 188 | /* 189 | * Validate a transaction with full context. 190 | * 191 | * This performs complete validation including: 192 | * - All syntactic checks 193 | * - Input scripts execute successfully 194 | * - Total input value >= total output value 195 | * - Locktime/sequence rules satisfied 196 | * 197 | * Parameters: 198 | * tx - Transaction to validate 199 | * ctx - Validation context with UTXO info and block context 200 | * result - Output: detailed validation result 201 | * 202 | * Returns: 203 | * ECHO_OK if transaction is valid 204 | * ECHO_ERR_* otherwise (result contains details) 205 | */ 206 | echo_result_t tx_validate(const tx_t *tx, const tx_validate_ctx_t *ctx, 207 | tx_validate_result_t *result); 208 | 209 | /* 210 | * Validate a single input's script execution. 211 | * 212 | * Parameters: 213 | * tx - The transaction 214 | * input_index - Index of the input to validate 215 | * utxo - UTXO information for this input 216 | * flags - Script verification flags 217 | * result - Output: validation result 218 | * 219 | * Returns: 220 | * ECHO_OK if input is valid 221 | * ECHO_ERR_SCRIPT_* otherwise 222 | */ 223 | echo_result_t tx_validate_input(const tx_t *tx, size_t input_index, 224 | const utxo_info_t *utxo, uint32_t flags, 225 | tx_validate_result_t *result); 226 | 227 | /* 228 | * Check if a transaction's locktime is satisfied. 229 | * 230 | * Parameters: 231 | * tx - Transaction to check 232 | * block_height - Current block height 233 | * block_time - Current block timestamp (or MTP if BIP-113) 234 | * 235 | * Returns: 236 | * ECHO_TRUE if locktime satisfied, ECHO_FALSE otherwise 237 | */ 238 | echo_bool_t tx_locktime_satisfied(const tx_t *tx, uint32_t block_height, 239 | uint32_t block_time); 240 | 241 | /* 242 | * Check if an input's relative locktime (BIP-68) is satisfied. 243 | * 244 | * Parameters: 245 | * tx - Transaction containing the input 246 | * input_index - Index of the input 247 | * utxo_height - Block height where UTXO was confirmed 248 | * utxo_time - Block time where UTXO was confirmed 249 | * block_height - Current block height 250 | * block_time - Current block timestamp 251 | * 252 | * Returns: 253 | * ECHO_TRUE if relative locktime satisfied, ECHO_FALSE otherwise 254 | */ 255 | echo_bool_t tx_sequence_satisfied(const tx_t *tx, size_t input_index, 256 | uint32_t utxo_height, uint32_t utxo_time, 257 | uint32_t block_height, uint32_t block_time); 258 | 259 | /* 260 | * Compute the fee of a transaction. 261 | * 262 | * Parameters: 263 | * tx - Transaction 264 | * utxos - Array of UTXO info for each input 265 | * utxo_count - Number of UTXOs (must equal input count) 266 | * fee - Output: transaction fee in satoshis 267 | * 268 | * Returns: 269 | * ECHO_OK on success 270 | * ECHO_ERR_* if inputs don't match or values overflow 271 | */ 272 | echo_result_t tx_compute_fee(const tx_t *tx, const utxo_info_t *utxos, 273 | size_t utxo_count, satoshi_t *fee); 274 | 275 | #endif /* ECHO_TX_VALIDATE_H */ 276 | -------------------------------------------------------------------------------- /include/peer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Bitcoin Echo — Peer Connection Management 3 | * 4 | * This module manages the lifecycle of P2P connections: 5 | * - Connection establishment (outbound and inbound) 6 | * - Handshake (version/verack exchange) 7 | * - Message sending and receiving 8 | * - Disconnection handling 9 | * - Per-peer state tracking 10 | * 11 | * Each peer represents one active TCP connection to/from another Bitcoin node. 12 | */ 13 | 14 | #ifndef ECHO_PEER_H 15 | #define ECHO_PEER_H 16 | 17 | #include "echo_types.h" 18 | #include "platform.h" 19 | #include "protocol.h" 20 | #include 21 | 22 | /* Maximum size of per-peer send queue (in messages) */ 23 | #define PEER_SEND_QUEUE_SIZE 128 24 | 25 | /* Maximum size of per-peer receive buffer (in bytes) */ 26 | #define PEER_RECV_BUFFER_SIZE (1024 * 1024) /* 1 MB */ 27 | 28 | /** 29 | * Peer connection state 30 | */ 31 | typedef enum { 32 | PEER_STATE_DISCONNECTED, /* Not connected */ 33 | PEER_STATE_CONNECTING, /* TCP connection in progress */ 34 | PEER_STATE_CONNECTED, /* TCP connected, handshake not started */ 35 | PEER_STATE_HANDSHAKE_SENT, /* version message sent, waiting for version */ 36 | PEER_STATE_HANDSHAKE_RECV, /* version received, waiting for verack */ 37 | PEER_STATE_READY, /* Handshake complete, ready for messages */ 38 | PEER_STATE_DISCONNECTING /* Disconnection in progress */ 39 | } peer_state_t; 40 | 41 | /** 42 | * Peer disconnect reason 43 | */ 44 | typedef enum { 45 | PEER_DISCONNECT_NONE = 0, 46 | PEER_DISCONNECT_USER, /* User requested disconnect */ 47 | PEER_DISCONNECT_PROTOCOL_ERROR, /* Protocol violation */ 48 | PEER_DISCONNECT_TIMEOUT, /* Connection timeout */ 49 | PEER_DISCONNECT_PEER_CLOSED, /* Peer closed connection */ 50 | PEER_DISCONNECT_NETWORK_ERROR, /* Network I/O error */ 51 | PEER_DISCONNECT_HANDSHAKE_FAIL, /* Handshake failed */ 52 | PEER_DISCONNECT_MISBEHAVING /* Peer misbehavior (DoS protection) */ 53 | } peer_disconnect_reason_t; 54 | 55 | /** 56 | * Message queue entry 57 | */ 58 | typedef struct { 59 | msg_t message; /* The message to send */ 60 | echo_bool_t 61 | allocated; /* Whether message contains dynamically allocated data */ 62 | } peer_msg_queue_entry_t; 63 | 64 | /** 65 | * Peer connection 66 | * 67 | * Represents a single P2P connection to/from another Bitcoin node. 68 | */ 69 | typedef struct { 70 | /* Connection info */ 71 | plat_socket_t *socket; /* TCP socket (allocated) */ 72 | peer_state_t state; /* Current connection state */ 73 | echo_bool_t inbound; /* True if peer connected to us */ 74 | 75 | /* Peer identification */ 76 | char address[64]; /* IP address string */ 77 | uint16_t port; /* Port number */ 78 | uint64_t nonce_local; /* Our nonce (for self-connection detection) */ 79 | uint64_t nonce_remote; /* Peer's nonce */ 80 | 81 | /* Protocol version information */ 82 | int32_t version; /* Peer's protocol version */ 83 | uint64_t services; /* Peer's service flags */ 84 | int32_t start_height; /* Peer's blockchain height */ 85 | char user_agent[MAX_USER_AGENT_LEN]; /* Peer's user agent string */ 86 | size_t user_agent_len; /* Actual length of user_agent */ 87 | echo_bool_t relay; /* Whether peer wants tx relay */ 88 | 89 | /* Connection timing */ 90 | uint64_t connect_time; /* When connection established (plat_time_ms) */ 91 | uint64_t last_send; /* Last successful send time */ 92 | uint64_t last_recv; /* Last successful receive time */ 93 | 94 | /* Send queue */ 95 | peer_msg_queue_entry_t send_queue[PEER_SEND_QUEUE_SIZE]; 96 | size_t send_queue_head; /* Next message to send */ 97 | size_t send_queue_tail; /* Where to add new messages */ 98 | size_t send_queue_count; /* Number of queued messages */ 99 | 100 | /* Receive buffer */ 101 | uint8_t recv_buffer[PEER_RECV_BUFFER_SIZE]; 102 | size_t recv_buffer_len; /* Bytes currently in buffer */ 103 | 104 | /* Disconnection info */ 105 | peer_disconnect_reason_t disconnect_reason; 106 | char disconnect_message[256]; /* Human-readable disconnect reason */ 107 | 108 | /* Statistics */ 109 | uint64_t bytes_sent; 110 | uint64_t bytes_recv; 111 | uint64_t messages_sent; 112 | uint64_t messages_recv; 113 | } peer_t; 114 | 115 | /** 116 | * Initialize peer structure. 117 | * 118 | * Must be called before using a peer_t. 119 | */ 120 | void peer_init(peer_t *peer); 121 | 122 | /** 123 | * Create outbound connection to remote peer. 124 | * 125 | * Establishes TCP connection and transitions peer to PEER_STATE_CONNECTED. 126 | * Does NOT send version message — call peer_send_version() after this. 127 | * 128 | * Parameters: 129 | * peer - Peer structure to initialize 130 | * address - IP address to connect to 131 | * port - Port number 132 | * nonce - Random nonce for self-connection detection 133 | * 134 | * Returns: 135 | * ECHO_SUCCESS on successful connection 136 | * ECHO_ERR_NETWORK on connection failure 137 | */ 138 | echo_result_t peer_connect(peer_t *peer, const char *address, uint16_t port, 139 | uint64_t nonce); 140 | 141 | /** 142 | * Accept inbound connection from listening socket. 143 | * 144 | * Accepts connection and transitions peer to PEER_STATE_CONNECTED. 145 | * Does NOT send version message — call peer_send_version() after this. 146 | * 147 | * Parameters: 148 | * peer - Peer structure to initialize 149 | * listener - Listening socket 150 | * nonce - Random nonce for self-connection detection 151 | * 152 | * Returns: 153 | * ECHO_SUCCESS on successful accept 154 | * ECHO_ERR_NETWORK on failure 155 | */ 156 | echo_result_t peer_accept(peer_t *peer, plat_socket_t *listener, 157 | uint64_t nonce); 158 | 159 | /** 160 | * Send version message to peer. 161 | * 162 | * Initiates handshake. Peer must be in PEER_STATE_CONNECTED. 163 | * Transitions peer to PEER_STATE_HANDSHAKE_SENT. 164 | * 165 | * Parameters: 166 | * peer - Peer to send version to 167 | * our_services - Our service flags 168 | * our_height - Our blockchain height 169 | * relay - Whether we want transaction relay 170 | * 171 | * Returns: 172 | * ECHO_SUCCESS on successful send 173 | * ECHO_ERR_NETWORK on send failure 174 | * ECHO_ERR_INVALID_STATE if peer not in correct state 175 | */ 176 | echo_result_t peer_send_version(peer_t *peer, uint64_t our_services, 177 | int32_t our_height, echo_bool_t relay); 178 | 179 | /** 180 | * Process incoming data from peer. 181 | * 182 | * Reads from socket, parses messages, handles handshake. 183 | * Should be called when socket is ready for reading. 184 | * 185 | * Parameters: 186 | * peer - Peer to receive from 187 | * msg - Output: received message (if any) 188 | * 189 | * Returns: 190 | * ECHO_SUCCESS if message received (*msg is valid) 191 | * ECHO_ERR_WOULD_BLOCK if no complete message available (not an error) 192 | * ECHO_ERR_NETWORK on network error (peer should be disconnected) 193 | * ECHO_ERR_PROTOCOL on protocol violation (peer should be disconnected) 194 | */ 195 | echo_result_t peer_receive(peer_t *peer, msg_t *msg); 196 | 197 | /** 198 | * Queue message for sending to peer. 199 | * 200 | * Adds message to send queue. Actual sending happens in peer_send_queued(). 201 | * 202 | * Parameters: 203 | * peer - Peer to send to 204 | * msg - Message to queue (will be copied) 205 | * 206 | * Returns: 207 | * ECHO_SUCCESS if queued 208 | * ECHO_ERR_FULL if send queue is full 209 | * ECHO_ERR_INVALID_STATE if peer not ready 210 | */ 211 | echo_result_t peer_queue_message(peer_t *peer, const msg_t *msg); 212 | 213 | /** 214 | * Send queued messages to peer. 215 | * 216 | * Attempts to send messages from queue. 217 | * Should be called when socket is ready for writing. 218 | * 219 | * Parameters: 220 | * peer - Peer to send to 221 | * 222 | * Returns: 223 | * ECHO_SUCCESS if messages sent (or queue empty) 224 | * ECHO_ERR_NETWORK on network error (peer should be disconnected) 225 | */ 226 | echo_result_t peer_send_queued(peer_t *peer); 227 | 228 | /** 229 | * Disconnect peer. 230 | * 231 | * Closes connection and transitions to PEER_STATE_DISCONNECTED. 232 | * 233 | * Parameters: 234 | * peer - Peer to disconnect 235 | * reason - Disconnect reason code 236 | * message - Human-readable reason (optional, can be NULL) 237 | */ 238 | void peer_disconnect(peer_t *peer, peer_disconnect_reason_t reason, 239 | const char *message); 240 | 241 | /** 242 | * Check if peer handshake is complete. 243 | * 244 | * Returns: 245 | * true if peer is in PEER_STATE_READY 246 | */ 247 | echo_bool_t peer_is_ready(const peer_t *peer); 248 | 249 | /** 250 | * Check if peer is connected (in any state except DISCONNECTED). 251 | * 252 | * Returns: 253 | * true if peer has active connection 254 | */ 255 | echo_bool_t peer_is_connected(const peer_t *peer); 256 | 257 | /** 258 | * Get human-readable string for peer state. 259 | * 260 | * Returns: 261 | * String representation of state (e.g., "READY") 262 | */ 263 | const char *peer_state_string(peer_state_t state); 264 | 265 | /** 266 | * Get human-readable string for disconnect reason. 267 | * 268 | * Returns: 269 | * String representation of reason (e.g., "PROTOCOL_ERROR") 270 | */ 271 | const char *peer_disconnect_reason_string(peer_disconnect_reason_t reason); 272 | 273 | #endif /* ECHO_PEER_H */ 274 | -------------------------------------------------------------------------------- /include/utxo_db.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — UTXO Database Interface 3 | * 4 | * Persistent storage for the Unspent Transaction Output (UTXO) set using 5 | * SQLite. Implements the schema specified in the whitepaper §6.2. 6 | * 7 | * The UTXO database stores all spendable outputs and supports: 8 | * - Fast lookup by outpoint (txid + vout) 9 | * - Batch insertion of new outputs 10 | * - Batch deletion of spent outputs 11 | * - Atomic updates within SQLite transactions 12 | * 13 | * All database changes for a single block occur within a single transaction, 14 | * guaranteeing consistency even if the process is terminated mid-operation. 15 | * 16 | * Schema (per whitepaper §6.2): 17 | * CREATE TABLE utxo ( 18 | * outpoint BLOB PRIMARY KEY, -- 36 bytes: txid (32) + vout (4) 19 | * value INTEGER NOT NULL, -- satoshis 20 | * script BLOB NOT NULL, -- scriptPubKey 21 | * height INTEGER NOT NULL, -- block height when created 22 | * coinbase INTEGER NOT NULL -- 1 if from coinbase, else 0 23 | * ); 24 | * 25 | * Build once. Build right. Stop. 26 | */ 27 | 28 | #ifndef ECHO_UTXO_DB_H 29 | #define ECHO_UTXO_DB_H 30 | 31 | #include "db.h" 32 | #include "echo_types.h" 33 | #include "tx.h" 34 | #include "utxo.h" 35 | #include 36 | #include 37 | #include 38 | 39 | /* 40 | * UTXO database handle. 41 | * Wraps a SQLite database configured for UTXO storage. 42 | */ 43 | typedef struct { 44 | db_t db; /* Underlying database handle */ 45 | db_stmt_t lookup_stmt; /* Prepared statement for lookups */ 46 | db_stmt_t insert_stmt; /* Prepared statement for inserts */ 47 | db_stmt_t delete_stmt; /* Prepared statement for deletes */ 48 | bool stmts_prepared; /* Whether statements are prepared */ 49 | } utxo_db_t; 50 | 51 | /* ======================================================================== 52 | * Database Lifecycle 53 | * ======================================================================== */ 54 | 55 | /** 56 | * Open or create a UTXO database. 57 | * 58 | * Parameters: 59 | * udb - UTXO database handle to initialize 60 | * path - Path to database file (will be created if doesn't exist) 61 | * 62 | * Returns: 63 | * ECHO_OK on success, error code on failure 64 | * 65 | * Notes: 66 | * - Creates the database file and schema if needed 67 | * - Prepares commonly-used statements for efficiency 68 | * - Enables WAL mode via the underlying db interface 69 | */ 70 | echo_result_t utxo_db_open(utxo_db_t *udb, const char *path); 71 | 72 | /** 73 | * Close a UTXO database. 74 | * 75 | * Parameters: 76 | * udb - UTXO database handle to close 77 | * 78 | * Notes: 79 | * - Finalizes all prepared statements 80 | * - Closes the underlying database 81 | * - Safe to call on already-closed database 82 | */ 83 | void utxo_db_close(utxo_db_t *udb); 84 | 85 | /* ======================================================================== 86 | * UTXO Operations 87 | * ======================================================================== */ 88 | 89 | /** 90 | * Lookup a UTXO by outpoint. 91 | * 92 | * Parameters: 93 | * udb - UTXO database handle 94 | * outpoint - The outpoint to lookup 95 | * entry - Output: UTXO entry (caller must free with utxo_entry_destroy) 96 | * 97 | * Returns: 98 | * ECHO_OK if found, ECHO_ERR_NOT_FOUND if not found, error code on failure 99 | * 100 | * Notes: 101 | * - On success, allocates and populates entry 102 | * - Caller is responsible for freeing the entry 103 | */ 104 | echo_result_t utxo_db_lookup(utxo_db_t *udb, const outpoint_t *outpoint, 105 | utxo_entry_t **entry); 106 | 107 | /** 108 | * Check if a UTXO exists in the database. 109 | * 110 | * Parameters: 111 | * udb - UTXO database handle 112 | * outpoint - The outpoint to check 113 | * exists - Output: true if exists, false otherwise 114 | * 115 | * Returns: 116 | * ECHO_OK on success, error code on failure 117 | */ 118 | echo_result_t utxo_db_exists(utxo_db_t *udb, const outpoint_t *outpoint, 119 | bool *exists); 120 | 121 | /** 122 | * Insert a single UTXO into the database. 123 | * 124 | * Parameters: 125 | * udb - UTXO database handle 126 | * entry - The UTXO entry to insert 127 | * 128 | * Returns: 129 | * ECHO_OK on success, error code on failure 130 | * 131 | * Notes: 132 | * - Should be called within a transaction for atomicity 133 | * - Will fail if outpoint already exists 134 | */ 135 | echo_result_t utxo_db_insert(utxo_db_t *udb, const utxo_entry_t *entry); 136 | 137 | /** 138 | * Delete a single UTXO from the database. 139 | * 140 | * Parameters: 141 | * udb - UTXO database handle 142 | * outpoint - The outpoint to delete 143 | * 144 | * Returns: 145 | * ECHO_OK on success, ECHO_ERR_NOT_FOUND if not found, error code on failure 146 | * 147 | * Notes: 148 | * - Should be called within a transaction for atomicity 149 | */ 150 | echo_result_t utxo_db_delete(utxo_db_t *udb, const outpoint_t *outpoint); 151 | 152 | /* ======================================================================== 153 | * Batch Operations 154 | * ======================================================================== */ 155 | 156 | /** 157 | * Insert multiple UTXOs in a batch. 158 | * 159 | * Parameters: 160 | * udb - UTXO database handle 161 | * entries - Array of UTXO entries to insert 162 | * count - Number of entries in array 163 | * 164 | * Returns: 165 | * ECHO_OK on success, error code on failure 166 | * 167 | * Notes: 168 | * - Should be called within a transaction for atomicity 169 | * - All insertions succeed or all fail (within transaction) 170 | */ 171 | echo_result_t utxo_db_insert_batch(utxo_db_t *udb, const utxo_entry_t **entries, 172 | size_t count); 173 | 174 | /** 175 | * Delete multiple UTXOs in a batch. 176 | * 177 | * Parameters: 178 | * udb - UTXO database handle 179 | * outpoints - Array of outpoints to delete 180 | * count - Number of outpoints in array 181 | * 182 | * Returns: 183 | * ECHO_OK on success, error code on failure 184 | * 185 | * Notes: 186 | * - Should be called within a transaction for atomicity 187 | * - All deletions succeed or all fail (within transaction) 188 | * - It's not an error if some outpoints don't exist 189 | */ 190 | echo_result_t utxo_db_delete_batch(utxo_db_t *udb, const outpoint_t *outpoints, 191 | size_t count); 192 | 193 | /* ======================================================================== 194 | * Block Application 195 | * ======================================================================== */ 196 | 197 | /** 198 | * Apply a block's changes to the UTXO set atomically. 199 | * This combines insertions (new outputs) and deletions (spent outputs) 200 | * in a single transaction. 201 | * 202 | * Parameters: 203 | * udb - UTXO database handle 204 | * new_utxos - Array of new UTXO entries to insert 205 | * new_count - Number of new UTXOs 206 | * spent_utxos - Array of outpoints being spent 207 | * spent_count - Number of spent UTXOs 208 | * 209 | * Returns: 210 | * ECHO_OK on success, error code on failure 211 | * 212 | * Notes: 213 | * - Wraps all changes in a single transaction 214 | * - Either all changes succeed or none do 215 | * - This is the primary interface for updating UTXO state 216 | */ 217 | echo_result_t utxo_db_apply_block(utxo_db_t *udb, 218 | const utxo_entry_t **new_utxos, 219 | size_t new_count, 220 | const outpoint_t *spent_utxos, 221 | size_t spent_count); 222 | 223 | /* ======================================================================== 224 | * Statistics and Queries 225 | * ======================================================================== */ 226 | 227 | /** 228 | * Get the total number of UTXOs in the database. 229 | * 230 | * Parameters: 231 | * udb - UTXO database handle 232 | * count - Output: number of UTXOs 233 | * 234 | * Returns: 235 | * ECHO_OK on success, error code on failure 236 | */ 237 | echo_result_t utxo_db_count(utxo_db_t *udb, size_t *count); 238 | 239 | /** 240 | * Get the total value of all UTXOs in the database. 241 | * 242 | * Parameters: 243 | * udb - UTXO database handle 244 | * total - Output: total value in satoshis 245 | * 246 | * Returns: 247 | * ECHO_OK on success, error code on failure 248 | * 249 | * Notes: 250 | * - This is a full table scan and may be slow 251 | * - Used primarily for testing and validation 252 | */ 253 | echo_result_t utxo_db_total_value(utxo_db_t *udb, int64_t *total); 254 | 255 | /* ======================================================================== 256 | * Iteration 257 | * ======================================================================== */ 258 | 259 | /** 260 | * Iterate over all UTXOs in the database. 261 | * 262 | * Parameters: 263 | * udb - UTXO database handle 264 | * callback - Function to call for each UTXO 265 | * user_data - User data to pass to callback 266 | * 267 | * Returns: 268 | * ECHO_OK on success, error code on failure 269 | * 270 | * Notes: 271 | * - Reads all UTXOs from database in unspecified order 272 | * - Callback can return false to stop iteration early 273 | * - Not recommended for large UTXO sets (full table scan) 274 | */ 275 | echo_result_t utxo_db_foreach(utxo_db_t *udb, utxo_iterator_fn callback, 276 | void *user_data); 277 | 278 | #endif /* ECHO_UTXO_DB_H */ 279 | -------------------------------------------------------------------------------- /src/storage/db.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Bitcoin Echo — Database Interface Implementation 3 | * 4 | * Thin wrapper around SQLite for persistent storage. 5 | * Provides transaction support and a clean interface for database operations. 6 | * 7 | * Build once. Build right. Stop. 8 | */ 9 | 10 | #include "db.h" 11 | #include "../../lib/sqlite/sqlite3.h" 12 | #include "echo_assert.h" 13 | #include "echo_types.h" 14 | #include 15 | #include 16 | #include 17 | 18 | /* ======================================================================== 19 | * Database Lifecycle 20 | * ======================================================================== */ 21 | 22 | echo_result_t db_open(db_t *db, const char *path) { 23 | ECHO_ASSERT(db != NULL); 24 | ECHO_ASSERT(path != NULL); 25 | 26 | /* Initialize structure */ 27 | memset(db, 0, sizeof(*db)); 28 | strncpy(db->path, path, sizeof(db->path) - 1); 29 | db->path[sizeof(db->path) - 1] = '\0'; 30 | 31 | /* Open or create database */ 32 | int rc = sqlite3_open(path, &db->handle); 33 | if (rc != SQLITE_OK) { 34 | db->handle = NULL; 35 | return ECHO_ERR_IO; 36 | } 37 | 38 | /* Enable WAL mode for better concurrency and crash resistance */ 39 | rc = sqlite3_exec(db->handle, "PRAGMA journal_mode=WAL", NULL, NULL, NULL); 40 | if (rc != SQLITE_OK) { 41 | sqlite3_close(db->handle); 42 | db->handle = NULL; 43 | return ECHO_ERR_IO; 44 | } 45 | 46 | /* Set synchronous to NORMAL (good balance of safety and performance) */ 47 | rc = sqlite3_exec(db->handle, "PRAGMA synchronous=NORMAL", NULL, NULL, NULL); 48 | if (rc != SQLITE_OK) { 49 | sqlite3_close(db->handle); 50 | db->handle = NULL; 51 | return ECHO_ERR_IO; 52 | } 53 | 54 | /* Enable foreign keys for referential integrity */ 55 | rc = sqlite3_exec(db->handle, "PRAGMA foreign_keys=ON", NULL, NULL, NULL); 56 | if (rc != SQLITE_OK) { 57 | sqlite3_close(db->handle); 58 | db->handle = NULL; 59 | return ECHO_ERR_IO; 60 | } 61 | 62 | return ECHO_OK; 63 | } 64 | 65 | void db_close(db_t *db) { 66 | if (!db || !db->handle) { 67 | return; 68 | } 69 | 70 | /* Commit any pending transaction */ 71 | if (db->in_transaction) { 72 | db_commit(db); 73 | } 74 | 75 | /* Close database */ 76 | sqlite3_close(db->handle); 77 | db->handle = NULL; 78 | } 79 | 80 | echo_result_t db_exec(db_t *db, const char *sql) { 81 | ECHO_ASSERT(db != NULL); 82 | ECHO_ASSERT(db->handle != NULL); 83 | ECHO_ASSERT(sql != NULL); 84 | 85 | char *errmsg = NULL; 86 | int rc = sqlite3_exec(db->handle, sql, NULL, NULL, &errmsg); 87 | 88 | if (rc != SQLITE_OK) { 89 | if (errmsg) { 90 | sqlite3_free(errmsg); 91 | } 92 | return ECHO_ERR_DB; 93 | } 94 | 95 | return ECHO_OK; 96 | } 97 | 98 | /* ======================================================================== 99 | * Transactions 100 | * ======================================================================== */ 101 | 102 | echo_result_t db_begin(db_t *db) { 103 | ECHO_ASSERT(db != NULL); 104 | ECHO_ASSERT(db->handle != NULL); 105 | 106 | if (db->in_transaction) { 107 | /* Already in transaction */ 108 | return ECHO_ERR_INVALID; 109 | } 110 | 111 | int rc = sqlite3_exec(db->handle, "BEGIN TRANSACTION", NULL, NULL, NULL); 112 | if (rc != SQLITE_OK) { 113 | return ECHO_ERR_DB; 114 | } 115 | 116 | db->in_transaction = 1; 117 | return ECHO_OK; 118 | } 119 | 120 | echo_result_t db_commit(db_t *db) { 121 | ECHO_ASSERT(db != NULL); 122 | ECHO_ASSERT(db->handle != NULL); 123 | 124 | if (!db->in_transaction) { 125 | /* No transaction active */ 126 | return ECHO_OK; 127 | } 128 | 129 | int rc = sqlite3_exec(db->handle, "COMMIT", NULL, NULL, NULL); 130 | if (rc != SQLITE_OK) { 131 | return ECHO_ERR_DB; 132 | } 133 | 134 | db->in_transaction = 0; 135 | return ECHO_OK; 136 | } 137 | 138 | echo_result_t db_rollback(db_t *db) { 139 | ECHO_ASSERT(db != NULL); 140 | ECHO_ASSERT(db->handle != NULL); 141 | 142 | if (!db->in_transaction) { 143 | /* No transaction active */ 144 | return ECHO_OK; 145 | } 146 | 147 | int rc = sqlite3_exec(db->handle, "ROLLBACK", NULL, NULL, NULL); 148 | if (rc != SQLITE_OK) { 149 | return ECHO_ERR_DB; 150 | } 151 | 152 | db->in_transaction = 0; 153 | return ECHO_OK; 154 | } 155 | 156 | /* ======================================================================== 157 | * Prepared Statements 158 | * ======================================================================== */ 159 | 160 | echo_result_t db_prepare(db_t *db, const char *sql, db_stmt_t *stmt) { 161 | ECHO_ASSERT(db != NULL); 162 | ECHO_ASSERT(db->handle != NULL); 163 | ECHO_ASSERT(sql != NULL); 164 | ECHO_ASSERT(stmt != NULL); 165 | 166 | memset(stmt, 0, sizeof(*stmt)); 167 | 168 | int rc = sqlite3_prepare_v2(db->handle, sql, -1, &stmt->stmt, NULL); 169 | if (rc != SQLITE_OK) { 170 | stmt->stmt = NULL; 171 | return ECHO_ERR_DB; 172 | } 173 | 174 | return ECHO_OK; 175 | } 176 | 177 | void db_stmt_finalize(db_stmt_t *stmt) { 178 | if (!stmt || !stmt->stmt) { 179 | return; 180 | } 181 | 182 | sqlite3_finalize(stmt->stmt); 183 | stmt->stmt = NULL; 184 | } 185 | 186 | echo_result_t db_stmt_reset(db_stmt_t *stmt) { 187 | ECHO_ASSERT(stmt != NULL); 188 | ECHO_ASSERT(stmt->stmt != NULL); 189 | 190 | int rc = sqlite3_reset(stmt->stmt); 191 | if (rc != SQLITE_OK) { 192 | return ECHO_ERR_DB; 193 | } 194 | 195 | /* Also clear bindings */ 196 | sqlite3_clear_bindings(stmt->stmt); 197 | 198 | return ECHO_OK; 199 | } 200 | 201 | /* ======================================================================== 202 | * Parameter Binding 203 | * ======================================================================== */ 204 | 205 | echo_result_t db_bind_int(db_stmt_t *stmt, int index, int value) { 206 | ECHO_ASSERT(stmt != NULL); 207 | ECHO_ASSERT(stmt->stmt != NULL); 208 | 209 | int rc = sqlite3_bind_int(stmt->stmt, index, value); 210 | if (rc != SQLITE_OK) { 211 | return ECHO_ERR_DB; 212 | } 213 | 214 | return ECHO_OK; 215 | } 216 | 217 | echo_result_t db_bind_int64(db_stmt_t *stmt, int index, int64_t value) { 218 | ECHO_ASSERT(stmt != NULL); 219 | ECHO_ASSERT(stmt->stmt != NULL); 220 | 221 | int rc = sqlite3_bind_int64(stmt->stmt, index, value); 222 | if (rc != SQLITE_OK) { 223 | return ECHO_ERR_DB; 224 | } 225 | 226 | return ECHO_OK; 227 | } 228 | 229 | echo_result_t db_bind_blob(db_stmt_t *stmt, int index, const void *data, 230 | size_t size) { 231 | ECHO_ASSERT(stmt != NULL); 232 | ECHO_ASSERT(stmt->stmt != NULL); 233 | 234 | /* SQLITE_TRANSIENT means SQLite will make a copy of the data */ 235 | int rc = 236 | sqlite3_bind_blob(stmt->stmt, index, data, (int)size, SQLITE_TRANSIENT); 237 | if (rc != SQLITE_OK) { 238 | return ECHO_ERR_DB; 239 | } 240 | 241 | return ECHO_OK; 242 | } 243 | 244 | echo_result_t db_bind_text(db_stmt_t *stmt, int index, const char *text) { 245 | ECHO_ASSERT(stmt != NULL); 246 | ECHO_ASSERT(stmt->stmt != NULL); 247 | 248 | /* SQLITE_TRANSIENT means SQLite will make a copy of the text */ 249 | int rc = sqlite3_bind_text(stmt->stmt, index, text, -1, SQLITE_TRANSIENT); 250 | if (rc != SQLITE_OK) { 251 | return ECHO_ERR_DB; 252 | } 253 | 254 | return ECHO_OK; 255 | } 256 | 257 | /* ======================================================================== 258 | * Statement Execution and Result Retrieval 259 | * ======================================================================== */ 260 | 261 | echo_result_t db_step(db_stmt_t *stmt) { 262 | ECHO_ASSERT(stmt != NULL); 263 | ECHO_ASSERT(stmt->stmt != NULL); 264 | 265 | int rc = sqlite3_step(stmt->stmt); 266 | 267 | if (rc == SQLITE_ROW) { 268 | return ECHO_OK; /* Row available */ 269 | } else if (rc == SQLITE_DONE) { 270 | return ECHO_DONE; /* No more rows */ 271 | } else { 272 | return ECHO_ERR_DB; /* Error */ 273 | } 274 | } 275 | 276 | int db_column_int(db_stmt_t *stmt, int index) { 277 | ECHO_ASSERT(stmt != NULL); 278 | ECHO_ASSERT(stmt->stmt != NULL); 279 | 280 | return sqlite3_column_int(stmt->stmt, index); 281 | } 282 | 283 | int64_t db_column_int64(db_stmt_t *stmt, int index) { 284 | ECHO_ASSERT(stmt != NULL); 285 | ECHO_ASSERT(stmt->stmt != NULL); 286 | 287 | return sqlite3_column_int64(stmt->stmt, index); 288 | } 289 | 290 | const void *db_column_blob(db_stmt_t *stmt, int index) { 291 | ECHO_ASSERT(stmt != NULL); 292 | ECHO_ASSERT(stmt->stmt != NULL); 293 | 294 | return sqlite3_column_blob(stmt->stmt, index); 295 | } 296 | 297 | const char *db_column_text(db_stmt_t *stmt, int index) { 298 | ECHO_ASSERT(stmt != NULL); 299 | ECHO_ASSERT(stmt->stmt != NULL); 300 | 301 | return (const char *)sqlite3_column_text(stmt->stmt, index); 302 | } 303 | 304 | int db_column_bytes(db_stmt_t *stmt, int index) { 305 | ECHO_ASSERT(stmt != NULL); 306 | ECHO_ASSERT(stmt->stmt != NULL); 307 | 308 | return sqlite3_column_bytes(stmt->stmt, index); 309 | } 310 | 311 | /* ======================================================================== 312 | * Utility Functions 313 | * ======================================================================== */ 314 | 315 | int64_t db_last_insert_rowid(db_t *db) { 316 | ECHO_ASSERT(db != NULL); 317 | ECHO_ASSERT(db->handle != NULL); 318 | 319 | return sqlite3_last_insert_rowid(db->handle); 320 | } 321 | 322 | int db_changes(db_t *db) { 323 | ECHO_ASSERT(db != NULL); 324 | ECHO_ASSERT(db->handle != NULL); 325 | 326 | return sqlite3_changes(db->handle); 327 | } 328 | 329 | const char *db_errmsg(db_t *db) { 330 | ECHO_ASSERT(db != NULL); 331 | ECHO_ASSERT(db->handle != NULL); 332 | 333 | return sqlite3_errmsg(db->handle); 334 | } 335 | -------------------------------------------------------------------------------- /include/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Bitcoin Echo — Minimal Logging System 3 | * 4 | * Implements a fixed-format, machine-parseable logging system for the 5 | * Bitcoin Echo node. Design priorities: 6 | * 7 | * - Machine-parseable: Fixed format enables automated analysis 8 | * - Minimal overhead: No dynamic allocation, minimal formatting cost 9 | * - Component-based: Log messages tagged by subsystem 10 | * - Level-filtered: Runtime log level control 11 | * 12 | * Log format: 13 | * YYYY-MM-DD HH:MM:SS.mmm [LEVEL] [COMPONENT] Message 14 | * 15 | * Example: 16 | * 2025-12-12 14:30:45.123 [INFO] [NET] Connected to 192.168.1.1:8333 17 | * 2025-12-12 14:30:45.456 [WARN] [CONS] Block 000000... has unusual timestamp 18 | * 19 | * Session 9.4: Logging system implementation. 20 | * 21 | * Build once. Build right. Stop. 22 | */ 23 | 24 | #ifndef ECHO_LOG_H 25 | #define ECHO_LOG_H 26 | 27 | #include 28 | 29 | /* 30 | * ============================================================================ 31 | * LOG LEVELS 32 | * ============================================================================ 33 | * 34 | * Log levels in order of severity. Lower values are more severe. 35 | * A configured level filters out all messages with lower severity. 36 | */ 37 | 38 | typedef enum { 39 | LOG_LEVEL_ERROR = 0, /* Unrecoverable errors requiring attention */ 40 | LOG_LEVEL_WARN = 1, /* Recoverable problems, potential issues */ 41 | LOG_LEVEL_INFO = 2, /* Normal operational messages */ 42 | LOG_LEVEL_DEBUG = 3 /* Detailed debugging information */ 43 | } log_level_t; 44 | 45 | /* 46 | * ============================================================================ 47 | * LOG COMPONENTS 48 | * ============================================================================ 49 | * 50 | * Component identifiers for log message categorization. 51 | * Each component represents a major subsystem of the node. 52 | */ 53 | 54 | typedef enum { 55 | LOG_COMP_MAIN = 0, /* Main/general */ 56 | LOG_COMP_NET, /* Networking (peer connections, sockets) */ 57 | LOG_COMP_P2P, /* P2P protocol (messages, handshake) */ 58 | LOG_COMP_CONS, /* Consensus engine (validation, chain) */ 59 | LOG_COMP_SYNC, /* Block synchronization (IBD, headers) */ 60 | LOG_COMP_POOL, /* Mempool (transaction acceptance) */ 61 | LOG_COMP_RPC, /* RPC interface */ 62 | LOG_COMP_DB, /* Database operations */ 63 | LOG_COMP_STORE, /* Block storage */ 64 | LOG_COMP_CRYPTO, /* Cryptographic operations */ 65 | LOG_COMP_COUNT /* Number of components (not a valid component) */ 66 | } log_component_t; 67 | 68 | /* 69 | * ============================================================================ 70 | * LOG CONFIGURATION 71 | * ============================================================================ 72 | */ 73 | 74 | /** 75 | * Initialize the logging system. 76 | * 77 | * Must be called before any logging functions. Sets default log level 78 | * to INFO and enables output to stderr. 79 | * 80 | * Thread-safe: Yes (uses internal mutex after init). 81 | */ 82 | void log_init(void); 83 | 84 | /** 85 | * Shutdown the logging system. 86 | * 87 | * Flushes any buffered output and releases resources. 88 | * No logging calls should be made after this. 89 | */ 90 | void log_shutdown(void); 91 | 92 | /** 93 | * Set the global log level. 94 | * 95 | * Messages with severity below this level will be discarded. 96 | * 97 | * Parameters: 98 | * level - New log level threshold 99 | * 100 | * Thread-safe: Yes. 101 | */ 102 | void log_set_level(log_level_t level); 103 | 104 | /** 105 | * Get the current log level. 106 | * 107 | * Returns: 108 | * Current log level threshold 109 | * 110 | * Thread-safe: Yes. 111 | */ 112 | log_level_t log_get_level(void); 113 | 114 | /** 115 | * Enable or disable a specific component. 116 | * 117 | * Disabled components produce no output regardless of level. 118 | * All components are enabled by default. 119 | * 120 | * Parameters: 121 | * comp - Component to configure 122 | * enabled - true to enable, false to disable 123 | * 124 | * Thread-safe: Yes. 125 | */ 126 | void log_set_component_enabled(log_component_t comp, bool enabled); 127 | 128 | /** 129 | * Check if a component is enabled. 130 | * 131 | * Parameters: 132 | * comp - Component to check 133 | * 134 | * Returns: 135 | * true if component is enabled 136 | * 137 | * Thread-safe: Yes. 138 | */ 139 | bool log_is_component_enabled(log_component_t comp); 140 | 141 | /** 142 | * Set log output file. 143 | * 144 | * By default, logs go to stderr. This function allows redirecting 145 | * to a file. Pass NULL to revert to stderr. 146 | * 147 | * Parameters: 148 | * path - Path to log file, or NULL for stderr 149 | * 150 | * Returns: 151 | * true on success, false if file cannot be opened 152 | * 153 | * Notes: 154 | * - File is opened in append mode 155 | * - Previous file is closed when switching 156 | * - Not thread-safe with concurrent log calls; call during init only 157 | */ 158 | bool log_set_output(const char *path); 159 | 160 | /* 161 | * ============================================================================ 162 | * LOGGING FUNCTIONS 163 | * ============================================================================ 164 | * 165 | * All logging functions are thread-safe and non-blocking (to the extent 166 | * possible given file I/O constraints). 167 | */ 168 | 169 | /** 170 | * Log a message at ERROR level. 171 | * 172 | * Use for unrecoverable errors that require immediate attention. 173 | * Examples: database corruption, critical assertion failures. 174 | * 175 | * Parameters: 176 | * comp - Component identifier 177 | * format - printf-style format string 178 | * ... - Format arguments 179 | */ 180 | void log_error(log_component_t comp, const char *format, ...) 181 | __attribute__((format(printf, 2, 3))); 182 | 183 | /** 184 | * Log a message at WARN level. 185 | * 186 | * Use for recoverable problems or potential issues. 187 | * Examples: peer misbehavior, unusual block timestamps. 188 | * 189 | * Parameters: 190 | * comp - Component identifier 191 | * format - printf-style format string 192 | * ... - Format arguments 193 | */ 194 | void log_warn(log_component_t comp, const char *format, ...) 195 | __attribute__((format(printf, 2, 3))); 196 | 197 | /** 198 | * Log a message at INFO level. 199 | * 200 | * Use for normal operational events. 201 | * Examples: peer connected, block received, sync progress. 202 | * 203 | * Parameters: 204 | * comp - Component identifier 205 | * format - printf-style format string 206 | * ... - Format arguments 207 | */ 208 | void log_info(log_component_t comp, const char *format, ...) 209 | __attribute__((format(printf, 2, 3))); 210 | 211 | /** 212 | * Log a message at DEBUG level. 213 | * 214 | * Use for detailed debugging information. 215 | * Examples: individual message parsing, state transitions. 216 | * 217 | * Parameters: 218 | * comp - Component identifier 219 | * format - printf-style format string 220 | * ... - Format arguments 221 | */ 222 | void log_debug(log_component_t comp, const char *format, ...) 223 | __attribute__((format(printf, 2, 3))); 224 | 225 | /** 226 | * Generic log function with explicit level. 227 | * 228 | * Use when the level is determined at runtime. 229 | * 230 | * Parameters: 231 | * level - Log level 232 | * comp - Component identifier 233 | * format - printf-style format string 234 | * ... - Format arguments 235 | */ 236 | void log_msg(log_level_t level, log_component_t comp, const char *format, ...) 237 | __attribute__((format(printf, 3, 4))); 238 | 239 | /* 240 | * ============================================================================ 241 | * HELPER FUNCTIONS 242 | * ============================================================================ 243 | */ 244 | 245 | /** 246 | * Get string name for a log level. 247 | * 248 | * Parameters: 249 | * level - Log level 250 | * 251 | * Returns: 252 | * Static string: "ERROR", "WARN", "INFO", or "DEBUG" 253 | */ 254 | const char *log_level_string(log_level_t level); 255 | 256 | /** 257 | * Get short string name for a component. 258 | * 259 | * Parameters: 260 | * comp - Component identifier 261 | * 262 | * Returns: 263 | * Static string (4 chars max): "MAIN", "NET", "P2P", etc. 264 | */ 265 | const char *log_component_string(log_component_t comp); 266 | 267 | /** 268 | * Check if a message at the given level would be logged. 269 | * 270 | * Useful to avoid expensive formatting when message won't be logged. 271 | * 272 | * Parameters: 273 | * level - Log level to check 274 | * comp - Component to check 275 | * 276 | * Returns: 277 | * true if a message would be logged 278 | */ 279 | bool log_would_log(log_level_t level, log_component_t comp); 280 | 281 | /* 282 | * ============================================================================ 283 | * CONVENIENCE MACROS 284 | * ============================================================================ 285 | * 286 | * These macros provide the component implicitly for common use cases. 287 | * Using LOG_COMPONENT before including this header customizes them. 288 | */ 289 | 290 | #ifndef LOG_COMPONENT 291 | #define LOG_COMPONENT LOG_COMP_MAIN 292 | #endif 293 | 294 | #define LOG_ERROR(...) log_error(LOG_COMPONENT, __VA_ARGS__) 295 | #define LOG_WARN(...) log_warn(LOG_COMPONENT, __VA_ARGS__) 296 | #define LOG_INFO(...) log_info(LOG_COMPONENT, __VA_ARGS__) 297 | #define LOG_DEBUG(...) log_debug(LOG_COMPONENT, __VA_ARGS__) 298 | 299 | /* 300 | * Conditional debug logging (can be compiled out for release builds). 301 | * Define ECHO_NO_DEBUG_LOGS to disable. 302 | */ 303 | #ifdef ECHO_NO_DEBUG_LOGS 304 | #define LOG_DEBUG_IF(cond, ...) ((void)0) 305 | #else 306 | #define LOG_DEBUG_IF(cond, ...) \ 307 | do { \ 308 | if (cond) \ 309 | log_debug(LOG_COMPONENT, __VA_ARGS__); \ 310 | } while (0) 311 | #endif 312 | 313 | #endif /* ECHO_LOG_H */ 314 | --------------------------------------------------------------------------------