#!/usr/bin/env bash set -euo pipefail # Dalanga Transparency Chain Verification Tool # # A standalone Bash script that verifies the integrity of a downloaded # Dalanga system chain export file. # # Requirements: jq, sha256sum (or shasum), bc # # Usage: # bash verify-chain.sh # # Examples: # bash verify-chain.sh system-chain-export.json # bash verify-chain.sh /tmp/chain-export.json # # What it verifies: # 1. Consecutive sequence numbers starting from 0 # 2. Hash linking: each block's previous_hash matches the preceding block's hash # 3. Hash recomputation: re-hashes each block's hash_input and compares to stored hash # 4. Genesis block validation: sequence 0 must have previous_hash = null # 5. Payload hash verification: re-hashes payload and compares to stored payload_hash # 6. Draw fairness: for draw.revealed events, re-derives the winner using the # algorithm_version from event_data and confirms the recorded winner matches # # Exit codes: # 0 = Chain is valid (all checks passed) # 1 = Chain is invalid (one or more checks failed) # 2 = Usage error (missing file, invalid JSON, etc.) # ANSI color codes GREEN="\033[32m" RED="\033[31m" RESET="\033[0m" BOLD="\033[1m" success() { echo -e "${GREEN}✓${RESET} $1"; } failure() { echo -e "${RED}✗${RESET} $1"; } # --- Dependency Checks ------------------------------------------------------- check_deps() { local missing=() if ! command -v jq &>/dev/null; then missing+=("jq") fi if ! command -v sha256sum &>/dev/null && ! command -v shasum &>/dev/null; then missing+=("sha256sum or shasum") fi if ! command -v bc &>/dev/null; then missing+=("bc") fi if [[ ${#missing[@]} -gt 0 ]]; then echo "Missing required dependencies: ${missing[*]}" echo "" echo "Install instructions:" echo " macOS (Homebrew): brew install ${missing[*]}" echo " Ubuntu/Debian: sudo apt-get install ${missing[*]}" echo " RHEL/Fedora: sudo yum install ${missing[*]}" exit 2 fi } check_deps # SHA-256 wrapper (Linux vs macOS) sha256_hash() { if command -v sha256sum &>/dev/null; then printf '%s' "$1" | sha256sum | cut -d' ' -f1 else printf '%s' "$1" | shasum -a 256 | cut -d' ' -f1 fi } # --- Input Validation -------------------------------------------------------- if [[ $# -lt 1 ]]; then echo "Dalanga Transparency Chain Verification Tool" echo "" echo "Usage: bash $(basename "$0") " echo "" echo "Verifies the cryptographic integrity of a chain export file." echo "For draw events, re-derives the winner to confirm fairness." exit 2 fi FILE="$1" if [[ ! -f "$FILE" ]]; then failure "File not found: $FILE" exit 2 fi if ! jq empty "$FILE" 2>/dev/null; then failure "Invalid JSON in file: $FILE" exit 2 fi if ! jq -e '.blocks | type == "array"' "$FILE" >/dev/null 2>&1; then failure 'Invalid export format: missing "blocks" array' exit 2 fi # --- Chain Verification ------------------------------------------------------ BLOCK_COUNT=$(jq '.blocks | length' "$FILE") ERRORS=0 echo -e "${BOLD}Dalanga Transparency Chain Verifier${RESET}" CHAIN=$(jq -r '.chain // empty' "$FILE") [[ -n "$CHAIN" ]] && echo "Chain: $CHAIN" EXPORTED_AT=$(jq -r '.exported_at // empty' "$FILE") [[ -n "$EXPORTED_AT" ]] && echo "Exported: $EXPORTED_AT" echo "Blocks: $BLOCK_COUNT" echo "" if [[ "$BLOCK_COUNT" -eq 0 ]]; then success "Chain is empty (0 blocks)" exit 0 fi # Chain integrity checks PREV_HASH="" for (( i=0; i/dev/null | cut -d' ' -f1 || printf '%s' "$SEED" | shasum -a 256 | cut -d' ' -f1) HEX_PREFIX="${DRAW_HASH:0:12}" HEX_UPPER=$(echo "$HEX_PREFIX" | tr 'a-f' 'A-F') WINNER_INDEX=$(echo "ibase=16; $HEX_UPPER % $TOTAL_ELIGIBLE" | bc) WINNER_TICKET=$(echo "$ELIGIBLE_TICKETS" | jq -r ".[$WINNER_INDEX]") if [[ "$WINNER_INDEX" != "$RECORDED_WINNER_INDEX" ]]; then failure "Block $BLOCK_SEQ: draw winner_index mismatch (expected $WINNER_INDEX, got $RECORDED_WINNER_INDEX)" ERRORS=$((ERRORS + 1)) elif [[ "$WINNER_TICKET" != "$RECORDED_WINNER_TICKET" ]]; then failure "Block $BLOCK_SEQ: draw winner_ticket_number mismatch (expected $WINNER_TICKET, got $RECORDED_WINNER_TICKET)" ERRORS=$((ERRORS + 1)) else success "Block $BLOCK_SEQ: draw winner independently verified (sha256-modulo-v1)" fi # Verify commitment hash COMMITTED_SEQ=$(echo "$COMMITTED" | jq -r '.sequence') COMMITMENT_HASH=$(echo "$COMMITTED" | jq -r '.event_data.commitment_hash // empty') if [[ -n "$COMMITMENT_HASH" ]]; then COMMITMENT_INPUT="${SEED}${ELIGIBLE_TICKETS}" RECOMPUTED_COMMITMENT=$(sha256_hash "$COMMITMENT_INPUT") if [[ "$COMMITMENT_HASH" != "$RECOMPUTED_COMMITMENT" ]]; then failure "Block $COMMITTED_SEQ: commitment hash mismatch" ERRORS=$((ERRORS + 1)) else success "Block $COMMITTED_SEQ: commitment hash verified" fi fi done # --- Summary ----------------------------------------------------------------- echo "" if [[ "$ERRORS" -eq 0 ]]; then echo -e "${GREEN}Chain valid: $BLOCK_COUNT blocks verified, 0 errors${RESET}" exit 0 fi echo -e "${RED}Chain INVALID: $BLOCK_COUNT blocks verified, $ERRORS error(s)${RESET}" exit 1